@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.cjs CHANGED
@@ -40,6 +40,14 @@ function makeDgmoError(line11, message, severity = "error", code) {
40
40
  function formatDgmoError(err) {
41
41
  return err.line > 0 ? `Line ${err.line}: ${err.message}` : err.message;
42
42
  }
43
+ function makeFail(result) {
44
+ return (line11, message) => {
45
+ const diag = makeDgmoError(line11, message);
46
+ result.diagnostics.push(diag);
47
+ result.error = formatDgmoError(diag);
48
+ return result;
49
+ };
50
+ }
43
51
  function levenshtein(a, b) {
44
52
  const m = a.length;
45
53
  const n = b.length;
@@ -306,21 +314,99 @@ function resolveColor(color, palette) {
306
314
  }
307
315
  return colorNames[lower] ?? null;
308
316
  }
317
+ function nearestNamedColor(input) {
318
+ const cssHex = INVALID_CSS_COLOR_HEX[input.trim().toLowerCase()];
319
+ if (cssHex) input = cssHex;
320
+ const m = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(input.trim());
321
+ if (!m) return null;
322
+ let h = m[1].toLowerCase();
323
+ if (h.length === 3)
324
+ h = h.split("").map((c) => c + c).join("");
325
+ const r = parseInt(h.slice(0, 2), 16) / 255;
326
+ const g = parseInt(h.slice(2, 4), 16) / 255;
327
+ const b = parseInt(h.slice(4, 6), 16) / 255;
328
+ const max = Math.max(r, g, b);
329
+ const min = Math.min(r, g, b);
330
+ const delta = max - min;
331
+ const l = (max + min) / 2;
332
+ const s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
333
+ if (s < 0.15) {
334
+ if (l < 0.2) return "black";
335
+ if (l > 0.85) return "white";
336
+ return "gray";
337
+ }
338
+ let hue;
339
+ if (max === r) hue = 60 * ((g - b) / delta % 6);
340
+ else if (max === g) hue = 60 * ((b - r) / delta + 2);
341
+ else hue = 60 * ((r - g) / delta + 4);
342
+ if (hue < 0) hue += 360;
343
+ const anchors = [
344
+ ["red", 0],
345
+ ["orange", 30],
346
+ ["yellow", 55],
347
+ ["green", 120],
348
+ ["teal", 170],
349
+ ["cyan", 190],
350
+ ["blue", 225],
351
+ ["purple", 285],
352
+ ["red", 360]
353
+ ];
354
+ let best = "red";
355
+ let bestD = Infinity;
356
+ for (const [name, deg] of anchors) {
357
+ const d = Math.abs(hue - deg);
358
+ if (d < bestD) {
359
+ bestD = d;
360
+ best = name;
361
+ }
362
+ }
363
+ return best;
364
+ }
365
+ function isInvalidColorToken(token) {
366
+ if (/^(#|rgba?\(|hsla?\()/i.test(token)) return true;
367
+ const lower = token.toLowerCase();
368
+ return INVALID_CSS_COLOR_HEX[lower] !== void 0 && !isRecognizedColorName(lower);
369
+ }
370
+ function invalidColorDiagnostic(token, line11) {
371
+ if (!isInvalidColorToken(token)) return null;
372
+ const nearest = nearestNamedColor(token);
373
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
374
+ return makeDgmoError(
375
+ line11,
376
+ `Color "${token}" is not a valid DGMO color \u2014 DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${near}`,
377
+ "warning",
378
+ INVALID_COLOR_CODE
379
+ );
380
+ }
309
381
  function resolveColorWithDiagnostic(color, line11, diagnostics, palette) {
310
382
  const resolved = resolveColor(color, palette);
311
383
  if (resolved !== null) return resolved;
384
+ if (/^(#|rgba?\(|hsla?\()/i.test(color)) {
385
+ const nearest = nearestNamedColor(color);
386
+ const near = nearest ? ` Nearest: ${nearest}.` : "";
387
+ diagnostics.push(
388
+ makeDgmoError(
389
+ line11,
390
+ `Color "${color}" is not supported \u2014 DGMO does not accept hex or CSS color values. Use a named palette color: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${near}`,
391
+ "error",
392
+ INVALID_COLOR_CODE
393
+ )
394
+ );
395
+ return void 0;
396
+ }
312
397
  const hint = suggest(color, RECOGNIZED_COLOR_NAMES);
313
398
  const suggestion = hint ? ` ${hint}` : "";
314
399
  diagnostics.push(
315
400
  makeDgmoError(
316
401
  line11,
317
- `Unknown color "${color}". Allowed: ${RECOGNIZED_COLOR_NAMES.join(", ")}.${suggestion}`,
318
- "warning"
402
+ `Unknown color "${color}". DGMO accepts only these 11 named colors: ${RECOGNIZED_COLOR_NAMES.join(", ")} (no hex, no CSS color names).${suggestion}`,
403
+ "warning",
404
+ INVALID_COLOR_CODE
319
405
  )
320
406
  );
321
407
  return void 0;
322
408
  }
323
- var nord, colorNames, RECOGNIZED_COLOR_NAMES, seriesColors;
409
+ var nord, colorNames, RECOGNIZED_COLOR_NAMES, CATEGORICAL_COLOR_ORDER, INVALID_COLOR_CODE, INVALID_CSS_COLOR_HEX, seriesColors;
324
410
  var init_colors = __esm({
325
411
  "src/colors.ts"() {
326
412
  "use strict";
@@ -378,6 +464,102 @@ var init_colors = __esm({
378
464
  "black",
379
465
  "white"
380
466
  ]);
467
+ CATEGORICAL_COLOR_ORDER = Object.freeze([
468
+ "red",
469
+ "green",
470
+ "blue",
471
+ "yellow",
472
+ "teal",
473
+ "purple",
474
+ "orange",
475
+ "cyan"
476
+ ]);
477
+ INVALID_COLOR_CODE = "E_INVALID_COLOR";
478
+ INVALID_CSS_COLOR_HEX = Object.freeze({
479
+ pink: "#ffc0cb",
480
+ hotpink: "#ff69b4",
481
+ deeppink: "#ff1493",
482
+ lightpink: "#ffb6c1",
483
+ palevioletred: "#db7093",
484
+ crimson: "#dc143c",
485
+ scarlet: "#ff2400",
486
+ firebrick: "#b22222",
487
+ darkred: "#8b0000",
488
+ maroon: "#800000",
489
+ salmon: "#fa8072",
490
+ lightsalmon: "#ffa07a",
491
+ darksalmon: "#e9967a",
492
+ coral: "#ff7f50",
493
+ lightcoral: "#f08080",
494
+ tomato: "#ff6347",
495
+ orangered: "#ff4500",
496
+ darkorange: "#ff8c00",
497
+ gold: "#ffd700",
498
+ goldenrod: "#daa520",
499
+ darkgoldenrod: "#b8860b",
500
+ khaki: "#f0e68c",
501
+ darkkhaki: "#bdb76b",
502
+ amber: "#ffbf00",
503
+ lavender: "#e6e6fa",
504
+ violet: "#ee82ee",
505
+ magenta: "#ff00ff",
506
+ fuchsia: "#ff00ff",
507
+ orchid: "#da70d6",
508
+ plum: "#dda0dd",
509
+ indigo: "#4b0082",
510
+ navy: "#000080",
511
+ midnightblue: "#191970",
512
+ darkblue: "#00008b",
513
+ mediumblue: "#0000cd",
514
+ royalblue: "#4169e1",
515
+ cornflowerblue: "#6495ed",
516
+ dodgerblue: "#1e90ff",
517
+ deepskyblue: "#00bfff",
518
+ skyblue: "#87ceeb",
519
+ lightskyblue: "#87cefa",
520
+ lightblue: "#add8e6",
521
+ powderblue: "#b0e0e6",
522
+ steelblue: "#4682b4",
523
+ slateblue: "#6a5acd",
524
+ cadetblue: "#5f9ea0",
525
+ turquoise: "#40e0d0",
526
+ aqua: "#00ffff",
527
+ aquamarine: "#7fffd4",
528
+ lime: "#00ff00",
529
+ limegreen: "#32cd32",
530
+ lightgreen: "#90ee90",
531
+ palegreen: "#98fb98",
532
+ seagreen: "#2e8b57",
533
+ mediumseagreen: "#3cb371",
534
+ forestgreen: "#228b22",
535
+ darkgreen: "#006400",
536
+ olive: "#808000",
537
+ olivedrab: "#6b8e23",
538
+ darkolivegreen: "#556b2f",
539
+ chartreuse: "#7fff00",
540
+ lawngreen: "#7cfc00",
541
+ springgreen: "#00ff7f",
542
+ greenyellow: "#adff2f",
543
+ brown: "#a52a2a",
544
+ sienna: "#a0522d",
545
+ chocolate: "#d2691e",
546
+ peru: "#cd853f",
547
+ tan: "#d2b48c",
548
+ beige: "#f5f5dc",
549
+ wheat: "#f5deb3",
550
+ ivory: "#fffff0",
551
+ silver: "#c0c0c0",
552
+ lightgray: "#d3d3d3",
553
+ lightgrey: "#d3d3d3",
554
+ darkgray: "#a9a9a9",
555
+ darkgrey: "#a9a9a9",
556
+ dimgray: "#696969",
557
+ dimgrey: "#696969",
558
+ slategray: "#708090",
559
+ slategrey: "#708090",
560
+ gainsboro: "#dcdcdc",
561
+ grey: "#808080"
562
+ });
381
563
  seriesColors = [
382
564
  nord.nord10,
383
565
  // blue
@@ -552,7 +734,13 @@ function extractColor(label, palette, diagnostics, line11) {
552
734
  );
553
735
  if (lastSpaceIdx < 0) return { label };
554
736
  const trailing = label.substring(lastSpaceIdx + 1);
555
- if (!RECOGNIZED_COLOR_SET.has(trailing)) return { label };
737
+ if (!RECOGNIZED_COLOR_SET.has(trailing)) {
738
+ if (diagnostics && line11 !== void 0) {
739
+ const diag = invalidColorDiagnostic(trailing, line11);
740
+ if (diag) diagnostics.push(diag);
741
+ }
742
+ return { label };
743
+ }
556
744
  let color;
557
745
  if (diagnostics && line11 !== void 0) {
558
746
  color = resolveColorWithDiagnostic(trailing, line11, diagnostics, palette);
@@ -1176,6 +1364,23 @@ function validateTagGroupNames(tagGroups, pushWarning, pushError) {
1176
1364
  }
1177
1365
  }
1178
1366
  }
1367
+ function cascadeTagMetadata(roots, tagGroups) {
1368
+ const keys = tagGroups.map((g) => g.name.toLowerCase());
1369
+ if (keys.length === 0) return;
1370
+ const walk = (node, inherited) => {
1371
+ const childInherited = { ...inherited };
1372
+ for (const key of keys) {
1373
+ const own = node.metadata[key];
1374
+ if (own) {
1375
+ childInherited[key] = own;
1376
+ } else if (inherited[key]) {
1377
+ node.metadata[key] = inherited[key];
1378
+ }
1379
+ }
1380
+ for (const child of node.children) walk(child, childInherited);
1381
+ };
1382
+ for (const root of roots) walk(root, {});
1383
+ }
1179
1384
  function injectDefaultTagMetadata(entities, tagGroups, skip) {
1180
1385
  const defaults = [];
1181
1386
  for (const group of tagGroups) {
@@ -1231,11 +1436,7 @@ var init_tag_groups = __esm({
1231
1436
  init_diagnostics();
1232
1437
  init_colors();
1233
1438
  AUTO_TAG_COLOR_SENTINEL = "";
1234
- autoTagColorCycle = Object.freeze(
1235
- RECOGNIZED_COLOR_NAMES.filter(
1236
- (n) => n !== "gray" && n !== "black" && n !== "white"
1237
- )
1238
- );
1439
+ autoTagColorCycle = CATEGORICAL_COLOR_ORDER;
1239
1440
  TAG_BLOCK_NOCOLON_RE = /^tag\s+/i;
1240
1441
  VALID_TAG_IDENT_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
1241
1442
  }
@@ -1718,7 +1919,12 @@ function parseVisualizationFull(content, palette) {
1718
1919
  }
1719
1920
  if (currentTimelineTagGroup && indent > 0) {
1720
1921
  const { text: entryText, isDefault } = stripDefaultModifier(line11);
1721
- const { label, color } = extractColor(entryText, palette);
1922
+ const { label, color } = extractColor(
1923
+ entryText,
1924
+ palette,
1925
+ result.diagnostics,
1926
+ lineNumber
1927
+ );
1722
1928
  if (color) {
1723
1929
  if (isDefault) {
1724
1930
  currentTimelineTagGroup.defaultValue = label;
@@ -3019,13 +3225,16 @@ function contrastText(bg, lightText, darkText) {
3019
3225
  }
3020
3226
  return lightText;
3021
3227
  }
3228
+ function themeBaseBg(palette, isDark) {
3229
+ return isDark ? palette.surface : palette.bg;
3230
+ }
3022
3231
  function shapeFill(palette, intent, isDark, opts) {
3023
3232
  if (opts?.solid) return intent;
3024
- return mix(intent, isDark ? palette.surface : palette.bg, 25);
3233
+ return mix(intent, themeBaseBg(palette, isDark), 25);
3025
3234
  }
3026
3235
  function getSeriesColors(palette) {
3027
3236
  const c = palette.colors;
3028
- return [c.blue, c.green, c.yellow, c.orange, c.purple, c.red, c.teal, c.cyan];
3237
+ return CATEGORICAL_COLOR_ORDER.map((name) => c[name]);
3029
3238
  }
3030
3239
  function getSegmentColors(palette, count) {
3031
3240
  if (count <= 0) return [];
@@ -3052,7 +3261,7 @@ function getSegmentColors(palette, count) {
3052
3261
  }
3053
3262
  function politicalTints(palette, count, isDark) {
3054
3263
  if (count <= 0) return [];
3055
- const base = isDark ? palette.surface : palette.bg;
3264
+ const base = themeBaseBg(palette, isDark);
3056
3265
  const c = palette.colors;
3057
3266
  const swatches = [
3058
3267
  .../* @__PURE__ */ new Set([
@@ -3078,6 +3287,7 @@ var POLITICAL_TINT_BANDS;
3078
3287
  var init_color_utils = __esm({
3079
3288
  "src/palettes/color-utils.ts"() {
3080
3289
  "use strict";
3290
+ init_colors();
3081
3291
  POLITICAL_TINT_BANDS = {
3082
3292
  light: [32, 48, 64, 80],
3083
3293
  dark: [44, 58, 72, 86]
@@ -3861,6 +4071,7 @@ __export(palettes_exports, {
3861
4071
  shade: () => shade,
3862
4072
  shapeFill: () => shapeFill,
3863
4073
  slatePalette: () => slatePalette,
4074
+ themeBaseBg: () => themeBaseBg,
3864
4075
  tidewaterPalette: () => tidewaterPalette,
3865
4076
  tint: () => tint,
3866
4077
  tokyoNightPalette: () => tokyoNightPalette
@@ -4094,6 +4305,30 @@ var init_scaling = __esm({
4094
4305
  const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4095
4306
  return new _ScaleContext(clamped, minScaleFactor);
4096
4307
  }
4308
+ /**
4309
+ * Fit content into a bounding box, scaling by whichever dimension is more
4310
+ * constraining (the smaller of the width- and height-fit ratios) so the
4311
+ * diagram never overflows the canvas in either axis. Like {@link from}, the
4312
+ * factor is clamped to `[minScaleFactor, 1]` (content is never enlarged, and
4313
+ * never shrunk past the readability floor).
4314
+ */
4315
+ static fromBox(containerWidth, idealWidth, containerHeight, idealHeight, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4316
+ const wRaw = idealWidth > 0 ? containerWidth / idealWidth : 1;
4317
+ const hRaw = idealHeight > 0 ? containerHeight / idealHeight : 1;
4318
+ const raw = Math.min(wRaw, hRaw);
4319
+ const clamped = Math.max(Math.min(raw, 1), minScaleFactor);
4320
+ return new _ScaleContext(clamped, minScaleFactor);
4321
+ }
4322
+ /**
4323
+ * Build a context from an explicit raw factor (clamped to
4324
+ * `[minScaleFactor, 1]`). Used to refine a fit iteratively: layout scaling is
4325
+ * non-linear (gaps shrink faster than floored text), so the first-pass factor
4326
+ * can still overflow — re-measure the laid-out result and tighten.
4327
+ */
4328
+ static fromFactor(rawFactor, minScaleFactor = DEFAULT_MIN_SCALE_FACTOR) {
4329
+ const clamped = Math.max(Math.min(rawFactor, 1), minScaleFactor);
4330
+ return new _ScaleContext(clamped, minScaleFactor);
4331
+ }
4097
4332
  static identity() {
4098
4333
  return new _ScaleContext(1, DEFAULT_MIN_SCALE_FACTOR);
4099
4334
  }
@@ -5794,12 +6029,7 @@ function parseSequenceDgmo(content, palette) {
5794
6029
  const trimmed = token.trim();
5795
6030
  return nameAliasMap.get(trimmed) ?? trimmed;
5796
6031
  };
5797
- const fail = (line11, message) => {
5798
- const diag = makeDgmoError(line11, message);
5799
- result.diagnostics.push(diag);
5800
- result.error = formatDgmoError(diag);
5801
- return result;
5802
- };
6032
+ const fail = makeFail(result);
5803
6033
  const pushError = (line11, message) => {
5804
6034
  const diag = makeDgmoError(line11, message);
5805
6035
  result.diagnostics.push(diag);
@@ -5814,6 +6044,7 @@ function parseSequenceDgmo(content, palette) {
5814
6044
  const lines = content.split("\n");
5815
6045
  let hasExplicitChart = false;
5816
6046
  let contentStarted = false;
6047
+ let bodyStarted = false;
5817
6048
  let firstLineIndex = -1;
5818
6049
  for (let fi = 0; fi < lines.length; fi++) {
5819
6050
  const fl = lines[fi].trim();
@@ -6125,6 +6356,7 @@ function parseSequenceDgmo(content, palette) {
6125
6356
  );
6126
6357
  }
6127
6358
  contentStarted = true;
6359
+ bodyStarted = true;
6128
6360
  const section = {
6129
6361
  kind: "section",
6130
6362
  // Capture group 1 guaranteed present after successful match.
@@ -6340,7 +6572,7 @@ function parseSequenceDgmo(content, palette) {
6340
6572
  alias: bareAlias
6341
6573
  } = splitPipe(trimmed, lineNumber);
6342
6574
  const inGroup = activeGroup && measureIndent(raw) > 0;
6343
- if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !contentStarted || bareMeta)) {
6575
+ if (/^\S+$/.test(bareCore) && !ARROW_PATTERN.test(bareCore) && (inGroup || !bodyStarted || bareMeta)) {
6344
6576
  contentStarted = true;
6345
6577
  const id = bareCore;
6346
6578
  if (bareAlias !== void 0) nameAliasMap.set(bareAlias, id);
@@ -6387,6 +6619,7 @@ function parseSequenceDgmo(content, palette) {
6387
6619
  }
6388
6620
  if (labeledArrow) {
6389
6621
  contentStarted = true;
6622
+ bodyStarted = true;
6390
6623
  const { from, to, label: rawLabel, async: isAsync } = labeledArrow;
6391
6624
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
6392
6625
  const toKey = addParticipant(resolveAlias(to), lineNumber);
@@ -6451,6 +6684,7 @@ function parseSequenceDgmo(content, palette) {
6451
6684
  const bareCall = bareCallSync || bareCallAsync;
6452
6685
  if (bareCall) {
6453
6686
  contentStarted = true;
6687
+ bodyStarted = true;
6454
6688
  const from = bareCall[1];
6455
6689
  const to = bareCall[2];
6456
6690
  const fromKey = addParticipant(resolveAlias(from), lineNumber);
@@ -6472,6 +6706,7 @@ function parseSequenceDgmo(content, palette) {
6472
6706
  const ifMatch = trimmed.match(/^if\s+(.+)$/i);
6473
6707
  if (ifMatch) {
6474
6708
  contentStarted = true;
6709
+ bodyStarted = true;
6475
6710
  const block = {
6476
6711
  kind: "block",
6477
6712
  type: "if",
@@ -6488,6 +6723,7 @@ function parseSequenceDgmo(content, palette) {
6488
6723
  const loopMatch = trimmed.match(/^loop\s+(.+)$/i);
6489
6724
  if (loopMatch) {
6490
6725
  contentStarted = true;
6726
+ bodyStarted = true;
6491
6727
  const block = {
6492
6728
  kind: "block",
6493
6729
  type: "loop",
@@ -6504,6 +6740,7 @@ function parseSequenceDgmo(content, palette) {
6504
6740
  const parallelMatch = trimmed.match(/^parallel(?:\s+(.+))?$/i);
6505
6741
  if (parallelMatch) {
6506
6742
  contentStarted = true;
6743
+ bodyStarted = true;
6507
6744
  const block = {
6508
6745
  kind: "block",
6509
6746
  type: "parallel",
@@ -6600,6 +6837,7 @@ function parseSequenceDgmo(content, palette) {
6600
6837
  lineNumber,
6601
6838
  endLineNumber: lineNumber
6602
6839
  };
6840
+ bodyStarted = true;
6603
6841
  currentContainer().push(note);
6604
6842
  continue;
6605
6843
  }
@@ -6624,6 +6862,7 @@ function parseSequenceDgmo(content, palette) {
6624
6862
  endLineNumber: i + 1
6625
6863
  // i has advanced past the body lines (1-based)
6626
6864
  };
6865
+ bodyStarted = true;
6627
6866
  currentContainer().push(note);
6628
6867
  continue;
6629
6868
  }
@@ -7468,6 +7707,15 @@ function parseNodeRef(text) {
7468
7707
  }
7469
7708
  return null;
7470
7709
  }
7710
+ function parseNodeRefLoose(text) {
7711
+ const t = text.trim();
7712
+ const shapeRe = /^(\[\[.+?\]\]|\[.+?~\]|\[.+?\]|\(.+?\)|<.+?>|\/.+?\/)\s+(\S.*)$/;
7713
+ const m = t.match(shapeRe);
7714
+ if (!m) return null;
7715
+ const ref = parseNodeRef(m[1]);
7716
+ if (!ref) return null;
7717
+ return { ref, trailing: m[2].trim() };
7718
+ }
7471
7719
  function splitArrows(line11) {
7472
7720
  const segments = [];
7473
7721
  const arrowPositions = [];
@@ -7563,6 +7811,7 @@ function parseFlowchart(content, palette) {
7563
7811
  const notes = [];
7564
7812
  let contentStarted = false;
7565
7813
  let firstLineParsed = false;
7814
+ let prevLineLastNodeId = null;
7566
7815
  const nameAliasMap = /* @__PURE__ */ new Map();
7567
7816
  function peelAlias2(seg) {
7568
7817
  const trimmed = seg.trim();
@@ -7570,6 +7819,19 @@ function parseFlowchart(content, palette) {
7570
7819
  if (!m) return { seg: trimmed };
7571
7820
  return { seg: m[1].trim(), alias: m[2] };
7572
7821
  }
7822
+ const suffixWarnedLines = /* @__PURE__ */ new Set();
7823
+ function warnUnsupportedSuffix(lineNumber, trailing) {
7824
+ if (suffixWarnedLines.has(lineNumber)) return;
7825
+ suffixWarnedLines.add(lineNumber);
7826
+ result.diagnostics.push(
7827
+ makeDgmoError(
7828
+ lineNumber,
7829
+ `Ignored unsupported text after a node shape: "${trailing}". Flowcharts have no tag groups or node metadata; node colors are assigned automatically by shape (e.g. terminals green/red, decisions yellow). Remove the suffix.`,
7830
+ "warning",
7831
+ "W_FLOWCHART_NODE_SUFFIX"
7832
+ )
7833
+ );
7834
+ }
7573
7835
  function getOrCreateNode(ref, lineNumber) {
7574
7836
  const key = ref.id;
7575
7837
  const existing = nodeMap.get(key);
@@ -7628,6 +7890,8 @@ function parseFlowchart(content, palette) {
7628
7890
  indentStack[indentStack.length - 1].nodeId
7629
7891
  ) : null;
7630
7892
  const segments = splitArrows(trimmed);
7893
+ const startsWithArrow = segments.length >= 2 && segments[0].trim() === "";
7894
+ const effectiveSource = implicitSourceId ?? (startsWithArrow ? prevLineLastNodeId : null);
7631
7895
  if (segments.length === 1) {
7632
7896
  const peeled = peelAlias2(segments[0]);
7633
7897
  const ref = parseNodeRef(peeled.seg);
@@ -7637,6 +7901,13 @@ function parseFlowchart(content, palette) {
7637
7901
  indentStack.push({ nodeId: node.id, indent });
7638
7902
  return node.id;
7639
7903
  }
7904
+ const loose = parseNodeRefLoose(peeled.seg);
7905
+ if (loose) {
7906
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7907
+ const node = getOrCreateNode(loose.ref, lineNumber);
7908
+ indentStack.push({ nodeId: node.id, indent });
7909
+ return node.id;
7910
+ }
7640
7911
  const aliasResolved = nameAliasMap.get(peeled.seg.trim());
7641
7912
  if (aliasResolved !== void 0) {
7642
7913
  indentStack.push({ nodeId: aliasResolved, indent });
@@ -7672,16 +7943,23 @@ function parseFlowchart(content, palette) {
7672
7943
  }
7673
7944
  }
7674
7945
  }
7946
+ if (!ref) {
7947
+ const loose = parseNodeRefLoose(peeled.seg);
7948
+ if (loose) {
7949
+ warnUnsupportedSuffix(lineNumber, loose.trailing);
7950
+ ref = loose.ref;
7951
+ }
7952
+ }
7675
7953
  if (!ref) continue;
7676
7954
  const node = getOrCreateNode(ref, lineNumber);
7677
7955
  if (peeled.alias) nameAliasMap.set(peeled.alias, node.id);
7678
7956
  if (pendingArrow !== null) {
7679
- const sourceId = lastNodeId ?? implicitSourceId;
7957
+ const sourceId = lastNodeId ?? effectiveSource;
7680
7958
  if (sourceId) {
7681
7959
  addEdge(sourceId, node.id, lineNumber, pendingArrow.label);
7682
7960
  }
7683
7961
  pendingArrow = null;
7684
- } else if (lastNodeId === null && implicitSourceId === null) {
7962
+ } else if (lastNodeId === null && effectiveSource === null) {
7685
7963
  }
7686
7964
  lastNodeId = node.id;
7687
7965
  }
@@ -7763,7 +8041,8 @@ function parseFlowchart(content, palette) {
7763
8041
  continue;
7764
8042
  }
7765
8043
  }
7766
- processContentLine(trimmed, lineNumber, indent);
8044
+ const lastId = processContentLine(trimmed, lineNumber, indent);
8045
+ if (lastId) prevLineLastNodeId = lastId;
7767
8046
  }
7768
8047
  if (result.nodes.length === 0 && !result.error) {
7769
8048
  const diag = makeDgmoError(
@@ -8846,7 +9125,12 @@ function parseERDiagram(content, palette) {
8846
9125
  }
8847
9126
  if (currentTagGroup && !contentStarted && indent > 0) {
8848
9127
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
8849
- const { label, color } = extractColor(cleanEntry, palette);
9128
+ const { label, color } = extractColor(
9129
+ cleanEntry,
9130
+ palette,
9131
+ result.diagnostics,
9132
+ lineNumber
9133
+ );
8850
9134
  if (isDefault) {
8851
9135
  currentTagGroup.defaultValue = label;
8852
9136
  } else if (currentTagGroup.entries.length === 0) {
@@ -9164,12 +9448,7 @@ function parseOrg(content, palette) {
9164
9448
  diagnostics: [],
9165
9449
  error: null
9166
9450
  };
9167
- const fail = (line11, message) => {
9168
- const diag = makeDgmoError(line11, message);
9169
- result.diagnostics.push(diag);
9170
- result.error = formatDgmoError(diag);
9171
- return result;
9172
- };
9451
+ const fail = makeFail(result);
9173
9452
  const pushError = (line11, message) => {
9174
9453
  const diag = makeDgmoError(line11, message);
9175
9454
  result.diagnostics.push(diag);
@@ -9278,7 +9557,12 @@ function parseOrg(content, palette) {
9278
9557
  const indent2 = measureIndent(line11);
9279
9558
  if (indent2 > 0) {
9280
9559
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9281
- const { label, color } = extractColor(cleanEntry, palette);
9560
+ const { label, color } = extractColor(
9561
+ cleanEntry,
9562
+ palette,
9563
+ result.diagnostics,
9564
+ lineNumber
9565
+ );
9282
9566
  if (isDefault) {
9283
9567
  currentTagGroup.defaultValue = label;
9284
9568
  } else if (currentTagGroup.entries.length === 0) {
@@ -9564,12 +9848,7 @@ function parseSitemap(content, palette) {
9564
9848
  diagnostics: [],
9565
9849
  error: null
9566
9850
  };
9567
- const fail = (line11, message) => {
9568
- const diag = makeDgmoError(line11, message);
9569
- result.diagnostics.push(diag);
9570
- result.error = formatDgmoError(diag);
9571
- return result;
9572
- };
9851
+ const fail = makeFail(result);
9573
9852
  const pushError = (line11, message) => {
9574
9853
  const diag = makeDgmoError(line11, message);
9575
9854
  result.diagnostics.push(diag);
@@ -9670,7 +9949,12 @@ function parseSitemap(content, palette) {
9670
9949
  const indent2 = measureIndent(line11);
9671
9950
  if (indent2 > 0) {
9672
9951
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
9673
- const { label, color } = extractColor(cleanEntry, palette);
9952
+ const { label, color } = extractColor(
9953
+ cleanEntry,
9954
+ palette,
9955
+ result.diagnostics,
9956
+ lineNumber
9957
+ );
9674
9958
  currentTagGroup.entries.push({
9675
9959
  value: label,
9676
9960
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -11385,7 +11669,7 @@ var init_chart_types = __esm({
11385
11669
  },
11386
11670
  {
11387
11671
  id: "sequence",
11388
- description: "Message / interaction flows",
11672
+ description: "Message request and response interaction flows",
11389
11673
  fallback: true
11390
11674
  },
11391
11675
  {
@@ -11476,7 +11760,7 @@ var init_chart_types = __esm({
11476
11760
  },
11477
11761
  {
11478
11762
  id: "slope",
11479
- description: "Change between two periods"
11763
+ description: "Change between 2 time periods"
11480
11764
  },
11481
11765
  {
11482
11766
  id: "sankey",
@@ -11505,7 +11789,7 @@ var init_chart_types = __esm({
11505
11789
  // ── Tier 4 — General-purpose data charts ──────────────────
11506
11790
  {
11507
11791
  id: "bar",
11508
- description: "Categorical comparisons",
11792
+ description: "Categorical comparisons for 3 - 5 figures",
11509
11793
  fallback: true
11510
11794
  },
11511
11795
  {
@@ -11767,7 +12051,9 @@ function parseChart(content, palette) {
11767
12051
  if (dataValues) {
11768
12052
  const { label: rawLabel, color: pointColor } = extractColor(
11769
12053
  dataValues.label,
11770
- palette
12054
+ palette,
12055
+ result.diagnostics,
12056
+ lineNumber
11771
12057
  );
11772
12058
  const [first, ...rest] = dataValues.values;
11773
12059
  result.data.push({
@@ -11832,6 +12118,12 @@ function parseChart(content, palette) {
11832
12118
  'Chart type "bar-stacked" requires multiple series names. Use: series Name1, Name2, Name3'
11833
12119
  );
11834
12120
  }
12121
+ if (!result.error && result.type === "bar" && (result.seriesNames?.length ?? 0) > 1) {
12122
+ warn2(
12123
+ result.seriesLineNumber ?? 1,
12124
+ `Plain "bar" shows only the first series ("${result.seriesNames[0]}"); the other ${result.seriesNames.length - 1} are dropped at render. Use "bar-stacked" for stacked bars or "multi-line" to plot every series.`
12125
+ );
12126
+ }
11835
12127
  if (!result.error && result.seriesNames) {
11836
12128
  const expectedCount = result.seriesNames.length;
11837
12129
  for (const dp of result.data) {
@@ -12080,7 +12372,9 @@ function parseScatterRow(line11, palette, currentCategory, lineNumber, diagnosti
12080
12372
  if (!dataRow || dataRow.values.length < 2) return null;
12081
12373
  const { label: rawLabel, color: pointColor } = extractColor(
12082
12374
  dataRow.label,
12083
- palette
12375
+ palette,
12376
+ diagnostics,
12377
+ lineNumber
12084
12378
  );
12085
12379
  return {
12086
12380
  name: rawLabel,
@@ -12218,11 +12512,15 @@ function parseExtendedChartFull(content, palette) {
12218
12512
  const targetResolved = resolveSlot(rawTarget);
12219
12513
  const { label: source, color: sourceColor } = extractColor(
12220
12514
  sourceResolved,
12221
- palette
12515
+ palette,
12516
+ result.diagnostics,
12517
+ lineNumber
12222
12518
  );
12223
12519
  const { label: target, color: targetColor } = extractColor(
12224
12520
  targetResolved,
12225
- palette
12521
+ palette,
12522
+ result.diagnostics,
12523
+ lineNumber
12226
12524
  );
12227
12525
  if (sourceColor || targetColor) {
12228
12526
  if (!result.nodeColors) result.nodeColors = {};
@@ -12274,7 +12572,9 @@ function parseExtendedChartFull(content, palette) {
12274
12572
  const targetResolved = resolveSlot(dataRow2.label);
12275
12573
  const { label: target, color: targetColor } = extractColor(
12276
12574
  targetResolved,
12277
- palette
12575
+ palette,
12576
+ result.diagnostics,
12577
+ lineNumber
12278
12578
  );
12279
12579
  if (targetColor) {
12280
12580
  if (!result.nodeColors) result.nodeColors = {};
@@ -12307,7 +12607,9 @@ function parseExtendedChartFull(content, palette) {
12307
12607
  const trimmedResolved = resolveSlot(trimmed);
12308
12608
  const { label: nodeName, color: nodeColor2 } = extractColor(
12309
12609
  trimmedResolved,
12310
- palette
12610
+ palette,
12611
+ result.diagnostics,
12612
+ lineNumber
12311
12613
  );
12312
12614
  if (nodeColor2) {
12313
12615
  if (!result.nodeColors) result.nodeColors = {};
@@ -12397,8 +12699,11 @@ function parseExtendedChartFull(content, palette) {
12397
12699
  min: parseFloat(rangeMatch[1]),
12398
12700
  max: parseFloat(rangeMatch[2])
12399
12701
  };
12702
+ continue;
12703
+ }
12704
+ if (!(result.type === "function" && trimmed.includes(":"))) {
12705
+ continue;
12400
12706
  }
12401
- continue;
12402
12707
  }
12403
12708
  }
12404
12709
  if (firstToken === "no-name") {
@@ -12458,7 +12763,9 @@ function parseExtendedChartFull(content, palette) {
12458
12763
  if (colonIndex >= 0) {
12459
12764
  const { label: fnName, color: fnColor } = extractColor(
12460
12765
  trimmed.substring(0, colonIndex).trim(),
12461
- palette
12766
+ palette,
12767
+ result.diagnostics,
12768
+ lineNumber
12462
12769
  );
12463
12770
  const fnValue = trimmed.substring(colonIndex + 1).trim();
12464
12771
  if (!result.functions) result.functions = [];
@@ -12506,7 +12813,9 @@ function parseExtendedChartFull(content, palette) {
12506
12813
  if (dataRow?.values.length === 1) {
12507
12814
  const { label: rawLabel, color: pointColor } = extractColor(
12508
12815
  dataRow.label,
12509
- palette
12816
+ palette,
12817
+ result.diagnostics,
12818
+ lineNumber
12510
12819
  );
12511
12820
  result.data.push({
12512
12821
  label: rawLabel,
@@ -12605,11 +12914,11 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12605
12914
  const sc = ctx ?? ScaleContext.identity();
12606
12915
  const { textColor, axisLineColor, gridOpacity, colors, titleConfig } = buildChartCommons(parsed, palette, isDark, sc);
12607
12916
  if (parsed.type === "sankey") {
12608
- const bg = isDark ? palette.surface : palette.bg;
12917
+ const bg = themeBaseBg(palette, isDark);
12609
12918
  return buildSankeyOption(parsed, textColor, colors, bg, titleConfig, sc);
12610
12919
  }
12611
12920
  if (parsed.type === "chord") {
12612
- const bg = isDark ? palette.surface : palette.bg;
12921
+ const bg = themeBaseBg(palette, isDark);
12613
12922
  return buildChordOption(
12614
12923
  parsed,
12615
12924
  palette,
@@ -12650,7 +12959,7 @@ function buildExtendedChartOption(parsed, palette, isDark, ctx) {
12650
12959
  );
12651
12960
  }
12652
12961
  if (parsed.type === "funnel") {
12653
- const bg = isDark ? palette.surface : palette.bg;
12962
+ const bg = themeBaseBg(palette, isDark);
12654
12963
  return buildFunnelOption(
12655
12964
  parsed,
12656
12965
  palette,
@@ -13246,7 +13555,8 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13246
13555
  const gridLeft = parsed.ylabel ? 12 : 3;
13247
13556
  const gridRight = 4;
13248
13557
  const gridBottom = hasCategories ? 15 : parsed.xlabel ? 10 : 3;
13249
- const gridTop = parsed.title && !parsed.noTitle ? 15 : 5;
13558
+ const hasTitle = !!(parsed.title && !parsed.noTitle);
13559
+ const gridTop = hasTitle ? hasCategories ? 22 : 15 : hasCategories ? 12 : 5;
13250
13560
  let graphic;
13251
13561
  if (showLabels && points.length > 0) {
13252
13562
  const labelPoints = [];
@@ -13386,7 +13696,7 @@ function buildScatterOption(parsed, palette, isDark, textColor, axisLineColor, g
13386
13696
  }
13387
13697
  function buildHeatmapOption(parsed, palette, isDark, textColor, axisLineColor, titleConfig, ctx) {
13388
13698
  const sc = ctx ?? ScaleContext.identity();
13389
- const bg = isDark ? palette.surface : palette.bg;
13699
+ const bg = themeBaseBg(palette, isDark);
13390
13700
  const heatmapRows = parsed.heatmapRows ?? [];
13391
13701
  const columns = parsed.columns ?? [];
13392
13702
  const rowLabels = heatmapRows.map((r) => r.label);
@@ -13705,7 +14015,7 @@ function buildSimpleChartOption(parsed, palette, isDark, chartWidth, ctx) {
13705
14015
  colors,
13706
14016
  titleConfig
13707
14017
  } = buildChartCommons(parsed, palette, isDark, sc);
13708
- const bg = isDark ? palette.surface : palette.bg;
14018
+ const bg = themeBaseBg(palette, isDark);
13709
14019
  switch (parsed.type) {
13710
14020
  case "bar":
13711
14021
  return buildBarOption(
@@ -14657,12 +14967,7 @@ function parseKanban(content, palette) {
14657
14967
  diagnostics: [],
14658
14968
  error: null
14659
14969
  };
14660
- const fail = (line11, message) => {
14661
- const diag = makeDgmoError(line11, message);
14662
- result.diagnostics.push(diag);
14663
- result.error = formatDgmoError(diag);
14664
- return result;
14665
- };
14970
+ const fail = makeFail(result);
14666
14971
  const warn2 = (line11, message) => {
14667
14972
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
14668
14973
  };
@@ -14765,7 +15070,12 @@ function parseKanban(content, palette) {
14765
15070
  const indent2 = measureIndent(line11);
14766
15071
  if (indent2 > 0) {
14767
15072
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
14768
- const { label, color } = extractColor(cleanEntry, palette);
15073
+ const { label, color } = extractColor(
15074
+ cleanEntry,
15075
+ palette,
15076
+ result.diagnostics,
15077
+ lineNumber
15078
+ );
14769
15079
  if (isDefault) {
14770
15080
  currentTagGroup.defaultValue = label;
14771
15081
  } else if (currentTagGroup.entries.length === 0) {
@@ -15109,12 +15419,7 @@ function parseC4(content, palette) {
15109
15419
  if (!result.error && severity === "error")
15110
15420
  result.error = formatDgmoError(diag);
15111
15421
  };
15112
- const fail = (line11, message) => {
15113
- const diag = makeDgmoError(line11, message);
15114
- result.diagnostics.push(diag);
15115
- result.error = formatDgmoError(diag);
15116
- return result;
15117
- };
15422
+ const fail = makeFail(result);
15118
15423
  if (!content?.trim()) {
15119
15424
  return fail(0, "No content provided");
15120
15425
  }
@@ -15215,7 +15520,12 @@ function parseC4(content, palette) {
15215
15520
  const indent2 = measureIndent(line11);
15216
15521
  if (indent2 > 0) {
15217
15522
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
15218
- const { label, color } = extractColor(cleanEntry, palette);
15523
+ const { label, color } = extractColor(
15524
+ cleanEntry,
15525
+ palette,
15526
+ result.diagnostics,
15527
+ lineNumber
15528
+ );
15219
15529
  if (isDefault) {
15220
15530
  currentTagGroup.defaultValue = label;
15221
15531
  } else if (currentTagGroup.entries.length === 0) {
@@ -16880,8 +17190,66 @@ function parseInfra(content) {
16880
17190
  }
16881
17191
  }
16882
17192
  validateTagGroupNames(result.tagGroups, warn2, setError);
17193
+ checkReachability(result);
16883
17194
  return result;
16884
17195
  }
17196
+ function checkReachability(result) {
17197
+ if (result.nodes.length === 0) return;
17198
+ const entries = result.nodes.filter((n) => n.isEdge);
17199
+ if (entries.length === 0) {
17200
+ const line11 = result.titleLineNumber ?? result.nodes[0]?.lineNumber ?? 1;
17201
+ result.diagnostics.push(
17202
+ makeDgmoError(
17203
+ line11,
17204
+ `Infra diagram has no 'internet' or 'edge' entry point \u2014 an infra diagram traces request traffic from an entry inward, so without one nothing carries traffic. Add an 'internet' or 'edge' node and route from it.`,
17205
+ "warning",
17206
+ "W_INFRA_NO_ENTRY"
17207
+ )
17208
+ );
17209
+ return;
17210
+ }
17211
+ const groupChildMap = /* @__PURE__ */ new Map();
17212
+ for (const node of result.nodes) {
17213
+ if (node.groupId) {
17214
+ const list = groupChildMap.get(node.groupId) ?? [];
17215
+ list.push(node.id);
17216
+ groupChildMap.set(node.groupId, list);
17217
+ }
17218
+ }
17219
+ const outbound = /* @__PURE__ */ new Map();
17220
+ for (const edge of result.edges) {
17221
+ const targets = groupChildMap.get(edge.targetId) ?? [edge.targetId];
17222
+ const list = outbound.get(edge.sourceId) ?? [];
17223
+ list.push(...targets);
17224
+ outbound.set(edge.sourceId, list);
17225
+ }
17226
+ const reachable = /* @__PURE__ */ new Set();
17227
+ const queue = [];
17228
+ for (const entry of entries) {
17229
+ reachable.add(entry.id);
17230
+ queue.push(entry.id);
17231
+ }
17232
+ while (queue.length > 0) {
17233
+ const current = queue.shift();
17234
+ for (const next of outbound.get(current) ?? []) {
17235
+ if (!reachable.has(next)) {
17236
+ reachable.add(next);
17237
+ queue.push(next);
17238
+ }
17239
+ }
17240
+ }
17241
+ for (const node of result.nodes) {
17242
+ if (node.isEdge || reachable.has(node.id)) continue;
17243
+ result.diagnostics.push(
17244
+ makeDgmoError(
17245
+ node.lineNumber,
17246
+ `'${node.label}' is unreachable from an 'internet'/'edge' entry \u2014 no request traffic flows to it, so it's dead on an infra diagram. Connect it downstream of an entry, or remove it.`,
17247
+ "warning",
17248
+ "W_INFRA_UNREACHABLE"
17249
+ )
17250
+ );
17251
+ }
17252
+ }
16885
17253
  function stripNodeDecorations(name) {
16886
17254
  let s = name.trim();
16887
17255
  const aliasMatch = s.match(/^(.*?)\s+as\s+[A-Za-z][A-Za-z0-9_]{0,11}\s*$/);
@@ -17171,7 +17539,12 @@ function parseGantt(content, palette) {
17171
17539
  const eraEntryMatch = line11.match(ERA_ENTRY_RE);
17172
17540
  if (eraEntryMatch) {
17173
17541
  const eraLabelRaw = eraEntryMatch[3].trim();
17174
- const eraExtracted = extractColor(eraLabelRaw, palette);
17542
+ const eraExtracted = extractColor(
17543
+ eraLabelRaw,
17544
+ palette,
17545
+ diagnostics,
17546
+ lineNumber
17547
+ );
17175
17548
  result.eras.push({
17176
17549
  startDate: eraEntryMatch[1],
17177
17550
  endDate: eraEntryMatch[2],
@@ -17193,7 +17566,12 @@ function parseGantt(content, palette) {
17193
17566
  const markerEntryMatch = line11.match(MARKER_ENTRY_RE);
17194
17567
  if (markerEntryMatch) {
17195
17568
  const markerLabelRaw = markerEntryMatch[2].trim();
17196
- const markerExtracted = extractColor(markerLabelRaw, palette);
17569
+ const markerExtracted = extractColor(
17570
+ markerLabelRaw,
17571
+ palette,
17572
+ diagnostics,
17573
+ lineNumber
17574
+ );
17197
17575
  result.markers.push({
17198
17576
  date: markerEntryMatch[1],
17199
17577
  label: markerExtracted.label,
@@ -17214,7 +17592,12 @@ function parseGantt(content, palette) {
17214
17592
  } else {
17215
17593
  if (COMMENT_RE.test(line11)) continue;
17216
17594
  const { text: cleanEntry, isDefault } = stripDefaultModifier(line11);
17217
- const extracted = extractColor(cleanEntry, palette);
17595
+ const extracted = extractColor(
17596
+ cleanEntry,
17597
+ palette,
17598
+ diagnostics,
17599
+ lineNumber
17600
+ );
17218
17601
  const color = extracted.color || seriesColors2[currentTagGroup.entries.length % seriesColors2.length] || "#888888";
17219
17602
  const isFirstEntry = currentTagGroup.entries.length === 0;
17220
17603
  currentTagGroup.entries.push({
@@ -17409,7 +17792,12 @@ function parseGantt(content, palette) {
17409
17792
  const startOff = parseOffsetPrefix("+" + eraOffsetMatch[1]);
17410
17793
  const endOff = parseOffsetPrefix("+" + eraOffsetMatch[2]);
17411
17794
  const eraLabelRaw = eraOffsetMatch[3].trim();
17412
- const eraExtracted = extractColor(eraLabelRaw, palette);
17795
+ const eraExtracted = extractColor(
17796
+ eraLabelRaw,
17797
+ palette,
17798
+ diagnostics,
17799
+ lineNumber
17800
+ );
17413
17801
  result.eras.push({
17414
17802
  startDate: "",
17415
17803
  endDate: "",
@@ -17425,7 +17813,12 @@ function parseGantt(content, palette) {
17425
17813
  if (markerOffsetMatch) {
17426
17814
  const dateOff = parseOffsetPrefix("+" + markerOffsetMatch[1]);
17427
17815
  const markerLabelRaw = markerOffsetMatch[2].trim();
17428
- const markerExtracted = extractColor(markerLabelRaw, palette);
17816
+ const markerExtracted = extractColor(
17817
+ markerLabelRaw,
17818
+ palette,
17819
+ diagnostics,
17820
+ lineNumber
17821
+ );
17429
17822
  result.markers.push({
17430
17823
  date: "",
17431
17824
  label: markerExtracted.label,
@@ -17499,7 +17892,12 @@ function parseGantt(content, palette) {
17499
17892
  const eraMatch = line11.match(ERA_RE);
17500
17893
  if (eraMatch) {
17501
17894
  const eraLabelRaw = eraMatch[3].trim();
17502
- const eraExtracted = extractColor(eraLabelRaw, palette);
17895
+ const eraExtracted = extractColor(
17896
+ eraLabelRaw,
17897
+ palette,
17898
+ diagnostics,
17899
+ lineNumber
17900
+ );
17503
17901
  result.eras.push({
17504
17902
  startDate: eraMatch[1],
17505
17903
  endDate: eraMatch[2],
@@ -17517,7 +17915,12 @@ function parseGantt(content, palette) {
17517
17915
  const markerMatch = line11.match(MARKER_RE);
17518
17916
  if (markerMatch) {
17519
17917
  const markerLabelRaw = markerMatch[2].trim();
17520
- const markerExtracted = extractColor(markerLabelRaw, palette);
17918
+ const markerExtracted = extractColor(
17919
+ markerLabelRaw,
17920
+ palette,
17921
+ diagnostics,
17922
+ lineNumber
17923
+ );
17521
17924
  result.markers.push({
17522
17925
  date: markerMatch[1],
17523
17926
  label: markerExtracted.label,
@@ -18914,6 +19317,7 @@ function parseBoxesAndLines(content, palette) {
18914
19317
  const nodes = [];
18915
19318
  const edges = [];
18916
19319
  const groups = [];
19320
+ const nodePositions = /* @__PURE__ */ new Map();
18917
19321
  const result = {
18918
19322
  type: "boxes-and-lines",
18919
19323
  title: null,
@@ -18947,6 +19351,8 @@ function parseBoxesAndLines(content, palette) {
18947
19351
  }
18948
19352
  const groupStack = [];
18949
19353
  let contentStarted = false;
19354
+ let inLayoutBlock = false;
19355
+ const LAYOUT_ENTRY_RE = /^(.+?):\s*(-?\d+(?:\.\d+)?)\s*,\s*(-?\d+(?:\.\d+)?)\s*$/;
18950
19356
  let currentTagGroup = null;
18951
19357
  const metaAliasMap = /* @__PURE__ */ new Map();
18952
19358
  const nameAliasMap = /* @__PURE__ */ new Map();
@@ -18986,7 +19392,7 @@ function parseBoxesAndLines(content, palette) {
18986
19392
  const trimmed = raw.trim();
18987
19393
  const indent = measureIndent2(raw);
18988
19394
  if (!trimmed || trimmed.startsWith("//")) continue;
18989
- if (trimmed.includes("|") && !/-\S*\|\S*->/.test(trimmed) && !/~\S*\|\S*~>/.test(trimmed)) {
19395
+ if (trimmed.includes("|") && !ARROW_LABEL_PIPE_DIRECTED_RE.test(trimmed) && !ARROW_LABEL_PIPE_UNDIRECTED_RE.test(trimmed)) {
18990
19396
  result.diagnostics.push(
18991
19397
  makeDgmoError(
18992
19398
  lineNum,
@@ -19115,7 +19521,12 @@ function parseBoxesAndLines(content, palette) {
19115
19521
  if (tagBlockMatch.inlineValues) {
19116
19522
  for (const rawVal of tagBlockMatch.inlineValues) {
19117
19523
  const { text: cleanVal, isDefault } = stripDefaultModifier(rawVal);
19118
- const { label, color } = extractColor(cleanVal);
19524
+ const { label, color } = extractColor(
19525
+ cleanVal,
19526
+ palette,
19527
+ result.diagnostics,
19528
+ lineNum
19529
+ );
19119
19530
  newTagGroup.entries.push({
19120
19531
  value: label,
19121
19532
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19132,7 +19543,12 @@ function parseBoxesAndLines(content, palette) {
19132
19543
  }
19133
19544
  if (currentTagGroup && !contentStarted && indent > 0) {
19134
19545
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19135
- const { label, color } = extractColor(cleanEntry);
19546
+ const { label, color } = extractColor(
19547
+ cleanEntry,
19548
+ palette,
19549
+ result.diagnostics,
19550
+ lineNum
19551
+ );
19136
19552
  currentTagGroup.entries.push({
19137
19553
  value: label,
19138
19554
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -19148,6 +19564,42 @@ function parseBoxesAndLines(content, palette) {
19148
19564
  if (currentTagGroup && indent === 0) {
19149
19565
  currentTagGroup = null;
19150
19566
  }
19567
+ if (!inLayoutBlock && indent === 0 && trimmed === "layout") {
19568
+ let isBlock = false;
19569
+ for (let j = i + 1; j < lines.length; j++) {
19570
+ const peek = lines[j];
19571
+ if (!peek.trim()) continue;
19572
+ isBlock = measureIndent2(peek) > 0 && LAYOUT_ENTRY_RE.test(peek.trim());
19573
+ break;
19574
+ }
19575
+ if (isBlock) {
19576
+ flushDescription();
19577
+ closeGroupsToIndent(0);
19578
+ inLayoutBlock = true;
19579
+ continue;
19580
+ }
19581
+ }
19582
+ if (inLayoutBlock) {
19583
+ if (indent > 0) {
19584
+ const lm = trimmed.match(LAYOUT_ENTRY_RE);
19585
+ if (lm) {
19586
+ nodePositions.set(lm[1].trim(), {
19587
+ x: Number(lm[2]),
19588
+ y: Number(lm[3])
19589
+ });
19590
+ } else {
19591
+ result.diagnostics.push(
19592
+ makeDgmoError(
19593
+ lineNum,
19594
+ `Invalid layout entry "${trimmed}" \u2014 expected "<node-id>: <x>, <y>"`,
19595
+ "warning"
19596
+ )
19597
+ );
19598
+ }
19599
+ continue;
19600
+ }
19601
+ inLayoutBlock = false;
19602
+ }
19151
19603
  if (descState !== null) {
19152
19604
  if (indent > descState.indent) {
19153
19605
  if (trimmed.includes("->") || trimmed.includes("<->")) {
@@ -19400,6 +19852,22 @@ function parseBoxesAndLines(content, palette) {
19400
19852
  );
19401
19853
  }
19402
19854
  finalizeAutoTagColors(result.tagGroups);
19855
+ if (nodePositions.size > 0) {
19856
+ const nodeLabelSet = new Set(result.nodes.map((n) => n.label));
19857
+ for (const id of nodePositions.keys()) {
19858
+ if (!nodeLabelSet.has(id)) {
19859
+ pushWarning(0, `layout entry for unknown node "${id}" (ignored)`);
19860
+ }
19861
+ }
19862
+ const unpositioned = result.nodes.filter((n) => !nodePositions.has(n.label)).map((n) => n.label);
19863
+ if (unpositioned.length > 0) {
19864
+ pushWarning(
19865
+ 0,
19866
+ `layout block is partial \u2014 ${unpositioned.length} node(s) without coordinates (${unpositioned.slice(0, 5).join(", ")}${unpositioned.length > 5 ? "\u2026" : ""}); ignoring the block and auto-laying-out`
19867
+ );
19868
+ }
19869
+ result.nodePositions = nodePositions;
19870
+ }
19403
19871
  if (result.tagGroups.length > 0) {
19404
19872
  injectDefaultTagMetadata(result.nodes, result.tagGroups);
19405
19873
  validateTagValues(result.nodes, result.tagGroups, pushWarning, suggest);
@@ -19678,7 +20146,7 @@ function parseEdgeLine(trimmed, lineNum, metaAliasMap, diagnostics, nameAliasMap
19678
20146
  metadata
19679
20147
  };
19680
20148
  }
19681
- var MAX_GROUP_DEPTH;
20149
+ var MAX_GROUP_DEPTH, ARROW_LABEL_PIPE_DIRECTED_RE, ARROW_LABEL_PIPE_UNDIRECTED_RE;
19682
20150
  var init_parser18 = __esm({
19683
20151
  "src/boxes-and-lines/parser.ts"() {
19684
20152
  "use strict";
@@ -19690,6 +20158,8 @@ var init_parser18 = __esm({
19690
20158
  init_reserved_key_registry();
19691
20159
  init_notes();
19692
20160
  MAX_GROUP_DEPTH = 2;
20161
+ ARROW_LABEL_PIPE_DIRECTED_RE = /-\S*\|\S*->/;
20162
+ ARROW_LABEL_PIPE_UNDIRECTED_RE = /~\S*\|\S*~>/;
19693
20163
  }
19694
20164
  });
19695
20165
 
@@ -19709,12 +20179,7 @@ function parseMindmap(content, palette) {
19709
20179
  diagnostics: [],
19710
20180
  error: null
19711
20181
  };
19712
- const fail = (line11, message) => {
19713
- const diag = makeDgmoError(line11, message);
19714
- result.diagnostics.push(diag);
19715
- result.error = formatDgmoError(diag);
19716
- return result;
19717
- };
20182
+ const fail = makeFail(result);
19718
20183
  const pushError = (line11, message) => {
19719
20184
  const diag = makeDgmoError(line11, message);
19720
20185
  result.diagnostics.push(diag);
@@ -19827,7 +20292,12 @@ function parseMindmap(content, palette) {
19827
20292
  const indent2 = measureIndent(line11);
19828
20293
  if (indent2 > 0) {
19829
20294
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
19830
- const { label, color } = extractColor(cleanEntry, palette);
20295
+ const { label, color } = extractColor(
20296
+ cleanEntry,
20297
+ palette,
20298
+ result.diagnostics,
20299
+ lineNumber
20300
+ );
19831
20301
  if (isDefault) {
19832
20302
  currentTagGroup.defaultValue = label;
19833
20303
  } else if (currentTagGroup.entries.length === 0) {
@@ -19904,6 +20374,7 @@ function parseMindmap(content, palette) {
19904
20374
  collectAll(result.roots);
19905
20375
  validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
19906
20376
  validateTagGroupNames(result.tagGroups, pushWarning);
20377
+ cascadeTagMetadata(result.roots, result.tagGroups);
19907
20378
  }
19908
20379
  if (result.roots.length === 0 && !result.error) {
19909
20380
  const diag = makeDgmoError(1, "No nodes found in mindmap");
@@ -20510,7 +20981,12 @@ function parseWireframe(content) {
20510
20981
  }
20511
20982
  if (indent > 0 && currentTagGroup) {
20512
20983
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
20513
- const { label, color } = extractColor(cleanEntry);
20984
+ const { label, color } = extractColor(
20985
+ cleanEntry,
20986
+ void 0,
20987
+ diagnostics,
20988
+ lineNumber
20989
+ );
20514
20990
  currentTagGroup.entries.push({
20515
20991
  value: label,
20516
20992
  color: color ?? AUTO_TAG_COLOR_SENTINEL,
@@ -20758,12 +21234,7 @@ function parseTechRadar(content) {
20758
21234
  diagnostics: [],
20759
21235
  error: null
20760
21236
  };
20761
- const fail = (line11, message) => {
20762
- const diag = makeDgmoError(line11, message);
20763
- result.diagnostics.push(diag);
20764
- result.error = formatDgmoError(diag);
20765
- return result;
20766
- };
21237
+ const fail = makeFail(result);
20767
21238
  const warn2 = (line11, message) => {
20768
21239
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
20769
21240
  };
@@ -21131,12 +21602,7 @@ function parseCycle(content) {
21131
21602
  let state = "top";
21132
21603
  let currentNode = null;
21133
21604
  let currentEdge = null;
21134
- const fail = (line11, message) => {
21135
- const diag = makeDgmoError(line11, message);
21136
- result.diagnostics.push(diag);
21137
- result.error = formatDgmoError(diag);
21138
- return result;
21139
- };
21605
+ const fail = makeFail(result);
21140
21606
  const warn2 = (line11, message, severity = "warning") => {
21141
21607
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21142
21608
  };
@@ -21413,12 +21879,7 @@ function parseJourneyMap(content, palette) {
21413
21879
  diagnostics: [],
21414
21880
  error: null
21415
21881
  };
21416
- const fail = (line11, message) => {
21417
- const diag = makeDgmoError(line11, message);
21418
- result.diagnostics.push(diag);
21419
- result.error = formatDgmoError(diag);
21420
- return result;
21421
- };
21882
+ const fail = makeFail(result);
21422
21883
  const warn2 = (line11, message) => {
21423
21884
  result.diagnostics.push(makeDgmoError(line11, message, "warning"));
21424
21885
  };
@@ -21500,7 +21961,18 @@ function parseJourneyMap(content, palette) {
21500
21961
  }
21501
21962
  }
21502
21963
  } else {
21503
- personaName = afterKeyword;
21964
+ const colorMatch = afterKeyword.match(/^(.+?)\s+color:\s*(\S+)$/i);
21965
+ if (colorMatch) {
21966
+ personaName = colorMatch[1].trim();
21967
+ personaColor = resolveColorWithDiagnostic(
21968
+ colorMatch[2],
21969
+ lineNumber,
21970
+ result.diagnostics,
21971
+ palette
21972
+ ) ?? void 0;
21973
+ } else {
21974
+ personaName = afterKeyword;
21975
+ }
21504
21976
  }
21505
21977
  if (!personaName) {
21506
21978
  return fail(lineNumber, "persona requires a name");
@@ -21556,7 +22028,12 @@ function parseJourneyMap(content, palette) {
21556
22028
  if (currentTagGroup && !contentStarted) {
21557
22029
  if (indent > 0) {
21558
22030
  const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
21559
- const { label, color } = extractColor(cleanEntry, palette);
22031
+ const { label, color } = extractColor(
22032
+ cleanEntry,
22033
+ palette,
22034
+ result.diagnostics,
22035
+ lineNumber
22036
+ );
21560
22037
  if (isDefault) {
21561
22038
  currentTagGroup.defaultValue = label;
21562
22039
  } else if (currentTagGroup.entries.length === 0) {
@@ -21928,12 +22405,7 @@ function parsePyramid(content) {
21928
22405
  const lines = content.split("\n");
21929
22406
  let headerParsed = false;
21930
22407
  let currentLayer = null;
21931
- const fail = (line11, message) => {
21932
- const diag = makeDgmoError(line11, message);
21933
- result.diagnostics.push(diag);
21934
- result.error = formatDgmoError(diag);
21935
- return result;
21936
- };
22408
+ const fail = makeFail(result);
21937
22409
  const warn2 = (line11, message, severity = "warning") => {
21938
22410
  result.diagnostics.push(makeDgmoError(line11, message, severity));
21939
22411
  };
@@ -22080,12 +22552,7 @@ function parseRing(content) {
22080
22552
  const lines = content.split("\n");
22081
22553
  let headerParsed = false;
22082
22554
  let currentLayer = null;
22083
- const fail = (line11, message) => {
22084
- const diag = makeDgmoError(line11, message);
22085
- result.diagnostics.push(diag);
22086
- result.error = formatDgmoError(diag);
22087
- return result;
22088
- };
22555
+ const fail = makeFail(result);
22089
22556
  const warn2 = (line11, message, severity = "warning") => {
22090
22557
  result.diagnostics.push(makeDgmoError(line11, message, severity));
22091
22558
  };
@@ -22549,12 +23016,7 @@ function parseRaci(content, palette) {
22549
23016
  diagnostics: [],
22550
23017
  error: null
22551
23018
  };
22552
- const fail = (line11, message) => {
22553
- const diag = makeDgmoError(line11, message);
22554
- result.diagnostics.push(diag);
22555
- result.error = formatDgmoError(diag);
22556
- return result;
22557
- };
23019
+ const fail = makeFail(result);
22558
23020
  const warn2 = (line11, message, code) => {
22559
23021
  result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22560
23022
  };
@@ -23157,6 +23619,81 @@ function measureInfra(content) {
23157
23619
  const parsed = parseInfra(content);
23158
23620
  return { nodes: parsed.nodes.length };
23159
23621
  }
23622
+ function minDimsSequence(c) {
23623
+ return {
23624
+ width: Math.max((c.participants ?? 2) * 80, 320),
23625
+ height: Math.max((c.messages ?? 1) * 20 + 120, 200)
23626
+ };
23627
+ }
23628
+ function minDimsRaci(c) {
23629
+ return {
23630
+ width: Math.max((c.roles ?? 2) * 50 + 180, 300),
23631
+ height: Math.max((c.tasks ?? 1) * 28 + 80, 200)
23632
+ };
23633
+ }
23634
+ function minDimsMindmap(c) {
23635
+ return {
23636
+ width: Math.max((c.nodes ?? 3) * 30, 300),
23637
+ height: Math.max((c.depth ?? 2) * 60, 200)
23638
+ };
23639
+ }
23640
+ function minDimsTechRadar() {
23641
+ return { width: 360, height: 400 };
23642
+ }
23643
+ function minDimsHeatmap(c) {
23644
+ return {
23645
+ width: Math.max((c.columns ?? 3) * 40, 300),
23646
+ height: Math.max((c.rows ?? 3) * 30 + 60, 200)
23647
+ };
23648
+ }
23649
+ function minDimsArc(c) {
23650
+ return {
23651
+ width: 300,
23652
+ height: Math.max((c.nodes ?? 3) * 20 + 120, 200)
23653
+ };
23654
+ }
23655
+ function minDimsOrg(c) {
23656
+ return {
23657
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23658
+ height: Math.max((c.depth ?? 2) * 80, 200)
23659
+ };
23660
+ }
23661
+ function minDimsGantt(c) {
23662
+ return {
23663
+ width: 400,
23664
+ height: Math.max((c.tasks ?? 3) * 24 + 80, 200)
23665
+ };
23666
+ }
23667
+ function minDimsKanban(c) {
23668
+ return {
23669
+ width: Math.max((c.columns ?? 3) * 120, 360),
23670
+ height: 300
23671
+ };
23672
+ }
23673
+ function minDimsEntities(c) {
23674
+ return {
23675
+ width: Math.max((c.nodes ?? 2) * 140, 300),
23676
+ height: Math.max((c.nodes ?? 2) * 80, 200)
23677
+ };
23678
+ }
23679
+ function minDimsGraph(c) {
23680
+ return {
23681
+ width: Math.max((c.nodes ?? 3) * 60, 300),
23682
+ height: Math.max((c.nodes ?? 3) * 50, 200)
23683
+ };
23684
+ }
23685
+ function minDimsPert(c) {
23686
+ return {
23687
+ width: Math.max((c.tasks ?? 3) * 80, 340),
23688
+ height: Math.max((c.tasks ?? 3) * 40 + 80, 200)
23689
+ };
23690
+ }
23691
+ function minDimsInfra(c) {
23692
+ return {
23693
+ width: Math.max((c.nodes ?? 3) * 80, 300),
23694
+ height: Math.max((c.nodes ?? 3) * 60, 200)
23695
+ };
23696
+ }
23160
23697
  function isExtendedChartParser(parse) {
23161
23698
  return EXTENDED_CHART_DOORS.has(parse);
23162
23699
  }
@@ -23200,33 +23737,50 @@ var init_chart_type_registry = __esm({
23200
23737
  id: "sequence",
23201
23738
  category: "diagram",
23202
23739
  parse: parseSequenceDgmo,
23203
- measure: measureSequence
23740
+ measure: measureSequence,
23741
+ minDims: minDimsSequence
23204
23742
  },
23205
23743
  {
23206
23744
  id: "flowchart",
23207
23745
  category: "diagram",
23208
23746
  parse: parseFlowchart,
23209
- measure: measureFlowchart
23747
+ measure: measureFlowchart,
23748
+ minDims: minDimsGraph
23210
23749
  },
23211
23750
  {
23212
23751
  id: "class",
23213
23752
  category: "diagram",
23214
23753
  parse: parseClassDiagram,
23215
- measure: measureClass
23754
+ measure: measureClass,
23755
+ minDims: minDimsEntities
23756
+ },
23757
+ {
23758
+ id: "er",
23759
+ category: "diagram",
23760
+ parse: parseERDiagram,
23761
+ measure: measureER,
23762
+ minDims: minDimsEntities
23216
23763
  },
23217
- { id: "er", category: "diagram", parse: parseERDiagram, measure: measureER },
23218
23764
  {
23219
23765
  id: "state",
23220
23766
  category: "diagram",
23221
23767
  parse: parseState,
23222
- measure: measureStateGraph
23768
+ measure: measureStateGraph,
23769
+ minDims: minDimsGraph
23770
+ },
23771
+ {
23772
+ id: "org",
23773
+ category: "diagram",
23774
+ parse: parseOrg,
23775
+ measure: measureOrg,
23776
+ minDims: minDimsOrg
23223
23777
  },
23224
- { id: "org", category: "diagram", parse: parseOrg, measure: measureOrg },
23225
23778
  {
23226
23779
  id: "kanban",
23227
23780
  category: "diagram",
23228
23781
  parse: parseKanban,
23229
- measure: measureKanban
23782
+ measure: measureKanban,
23783
+ minDims: minDimsKanban
23230
23784
  },
23231
23785
  { id: "c4", category: "diagram", parse: parseC4 },
23232
23786
  { id: "sitemap", category: "diagram", parse: parseSitemap },
@@ -23234,25 +23788,40 @@ var init_chart_type_registry = __esm({
23234
23788
  id: "infra",
23235
23789
  category: "diagram",
23236
23790
  parse: parseInfra,
23237
- measure: measureInfra
23791
+ measure: measureInfra,
23792
+ minDims: minDimsInfra
23238
23793
  },
23239
23794
  {
23240
23795
  id: "gantt",
23241
23796
  category: "diagram",
23242
23797
  parse: parseGantt,
23243
- measure: measureGantt
23798
+ measure: measureGantt,
23799
+ minDims: minDimsGantt
23800
+ },
23801
+ {
23802
+ id: "pert",
23803
+ category: "diagram",
23804
+ parse: parsePert,
23805
+ measure: measurePert,
23806
+ minDims: minDimsPert
23244
23807
  },
23245
- { id: "pert", category: "diagram", parse: parsePert, measure: measurePert },
23246
23808
  { id: "boxes-and-lines", category: "diagram", parse: parseBoxesAndLines },
23247
23809
  {
23248
23810
  id: "mindmap",
23249
23811
  category: "diagram",
23250
23812
  parse: parseMindmap,
23251
- measure: measureMindmap
23813
+ measure: measureMindmap,
23814
+ minDims: minDimsMindmap
23252
23815
  },
23253
23816
  { id: "wireframe", category: "diagram", parse: parseWireframe },
23254
23817
  { id: "journey-map", category: "diagram", parse: parseJourneyMap },
23255
- { id: "raci", category: "diagram", parse: parseRaci, measure: measureRaci },
23818
+ {
23819
+ id: "raci",
23820
+ category: "diagram",
23821
+ parse: parseRaci,
23822
+ measure: measureRaci,
23823
+ minDims: minDimsRaci
23824
+ },
23256
23825
  { id: "rasci", category: "diagram", parse: parseRaci, measure: measureRaci },
23257
23826
  { id: "daci", category: "diagram", parse: parseRaci, measure: measureRaci },
23258
23827
  // ── Standard ECharts charts (parseChart) ──────────────────
@@ -23274,7 +23843,8 @@ var init_chart_type_registry = __esm({
23274
23843
  id: "heatmap",
23275
23844
  category: "data-chart",
23276
23845
  parse: parseHeatmap,
23277
- measure: measureHeatmap
23846
+ measure: measureHeatmap,
23847
+ minDims: minDimsHeatmap
23278
23848
  },
23279
23849
  { id: "funnel", category: "data-chart", parse: parseFunnel },
23280
23850
  // ── D3 visualizations — own per-viz parser door (Story 109.2) ──
@@ -23284,7 +23854,8 @@ var init_chart_type_registry = __esm({
23284
23854
  id: "arc",
23285
23855
  category: "visualization",
23286
23856
  parse: parseArc,
23287
- measure: measureArc
23857
+ measure: measureArc,
23858
+ minDims: minDimsArc
23288
23859
  },
23289
23860
  { id: "timeline", category: "visualization", parse: parseTimeline },
23290
23861
  { id: "venn", category: "visualization", parse: parseVenn },
@@ -23294,7 +23865,8 @@ var init_chart_type_registry = __esm({
23294
23865
  id: "tech-radar",
23295
23866
  category: "visualization",
23296
23867
  parse: parseTechRadar,
23297
- measure: measureTechRadar
23868
+ measure: measureTechRadar,
23869
+ minDims: minDimsTechRadar
23298
23870
  },
23299
23871
  { id: "cycle", category: "visualization", parse: parseCycle },
23300
23872
  { id: "pyramid", category: "visualization", parse: parsePyramid },
@@ -24441,6 +25013,63 @@ var init_export_container = __esm({
24441
25013
  }
24442
25014
  });
24443
25015
 
25016
+ // src/utils/card.ts
25017
+ function renderNodeCard(container, opts) {
25018
+ const rect = container.append("rect").attr("x", 0).attr("y", 0).attr("width", opts.width).attr("height", opts.height).attr("rx", opts.rx).attr("fill", opts.fill).attr("stroke", opts.stroke).attr("stroke-width", opts.strokeWidth);
25019
+ if (opts.dashed) {
25020
+ rect.attr("stroke-dasharray", "6 3");
25021
+ }
25022
+ container.append("text").attr("x", opts.width / 2).attr("y", opts.headerHeight / 2 + opts.labelFontSize / 2 - 2).attr("text-anchor", "middle").attr("fill", opts.labelColor).attr("font-size", opts.labelFontSize).attr("font-weight", "bold").text(opts.label);
25023
+ const meta = opts.meta;
25024
+ if (!meta || meta.rows.length === 0) return;
25025
+ container.append("line").attr("x1", 0).attr("y1", opts.headerHeight).attr("x2", opts.width).attr("y2", opts.headerHeight).attr("stroke", meta.separatorColor).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
25026
+ const keyX = meta.keyX ?? 10;
25027
+ const maxKeyWidth = Math.max(
25028
+ ...meta.rows.map(([key]) => measureText(`${key}: `, meta.fontSize))
25029
+ );
25030
+ const valueX = keyX + maxKeyWidth;
25031
+ const metaStartY = opts.headerHeight + meta.separatorGap + meta.fontSize;
25032
+ for (let i = 0; i < meta.rows.length; i++) {
25033
+ const [displayKey, value] = meta.rows[i];
25034
+ const rowY = metaStartY + i * meta.lineHeight;
25035
+ container.append("text").attr("x", keyX).attr("y", rowY).attr("fill", meta.textColor).attr("font-size", meta.fontSize).text(`${displayKey}: `);
25036
+ container.append("text").attr("x", valueX).attr("y", rowY).attr("fill", meta.textColor).attr("font-size", meta.fontSize).text(value);
25037
+ }
25038
+ }
25039
+ function renderCollapseBar(container, opts) {
25040
+ container.append("clipPath").attr("id", opts.clipId).append("rect").attr("width", opts.width).attr("height", opts.height).attr("rx", opts.rx);
25041
+ container.append("rect").attr("x", opts.inset).attr("y", opts.height - opts.barHeight).attr("width", opts.width - opts.inset * 2).attr("height", opts.barHeight).attr("fill", opts.fill).attr("clip-path", `url(#${opts.clipId})`).attr("class", opts.className);
25042
+ }
25043
+ var init_card = __esm({
25044
+ "src/utils/card.ts"() {
25045
+ "use strict";
25046
+ init_text_measure();
25047
+ }
25048
+ });
25049
+
25050
+ // src/utils/visual-conventions.ts
25051
+ var NODE_STROKE_WIDTH, EDGE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, COLLAPSE_BAR_INSET, HEADER_HEIGHT2, LABEL_FONT_SIZE2, META_FONT_SIZE2, META_LINE_HEIGHT2, SEPARATOR_GAP2, COLLAPSE_BAR_HEIGHT, CONTAINER_HEADER_HEIGHT, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2;
25052
+ var init_visual_conventions = __esm({
25053
+ "src/utils/visual-conventions.ts"() {
25054
+ "use strict";
25055
+ NODE_STROKE_WIDTH = 1.5;
25056
+ EDGE_STROKE_WIDTH = 1.5;
25057
+ CARD_RADIUS = 6;
25058
+ CONTAINER_RADIUS = 8;
25059
+ COLLAPSE_BAR_INSET = 0;
25060
+ HEADER_HEIGHT2 = 28;
25061
+ LABEL_FONT_SIZE2 = 13;
25062
+ META_FONT_SIZE2 = 11;
25063
+ META_LINE_HEIGHT2 = 16;
25064
+ SEPARATOR_GAP2 = 6;
25065
+ COLLAPSE_BAR_HEIGHT = 6;
25066
+ CONTAINER_HEADER_HEIGHT = 28;
25067
+ CONTAINER_LABEL_FONT_SIZE = 13;
25068
+ CONTAINER_META_FONT_SIZE = 11;
25069
+ CONTAINER_META_LINE_HEIGHT2 = 16;
25070
+ }
25071
+ });
25072
+
24444
25073
  // src/org/renderer.ts
24445
25074
  var renderer_exports = {};
24446
25075
  __export(renderer_exports, {
@@ -24458,7 +25087,7 @@ function nodeStroke(palette, nodeColor2) {
24458
25087
  }
24459
25088
  function containerFill(palette, isDark, nodeColor2) {
24460
25089
  if (nodeColor2) {
24461
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
25090
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
24462
25091
  }
24463
25092
  return mix(palette.surface, palette.bg, 40);
24464
25093
  }
@@ -24598,9 +25227,16 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24598
25227
  }
24599
25228
  }
24600
25229
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
24601
- const clipId = `clip-${c.nodeId}`;
24602
- cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", sContainerRadius);
24603
- cG.append("rect").attr("x", sCollapseBarInset).attr("y", c.height - sCollapseBarHeight).attr("width", c.width - sCollapseBarInset * 2).attr("height", sCollapseBarHeight).attr("fill", containerStroke(palette, colorOff ? void 0 : c.color)).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
25230
+ renderCollapseBar(cG, {
25231
+ width: c.width,
25232
+ height: c.height,
25233
+ barHeight: sCollapseBarHeight,
25234
+ inset: sCollapseBarInset,
25235
+ rx: sContainerRadius,
25236
+ fill: containerStroke(palette, colorOff ? void 0 : c.color),
25237
+ clipId: `clip-${c.nodeId}`,
25238
+ className: "org-collapse-bar"
25239
+ });
24604
25240
  }
24605
25241
  if (!exportDims && c.hasChildren && !rootNodeIds.has(c.nodeId)) {
24606
25242
  const iconSize = 14;
@@ -24609,10 +25245,15 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24609
25245
  const iconY = iconPad;
24610
25246
  const focusG = cG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", c.nodeId).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24611
25247
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25248
+ const iconColor = contrastText(
25249
+ fill2,
25250
+ palette.textOnFillLight,
25251
+ palette.textOnFillDark
25252
+ );
24612
25253
  const cx = iconSize / 2;
24613
25254
  const cy = iconSize / 2;
24614
- 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);
24615
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25255
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25256
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24616
25257
  }
24617
25258
  }
24618
25259
  for (const edge of layout.edges) {
@@ -24651,42 +25292,48 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24651
25292
  solid
24652
25293
  );
24653
25294
  const stroke2 = nodeStroke(palette, colorOff ? void 0 : node.color);
24654
- 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);
24655
- if (node.isContainer) {
24656
- rect.attr("stroke-dasharray", "6 3");
24657
- }
24658
25295
  const labelColor = contrastText(
24659
25296
  fill2,
24660
25297
  palette.textOnFillLight,
24661
25298
  palette.textOnFillDark
24662
25299
  );
24663
- 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);
24664
25300
  const metaEntries = Object.entries(node.metadata);
24665
- if (metaEntries.length > 0) {
24666
- 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);
24667
- const metaDisplayKeys = metaEntries.map(
24668
- ([k]) => displayNames.get(k) ?? k
24669
- );
24670
- const maxKeyWidth = Math.max(
24671
- ...metaDisplayKeys.map((k) => measureText(`${k}: `, sMetaFontSize))
24672
- );
24673
- const valueX = 10 + maxKeyWidth;
24674
- const metaStartY = sHeaderHeight + sSeparatorGap + sMetaFontSize;
24675
- for (let i = 0; i < metaEntries.length; i++) {
24676
- const [, value] = metaEntries[i];
24677
- const displayKey = metaDisplayKeys[i];
24678
- const rowY = metaStartY + i * sMetaLineHeight;
24679
- nodeG.append("text").attr("x", 10).attr("y", rowY).attr("fill", labelColor).attr("font-size", sMetaFontSize).text(`${displayKey}: `);
24680
- nodeG.append("text").attr("x", valueX).attr("y", rowY).attr("fill", labelColor).attr("font-size", sMetaFontSize).text(value);
25301
+ renderNodeCard(nodeG, {
25302
+ width: node.width,
25303
+ height: node.height,
25304
+ rx: sCardRadius,
25305
+ fill: fill2,
25306
+ stroke: stroke2,
25307
+ strokeWidth: sNodeStrokeWidth,
25308
+ ...node.isContainer && { dashed: true },
25309
+ label: node.label,
25310
+ labelColor,
25311
+ labelFontSize: sLabelFontSize,
25312
+ headerHeight: sHeaderHeight,
25313
+ ...metaEntries.length > 0 && {
25314
+ meta: {
25315
+ rows: metaEntries.map(
25316
+ ([k, value]) => [displayNames.get(k) ?? k, value]
25317
+ ),
25318
+ fontSize: sMetaFontSize,
25319
+ lineHeight: sMetaLineHeight,
25320
+ separatorGap: sSeparatorGap,
25321
+ separatorColor: solid ? labelColor : stroke2,
25322
+ textColor: labelColor
25323
+ }
24681
25324
  }
24682
- }
25325
+ });
24683
25326
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
24684
- const clipId = `clip-${node.id}`;
24685
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", sCardRadius);
24686
- nodeG.append("rect").attr("x", sCollapseBarInset).attr("y", node.height - sCollapseBarHeight).attr("width", node.width - sCollapseBarInset * 2).attr("height", sCollapseBarHeight).attr(
24687
- "fill",
24688
- solid ? labelColor : nodeStroke(palette, colorOff ? void 0 : node.color)
24689
- ).attr("clip-path", `url(#${clipId})`).attr("class", "org-collapse-bar");
25327
+ renderCollapseBar(nodeG, {
25328
+ width: node.width,
25329
+ height: node.height,
25330
+ barHeight: sCollapseBarHeight,
25331
+ inset: sCollapseBarInset,
25332
+ rx: sCardRadius,
25333
+ fill: solid ? labelColor : nodeStroke(palette, colorOff ? void 0 : node.color),
25334
+ clipId: `clip-${node.id}`,
25335
+ className: "org-collapse-bar"
25336
+ });
24690
25337
  }
24691
25338
  if (!exportDims && node.hasChildren && !rootNodeIds.has(node.id)) {
24692
25339
  const iconSize = 14;
@@ -24695,10 +25342,11 @@ function renderOrg(container, parsed, layout, palette, isDark, onClickItem, expo
24695
25342
  const iconY = iconPad;
24696
25343
  const focusG = nodeG.append("g").attr("class", "org-focus-icon").attr("data-focus-node", node.id).attr("data-export-ignore", "true").attr("transform", `translate(${iconX}, ${iconY})`);
24697
25344
  focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
25345
+ const iconColor = labelColor;
24698
25346
  const cx = iconSize / 2;
24699
25347
  const cy = iconSize / 2;
24700
- 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);
24701
- focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
25348
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", "none").attr("stroke", iconColor).attr("stroke-width", 1.5);
25349
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", iconColor);
24702
25350
  }
24703
25351
  }
24704
25352
  if (hasAncestorTrail) {
@@ -24830,7 +25478,7 @@ function renderOrgForExport(content, theme, palette) {
24830
25478
  return extractExportSvg(container, theme);
24831
25479
  });
24832
25480
  }
24833
- var d3Selection6, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, LABEL_FONT_SIZE2, META_FONT_SIZE2, META_LINE_HEIGHT2, HEADER_HEIGHT2, SEPARATOR_GAP2, EDGE_STROKE_WIDTH, NODE_STROKE_WIDTH, CARD_RADIUS, CONTAINER_RADIUS, CONTAINER_LABEL_FONT_SIZE, CONTAINER_META_FONT_SIZE, CONTAINER_META_LINE_HEIGHT2, CONTAINER_HEADER_HEIGHT, COLLAPSE_BAR_HEIGHT, COLLAPSE_BAR_INSET, ANCESTOR_DOT_R, ANCESTOR_LABEL_FONT_SIZE, ANCESTOR_ROW_HEIGHT, ANCESTOR_TRAIL_BOTTOM_GAP, LEGEND_FIXED_GAP;
25481
+ var d3Selection6, DIAGRAM_PADDING, MAX_SCALE, TITLE_HEIGHT, ANCESTOR_DOT_R, ANCESTOR_LABEL_FONT_SIZE, ANCESTOR_ROW_HEIGHT, ANCESTOR_TRAIL_BOTTOM_GAP, LEGEND_FIXED_GAP;
24834
25482
  var init_renderer = __esm({
24835
25483
  "src/org/renderer.ts"() {
24836
25484
  "use strict";
@@ -24844,27 +25492,14 @@ var init_renderer = __esm({
24844
25492
  init_layout();
24845
25493
  init_legend_constants();
24846
25494
  init_legend_integration();
25495
+ init_card();
24847
25496
  init_text_measure();
24848
25497
  init_legend_layout();
24849
25498
  init_title_constants();
25499
+ init_visual_conventions();
24850
25500
  DIAGRAM_PADDING = 20;
24851
25501
  MAX_SCALE = 3;
24852
25502
  TITLE_HEIGHT = 30;
24853
- LABEL_FONT_SIZE2 = 13;
24854
- META_FONT_SIZE2 = 11;
24855
- META_LINE_HEIGHT2 = 16;
24856
- HEADER_HEIGHT2 = 28;
24857
- SEPARATOR_GAP2 = 6;
24858
- EDGE_STROKE_WIDTH = 1.5;
24859
- NODE_STROKE_WIDTH = 1.5;
24860
- CARD_RADIUS = 6;
24861
- CONTAINER_RADIUS = 8;
24862
- CONTAINER_LABEL_FONT_SIZE = 13;
24863
- CONTAINER_META_FONT_SIZE = 11;
24864
- CONTAINER_META_LINE_HEIGHT2 = 16;
24865
- CONTAINER_HEADER_HEIGHT = 28;
24866
- COLLAPSE_BAR_HEIGHT = 6;
24867
- COLLAPSE_BAR_INSET = 0;
24868
25503
  ANCESTOR_DOT_R = 4;
24869
25504
  ANCESTOR_LABEL_FONT_SIZE = 11;
24870
25505
  ANCESTOR_ROW_HEIGHT = 22;
@@ -25632,7 +26267,7 @@ function nodeStroke2(_palette, nodeColor2) {
25632
26267
  }
25633
26268
  function containerFill2(palette, isDark, nodeColor2) {
25634
26269
  if (nodeColor2) {
25635
- return mix(nodeColor2, isDark ? palette.surface : palette.bg, 10);
26270
+ return mix(nodeColor2, themeBaseBg(palette, isDark), 10);
25636
26271
  }
25637
26272
  return mix(palette.surface, palette.bg, 40);
25638
26273
  }
@@ -25646,21 +26281,21 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25646
26281
  if (width <= 0 || height <= 0) return;
25647
26282
  const ctx = ScaleContext.identity();
25648
26283
  const sDiagramPadding = ctx.aesthetic(DIAGRAM_PADDING2);
25649
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE4);
25650
- const sMetaFontSize = ctx.text(META_FONT_SIZE4);
25651
- const sMetaLineHeight = ctx.structural(META_LINE_HEIGHT4);
25652
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT4);
25653
- const sSeparatorGap = ctx.structural(SEPARATOR_GAP4);
25654
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH2);
25655
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH2);
26284
+ const sLabelFontSize = ctx.text(LABEL_FONT_SIZE2);
26285
+ const sMetaFontSize = ctx.text(META_FONT_SIZE2);
26286
+ const sMetaLineHeight = ctx.structural(META_LINE_HEIGHT2);
26287
+ const sHeaderHeight = ctx.structural(HEADER_HEIGHT2);
26288
+ const sSeparatorGap = ctx.structural(SEPARATOR_GAP2);
26289
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
26290
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
25656
26291
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE);
25657
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE3);
25658
- const sContainerMetaFontSize = ctx.text(CONTAINER_META_FONT_SIZE2);
25659
- const sContainerMetaLineHeight = ctx.structural(CONTAINER_META_LINE_HEIGHT4);
25660
- const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT2);
26292
+ const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE);
26293
+ const sContainerMetaFontSize = ctx.text(CONTAINER_META_FONT_SIZE);
26294
+ const sContainerMetaLineHeight = ctx.structural(CONTAINER_META_LINE_HEIGHT2);
26295
+ const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT);
25661
26296
  const sTitleFontSize = ctx.text(TITLE_FONT_SIZE);
25662
26297
  const sTitleHeight = ctx.structural(TITLE_HEIGHT2);
25663
- const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT2);
26298
+ const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
25664
26299
  const sLegendFixedGap = ctx.aesthetic(LEGEND_FIXED_GAP2);
25665
26300
  const hasLegend = layout.legend.length > 0;
25666
26301
  const layoutLegendShift = LEGEND_HEIGHT + LEGEND_GROUP_GAP;
@@ -25751,7 +26386,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25751
26386
  }
25752
26387
  const fill2 = containerFill2(palette, isDark, c.color);
25753
26388
  const stroke2 = containerStroke2(palette, c.color);
25754
- cG.append("rect").attr("x", 0).attr("y", 0).attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS2).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-opacity", 0.35).attr("stroke-width", sNodeStrokeWidth);
26389
+ cG.append("rect").attr("x", 0).attr("y", 0).attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-opacity", 0.35).attr("stroke-width", sNodeStrokeWidth);
25755
26390
  cG.append("text").attr("x", c.width / 2).attr("y", sContainerHeaderHeight / 2 + sContainerLabelFontSize / 2 - 2).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-size", sContainerLabelFontSize).attr("font-weight", "bold").text(c.label);
25756
26391
  const metaEntries = Object.entries(c.metadata);
25757
26392
  if (metaEntries.length > 0) {
@@ -25776,7 +26411,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25776
26411
  }
25777
26412
  if (!exportDims && c.hiddenCount && c.hiddenCount > 0) {
25778
26413
  const clipId = `clip-${c.nodeId}`;
25779
- cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS2);
26414
+ cG.append("clipPath").attr("id", clipId).append("rect").attr("width", c.width).attr("height", c.height).attr("rx", CONTAINER_RADIUS);
25780
26415
  cG.append("rect").attr("y", c.height - sCollapseBarHeight).attr("width", c.width).attr("height", sCollapseBarHeight).attr("fill", c.color ?? palette.primary).attr("opacity", 0.5).attr("clip-path", `url(#${clipId})`);
25781
26416
  cG.append("text").attr("x", c.width / 2).attr("y", c.height - sCollapseBarHeight - 6).attr("text-anchor", "middle").attr("fill", palette.textMuted).attr("font-size", sMetaFontSize).text(`+${c.hiddenCount}`);
25782
26417
  }
@@ -25815,13 +26450,23 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25815
26450
  const solid = parsed.options["solid-fill"] === "on";
25816
26451
  const fill2 = nodeFill2(palette, isDark, node.color, solid);
25817
26452
  const stroke2 = nodeStroke2(palette, node.color);
25818
- 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);
25819
26453
  const labelColor = contrastText(
25820
26454
  fill2,
25821
26455
  palette.textOnFillLight,
25822
26456
  palette.textOnFillDark
25823
26457
  );
25824
- nodeG.append("text").attr("x", node.width / 2).attr("y", sHeaderHeight / 2 + sLabelFontSize / 2 - 2).attr("text-anchor", "middle").attr("fill", labelColor).attr("font-size", sLabelFontSize).attr("font-weight", "bold").text(node.label);
26458
+ renderNodeCard(nodeG, {
26459
+ width: node.width,
26460
+ height: node.height,
26461
+ rx: CARD_RADIUS,
26462
+ fill: fill2,
26463
+ stroke: stroke2,
26464
+ strokeWidth: sNodeStrokeWidth,
26465
+ label: node.label,
26466
+ labelColor,
26467
+ labelFontSize: sLabelFontSize,
26468
+ headerHeight: sHeaderHeight
26469
+ });
25825
26470
  const metaEntries = Object.entries(node.metadata);
25826
26471
  if (metaEntries.length > 0) {
25827
26472
  nodeG.append("line").attr("x1", 0).attr("y1", sHeaderHeight).attr("x2", node.width).attr("y2", sHeaderHeight).attr("stroke", solid ? labelColor : stroke2).attr("stroke-opacity", 0.3);
@@ -25856,7 +26501,7 @@ function renderSitemap(container, parsed, layout, palette, isDark, onClickItem,
25856
26501
  }
25857
26502
  if (!exportDims && node.hiddenCount && node.hiddenCount > 0) {
25858
26503
  const clipId = `clip-${node.id}`;
25859
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS2);
26504
+ nodeG.append("clipPath").attr("id", clipId).append("rect").attr("width", node.width).attr("height", node.height).attr("rx", CARD_RADIUS);
25860
26505
  nodeG.append("rect").attr("y", node.height - sCollapseBarHeight).attr("width", node.width).attr("height", sCollapseBarHeight).attr("fill", solid ? labelColor : node.color ?? palette.primary).attr("opacity", 0.5).attr("clip-path", `url(#${clipId})`);
25861
26506
  }
25862
26507
  }
@@ -25986,7 +26631,7 @@ async function renderSitemapForExport(content, theme, palette) {
25986
26631
  document.body.removeChild(container);
25987
26632
  return svgHtml;
25988
26633
  }
25989
- var d3Selection7, d3Shape2, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, LABEL_FONT_SIZE4, META_FONT_SIZE4, META_LINE_HEIGHT4, HEADER_HEIGHT4, SEPARATOR_GAP4, EDGE_STROKE_WIDTH2, NODE_STROKE_WIDTH2, CARD_RADIUS2, CONTAINER_RADIUS2, CONTAINER_LABEL_FONT_SIZE3, CONTAINER_META_FONT_SIZE2, CONTAINER_META_LINE_HEIGHT4, CONTAINER_HEADER_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, COLLAPSE_BAR_HEIGHT2, LEGEND_FIXED_GAP2, lineGenerator, lineGeneratorLinear;
26634
+ var d3Selection7, d3Shape2, DIAGRAM_PADDING2, MAX_SCALE2, TITLE_HEIGHT2, ARROWHEAD_W, ARROWHEAD_H, EDGE_LABEL_FONT_SIZE, LEGEND_FIXED_GAP2, lineGenerator, lineGeneratorLinear;
25990
26635
  var init_renderer2 = __esm({
25991
26636
  "src/sitemap/renderer.ts"() {
25992
26637
  "use strict";
@@ -26001,27 +26646,15 @@ var init_renderer2 = __esm({
26001
26646
  init_legend_integration();
26002
26647
  init_legend_layout();
26003
26648
  init_scaling();
26649
+ init_card();
26004
26650
  init_title_constants();
26651
+ init_visual_conventions();
26005
26652
  DIAGRAM_PADDING2 = 20;
26006
26653
  MAX_SCALE2 = 3;
26007
26654
  TITLE_HEIGHT2 = 30;
26008
- LABEL_FONT_SIZE4 = 13;
26009
- META_FONT_SIZE4 = 11;
26010
- META_LINE_HEIGHT4 = 16;
26011
- HEADER_HEIGHT4 = 28;
26012
- SEPARATOR_GAP4 = 6;
26013
- EDGE_STROKE_WIDTH2 = 1.5;
26014
- NODE_STROKE_WIDTH2 = 1.5;
26015
- CARD_RADIUS2 = 6;
26016
- CONTAINER_RADIUS2 = 8;
26017
- CONTAINER_LABEL_FONT_SIZE3 = 13;
26018
- CONTAINER_META_FONT_SIZE2 = 11;
26019
- CONTAINER_META_LINE_HEIGHT4 = 16;
26020
- CONTAINER_HEADER_HEIGHT2 = 28;
26021
26655
  ARROWHEAD_W = 10;
26022
26656
  ARROWHEAD_H = 7;
26023
26657
  EDGE_LABEL_FONT_SIZE = 11;
26024
- COLLAPSE_BAR_HEIGHT2 = 6;
26025
26658
  LEGEND_FIXED_GAP2 = 8;
26026
26659
  lineGenerator = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveBasis);
26027
26660
  lineGeneratorLinear = d3Shape2.line().x((d) => d.x).y((d) => d.y).curve(d3Shape2.curveLinear);
@@ -26188,7 +26821,7 @@ function renderKanban(container, parsed, palette, isDark, options) {
26188
26821
  const sCardMetaLineHeight = ctx.structural(CARD_META_LINE_HEIGHT);
26189
26822
  const sCardSeparatorGap = ctx.structural(CARD_SEPARATOR_GAP);
26190
26823
  const sCardGap = ctx.aesthetic(CARD_GAP);
26191
- const sCardRadius = ctx.structural(CARD_RADIUS3);
26824
+ const sCardRadius = ctx.structural(CARD_RADIUS);
26192
26825
  const sCardPaddingX = ctx.aesthetic(CARD_PADDING_X);
26193
26826
  const sCardPaddingY = ctx.aesthetic(CARD_PADDING_Y);
26194
26827
  const sCardStrokeWidth = ctx.structural(CARD_STROKE_WIDTH);
@@ -26568,7 +27201,7 @@ function computeSwimlaneLayout(parsed, buckets, baseLayout, collapsedLanes, coll
26568
27201
  const totalHeight = laneY - sLaneGap + sColumnPadding + sDiagramPadding;
26569
27202
  return { columnXs, lanes, totalWidth, totalHeight, startY };
26570
27203
  }
26571
- function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, isDark, activeTagGroup, collapsedLanes, collapsedColumns, hiddenMetaGroups, sDiagramPadding = DIAGRAM_PADDING3, sTitleHeight = TITLE_HEIGHT3, sLaneHeaderWidth = LANE_HEADER_WIDTH, sColumnGap = COLUMN_GAP, sCollapsedColumnWidth = COLLAPSED_COLUMN_WIDTH, sColumnHeaderHeight = COLUMN_HEADER_HEIGHT, sColumnPadding = COLUMN_PADDING, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardPaddingY = CARD_PADDING_Y, sCardGap = CARD_GAP, sCollapsedLaneHeight = COLLAPSED_LANE_HEIGHT, sLaneGap = LANE_GAP, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT, sColumnHeaderFontSize = COLUMN_HEADER_FONT_SIZE, sColumnRadius = COLUMN_RADIUS, sColumnHeaderRadius = COLUMN_HEADER_RADIUS, sWipFontSize = WIP_FONT_SIZE, sCardRadius = CARD_RADIUS3, sCardPaddingX = CARD_PADDING_X, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE) {
27204
+ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, isDark, activeTagGroup, collapsedLanes, collapsedColumns, hiddenMetaGroups, sDiagramPadding = DIAGRAM_PADDING3, sTitleHeight = TITLE_HEIGHT3, sLaneHeaderWidth = LANE_HEADER_WIDTH, sColumnGap = COLUMN_GAP, sCollapsedColumnWidth = COLLAPSED_COLUMN_WIDTH, sColumnHeaderHeight = COLUMN_HEADER_HEIGHT, sColumnPadding = COLUMN_PADDING, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardPaddingY = CARD_PADDING_Y, sCardGap = CARD_GAP, sCollapsedLaneHeight = COLLAPSED_LANE_HEIGHT, sLaneGap = LANE_GAP, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT, sColumnHeaderFontSize = COLUMN_HEADER_FONT_SIZE, sColumnRadius = COLUMN_RADIUS, sColumnHeaderRadius = COLUMN_HEADER_RADIUS, sWipFontSize = WIP_FONT_SIZE, sCardRadius = CARD_RADIUS, sCardPaddingX = CARD_PADDING_X, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE) {
26572
27205
  const visibleColumns = parsed.columns.filter((c) => !isArchiveColumn(c.name));
26573
27206
  const buckets = bucketCardsBySwimlane(visibleColumns, swimlaneGroup);
26574
27207
  const grid = computeSwimlaneLayout(
@@ -26708,7 +27341,7 @@ function renderSwimlaneBoard(svg, parsed, baseLayout, swimlaneGroup, palette, is
26708
27341
  }
26709
27342
  }
26710
27343
  }
26711
- function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palette, isDark, hiddenMetaGroups, solid, sCardRadius = CARD_RADIUS3, sCardPaddingX = CARD_PADDING_X, sCardPaddingY = CARD_PADDING_Y, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT) {
27344
+ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palette, isDark, hiddenMetaGroups, solid, sCardRadius = CARD_RADIUS, sCardPaddingX = CARD_PADDING_X, sCardPaddingY = CARD_PADDING_Y, sCardStrokeWidth = CARD_STROKE_WIDTH, sCardTitleFontSize = CARD_TITLE_FONT_SIZE, sCardMetaFontSize = CARD_META_FONT_SIZE, sCardHeaderHeight = CARD_HEADER_HEIGHT, sCardSeparatorGap = CARD_SEPARATOR_GAP, sCardMetaLineHeight = CARD_META_LINE_HEIGHT) {
26712
27345
  const card = cardLayout.card;
26713
27346
  const resolvedColor = resolveCardTagColor(card, tagGroups, activeTagGroup);
26714
27347
  const tagMeta = resolveCardTagMeta(card, tagGroups, hiddenMetaGroups);
@@ -26758,7 +27391,7 @@ function renderSwimlaneCard(parent, cardLayout, tagGroups, activeTagGroup, palet
26758
27391
  }
26759
27392
  }
26760
27393
  }
26761
- var d3Selection8, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_RADIUS3, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS, COLLAPSED_COLUMN_WIDTH, COLLAPSED_LANE_HEIGHT, LANE_HEADER_WIDTH, LANE_GAP;
27394
+ var d3Selection8, DIAGRAM_PADDING3, COLUMN_GAP, COLUMN_HEADER_HEIGHT, COLUMN_PADDING, COLUMN_MIN_WIDTH, CARD_HEADER_HEIGHT, CARD_META_LINE_HEIGHT, CARD_SEPARATOR_GAP, CARD_GAP, CARD_PADDING_X, CARD_PADDING_Y, CARD_STROKE_WIDTH, TITLE_HEIGHT3, COLUMN_HEADER_FONT_SIZE, CARD_TITLE_FONT_SIZE, CARD_META_FONT_SIZE, WIP_FONT_SIZE, COLUMN_RADIUS, COLUMN_HEADER_RADIUS, COLLAPSED_COLUMN_WIDTH, COLLAPSED_LANE_HEIGHT, LANE_HEADER_WIDTH, LANE_GAP;
26762
27395
  var init_renderer3 = __esm({
26763
27396
  "src/kanban/renderer.ts"() {
26764
27397
  "use strict";
@@ -26773,6 +27406,7 @@ var init_renderer3 = __esm({
26773
27406
  init_legend_integration();
26774
27407
  init_scaling();
26775
27408
  init_text_measure();
27409
+ init_visual_conventions();
26776
27410
  init_title_constants();
26777
27411
  DIAGRAM_PADDING3 = 20;
26778
27412
  COLUMN_GAP = 16;
@@ -26783,7 +27417,6 @@ var init_renderer3 = __esm({
26783
27417
  CARD_META_LINE_HEIGHT = 14;
26784
27418
  CARD_SEPARATOR_GAP = 4;
26785
27419
  CARD_GAP = 8;
26786
- CARD_RADIUS3 = 6;
26787
27420
  CARD_PADDING_X = 10;
26788
27421
  CARD_PADDING_Y = 6;
26789
27422
  CARD_STROKE_WIDTH = 1.5;
@@ -27084,8 +27717,8 @@ function renderClassDiagram(container, parsed, layout, palette, isDark, onClickI
27084
27717
  const sClassFontSize = ctx.text(CLASS_FONT_SIZE2);
27085
27718
  const sMemberFontSize = ctx.text(MEMBER_FONT_SIZE2);
27086
27719
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE2);
27087
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH3);
27088
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH3);
27720
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
27721
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
27089
27722
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT2);
27090
27723
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y2);
27091
27724
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X);
@@ -27344,7 +27977,7 @@ function renderClassDiagramForExport(content, theme, palette) {
27344
27977
  return extractExportSvg(container, theme);
27345
27978
  });
27346
27979
  }
27347
- var d3Selection9, d3Shape3, DIAGRAM_PADDING4, MAX_SCALE3, CLASS_FONT_SIZE2, MEMBER_FONT_SIZE2, EDGE_LABEL_FONT_SIZE2, EDGE_STROKE_WIDTH3, NODE_STROKE_WIDTH3, MEMBER_LINE_HEIGHT2, COMPARTMENT_PADDING_Y2, MEMBER_PADDING_X, CLASS_TYPE_MAP, CLASS_TYPE_ORDER, LEGEND_GROUP_NAME, lineGenerator2;
27980
+ var d3Selection9, d3Shape3, DIAGRAM_PADDING4, MAX_SCALE3, CLASS_FONT_SIZE2, MEMBER_FONT_SIZE2, EDGE_LABEL_FONT_SIZE2, MEMBER_LINE_HEIGHT2, COMPARTMENT_PADDING_Y2, MEMBER_PADDING_X, CLASS_TYPE_MAP, CLASS_TYPE_ORDER, LEGEND_GROUP_NAME, lineGenerator2;
27348
27981
  var init_renderer4 = __esm({
27349
27982
  "src/class/renderer.ts"() {
27350
27983
  "use strict";
@@ -27361,13 +27994,12 @@ var init_renderer4 = __esm({
27361
27994
  init_scaling();
27362
27995
  init_text_measure();
27363
27996
  init_note_box();
27997
+ init_visual_conventions();
27364
27998
  DIAGRAM_PADDING4 = 20;
27365
27999
  MAX_SCALE3 = 3;
27366
28000
  CLASS_FONT_SIZE2 = 13;
27367
28001
  MEMBER_FONT_SIZE2 = 11;
27368
28002
  EDGE_LABEL_FONT_SIZE2 = 11;
27369
- EDGE_STROKE_WIDTH3 = 1.5;
27370
- NODE_STROKE_WIDTH3 = 1.5;
27371
28003
  MEMBER_LINE_HEIGHT2 = 18;
27372
28004
  COMPARTMENT_PADDING_Y2 = 8;
27373
28005
  MEMBER_PADDING_X = 10;
@@ -27861,7 +28493,7 @@ function constraintIcon(constraint) {
27861
28493
  return "\u25CB";
27862
28494
  }
27863
28495
  }
27864
- function drawCardinality(g, point, prevPoint, cardinality, color, useLabels, edgeLabelFontSize = EDGE_LABEL_FONT_SIZE4, edgeStrokeWidth = EDGE_STROKE_WIDTH4) {
28496
+ function drawCardinality(g, point, prevPoint, cardinality, color, useLabels, edgeLabelFontSize = EDGE_LABEL_FONT_SIZE4, edgeStrokeWidth = EDGE_STROKE_WIDTH) {
27865
28497
  const dx = point.x - prevPoint.x;
27866
28498
  const dy = point.y - prevPoint.y;
27867
28499
  const len = Math.sqrt(dx * dx + dy * dy);
@@ -27921,8 +28553,8 @@ function renderERDiagram(container, parsed, layout, palette, isDark, onClickItem
27921
28553
  const sTableFontSize = ctx.text(TABLE_FONT_SIZE2);
27922
28554
  const sColumnFontSize = ctx.text(COLUMN_FONT_SIZE2);
27923
28555
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE4);
27924
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH4);
27925
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH4);
28556
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
28557
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
27926
28558
  const sMemberLineHeight = ctx.structural(MEMBER_LINE_HEIGHT4);
27927
28559
  const sCompartmentPaddingY = ctx.structural(COMPARTMENT_PADDING_Y4);
27928
28560
  const sMemberPaddingX = ctx.structural(MEMBER_PADDING_X2);
@@ -28200,7 +28832,7 @@ function renderERDiagramForExport(content, theme, palette) {
28200
28832
  document.body.removeChild(container);
28201
28833
  }
28202
28834
  }
28203
- var d3Selection10, d3Shape4, DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COLUMN_FONT_SIZE2, EDGE_LABEL_FONT_SIZE4, EDGE_STROKE_WIDTH4, NODE_STROKE_WIDTH4, MEMBER_LINE_HEIGHT4, COMPARTMENT_PADDING_Y4, MEMBER_PADDING_X2, lineGenerator3;
28835
+ var d3Selection10, d3Shape4, DIAGRAM_PADDING5, MAX_SCALE4, TABLE_FONT_SIZE2, COLUMN_FONT_SIZE2, EDGE_LABEL_FONT_SIZE4, MEMBER_LINE_HEIGHT4, COMPARTMENT_PADDING_Y4, MEMBER_PADDING_X2, lineGenerator3;
28204
28836
  var init_renderer5 = __esm({
28205
28837
  "src/er/renderer.ts"() {
28206
28838
  "use strict";
@@ -28219,13 +28851,12 @@ var init_renderer5 = __esm({
28219
28851
  init_parser9();
28220
28852
  init_layout4();
28221
28853
  init_classify();
28854
+ init_visual_conventions();
28222
28855
  DIAGRAM_PADDING5 = 20;
28223
28856
  MAX_SCALE4 = 3;
28224
28857
  TABLE_FONT_SIZE2 = 13;
28225
28858
  COLUMN_FONT_SIZE2 = 11;
28226
28859
  EDGE_LABEL_FONT_SIZE4 = 11;
28227
- EDGE_STROKE_WIDTH4 = 1.5;
28228
- NODE_STROKE_WIDTH4 = 1.5;
28229
28860
  MEMBER_LINE_HEIGHT4 = 18;
28230
28861
  COMPARTMENT_PADDING_Y4 = 8;
28231
28862
  MEMBER_PADDING_X2 = 10;
@@ -28443,7 +29074,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28443
29074
  onToggleDescriptions,
28444
29075
  onToggleControlsExpand,
28445
29076
  exportMode = false,
28446
- controlsHost
29077
+ controlsHost,
29078
+ rampDomain
28447
29079
  } = options ?? {};
28448
29080
  d3Selection11.select(container).selectAll(":not([data-d3-tooltip])").remove();
28449
29081
  const width = exportDims?.width ?? container.clientWidth;
@@ -28453,9 +29085,9 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28453
29085
  const sDiagramPadding = sctx.aesthetic(DIAGRAM_PADDING6);
28454
29086
  const sMinNodeFontSize = sctx.text(MIN_NODE_FONT_SIZE);
28455
29087
  const sEdgeLabelFontSize = sctx.text(EDGE_LABEL_FONT_SIZE5);
28456
- const sEdgeStrokeWidth = sctx.structural(EDGE_STROKE_WIDTH5);
28457
- const sNodeStrokeWidth = sctx.structural(NODE_STROKE_WIDTH5);
28458
- const sCollapseBarHeight = sctx.structural(COLLAPSE_BAR_HEIGHT3);
29088
+ const sEdgeStrokeWidth = sctx.structural(EDGE_STROKE_WIDTH);
29089
+ const sNodeStrokeWidth = sctx.structural(NODE_STROKE_WIDTH);
29090
+ const sCollapseBarHeight = sctx.structural(COLLAPSE_BAR_HEIGHT2);
28459
29091
  const sDescFontSize = sctx.text(DESC_FONT_SIZE);
28460
29092
  const sGroupLabelFontSize = sctx.text(GROUP_LABEL_FONT_SIZE);
28461
29093
  const sGroupLabelZone = sctx.structural(GROUP_LABEL_ZONE);
@@ -28463,8 +29095,8 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28463
29095
  const sTitleY = sctx.structural(TITLE_Y);
28464
29096
  const nodeValues = parsed.nodes.filter((n) => n.value !== void 0).map((n) => n.value);
28465
29097
  const hasRamp = nodeValues.length > 0;
28466
- const rampMin = hasRamp ? Math.min(...nodeValues) : 0;
28467
- const rampMax = Math.max(...nodeValues);
29098
+ const rampMin = rampDomain?.min ?? (hasRamp ? Math.min(...nodeValues) : 0);
29099
+ const rampMax = rampDomain?.max ?? Math.max(...nodeValues);
28468
29100
  const rampHue = resolveColor(parsed.boxMetricColor ?? "", palette) ?? palette.primary;
28469
29101
  const rampLow = parsed.boxMetricLowColor ? resolveColor(parsed.boxMetricLowColor, palette) ?? void 0 : void 0;
28470
29102
  const rampBase = isDark ? mix(palette.surface, palette.text, 28) : palette.bg;
@@ -28547,8 +29179,32 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28547
29179
  const scaleX = width / (contentW + sDiagramPadding * 2);
28548
29180
  const scaleY = height / (contentH + sDiagramPadding * 2);
28549
29181
  const scale = Math.min(scaleX, scaleY, 3);
28550
- const offsetX = (width - contentW * scale) / 2;
28551
- const offsetY = sDiagramPadding + titleOffset + legendH;
29182
+ let centerShiftX = 0;
29183
+ let centerShiftY = 0;
29184
+ if (parsed.nodePositions && parsed.nodePositions.size > 0) {
29185
+ let bMinX = Infinity, bMinY = Infinity, bMaxX = -Infinity, bMaxY = -Infinity;
29186
+ const accB = (x, y) => {
29187
+ if (x < bMinX) bMinX = x;
29188
+ if (x > bMaxX) bMaxX = x;
29189
+ if (y < bMinY) bMinY = y;
29190
+ if (y > bMaxY) bMaxY = y;
29191
+ };
29192
+ for (const n of layout.nodes) {
29193
+ accB(n.x - n.width / 2, n.y - n.height / 2);
29194
+ accB(n.x + n.width / 2, n.y + n.height / 2);
29195
+ }
29196
+ for (const g of layout.groups) {
29197
+ accB(g.x - g.width / 2, g.y - g.height / 2);
29198
+ accB(g.x + g.width / 2, g.y + g.height / 2);
29199
+ }
29200
+ for (const e of layout.edges) for (const p of e.points) accB(p.x, p.y);
29201
+ if (Number.isFinite(bMinX)) {
29202
+ centerShiftX = (layout.width - bMaxX - bMinX) / 2;
29203
+ centerShiftY = (layout.height - bMaxY - bMinY) / 2;
29204
+ }
29205
+ }
29206
+ const offsetX = (width - contentW * scale) / 2 + centerShiftX * scale;
29207
+ const offsetY = sDiagramPadding + titleOffset + legendH + centerShiftY * scale;
28552
29208
  const svg = d3Selection11.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").style("font-family", FONT_FAMILY).style("background", palette.bg);
28553
29209
  if (sctx.isBelowFloor) {
28554
29210
  svg.attr("width", "100%");
@@ -28592,7 +29248,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28592
29248
  group.collapsed ? "bl-group bl-group-collapsed" : "bl-group"
28593
29249
  ).attr("data-line-number", String(group.lineNumber)).attr("data-node-id", group.label).attr("data-group-toggle", group.label).style("cursor", "pointer");
28594
29250
  if (group.collapsed) {
28595
- const fillColor = isDark ? palette.surface : palette.bg;
29251
+ const fillColor = themeBaseBg(palette, isDark);
28596
29252
  const strokeColor = palette.border;
28597
29253
  groupG.append("rect").attr("x", gx).attr("y", gy).attr("width", group.width).attr("height", group.height).attr("rx", NODE_RX).attr("ry", NODE_RX).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", sNodeStrokeWidth);
28598
29254
  const clipId = `bl-clip-${group.label.replace(/[[\]\s]/g, "")}`;
@@ -28684,7 +29340,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28684
29340
  const edgeG = diagramG.append("g").attr("class", "bl-edge-group").attr("data-line-number", String(le.lineNumber));
28685
29341
  edgeGroups.set(i, edgeG);
28686
29342
  const markerId = `bl-arrow-${color.replace("#", "")}`;
28687
- const gen = parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
29343
+ const gen = le.straight ? lineGeneratorStraight : parsed.direction === "TB" ? lineGeneratorTB : lineGeneratorLR;
28688
29344
  const path = edgeG.append("path").attr("class", "bl-edge").attr("d", gen(points) ?? "").attr("fill", "none").attr("stroke", color).attr("stroke-width", sEdgeStrokeWidth).attr("marker-end", `url(#${markerId})`);
28689
29345
  if (le.bidirectional) {
28690
29346
  const revId = `bl-arrow-rev-${color.replace("#", "")}`;
@@ -28948,6 +29604,15 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
28948
29604
  });
28949
29605
  legendG.selectAll("[data-legend-group]").classed("bl-legend-group", true);
28950
29606
  }
29607
+ if (!exportDims && !exportMode) {
29608
+ const iconSize = 14;
29609
+ const focusG = svg.append("g").attr("class", "bl-focus-icon").attr("data-export-ignore", "true").style("display", "none").style("pointer-events", "auto").style("cursor", "pointer");
29610
+ focusG.append("rect").attr("x", -3).attr("y", -3).attr("width", iconSize + 6).attr("height", iconSize + 6).attr("fill", "transparent");
29611
+ const cx = iconSize / 2;
29612
+ const cy = iconSize / 2;
29613
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", iconSize / 2 - 1).attr("fill", palette.bg).attr("stroke", palette.textMuted).attr("stroke-width", 1.5);
29614
+ focusG.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 2).attr("fill", palette.textMuted);
29615
+ }
28951
29616
  }
28952
29617
  function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark, options) {
28953
29618
  renderBoxesAndLines(container, parsed, layout, palette, isDark, {
@@ -28965,7 +29630,7 @@ function renderBoxesAndLinesForExport(container, parsed, layout, palette, isDark
28965
29630
  }
28966
29631
  });
28967
29632
  }
28968
- var d3Selection11, d3Shape5, DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE5, EDGE_STROKE_WIDTH5, NODE_STROKE_WIDTH5, NODE_RX, COLLAPSE_BAR_HEIGHT3, ARROWHEAD_W2, ARROWHEAD_H2, DESC_FONT_SIZE, DESC_LINE_HEIGHT, MAX_DESC_LINES, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, GROUP_LABEL_ZONE, RAMP_FLOOR, VALUE_FONT_SIZE, lineGeneratorLR, lineGeneratorTB;
29633
+ var d3Selection11, d3Shape5, DIAGRAM_PADDING6, NODE_FONT_SIZE, MIN_NODE_FONT_SIZE, EDGE_LABEL_FONT_SIZE5, NODE_RX, COLLAPSE_BAR_HEIGHT2, ARROWHEAD_W2, ARROWHEAD_H2, DESC_FONT_SIZE, DESC_LINE_HEIGHT, MAX_DESC_LINES, NODE_TEXT_PADDING, GROUP_RX, GROUP_LABEL_FONT_SIZE, GROUP_LABEL_ZONE, RAMP_FLOOR, VALUE_FONT_SIZE, lineGeneratorLR, lineGeneratorTB, lineGeneratorStraight;
28969
29634
  var init_renderer6 = __esm({
28970
29635
  "src/boxes-and-lines/renderer.ts"() {
28971
29636
  "use strict";
@@ -28983,14 +29648,13 @@ var init_renderer6 = __esm({
28983
29648
  init_wrapped_desc();
28984
29649
  init_scaling();
28985
29650
  init_text_measure();
29651
+ init_visual_conventions();
28986
29652
  DIAGRAM_PADDING6 = 20;
28987
29653
  NODE_FONT_SIZE = 11;
28988
29654
  MIN_NODE_FONT_SIZE = 9;
28989
29655
  EDGE_LABEL_FONT_SIZE5 = 11;
28990
- EDGE_STROKE_WIDTH5 = 1.5;
28991
- NODE_STROKE_WIDTH5 = 1.5;
28992
29656
  NODE_RX = 8;
28993
- COLLAPSE_BAR_HEIGHT3 = 4;
29657
+ COLLAPSE_BAR_HEIGHT2 = 4;
28994
29658
  ARROWHEAD_W2 = 5;
28995
29659
  ARROWHEAD_H2 = 4;
28996
29660
  DESC_FONT_SIZE = 10;
@@ -29004,6 +29668,7 @@ var init_renderer6 = __esm({
29004
29668
  VALUE_FONT_SIZE = 11;
29005
29669
  lineGeneratorLR = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
29006
29670
  lineGeneratorTB = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveBasis);
29671
+ lineGeneratorStraight = d3Shape5.line().x((d) => d.x).y((d) => d.y).curve(d3Shape5.curveLinear);
29007
29672
  }
29008
29673
  });
29009
29674
 
@@ -29544,7 +30209,7 @@ function shuffle(a, r) {
29544
30209
  return x;
29545
30210
  }
29546
30211
  function flatten(d) {
29547
- const toks = d.match(/[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi) ?? [];
30212
+ const toks = d.match(PATH_TOKEN_RE) ?? [];
29548
30213
  const pts = [];
29549
30214
  let i = 0, cx = 0, cy = 0, cmd = "";
29550
30215
  const num = () => parseFloat(toks[i++]);
@@ -29588,17 +30253,10 @@ function flatten(d) {
29588
30253
  }
29589
30254
  return pts;
29590
30255
  }
29591
- function segPoint(p1, p2, p3, p4) {
29592
- const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
29593
- if (Math.abs(den) < 1e-9) return null;
29594
- 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;
29595
- 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;
29596
- }
29597
- function countSplineCrossings(layout) {
29598
- const center = /* @__PURE__ */ new Map();
29599
- for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
29600
- for (const g of layout.groups)
29601
- if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30256
+ function flatPolys(layout) {
30257
+ const key = layout.edges;
30258
+ const hit = FLAT_CACHE.get(key);
30259
+ if (hit) return hit;
29602
30260
  const polys = layout.edges.map((e) => {
29603
30261
  const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29604
30262
  let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
@@ -29608,8 +30266,24 @@ function countSplineCrossings(layout) {
29608
30266
  if (p.y < y0) y0 = p.y;
29609
30267
  if (p.y > y1) y1 = p.y;
29610
30268
  }
29611
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
30269
+ return { pts, x0, y0, x1, y1 };
29612
30270
  });
30271
+ FLAT_CACHE.set(key, polys);
30272
+ return polys;
30273
+ }
30274
+ function segPoint(p1, p2, p3, p4) {
30275
+ const den = (p2.x - p1.x) * (p4.y - p3.y) - (p2.y - p1.y) * (p4.x - p3.x);
30276
+ if (Math.abs(den) < 1e-9) return null;
30277
+ const t = ((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / den, u = ((p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x)) / den;
30278
+ return t > 0 && t < 1 && u > 0 && u < 1 ? { x: p1.x + t * (p2.x - p1.x), y: p1.y + t * (p2.y - p1.y) } : null;
30279
+ }
30280
+ function countSplineCrossings(layout, floor = Infinity) {
30281
+ const center = /* @__PURE__ */ new Map();
30282
+ for (const n of layout.nodes) center.set(n.label, { x: n.x, y: n.y });
30283
+ for (const g of layout.groups)
30284
+ if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
30285
+ const polys = flatPolys(layout);
30286
+ const edges = layout.edges;
29613
30287
  const R = 34;
29614
30288
  let total = 0;
29615
30289
  for (let a = 0; a < polys.length; a++)
@@ -29617,23 +30291,33 @@ function countSplineCrossings(layout) {
29617
30291
  const A = polys[a], B = polys[b];
29618
30292
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29619
30293
  if (A.x1 < B.x0 || B.x1 < A.x0 || A.y1 < B.y0 || B.y1 < A.y0) continue;
29620
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => center.get(n)).filter(Boolean);
30294
+ const ea = edges[a], eb = edges[b];
30295
+ let sh0, sh1;
30296
+ if (ea.source === eb.source || ea.source === eb.target)
30297
+ sh0 = center.get(ea.source);
30298
+ if (ea.target === eb.source || ea.target === eb.target)
30299
+ sh1 = center.get(ea.target);
29621
30300
  const hits = [];
29622
- for (let i = 1; i < A.pts.length; i++)
29623
- for (let j = 1; j < B.pts.length; j++) {
29624
- const p = segPoint(
29625
- A.pts[i - 1],
29626
- A.pts[i],
29627
- B.pts[j - 1],
29628
- B.pts[j]
29629
- );
30301
+ const ap = A.pts, bp = B.pts;
30302
+ for (let i = 1; i < ap.length; i++) {
30303
+ const a0 = ap[i - 1], a1 = ap[i];
30304
+ const axMin = a0.x < a1.x ? a0.x : a1.x, axMax = a0.x > a1.x ? a0.x : a1.x, ayMin = a0.y < a1.y ? a0.y : a1.y, ayMax = a0.y > a1.y ? a0.y : a1.y;
30305
+ for (let j = 1; j < bp.length; j++) {
30306
+ const b0 = bp[j - 1], b1 = bp[j];
30307
+ if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
30308
+ if ((b0.x > b1.x ? b0.x : b1.x) < axMin) continue;
30309
+ if (ayMax < (b0.y < b1.y ? b0.y : b1.y)) continue;
30310
+ if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
30311
+ const p = segPoint(a0, a1, b0, b1);
29630
30312
  if (!p) continue;
29631
- if (shared.some((c) => Math.hypot(p.x - c.x, p.y - c.y) < R))
29632
- continue;
30313
+ if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
30314
+ if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
29633
30315
  if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
29634
30316
  hits.push(p);
29635
30317
  }
30318
+ }
29636
30319
  total += hits.length;
30320
+ if (total > floor) return total;
29637
30321
  }
29638
30322
  return total;
29639
30323
  }
@@ -29671,17 +30355,8 @@ function detectEdgeOverlaps(layout, opts) {
29671
30355
  w: g.width,
29672
30356
  h: g.height
29673
30357
  });
29674
- const polys = layout.edges.map((e) => {
29675
- const pts = e.points.length >= 2 ? flatten(splineGen(e.points) ?? "") : [];
29676
- let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
29677
- for (const p of pts) {
29678
- if (p.x < x0) x0 = p.x;
29679
- if (p.x > x1) x1 = p.x;
29680
- if (p.y < y0) y0 = p.y;
29681
- if (p.y > y1) y1 = p.y;
29682
- }
29683
- return { pts, s: e.source, t: e.target, x0, y0, x1, y1 };
29684
- });
30358
+ const polys = flatPolys(layout);
30359
+ const edges = layout.edges;
29685
30360
  const runs = [];
29686
30361
  for (let a = 0; a < polys.length; a++)
29687
30362
  for (let b = a + 1; b < polys.length; b++) {
@@ -29689,7 +30364,12 @@ function detectEdgeOverlaps(layout, opts) {
29689
30364
  if (A.pts.length < 2 || B.pts.length < 2) continue;
29690
30365
  if (A.x1 + dist < B.x0 || B.x1 + dist < A.x0 || A.y1 + dist < B.y0 || B.y1 + dist < A.y0)
29691
30366
  continue;
29692
- const shared = [A.s, A.t].filter((n) => n === B.s || n === B.t).map((n) => rect.get(n)).filter(Boolean);
30367
+ const ea = edges[a], eb = edges[b];
30368
+ let shr0, shr1;
30369
+ if (ea.source === eb.source || ea.source === eb.target)
30370
+ shr0 = rect.get(ea.source);
30371
+ if (ea.target === eb.source || ea.target === eb.target)
30372
+ shr1 = rect.get(ea.target);
29693
30373
  let run2 = [];
29694
30374
  let runLen = 0;
29695
30375
  const flush = () => {
@@ -29703,7 +30383,7 @@ function detectEdgeOverlaps(layout, opts) {
29703
30383
  runLen = 0;
29704
30384
  };
29705
30385
  for (const p of A.pts) {
29706
- const nearShared = shared.some((r) => pointRectDist(p, r) < nodeClear);
30386
+ const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
29707
30387
  const covered = !nearShared && distToPoly(p, B.pts) < dist;
29708
30388
  if (covered) {
29709
30389
  if (run2.length)
@@ -29738,9 +30418,10 @@ function detectEdgeNodePierces(layout, opts) {
29738
30418
  });
29739
30419
  const inside = (p, r) => Math.abs(p.x - r.x) < r.w / 2 - inset && Math.abs(p.y - r.y) < r.h / 2 - inset;
29740
30420
  const out = [];
30421
+ const polys = flatPolys(layout);
29741
30422
  layout.edges.forEach((e, idx) => {
29742
30423
  if (e.points.length < 2) return;
29743
- const poly = flatten(splineGen(e.points) ?? "");
30424
+ const poly = polys[idx].pts;
29744
30425
  for (const r of rects) {
29745
30426
  if (r.key === e.source || r.key === e.target || "__group_" + r.key === e.source || "__group_" + r.key === e.target)
29746
30427
  continue;
@@ -30087,8 +30768,10 @@ function edgeLength(layout) {
30087
30768
  );
30088
30769
  return total;
30089
30770
  }
30090
- function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30771
+ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30091
30772
  const hideDescriptions = opts?.hideDescriptions ?? false;
30773
+ const onProgress = opts?.onProgress;
30774
+ const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
30092
30775
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
30093
30776
  if (collapseInfo) {
30094
30777
  const missing = /* @__PURE__ */ new Set();
@@ -30191,6 +30874,156 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30191
30874
  return { x: rect.x + dx * s, y: rect.y + dy * s };
30192
30875
  };
30193
30876
  const isInsideRect = (p, rect) => Math.abs(p.x - rect.x) <= rect.w / 2 && Math.abs(p.y - rect.y) <= rect.h / 2;
30877
+ const pinned = parsed.nodePositions;
30878
+ const groupLabelSet = new Set(parsed.groups.map((g) => g.label));
30879
+ const groupsAreFlat = parsed.groups.every(
30880
+ (g) => !g.parentGroup && !g.children.some((c) => groupLabelSet.has(c))
30881
+ );
30882
+ const allOriginalGroupLabels = new Set(
30883
+ (collapseInfo?.originalGroups ?? parsed.groups).map((g) => g.label)
30884
+ );
30885
+ const collapsedAreFlatPinned = collapsedGroupLabels.size === 0 || pinned !== void 0 && collapseInfo !== void 0 && [...collapsedGroupLabels].every((label) => {
30886
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
30887
+ if (!og || og.parentGroup) return false;
30888
+ return og.children.every(
30889
+ (c) => pinned.has(c) && !allOriginalGroupLabels.has(c)
30890
+ );
30891
+ });
30892
+ const allPinned = pinned !== void 0 && (parsed.nodes.length > 0 || collapsedGroupLabels.size > 0) && parsed.nodes.every((n2) => pinned.has(n2.label)) && groupsAreFlat && collapsedAreFlatPinned;
30893
+ function placePinned(pins) {
30894
+ const collapsedPosByGid = /* @__PURE__ */ new Map();
30895
+ const collapsedBoxes = [];
30896
+ if (collapseInfo)
30897
+ for (const label of collapsedGroupLabels) {
30898
+ const og = collapseInfo.originalGroups.find((g) => g.label === label);
30899
+ if (!og) continue;
30900
+ let cx0 = Infinity, cy0 = Infinity, cx1 = -Infinity, cy1 = -Infinity;
30901
+ for (const c of og.children) {
30902
+ const p = pins.get(c);
30903
+ if (!p) continue;
30904
+ cx0 = Math.min(cx0, p.x);
30905
+ cx1 = Math.max(cx1, p.x);
30906
+ cy0 = Math.min(cy0, p.y);
30907
+ cy1 = Math.max(cy1, p.y);
30908
+ }
30909
+ if (!Number.isFinite(cx0)) continue;
30910
+ const cx = (cx0 + cx1) / 2;
30911
+ const cy = (cy0 + cy1) / 2;
30912
+ collapsedPosByGid.set(`__group_${label}`, { x: cx, y: cy });
30913
+ collapsedBoxes.push({
30914
+ label,
30915
+ lineNumber: og.lineNumber,
30916
+ childCount: collapseInfo.collapsedChildCounts.get(label) ?? og.children.length,
30917
+ x: cx,
30918
+ y: cy
30919
+ });
30920
+ }
30921
+ const posOf = (label) => pins.get(label) ?? collapsedPosByGid.get(label);
30922
+ const rectOf = (label) => {
30923
+ const p = posOf(label);
30924
+ const s = sizes.get(label) ?? { width: NODE_WIDTH, height: NODE_HEIGHT };
30925
+ return { x: p.x, y: p.y, w: s.width, h: s.height };
30926
+ };
30927
+ const nodes = parsed.nodes.map((n2) => {
30928
+ const r = rectOf(n2.label);
30929
+ return { label: n2.label, x: r.x, y: r.y, width: r.w, height: r.h };
30930
+ });
30931
+ const edges = parsed.edges.flatMap((e) => {
30932
+ const sp = posOf(e.source);
30933
+ const tp = posOf(e.target);
30934
+ if (!sp || !tp) return [];
30935
+ const srcRect = rectOf(e.source);
30936
+ const tgtRect = rectOf(e.target);
30937
+ const p0 = rectBorderPoint(srcRect, tp);
30938
+ const p1 = rectBorderPoint(tgtRect, sp);
30939
+ return [
30940
+ {
30941
+ source: e.source,
30942
+ target: e.target,
30943
+ ...e.label !== void 0 && { label: e.label },
30944
+ bidirectional: e.bidirectional,
30945
+ lineNumber: e.lineNumber,
30946
+ points: [p0, p1],
30947
+ yOffset: 0,
30948
+ parallelCount: 1,
30949
+ metadata: e.metadata,
30950
+ straight: true
30951
+ }
30952
+ ];
30953
+ });
30954
+ const GROUP_PAD = 16;
30955
+ const nodeByLabel = new Map(nodes.map((n2) => [n2.label, n2]));
30956
+ const groups = [];
30957
+ for (const grp of parsed.groups) {
30958
+ let gx0 = Infinity, gy0 = Infinity, gx1 = -Infinity, gy1 = -Infinity;
30959
+ for (const c of grp.children) {
30960
+ const n2 = nodeByLabel.get(c);
30961
+ if (!n2) continue;
30962
+ gx0 = Math.min(gx0, n2.x - n2.width / 2);
30963
+ gx1 = Math.max(gx1, n2.x + n2.width / 2);
30964
+ gy0 = Math.min(gy0, n2.y - n2.height / 2);
30965
+ gy1 = Math.max(gy1, n2.y + n2.height / 2);
30966
+ }
30967
+ if (!Number.isFinite(gx0)) continue;
30968
+ const x0 = gx0 - GROUP_PAD;
30969
+ const x1 = gx1 + GROUP_PAD;
30970
+ const y0 = gy0 - GROUP_LABEL_ZONE2;
30971
+ const y1 = gy1 + GROUP_PAD;
30972
+ groups.push({
30973
+ label: grp.label,
30974
+ lineNumber: grp.lineNumber,
30975
+ x: (x0 + x1) / 2,
30976
+ y: (y0 + y1) / 2,
30977
+ width: x1 - x0,
30978
+ height: y1 - y0,
30979
+ collapsed: false,
30980
+ childCount: grp.children.length
30981
+ });
30982
+ }
30983
+ for (const cb of collapsedBoxes) {
30984
+ groups.push({
30985
+ label: cb.label,
30986
+ lineNumber: cb.lineNumber,
30987
+ x: cb.x,
30988
+ y: cb.y,
30989
+ width: NODE_WIDTH,
30990
+ height: NODE_HEIGHT,
30991
+ collapsed: true,
30992
+ childCount: cb.childCount
30993
+ });
30994
+ }
30995
+ const M = 40;
30996
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
30997
+ const acc = (x, y) => {
30998
+ if (x < minX) minX = x;
30999
+ if (x > maxX) maxX = x;
31000
+ if (y < minY) minY = y;
31001
+ if (y > maxY) maxY = y;
31002
+ };
31003
+ for (const n2 of nodes) {
31004
+ acc(n2.x - n2.width / 2, n2.y - n2.height / 2);
31005
+ acc(n2.x + n2.width / 2, n2.y + n2.height / 2);
31006
+ }
31007
+ for (const e of edges) for (const p of e.points) acc(p.x, p.y);
31008
+ for (const gr of groups) {
31009
+ acc(gr.x - gr.width / 2, gr.y - gr.height / 2);
31010
+ acc(gr.x + gr.width / 2, gr.y + gr.height / 2);
31011
+ }
31012
+ const TOL = 2;
31013
+ const sx = minX < M - TOL ? M - minX : 0;
31014
+ const sy = minY < M - TOL ? M - minY : 0;
31015
+ const shifted = sx !== 0 || sy !== 0;
31016
+ return {
31017
+ nodes: shifted ? nodes.map((n2) => ({ ...n2, x: n2.x + sx, y: n2.y + sy })) : nodes,
31018
+ edges: shifted ? edges.map((e) => ({
31019
+ ...e,
31020
+ points: e.points.map((p) => ({ x: p.x + sx, y: p.y + sy }))
31021
+ })) : edges,
31022
+ groups: shifted ? groups.map((gr) => ({ ...gr, x: gr.x + sx, y: gr.y + sy })) : groups,
31023
+ width: maxX + sx + M,
31024
+ height: maxY + sy + M
31025
+ };
31026
+ }
30194
31027
  function place(cfg) {
30195
31028
  const r = cfg.seed === void 0 ? null : rng2(cfg.seed + 1);
30196
31029
  const ord = (a) => r ? shuffle(a, r) : a.slice();
@@ -30329,6 +31162,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30329
31162
  height: gg.height ?? 600
30330
31163
  };
30331
31164
  }
31165
+ if (allPinned) return placePinned(pinned);
30332
31166
  const n = parsed.nodes.length;
30333
31167
  const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
30334
31168
  const REFINE_K = opts?.refineK ?? 6;
@@ -30351,17 +31185,25 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30351
31185
  seed: s
30352
31186
  });
30353
31187
  const badness = (lay, floor) => {
30354
- const x = countSplineCrossings(lay);
31188
+ const x = countSplineCrossings(lay, floor);
30355
31189
  if (x > floor) return Infinity;
30356
31190
  return x + countEdgeOverlaps(lay) + countEdgeNodePierces(lay) + countGroupOverlaps(lay);
30357
31191
  };
30358
31192
  const objective = (lay, viol) => viol * 1e6 + edgeLength(lay) + lambda * meanDrift(lay, prev) * 10;
31193
+ const progressTotal = configs.length + Math.min(opts?.refineK ?? 6, configs.length);
31194
+ let progressDone = 0;
31195
+ const step = async (phase) => {
31196
+ if (!onProgress) return;
31197
+ onProgress(++progressDone, progressTotal, phase);
31198
+ await tick();
31199
+ };
30359
31200
  const pool = [];
30360
31201
  for (const cfg of configs) {
30361
31202
  try {
30362
31203
  pool.push(place(cfg));
30363
31204
  } catch {
30364
31205
  }
31206
+ await step("Optimizing layout");
30365
31207
  }
30366
31208
  if (!pool.length)
30367
31209
  return place({ ranker: "network-simplex", nodesep: 50, ranksep: 60 });
@@ -30370,9 +31212,9 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30370
31212
  layered = layeredCandidates(parsed, sizes);
30371
31213
  } catch {
30372
31214
  }
30373
- pool.sort(
30374
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30375
- );
31215
+ const fastKey = /* @__PURE__ */ new Map();
31216
+ for (const lay of pool) fastKey.set(lay, objective(lay, countCrossingsFast(lay)));
31217
+ pool.sort((a, b) => fastKey.get(a) - fastKey.get(b));
30376
31218
  const refineK = Math.min(REFINE_K, pool.length);
30377
31219
  let best = pool[0];
30378
31220
  let bestObj = Infinity;
@@ -30387,7 +31229,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30387
31229
  best = lay;
30388
31230
  }
30389
31231
  };
30390
- for (const lay of pool.slice(0, refineK)) consider(lay);
31232
+ for (const lay of pool.slice(0, refineK)) {
31233
+ consider(lay);
31234
+ await step("Refining layout");
31235
+ }
30391
31236
  if (bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
30392
31237
  const extra = [];
30393
31238
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -30403,9 +31248,10 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30403
31248
  } catch {
30404
31249
  }
30405
31250
  }
30406
- extra.sort(
30407
- (a, b) => objective(a, countCrossingsFast(a)) - objective(b, countCrossingsFast(b))
30408
- );
31251
+ const extraKey = /* @__PURE__ */ new Map();
31252
+ for (const lay of extra)
31253
+ extraKey.set(lay, objective(lay, countCrossingsFast(lay)));
31254
+ extra.sort((a, b) => extraKey.get(a) - extraKey.get(b));
30409
31255
  for (const lay of extra.slice(0, ESCALATE_REFINE)) consider(lay);
30410
31256
  }
30411
31257
  for (const lay of layered) {
@@ -30432,7 +31278,7 @@ function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
30432
31278
  }
30433
31279
  return best;
30434
31280
  }
30435
- var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, GROUP_LABEL_ZONE2;
31281
+ var import_dagre4, import_d3_shape, DEFAULT_LAMBDA, ESCALATE_THRESHOLD, ESCALATE_MAX_N, ESCALATE_SEEDS, ESCALATE_REFINE, splineGen, PATH_TOKEN_RE, FLAT_CACHE, GROUP_LABEL_ZONE2;
30436
31282
  var init_layout_search = __esm({
30437
31283
  "src/boxes-and-lines/layout-search.ts"() {
30438
31284
  "use strict";
@@ -30446,6 +31292,8 @@ var init_layout_search = __esm({
30446
31292
  ESCALATE_SEEDS = 18;
30447
31293
  ESCALATE_REFINE = 10;
30448
31294
  splineGen = (0, import_d3_shape.line)().x((d) => d.x).y((d) => d.y).curve(import_d3_shape.curveBasis);
31295
+ PATH_TOKEN_RE = /[MLQC]|-?\d*\.?\d+(?:e-?\d+)?/gi;
31296
+ FLAT_CACHE = /* @__PURE__ */ new WeakMap();
30449
31297
  GROUP_LABEL_ZONE2 = 32;
30450
31298
  }
30451
31299
  });
@@ -30525,17 +31373,20 @@ function computeNodeSize(node, reserveValueRow) {
30525
31373
  }
30526
31374
  totalRenderedLines = Math.min(totalRenderedLines, MAX_DESC_LINES2);
30527
31375
  const descriptionHeight = totalRenderedLines * DESC_FONT_SIZE2 * DESC_LINE_HEIGHT2;
30528
- const totalHeight = labelHeight + SEPARATOR_GAP5 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
31376
+ const totalHeight = labelHeight + SEPARATOR_GAP4 + DESC_PADDING + descriptionHeight + DESC_PADDING + (reserveValueRow ? VALUE_ROW_H : 0);
30529
31377
  return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
30530
31378
  }
30531
31379
  async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
30532
31380
  const { layoutBoxesAndLinesSearch: layoutBoxesAndLinesSearch2 } = await Promise.resolve().then(() => (init_layout_search(), layout_search_exports));
30533
- const searched = layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
31381
+ const searched = await layoutBoxesAndLinesSearch2(parsed, collapseInfo, {
30534
31382
  ...layoutOptions?.hideDescriptions !== void 0 && {
30535
31383
  hideDescriptions: layoutOptions.hideDescriptions
30536
31384
  },
30537
31385
  ...layoutOptions?.previousPositions !== void 0 && {
30538
31386
  previousPositions: layoutOptions.previousPositions
31387
+ },
31388
+ ...layoutOptions?.onProgress !== void 0 && {
31389
+ onProgress: layoutOptions.onProgress
30539
31390
  }
30540
31391
  });
30541
31392
  return attachNotes(
@@ -30654,7 +31505,7 @@ function applyParallelEdgeOffsets(layout) {
30654
31505
  }))
30655
31506
  };
30656
31507
  }
30657
- var MARGIN4, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP5, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
31508
+ var MARGIN4, MAX_PARALLEL_EDGES, PARALLEL_SPACING, PHI, NODE_HEIGHT, NODE_WIDTH, DESC_NODE_WIDTH, DESC_FONT_SIZE2, DESC_LINE_HEIGHT2, DESC_PADDING, SEPARATOR_GAP4, MAX_DESC_LINES2, MAX_LABEL_LINES, LABEL_LINE_HEIGHT, LABEL_PAD, VALUE_ROW_FONT, VALUE_ROW_H;
30658
31509
  var init_layout5 = __esm({
30659
31510
  "src/boxes-and-lines/layout.ts"() {
30660
31511
  "use strict";
@@ -30670,13 +31521,13 @@ var init_layout5 = __esm({
30670
31521
  DESC_FONT_SIZE2 = 10;
30671
31522
  DESC_LINE_HEIGHT2 = 1.4;
30672
31523
  DESC_PADDING = 8;
30673
- SEPARATOR_GAP5 = 4;
31524
+ SEPARATOR_GAP4 = 4;
30674
31525
  MAX_DESC_LINES2 = 6;
30675
31526
  MAX_LABEL_LINES = 3;
30676
31527
  LABEL_LINE_HEIGHT = 1.3;
30677
31528
  LABEL_PAD = 12;
30678
31529
  VALUE_ROW_FONT = 11;
30679
- VALUE_ROW_H = SEPARATOR_GAP5 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
31530
+ VALUE_ROW_H = SEPARATOR_GAP4 + VALUE_ROW_FONT * DESC_LINE_HEIGHT2 + DESC_PADDING;
30680
31531
  }
30681
31532
  });
30682
31533
 
@@ -31319,7 +32170,12 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31319
32170
  const titleReserve = fixedTitle ? TITLE_HEIGHT4 : 0;
31320
32171
  const availWidth = containerWidth;
31321
32172
  const availHeight = containerHeight - DIAGRAM_PADDING7 * 2 - legendReserve - titleReserve;
31322
- const ctx = isExport ? ScaleContext.identity() : ScaleContext.from(availWidth, layout.width);
32173
+ let ctx = isExport ? ScaleContext.identity() : ScaleContext.fromBox(
32174
+ availWidth,
32175
+ layout.width,
32176
+ availHeight,
32177
+ layout.height
32178
+ );
31323
32179
  let renderLayout = layout;
31324
32180
  if (ctx.factor < 1) {
31325
32181
  const hiddenCounts = /* @__PURE__ */ new Map();
@@ -31328,17 +32184,37 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31328
32184
  hiddenCounts.set(n.id, n.hiddenCount);
31329
32185
  }
31330
32186
  }
31331
- renderLayout = layoutMindmap(parsed, palette, {
32187
+ const relayout = (c) => layoutMindmap(parsed, palette, {
31332
32188
  interactive: !isExport,
31333
32189
  ...hiddenCounts.size > 0 && { hiddenCounts },
31334
32190
  activeTagGroup: activeTagGroup ?? null,
31335
32191
  ...hideDescriptions !== void 0 && { hideDescriptions },
31336
- ctx
32192
+ ctx: c
31337
32193
  });
32194
+ renderLayout = relayout(ctx);
32195
+ for (let i = 0; i < 3 && !ctx.isBelowFloor; i++) {
32196
+ const refit = Math.min(
32197
+ availWidth / renderLayout.width,
32198
+ availHeight / renderLayout.height
32199
+ );
32200
+ if (refit >= 0.999) break;
32201
+ ctx = ScaleContext.fromFactor(ctx.factor * refit);
32202
+ renderLayout = relayout(ctx);
32203
+ }
31338
32204
  }
31339
- const offsetX = Math.max(0, (availWidth - renderLayout.width) / 2);
31340
- const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - renderLayout.height) / 2);
31341
- const mainG = svg.append("g").attr("transform", `translate(${offsetX}, ${offsetY})`);
32205
+ const fitScale = isExport ? 1 : Math.min(
32206
+ 1,
32207
+ renderLayout.width > 0 ? availWidth / renderLayout.width : 1,
32208
+ renderLayout.height > 0 ? availHeight / renderLayout.height : 1
32209
+ );
32210
+ const scaledWidth = renderLayout.width * fitScale;
32211
+ const scaledHeight = renderLayout.height * fitScale;
32212
+ const offsetX = Math.max(0, (availWidth - scaledWidth) / 2);
32213
+ const offsetY = DIAGRAM_PADDING7 + legendReserve + titleReserve + Math.max(0, (availHeight - scaledHeight) / 2);
32214
+ const mainG = svg.append("g").attr(
32215
+ "transform",
32216
+ `translate(${offsetX}, ${offsetY})${fitScale < 1 ? ` scale(${fitScale})` : ""}`
32217
+ );
31342
32218
  if (ctx.isBelowFloor) {
31343
32219
  svg.attr("width", "100%");
31344
32220
  }
@@ -31437,11 +32313,11 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31437
32313
  });
31438
32314
  }
31439
32315
  for (const edge of renderLayout.edges) {
31440
- mainG.append("path").attr("class", "mindmap-edge").attr("d", edge.path).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", EDGE_STROKE_WIDTH6).attr("stroke-opacity", 0.5);
32316
+ mainG.append("path").attr("class", "mindmap-edge").attr("d", edge.path).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", EDGE_STROKE_WIDTH).attr("stroke-opacity", 0.5);
31441
32317
  }
31442
32318
  for (const node of renderLayout.nodes) {
31443
32319
  const isRoot = node.radius === 0 && renderLayout.nodes.indexOf(node) === 0;
31444
- const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH6;
32320
+ const strokeW = isRoot ? ROOT_STROKE_WIDTH : NODE_STROKE_WIDTH;
31445
32321
  const effectiveColor = options?.colorByDepth ? depthColor(node.depth, palette) : node.color;
31446
32322
  const fill2 = nodeFill4(
31447
32323
  palette,
@@ -31524,7 +32400,7 @@ function renderMindmap(container, parsed, layout, palette, isDark, onClickItem,
31524
32400
  const clipId = `collapse-clip-${node.id}`;
31525
32401
  const defs = mainG.append("defs");
31526
32402
  defs.append("clipPath").attr("id", clipId).append("rect").attr("x", node.x).attr("y", node.y).attr("width", node.width).attr("height", node.height).attr("rx", NODE_RADIUS).attr("ry", NODE_RADIUS);
31527
- nodeG.append("rect").attr("class", "collapse-bar").attr("x", node.x).attr("y", node.y + node.height - COLLAPSE_BAR_HEIGHT4).attr("width", node.width).attr("height", COLLAPSE_BAR_HEIGHT4).attr("fill", stroke2).attr("clip-path", `url(#${clipId})`);
32403
+ nodeG.append("rect").attr("class", "collapse-bar").attr("x", node.x).attr("y", node.y + node.height - COLLAPSE_BAR_HEIGHT).attr("width", node.width).attr("height", COLLAPSE_BAR_HEIGHT).attr("fill", stroke2).attr("clip-path", `url(#${clipId})`);
31528
32404
  }
31529
32405
  if (onClickItem) {
31530
32406
  nodeG.style("cursor", "pointer").on("click", (event) => {
@@ -31572,7 +32448,7 @@ function renderMindmapForExport(content, theme, palette) {
31572
32448
  return extractExportSvg(container, theme);
31573
32449
  });
31574
32450
  }
31575
- var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, NODE_STROKE_WIDTH6, EDGE_STROKE_WIDTH6, COLLAPSE_BAR_HEIGHT4, DEPTH_COLOR_KEYS;
32451
+ var d3Selection12, DIAGRAM_PADDING7, TITLE_HEIGHT4, SINGLE_LABEL_HEIGHT2, LABEL_LINE_HEIGHT3, DESC_LINE_HEIGHT4, NODE_RADIUS, ROOT_STROKE_WIDTH, DEPTH_COLOR_KEYS;
31576
32452
  var init_renderer7 = __esm({
31577
32453
  "src/mindmap/renderer.ts"() {
31578
32454
  "use strict";
@@ -31590,6 +32466,7 @@ var init_renderer7 = __esm({
31590
32466
  init_legend_layout();
31591
32467
  init_title_constants();
31592
32468
  init_scaling();
32469
+ init_visual_conventions();
31593
32470
  DIAGRAM_PADDING7 = 20;
31594
32471
  TITLE_HEIGHT4 = 30;
31595
32472
  SINGLE_LABEL_HEIGHT2 = 28;
@@ -31597,9 +32474,6 @@ var init_renderer7 = __esm({
31597
32474
  DESC_LINE_HEIGHT4 = 14;
31598
32475
  NODE_RADIUS = 6;
31599
32476
  ROOT_STROKE_WIDTH = 2.5;
31600
- NODE_STROKE_WIDTH6 = 1.5;
31601
- EDGE_STROKE_WIDTH6 = 1.5;
31602
- COLLAPSE_BAR_HEIGHT4 = 6;
31603
32477
  DEPTH_COLOR_KEYS = [
31604
32478
  "red",
31605
32479
  "orange",
@@ -32827,10 +33701,10 @@ function computeC4NodeDimensions(el, options) {
32827
33701
  const metaEntries = collectCardMetadata(el.metadata);
32828
33702
  if (metaEntries.length > 0) {
32829
33703
  height2 += DIVIDER_GAP;
32830
- height2 += metaEntries.length * META_LINE_HEIGHT5;
33704
+ height2 += metaEntries.length * META_LINE_HEIGHT4;
32831
33705
  const maxMetaWidth = Math.max(
32832
33706
  ...metaEntries.map(
32833
- (e) => measureText(`${e.key}: ${e.value}`, META_FONT_SIZE5) + CARD_H_PAD3 * 2
33707
+ (e) => measureText(`${e.key}: ${e.value}`, META_FONT_SIZE4) + CARD_H_PAD3 * 2
32834
33708
  )
32835
33709
  );
32836
33710
  if (maxMetaWidth > width) width = Math.min(MAX_NODE_WIDTH, maxMetaWidth);
@@ -34107,7 +34981,7 @@ function layoutC4Deployment(parsed, activeTagGroup) {
34107
34981
  height: totalHeight
34108
34982
  };
34109
34983
  }
34110
- var import_dagre5, gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT5, META_FONT_SIZE5, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
34984
+ var import_dagre5, gNode, gEdge, MIN_NODE_WIDTH, MAX_NODE_WIDTH, TYPE_LABEL_HEIGHT, DIVIDER_GAP, NAME_HEIGHT, NAME_FONT_SIZE, DESC_LINE_HEIGHT5, DESC_FONT_SIZE4, CARD_V_PAD3, CARD_H_PAD3, META_LINE_HEIGHT4, META_FONT_SIZE4, MARGIN6, BOUNDARY_PAD, GROUP_BOUNDARY_PAD, EDGE_NODE_COLLISION_WEIGHT, META_EXCLUDE_KEYS;
34111
34985
  var init_layout8 = __esm({
34112
34986
  "src/c4/layout.ts"() {
34113
34987
  "use strict";
@@ -34126,8 +35000,8 @@ var init_layout8 = __esm({
34126
35000
  DESC_FONT_SIZE4 = 11;
34127
35001
  CARD_V_PAD3 = 14;
34128
35002
  CARD_H_PAD3 = 20;
34129
- META_LINE_HEIGHT5 = 16;
34130
- META_FONT_SIZE5 = 11;
35003
+ META_LINE_HEIGHT4 = 16;
35004
+ META_FONT_SIZE4 = 11;
34131
35005
  MARGIN6 = 40;
34132
35006
  BOUNDARY_PAD = 40;
34133
35007
  GROUP_BOUNDARY_PAD = 24;
@@ -34248,7 +35122,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34248
35122
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34249
35123
  const pathD = lineGenerator4(edge.points);
34250
35124
  if (pathD) {
34251
- const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH7).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
35125
+ const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
34252
35126
  if (dashed) {
34253
35127
  pathEl.attr("stroke-dasharray", "6 3");
34254
35128
  }
@@ -34314,7 +35188,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34314
35188
  palette.textOnFillLight,
34315
35189
  palette.textOnFillDark
34316
35190
  );
34317
- nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4).attr("ry", CARD_RADIUS4).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35191
+ nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS).attr("ry", CARD_RADIUS).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34318
35192
  let yPos = -h / 2 + CARD_V_PAD4;
34319
35193
  const typeLabel = `\xAB${node.type}\xBB`;
34320
35194
  nodeG.append("text").attr("x", 0).attr("y", yPos + TYPE_FONT_SIZE / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", TYPE_FONT_SIZE).attr("font-style", "italic").text(typeLabel);
@@ -34358,7 +35232,7 @@ function renderC4Context(container, parsed, layout, palette, isDark, onClickItem
34358
35232
  }
34359
35233
  if (node.drillable) {
34360
35234
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
34361
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4);
35235
+ nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS);
34362
35236
  nodeG.append("rect").attr("x", -w / 2).attr("y", h / 2 - DRILL_BAR_HEIGHT).attr("width", w).attr("height", DRILL_BAR_HEIGHT).attr("fill", solid ? onFillText : stroke2).attr("clip-path", `url(#${clipId})`).attr("class", "c4-drill-bar");
34363
35237
  }
34364
35238
  }
@@ -34418,14 +35292,14 @@ function drawCylinderCard(nodeG, w, h, fill2, stroke2, dashed) {
34418
35292
  `A ${w / 2} ${ry} 0 0 1 ${-w / 2} ${h / 2 - ry}`,
34419
35293
  "Z"
34420
35294
  ].join(" ");
34421
- const el = nodeG.append("path").attr("d", path).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35295
+ const el = nodeG.append("path").attr("d", path).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34422
35296
  if (dashed) {
34423
35297
  el.attr("stroke-dasharray", "6 3");
34424
35298
  }
34425
- nodeG.append("ellipse").attr("cx", 0).attr("cy", -h / 2 + ry).attr("rx", w / 2).attr("ry", ry).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35299
+ nodeG.append("ellipse").attr("cx", 0).attr("cy", -h / 2 + ry).attr("rx", w / 2).attr("ry", ry).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34426
35300
  }
34427
35301
  function drawCardRect(nodeG, w, h, fill2, stroke2, dashed) {
34428
- const el = nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4).attr("ry", CARD_RADIUS4).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH7);
35302
+ const el = nodeG.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS).attr("ry", CARD_RADIUS).attr("fill", fill2).attr("stroke", stroke2).attr("stroke-width", NODE_STROKE_WIDTH);
34429
35303
  if (dashed) {
34430
35304
  el.attr("stroke-dasharray", "6 3");
34431
35305
  }
@@ -34445,7 +35319,7 @@ function renderEdges(contentG, edges, palette, onClickItem, obstacleRects) {
34445
35319
  const bidir = hasBidirectionalMarkers(edge.arrowType);
34446
35320
  const pathD = lineGenerator4(edge.points);
34447
35321
  if (pathD) {
34448
- const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH7).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
35322
+ const pathEl = edgeG.append("path").attr("d", pathD).attr("fill", "none").attr("stroke", edgeColor3).attr("stroke-width", EDGE_STROKE_WIDTH).attr("class", "c4-edge").attr("marker-end", "url(#c4-arrow-end)");
34449
35323
  if (dashed) {
34450
35324
  pathEl.attr("stroke-dasharray", "6 3");
34451
35325
  }
@@ -34940,13 +35814,13 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
34940
35814
  nodeG.append("line").attr("x1", -w / 2 + CARD_H_PAD4 / 2).attr("y1", yPos).attr("x2", w / 2 - CARD_H_PAD4 / 2).attr("y2", yPos).attr("stroke", solid ? onFillText : stroke2).attr("stroke-width", 0.5).attr("stroke-opacity", 0.4);
34941
35815
  yPos += DIVIDER_GAP2;
34942
35816
  const maxKeyWidth = Math.max(
34943
- ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE6))
35817
+ ...metaEntries.map((e) => measureText(`${e.key}: `, META_FONT_SIZE2))
34944
35818
  );
34945
35819
  const valueX = -w / 2 + CARD_H_PAD4 + maxKeyWidth;
34946
35820
  for (const entry of metaEntries) {
34947
- 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}:`);
34948
- 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);
34949
- yPos += META_LINE_HEIGHT6;
35821
+ nodeG.append("text").attr("x", -w / 2 + CARD_H_PAD4).attr("y", yPos + META_FONT_SIZE2 / 2).attr("text-anchor", "start").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", META_FONT_SIZE2).text(`${entry.key}:`);
35822
+ nodeG.append("text").attr("x", valueX).attr("y", yPos + META_FONT_SIZE2 / 2).attr("text-anchor", "start").attr("dominant-baseline", "central").attr("fill", onFillText).attr("font-size", META_FONT_SIZE2).text(entry.value);
35823
+ yPos += META_LINE_HEIGHT2;
34950
35824
  }
34951
35825
  }
34952
35826
  } else {
@@ -34973,7 +35847,7 @@ function renderC4Containers(container, parsed, layout, palette, isDark, onClickI
34973
35847
  }
34974
35848
  if (node.drillable) {
34975
35849
  const clipId = `clip-drill-${node.id.replace(/\s+/g, "-")}`;
34976
- nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS4);
35850
+ nodeG.append("clipPath").attr("id", clipId).append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", CARD_RADIUS);
34977
35851
  nodeG.append("rect").attr("x", -w / 2).attr("y", h / 2 - DRILL_BAR_HEIGHT).attr("width", w).attr("height", DRILL_BAR_HEIGHT).attr("fill", solid ? onFillText : stroke2).attr("clip-path", `url(#${clipId})`).attr("class", "c4-drill-bar");
34978
35852
  }
34979
35853
  }
@@ -35102,7 +35976,7 @@ function renderC4DeploymentForExport(content, theme, palette) {
35102
35976
  document.body.removeChild(el);
35103
35977
  }
35104
35978
  }
35105
- var d3Selection14, d3Shape6, DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE6, TECH_FONT_SIZE, EDGE_STROKE_WIDTH7, NODE_STROKE_WIDTH7, CARD_RADIUS4, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, META_FONT_SIZE6, META_LINE_HEIGHT6, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
35979
+ var d3Selection14, d3Shape6, DIAGRAM_PADDING8, MAX_SCALE5, TITLE_HEIGHT6, TYPE_FONT_SIZE, NAME_FONT_SIZE2, DESC_FONT_SIZE5, DESC_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE6, TECH_FONT_SIZE, CARD_H_PAD4, CARD_V_PAD4, TYPE_LABEL_HEIGHT2, DIVIDER_GAP2, NAME_HEIGHT2, BOUNDARY_LABEL_FONT_SIZE, BOUNDARY_STROKE_WIDTH, BOUNDARY_RADIUS, DRILL_BAR_HEIGHT, CYLINDER_RY, PERSON_HEAD_R, PERSON_ARM_SPAN, PERSON_LEG_SPAN, PERSON_ICON_W, PERSON_SW, lineGenerator4;
35106
35980
  var init_renderer9 = __esm({
35107
35981
  "src/c4/renderer.ts"() {
35108
35982
  "use strict";
@@ -35118,6 +35992,7 @@ var init_renderer9 = __esm({
35118
35992
  init_legend_constants();
35119
35993
  init_legend_integration();
35120
35994
  init_title_constants();
35995
+ init_visual_conventions();
35121
35996
  DIAGRAM_PADDING8 = 20;
35122
35997
  MAX_SCALE5 = 3;
35123
35998
  TITLE_HEIGHT6 = 30;
@@ -35127,16 +36002,11 @@ var init_renderer9 = __esm({
35127
36002
  DESC_LINE_HEIGHT6 = 16;
35128
36003
  EDGE_LABEL_FONT_SIZE6 = 11;
35129
36004
  TECH_FONT_SIZE = 10;
35130
- EDGE_STROKE_WIDTH7 = 1.5;
35131
- NODE_STROKE_WIDTH7 = 1.5;
35132
- CARD_RADIUS4 = 6;
35133
36005
  CARD_H_PAD4 = 20;
35134
36006
  CARD_V_PAD4 = 14;
35135
36007
  TYPE_LABEL_HEIGHT2 = 18;
35136
36008
  DIVIDER_GAP2 = 6;
35137
36009
  NAME_HEIGHT2 = 20;
35138
- META_FONT_SIZE6 = 11;
35139
- META_LINE_HEIGHT6 = 16;
35140
36010
  BOUNDARY_LABEL_FONT_SIZE = 12;
35141
36011
  BOUNDARY_STROKE_WIDTH = 1.5;
35142
36012
  BOUNDARY_RADIUS = 8;
@@ -35540,7 +36410,7 @@ function nodeFill6(palette, isDark, shape, nodeColor2, isEndTerminal, colorOff,
35540
36410
  function nodeStroke6(palette, shape, nodeColor2, isEndTerminal, colorOff) {
35541
36411
  return nodeColor2 ?? shapeDefaultColor(shape, palette, isEndTerminal, colorOff);
35542
36412
  }
35543
- function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36413
+ function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35544
36414
  const w = node.width;
35545
36415
  const h = node.height;
35546
36416
  const rx = h / 2;
@@ -35552,7 +36422,7 @@ function renderTerminal(g, node, palette, isDark, isEnd, colorOff, solid, sNodeS
35552
36422
  nodeStroke6(palette, node.shape, node.color, isEnd, colorOff)
35553
36423
  ).attr("stroke-width", sNodeStrokeWidth);
35554
36424
  }
35555
- function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36425
+ function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35556
36426
  const w = node.width;
35557
36427
  const h = node.height;
35558
36428
  g.append("rect").attr("x", -w / 2).attr("y", -h / 2).attr("width", w).attr("height", h).attr("rx", 3).attr("ry", 3).attr(
@@ -35571,7 +36441,7 @@ function renderProcess(g, node, palette, isDark, colorOff, solid, sNodeStrokeWid
35571
36441
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35572
36442
  ).attr("stroke-width", sNodeStrokeWidth);
35573
36443
  }
35574
- function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8) {
36444
+ function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH) {
35575
36445
  const w = node.width / 2;
35576
36446
  const h = node.height / 2;
35577
36447
  const points = [`${0},${-h}`, `${w},${0}`, `${0},${h}`, `${-w},${0}`].join(
@@ -35593,7 +36463,7 @@ function renderDecision(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35593
36463
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35594
36464
  ).attr("stroke-width", sNodeStrokeWidth);
35595
36465
  }
35596
- function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sIoSkew = IO_SKEW) {
36466
+ function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sIoSkew = IO_SKEW) {
35597
36467
  const w = node.width / 2;
35598
36468
  const h = node.height / 2;
35599
36469
  const sk = sIoSkew;
@@ -35619,7 +36489,7 @@ function renderIO(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth =
35619
36489
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35620
36490
  ).attr("stroke-width", sNodeStrokeWidth);
35621
36491
  }
35622
- function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sSubroutineInset = SUBROUTINE_INSET) {
36492
+ function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sSubroutineInset = SUBROUTINE_INSET) {
35623
36493
  const w = node.width;
35624
36494
  const h = node.height;
35625
36495
  const s = nodeStroke6(palette, node.shape, node.color, void 0, colorOff);
@@ -35637,7 +36507,7 @@ function renderSubroutine(g, node, palette, isDark, colorOff, solid, sNodeStroke
35637
36507
  g.append("line").attr("x1", -w / 2 + sSubroutineInset).attr("y1", -h / 2).attr("x2", -w / 2 + sSubroutineInset).attr("y2", h / 2).attr("stroke", innerStroke).attr("stroke-width", sNodeStrokeWidth);
35638
36508
  g.append("line").attr("x1", w / 2 - sSubroutineInset).attr("y1", -h / 2).attr("x2", w / 2 - sSubroutineInset).attr("y2", h / 2).attr("stroke", innerStroke).attr("stroke-width", sNodeStrokeWidth);
35639
36509
  }
35640
- function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sDocWaveHeight = DOC_WAVE_HEIGHT) {
36510
+ function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sDocWaveHeight = DOC_WAVE_HEIGHT) {
35641
36511
  const w = node.width;
35642
36512
  const h = node.height;
35643
36513
  const waveH = sDocWaveHeight;
@@ -35668,7 +36538,7 @@ function renderDocument(g, node, palette, isDark, colorOff, solid, sNodeStrokeWi
35668
36538
  nodeStroke6(palette, node.shape, node.color, void 0, colorOff)
35669
36539
  ).attr("stroke-width", sNodeStrokeWidth);
35670
36540
  }
35671
- function renderNodeShape(g, node, palette, isDark, endTerminalIds, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH8, sIoSkew = IO_SKEW, sSubroutineInset = SUBROUTINE_INSET, sDocWaveHeight = DOC_WAVE_HEIGHT) {
36541
+ function renderNodeShape(g, node, palette, isDark, endTerminalIds, colorOff, solid, sNodeStrokeWidth = NODE_STROKE_WIDTH, sIoSkew = IO_SKEW, sSubroutineInset = SUBROUTINE_INSET, sDocWaveHeight = DOC_WAVE_HEIGHT) {
35672
36542
  switch (node.shape) {
35673
36543
  case "terminal":
35674
36544
  renderTerminal(
@@ -35755,8 +36625,8 @@ function renderFlowchart(container, graph, layout, palette, isDark, onClickItem,
35755
36625
  const sTitleY = ctx.structural(TITLE_Y);
35756
36626
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE2);
35757
36627
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE7);
35758
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH8);
35759
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH8);
36628
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
36629
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
35760
36630
  const sArrowheadW = ctx.structural(ARROWHEAD_W3);
35761
36631
  const sArrowheadH = ctx.structural(ARROWHEAD_H3);
35762
36632
  const sIoSkew = ctx.structural(IO_SKEW);
@@ -35980,7 +36850,7 @@ function renderFlowchartForExport(content, theme, palette) {
35980
36850
  document.body.removeChild(container);
35981
36851
  }
35982
36852
  }
35983
- var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE7, EDGE_STROKE_WIDTH8, NODE_STROKE_WIDTH8, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
36853
+ var d3Selection15, DIAGRAM_PADDING9, MAX_SCALE6, NODE_FONT_SIZE2, EDGE_LABEL_FONT_SIZE7, ARROWHEAD_W3, ARROWHEAD_H3, IO_SKEW, SUBROUTINE_INSET, DOC_WAVE_HEIGHT;
35984
36854
  var init_flowchart_renderer = __esm({
35985
36855
  "src/graph/flowchart-renderer.ts"() {
35986
36856
  "use strict";
@@ -35996,12 +36866,11 @@ var init_flowchart_renderer = __esm({
35996
36866
  init_scaling();
35997
36867
  init_text_measure();
35998
36868
  init_note_box();
36869
+ init_visual_conventions();
35999
36870
  DIAGRAM_PADDING9 = 20;
36000
36871
  MAX_SCALE6 = 3;
36001
36872
  NODE_FONT_SIZE2 = 13;
36002
36873
  EDGE_LABEL_FONT_SIZE7 = 11;
36003
- EDGE_STROKE_WIDTH8 = 1.5;
36004
- NODE_STROKE_WIDTH8 = 1.5;
36005
36874
  ARROWHEAD_W3 = 10;
36006
36875
  ARROWHEAD_H3 = 7;
36007
36876
  IO_SKEW = 15;
@@ -36985,7 +37854,7 @@ function hasRoles(node) {
36985
37854
  }
36986
37855
  function computeNodeWidth2(node, expanded, options) {
36987
37856
  const badgeVal = node.computedConcurrentInvocations === 0 && node.computedInstances > 1 ? node.computedInstances : 0;
36988
- const badgeWidth = badgeVal > 0 ? measureText(`${badgeVal}x`, META_FONT_SIZE7) + 2 * CHAR_WIDTH_RATIO * NODE_FONT_SIZE3 : 0;
37857
+ const badgeWidth = badgeVal > 0 ? measureText(`${badgeVal}x`, META_FONT_SIZE5) + 2 * CHAR_WIDTH_RATIO * NODE_FONT_SIZE3 : 0;
36989
37858
  const labelWidth2 = measureText(node.label, NODE_FONT_SIZE3) + badgeWidth + PADDING_X3;
36990
37859
  const allKeys = [];
36991
37860
  if (node.computedRps > 0) allKeys.push("RPS");
@@ -37032,7 +37901,7 @@ function computeNodeWidth2(node, expanded, options) {
37032
37901
  }
37033
37902
  if (allKeys.length === 0) return Math.max(MIN_NODE_WIDTH2, labelWidth2);
37034
37903
  const keyColWidth = Math.max(
37035
- ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE7))
37904
+ ...allKeys.map((k) => measureText(`${k}: `, META_FONT_SIZE5))
37036
37905
  );
37037
37906
  let maxRowWidth = 0;
37038
37907
  if (node.computedRps > 0) {
@@ -37045,7 +37914,7 @@ function computeNodeWidth2(node, expanded, options) {
37045
37914
  const rpsVal = effectiveCap > 0 && !node.isEdge ? `${formatRpsShort(node.computedRps)} / ${formatRpsShort(effectiveCap)}` : formatRps(node.computedRps);
37046
37915
  maxRowWidth = Math.max(
37047
37916
  maxRowWidth,
37048
- keyColWidth + measureText(rpsVal, META_FONT_SIZE7)
37917
+ keyColWidth + measureText(rpsVal, META_FONT_SIZE5)
37049
37918
  );
37050
37919
  }
37051
37920
  if (expanded) {
@@ -37062,7 +37931,7 @@ function computeNodeWidth2(node, expanded, options) {
37062
37931
  const valStr = p.key === "max-rps" || p.key === "ratelimit-rps" ? formatRpsShort(numVal) : p.key === "latency-ms" || p.key === "cb-latency-threshold-ms" || p.key === "duration-ms" || p.key === "cold-start-ms" ? formatMs(numVal) : PCT_KEYS.includes(p.key) ? `${numVal}%` : String(p.value);
37063
37932
  maxRowWidth = Math.max(
37064
37933
  maxRowWidth,
37065
- keyColWidth + measureText(valStr, META_FONT_SIZE7)
37934
+ keyColWidth + measureText(valStr, META_FONT_SIZE5)
37066
37935
  );
37067
37936
  }
37068
37937
  }
@@ -37073,7 +37942,7 @@ function computeNodeWidth2(node, expanded, options) {
37073
37942
  if (ms > 0) {
37074
37943
  maxRowWidth = Math.max(
37075
37944
  maxRowWidth,
37076
- keyColWidth + measureText(formatMs(ms), META_FONT_SIZE7)
37945
+ keyColWidth + measureText(formatMs(ms), META_FONT_SIZE5)
37077
37946
  );
37078
37947
  }
37079
37948
  }
@@ -37084,37 +37953,37 @@ function computeNodeWidth2(node, expanded, options) {
37084
37953
  const combinedVal = `${formatMs(perc.p90)} / ${formatMs(threshold)}`;
37085
37954
  maxRowWidth = Math.max(
37086
37955
  maxRowWidth,
37087
- keyColWidth + measureText(combinedVal, META_FONT_SIZE7)
37956
+ keyColWidth + measureText(combinedVal, META_FONT_SIZE5)
37088
37957
  );
37089
37958
  }
37090
37959
  }
37091
37960
  if (node.computedUptime < 1) {
37092
37961
  maxRowWidth = Math.max(
37093
37962
  maxRowWidth,
37094
- keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE7)
37963
+ keyColWidth + measureText(formatUptime(node.computedUptime), META_FONT_SIZE5)
37095
37964
  );
37096
37965
  }
37097
37966
  if (node.computedAvailability < 1) {
37098
37967
  maxRowWidth = Math.max(
37099
37968
  maxRowWidth,
37100
- keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE7)
37969
+ keyColWidth + measureText(formatUptime(node.computedAvailability), META_FONT_SIZE5)
37101
37970
  );
37102
37971
  }
37103
37972
  if (node.computedCbState === "open") {
37104
37973
  maxRowWidth = Math.max(
37105
37974
  maxRowWidth,
37106
- measureText("CB: OPEN", META_FONT_SIZE7) + 8
37975
+ measureText("CB: OPEN", META_FONT_SIZE5) + 8
37107
37976
  );
37108
37977
  }
37109
37978
  }
37110
- const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE7;
37979
+ const DESC_MAX_WIDTH2 = 120 * CHAR_WIDTH_RATIO * META_FONT_SIZE5;
37111
37980
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
37112
37981
  let descWidth = 0;
37113
37982
  for (const dl of descLines) {
37114
- const truncated = truncateText(dl, META_FONT_SIZE7, DESC_MAX_WIDTH2);
37983
+ const truncated = truncateText(dl, META_FONT_SIZE5, DESC_MAX_WIDTH2);
37115
37984
  descWidth = Math.max(
37116
37985
  descWidth,
37117
- measureText(truncated, META_FONT_SIZE7) + PADDING_X3
37986
+ measureText(truncated, META_FONT_SIZE5) + PADDING_X3
37118
37987
  );
37119
37988
  }
37120
37989
  return Math.max(MIN_NODE_WIDTH2, labelWidth2, maxRowWidth + 20, descWidth);
@@ -37124,17 +37993,17 @@ function computeNodeHeight2(node, expanded, options) {
37124
37993
  const computedCount = countComputedRows(node, expanded);
37125
37994
  const hasRps = node.computedRps > 0;
37126
37995
  const descLineCount = expanded && node.description && !node.isEdge ? node.description.length : 0;
37127
- const descH = descLineCount * META_LINE_HEIGHT7;
37996
+ const descH = descLineCount * META_LINE_HEIGHT5;
37128
37997
  if (propCount === 0 && computedCount === 0 && !hasRps)
37129
37998
  return NODE_HEADER_HEIGHT + descH + NODE_PAD_BOTTOM;
37130
37999
  let h = NODE_HEADER_HEIGHT + descH + NODE_SEPARATOR_GAP;
37131
38000
  const computedSectionCount = (hasRps ? 1 : 0) + computedCount;
37132
- h += computedSectionCount * META_LINE_HEIGHT7;
38001
+ h += computedSectionCount * META_LINE_HEIGHT5;
37133
38002
  if (computedSectionCount > 0 && propCount > 0) h += NODE_SEPARATOR_GAP;
37134
- h += propCount * META_LINE_HEIGHT7;
38003
+ h += propCount * META_LINE_HEIGHT5;
37135
38004
  if (hasRoles(node)) h += ROLE_DOT_ROW;
37136
38005
  h += NODE_PAD_BOTTOM;
37137
- if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT5;
38006
+ if (node.id.startsWith("[")) h += COLLAPSE_BAR_HEIGHT3;
37138
38007
  return h;
37139
38008
  }
37140
38009
  function formatRps(rps) {
@@ -37469,7 +38338,7 @@ function layoutInfra(computed, expandedNodeIds, collapsedNodes) {
37469
38338
  height: totalHeight
37470
38339
  };
37471
38340
  }
37472
- var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT7, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT5, NODE_FONT_SIZE3, META_FONT_SIZE7, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
38341
+ var import_dagre7, MIN_NODE_WIDTH2, NODE_HEADER_HEIGHT, META_LINE_HEIGHT5, NODE_SEPARATOR_GAP, NODE_PAD_BOTTOM, ROLE_DOT_ROW, COLLAPSE_BAR_HEIGHT3, NODE_FONT_SIZE3, META_FONT_SIZE5, EDGE_LABEL_FONT_SIZE8, PADDING_X3, GROUP_PADDING2, GROUP_HEADER_HEIGHT, EDGE_MARGIN, DISPLAY_KEYS, DISPLAY_NAMES, GROUP_GAP;
37473
38342
  var init_layout10 = __esm({
37474
38343
  "src/infra/layout.ts"() {
37475
38344
  "use strict";
@@ -37477,13 +38346,13 @@ var init_layout10 = __esm({
37477
38346
  init_text_measure();
37478
38347
  MIN_NODE_WIDTH2 = 140;
37479
38348
  NODE_HEADER_HEIGHT = 28;
37480
- META_LINE_HEIGHT7 = 14;
38349
+ META_LINE_HEIGHT5 = 14;
37481
38350
  NODE_SEPARATOR_GAP = 4;
37482
38351
  NODE_PAD_BOTTOM = 10;
37483
38352
  ROLE_DOT_ROW = 12;
37484
- COLLAPSE_BAR_HEIGHT5 = 6;
38353
+ COLLAPSE_BAR_HEIGHT3 = 6;
37485
38354
  NODE_FONT_SIZE3 = 13;
37486
- META_FONT_SIZE7 = 10;
38355
+ META_FONT_SIZE5 = 10;
37487
38356
  EDGE_LABEL_FONT_SIZE8 = 11;
37488
38357
  PADDING_X3 = 24;
37489
38358
  GROUP_PADDING2 = 20;
@@ -37587,19 +38456,19 @@ __export(renderer_exports10, {
37587
38456
  function buildScaledConstants(ctx) {
37588
38457
  return {
37589
38458
  sNodeFontSize: ctx.text(NODE_FONT_SIZE4),
37590
- sMetaFontSize: ctx.text(META_FONT_SIZE8),
37591
- sMetaLineHeight: ctx.structural(META_LINE_HEIGHT8),
38459
+ sMetaFontSize: ctx.text(META_FONT_SIZE6),
38460
+ sMetaLineHeight: ctx.structural(META_LINE_HEIGHT6),
37592
38461
  sEdgeLabelFontSize: ctx.text(EDGE_LABEL_FONT_SIZE9),
37593
38462
  sGroupLabelFontSize: ctx.text(GROUP_LABEL_FONT_SIZE2),
37594
38463
  sNodeBorderRadius: ctx.structural(NODE_BORDER_RADIUS),
37595
- sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH9),
37596
- sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH9),
38464
+ sEdgeStrokeWidth: ctx.structural(EDGE_STROKE_WIDTH),
38465
+ sNodeStrokeWidth: ctx.structural(NODE_STROKE_WIDTH),
37597
38466
  sOverloadStrokeWidth: ctx.structural(OVERLOAD_STROKE_WIDTH),
37598
38467
  sRoleDotRadius: ctx.structural(ROLE_DOT_RADIUS),
37599
38468
  sNodeSeparatorGap: ctx.structural(NODE_SEPARATOR_GAP2),
37600
38469
  sNodePadBottom: ctx.structural(NODE_PAD_BOTTOM2),
37601
- sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT6),
37602
- sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET2),
38470
+ sCollapseBarHeight: ctx.structural(COLLAPSE_BAR_HEIGHT),
38471
+ sCollapseBarInset: ctx.structural(COLLAPSE_BAR_INSET),
37603
38472
  sParticleR: ctx.structural(PARTICLE_R),
37604
38473
  sRejectParticleR: ctx.structural(PARTICLE_R),
37605
38474
  sRejectDropDistance: ctx.structural(REJECT_DROP_DISTANCE),
@@ -37923,7 +38792,7 @@ function isWarning(node) {
37923
38792
  return cap > 0 && node.computedRps / cap > 0.7;
37924
38793
  }
37925
38794
  function truncateDesc(text) {
37926
- return truncateText(text, META_FONT_SIZE8, DESC_MAX_WIDTH);
38795
+ return truncateText(text, META_FONT_SIZE6, DESC_MAX_WIDTH);
37927
38796
  }
37928
38797
  function sloLatencyColor(p90, slo) {
37929
38798
  const t = slo.latencyP90 ?? 0;
@@ -38402,7 +39271,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38402
39271
  if (!isNodeCollapsed) {
38403
39272
  const expanded = expandedNodeIds?.has(node.id) ?? false;
38404
39273
  const descLines = expanded && node.description && !node.isEdge ? node.description : [];
38405
- const descH = descLines.length * META_LINE_HEIGHT8;
39274
+ const descH = descLines.length * META_LINE_HEIGHT6;
38406
39275
  for (let di = 0; di < descLines.length; di++) {
38407
39276
  const rawLine = descLines[di];
38408
39277
  const processed = preprocessDescriptionLine(rawLine);
@@ -38410,7 +39279,7 @@ function renderNodes(svg, nodes, palette, isDark, animate, expandedNodeIds, acti
38410
39279
  const isTruncated = descTruncated !== processed;
38411
39280
  const textEl = g.append("text").attr("x", node.x).attr(
38412
39281
  "y",
38413
- y + NODE_HEADER_HEIGHT2 + di * META_LINE_HEIGHT8 + META_LINE_HEIGHT8 / 2 + sc.sMetaFontSize * 0.35
39282
+ y + NODE_HEADER_HEIGHT2 + di * META_LINE_HEIGHT6 + META_LINE_HEIGHT6 / 2 + sc.sMetaFontSize * 0.35
38414
39283
  ).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("font-size", sc.sMetaFontSize).attr("fill", textFill);
38415
39284
  renderInlineText(textEl, descTruncated, palette, sc.sMetaFontSize);
38416
39285
  if (isTruncated) textEl.append("title").text(rawLine);
@@ -38928,7 +39797,7 @@ function parseAndLayoutInfra(content) {
38928
39797
  const layout = layoutInfra(computed);
38929
39798
  return { parsed, computed, layout };
38930
39799
  }
38931
- var d3Selection16, d3Shape8, NODE_FONT_SIZE4, META_FONT_SIZE8, META_LINE_HEIGHT8, EDGE_LABEL_FONT_SIZE9, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, EDGE_STROKE_WIDTH9, NODE_STROKE_WIDTH9, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, COLLAPSE_BAR_HEIGHT6, COLLAPSE_BAR_INSET2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
39800
+ var d3Selection16, d3Shape8, NODE_FONT_SIZE4, META_FONT_SIZE6, META_LINE_HEIGHT6, EDGE_LABEL_FONT_SIZE9, GROUP_LABEL_FONT_SIZE2, NODE_BORDER_RADIUS, OVERLOAD_STROKE_WIDTH, ROLE_DOT_RADIUS, NODE_HEADER_HEIGHT2, NODE_SEPARATOR_GAP2, NODE_PAD_BOTTOM2, LEGEND_FIXED_GAP3, SPEED_BADGE_H_PAD, SPEED_BADGE_V_PAD, SPEED_BADGE_GAP, COLOR_HEALTHY, COLOR_WARNING, COLOR_OVERLOADED, FLOW_SPEED_MIN, FLOW_SPEED_MAX, PARTICLE_R, PARTICLE_COUNT_MIN, PARTICLE_COUNT_MAX, NODE_PULSE_SPEED, NODE_PULSE_OVERLOAD, REJECT_DROP_DISTANCE, REJECT_DURATION_MIN, REJECT_DURATION_MAX, REJECT_COUNT_MIN, REJECT_COUNT_MAX, PROP_DISPLAY, DESC_MAX_CHARS, DESC_MAX_WIDTH, RPS_FORMAT_KEYS, MS_FORMAT_KEYS, PCT_FORMAT_KEYS;
38932
39801
  var init_renderer10 = __esm({
38933
39802
  "src/infra/renderer.ts"() {
38934
39803
  "use strict";
@@ -38949,21 +39818,18 @@ var init_renderer10 = __esm({
38949
39818
  init_legend_layout();
38950
39819
  init_title_constants();
38951
39820
  init_scaling();
39821
+ init_visual_conventions();
38952
39822
  NODE_FONT_SIZE4 = 13;
38953
- META_FONT_SIZE8 = 10;
38954
- META_LINE_HEIGHT8 = 14;
39823
+ META_FONT_SIZE6 = 10;
39824
+ META_LINE_HEIGHT6 = 14;
38955
39825
  EDGE_LABEL_FONT_SIZE9 = 11;
38956
39826
  GROUP_LABEL_FONT_SIZE2 = 14;
38957
39827
  NODE_BORDER_RADIUS = 8;
38958
- EDGE_STROKE_WIDTH9 = 1.5;
38959
- NODE_STROKE_WIDTH9 = 1.5;
38960
39828
  OVERLOAD_STROKE_WIDTH = 3;
38961
39829
  ROLE_DOT_RADIUS = 3;
38962
39830
  NODE_HEADER_HEIGHT2 = 28;
38963
39831
  NODE_SEPARATOR_GAP2 = 4;
38964
39832
  NODE_PAD_BOTTOM2 = 10;
38965
- COLLAPSE_BAR_HEIGHT6 = 6;
38966
- COLLAPSE_BAR_INSET2 = 0;
38967
39833
  LEGEND_FIXED_GAP3 = 16;
38968
39834
  SPEED_BADGE_H_PAD = 5;
38969
39835
  SPEED_BADGE_V_PAD = 3;
@@ -39002,7 +39868,7 @@ var init_renderer10 = __esm({
39002
39868
  partitions: "partitions"
39003
39869
  };
39004
39870
  DESC_MAX_CHARS = 120;
39005
- DESC_MAX_WIDTH = DESC_MAX_CHARS * CHAR_WIDTH_RATIO * META_FONT_SIZE8;
39871
+ DESC_MAX_WIDTH = DESC_MAX_CHARS * CHAR_WIDTH_RATIO * META_FONT_SIZE6;
39006
39872
  RPS_FORMAT_KEYS = /* @__PURE__ */ new Set(["max-rps", "ratelimit-rps"]);
39007
39873
  MS_FORMAT_KEYS = /* @__PURE__ */ new Set([
39008
39874
  "latency-ms",
@@ -40691,7 +41557,7 @@ __export(renderer_exports11, {
40691
41557
  renderPertForExport: () => renderPertForExport
40692
41558
  });
40693
41559
  function analysisBlockChrome(palette, isDark) {
40694
- const surfaceBg = isDark ? palette.surface : palette.bg;
41560
+ const surfaceBg = themeBaseBg(palette, isDark);
40695
41561
  return {
40696
41562
  fill: mix(palette.surface, palette.bg, 40),
40697
41563
  stroke: mix(palette.textMuted, surfaceBg, 35)
@@ -40789,18 +41655,18 @@ function renderPert(container, resolved, layout, palette, isDark, options = {})
40789
41655
  const sLegendPillHeight = ctx.structural(LEGEND_PILL_HEIGHT);
40790
41656
  const sLegendBlockHeight = showTagLegend ? sLegendTopGap + sLegendPillHeight + sLegendBottomGap : 0;
40791
41657
  const sNodeRadius = ctx.structural(NODE_RADIUS2);
40792
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH10);
41658
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
40793
41659
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE5);
40794
41660
  const sNodeCellFontSize = ctx.text(NODE_CELL_FONT_SIZE2);
40795
41661
  const sNodeTopRowHeight = ctx.structural(NODE_TOP_ROW_HEIGHT);
40796
41662
  const sNodeBottomRowHeight = ctx.structural(NODE_BOTTOM_ROW_HEIGHT);
40797
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH10);
41663
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
40798
41664
  const sArrowheadW = ctx.structural(ARROWHEAD_W4);
40799
41665
  const sArrowheadH = ctx.structural(ARROWHEAD_H4);
40800
- const sContainerRadius = ctx.structural(CONTAINER_RADIUS3);
40801
- const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE4);
40802
- const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT3);
40803
- const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT7);
41666
+ const sContainerRadius = ctx.structural(CONTAINER_RADIUS);
41667
+ const sContainerLabelFontSize = ctx.text(CONTAINER_LABEL_FONT_SIZE);
41668
+ const sContainerHeaderHeight = ctx.structural(CONTAINER_HEADER_HEIGHT);
41669
+ const sCollapseBarHeight = ctx.structural(COLLAPSE_BAR_HEIGHT);
40804
41670
  const sPinIconW = ctx.structural(PIN_ICON_W);
40805
41671
  const sPinIconH = ctx.structural(PIN_ICON_H);
40806
41672
  const scaledWidth = layout.width + sDiagramPad * 2;
@@ -41367,7 +42233,7 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41367
42233
  palette.textOnFillDark
41368
42234
  );
41369
42235
  const sNR = sc.nodeRadius ?? NODE_RADIUS2;
41370
- const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT7;
42236
+ const sCBH = sc.collapseBarHeight ?? COLLAPSE_BAR_HEIGHT;
41371
42237
  drawTextbookCard(g, {
41372
42238
  width: grp.width,
41373
42239
  height: grp.height,
@@ -41401,10 +42267,10 @@ function renderGroups2(root, resolved, layout, palette, isDark, collapsedSet, si
41401
42267
  g.append("rect").attr("class", "pert-collapse-bar").attr("x", grp.x).attr("y", grp.y + grp.height - sCBH).attr("width", grp.width).attr("height", sCBH).attr("fill", cardBaseColor).attr("clip-path", `url(#${clipId})`);
41402
42268
  continue;
41403
42269
  }
41404
- const sCR = sc.containerRadius ?? CONTAINER_RADIUS3;
41405
- const sCLFS = sc.containerLabelFontSize ?? CONTAINER_LABEL_FONT_SIZE4;
41406
- const sCHH = sc.containerHeaderHeight ?? CONTAINER_HEADER_HEIGHT3;
41407
- const sNSW = sc.nodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42270
+ const sCR = sc.containerRadius ?? CONTAINER_RADIUS;
42271
+ const sCLFS = sc.containerLabelFontSize ?? CONTAINER_LABEL_FONT_SIZE;
42272
+ const sCHH = sc.containerHeaderHeight ?? CONTAINER_HEADER_HEIGHT;
42273
+ const sNSW = sc.nodeStrokeWidth ?? NODE_STROKE_WIDTH;
41408
42274
  g.append("rect").attr("x", grp.x).attr("y", grp.y).attr("width", grp.width).attr("height", grp.height).attr("rx", sCR).attr("ry", sCR).attr("fill", containerFill3).attr("stroke", containerStroke3).attr("stroke-opacity", 0.35).attr("stroke-width", sNSW);
41409
42275
  g.append("text").attr("x", grp.x + grp.width / 2).attr("y", grp.y + sCHH / 2 + sCLFS / 2 - 2).attr("text-anchor", "middle").attr("font-family", FONT_FAMILY).attr("fill", palette.text).attr("font-size", sCLFS).attr("font-weight", "bold").text(label);
41410
42276
  }
@@ -41457,7 +42323,7 @@ function renderEdges2(root, resolved, layout, palette, collapsedSet, sc = {}) {
41457
42323
  }
41458
42324
  const path = lineGenerator6(e.points);
41459
42325
  if (!path) continue;
41460
- const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH10;
42326
+ const sESW = sc.edgeStrokeWidth ?? EDGE_STROKE_WIDTH;
41461
42327
  const sELFS = sc.edgeLabelFontSize ?? 10;
41462
42328
  layer.append("path").attr("class", "pert-edge").attr("d", path).attr("fill", "none").attr("stroke", bandColor(band, palette, palette.textMuted)).attr("stroke-width", sESW).attr("marker-end", `url(#${bandArrow(band)})`).attr("data-source", e.source).attr("data-target", e.target).attr("data-critical", String(isCritical)).attr("data-critical-path", String(isCritical)).attr("data-criticality-band", band ?? "");
41463
42329
  const parsedEdge = edgeByKey.get(`${e.source}->${e.target}`);
@@ -41639,7 +42505,7 @@ function computeDurationEmphasis(activities) {
41639
42505
  function drawTextbookCard(g, a) {
41640
42506
  const { width: w, height: h, x, y } = a;
41641
42507
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41642
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42508
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41643
42509
  const sTRH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41644
42510
  const sBRH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41645
42511
  const sNFS = a.sNodeFontSize ?? NODE_FONT_SIZE5;
@@ -41738,7 +42604,7 @@ function drawTextbookCard(g, a) {
41738
42604
  function drawMilestonePill(g, a) {
41739
42605
  const { width: w, height: h, x, y } = a;
41740
42606
  const sNR = a.sNodeRadius ?? NODE_RADIUS2;
41741
- const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH10;
42607
+ const sNSW = a.sNodeStrokeWidth ?? NODE_STROKE_WIDTH;
41742
42608
  const topRowH = a.sNodeTopRowHeight ?? NODE_TOP_ROW_HEIGHT;
41743
42609
  const botRowH = a.sNodeBottomRowHeight ?? NODE_BOTTOM_ROW_HEIGHT;
41744
42610
  const sNCFS = a.sNodeCellFontSize ?? NODE_CELL_FONT_SIZE2;
@@ -41871,7 +42737,7 @@ function renderCaptionBlock(svg, bullets, args) {
41871
42737
  palette.textOnFillDark
41872
42738
  );
41873
42739
  const block = svg.append("g").attr("class", "pert-caption-block").attr("data-pert-caption", "");
41874
- block.append("rect").attr("class", "pert-caption-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH10);
42740
+ block.append("rect").attr("class", "pert-caption-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH);
41875
42741
  block.append("text").attr("class", "pert-caption-header").attr("x", x + width / 2).attr("y", y + CAPTION_BOX_PADDING_Y + CAPTION_FONT_SIZE).attr("text-anchor", "middle").attr("fill", labelColor).attr("font-size", CAPTION_FONT_SIZE).attr("font-weight", "700").text("Summary");
41876
42742
  const textX = x + CAPTION_BOX_PADDING_X;
41877
42743
  const firstBaselineY = y + CAPTION_BOX_PADDING_Y + CAPTION_HEADER_BAND_HEIGHT + CAPTION_FONT_SIZE;
@@ -41988,7 +42854,7 @@ function renderTornadoBlock(svg, rows, args) {
41988
42854
  palette.textOnFillDark
41989
42855
  );
41990
42856
  const block = svg.append("g").attr("class", "pert-tornado-block").attr("data-pert-tornado", "");
41991
- block.append("rect").attr("class", "pert-tornado-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH10);
42857
+ block.append("rect").attr("class", "pert-tornado-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH);
41992
42858
  block.append("text").attr("class", "pert-tornado-header").attr("x", x + width / 2).attr("y", y + CAPTION_BOX_PADDING_Y + CAPTION_FONT_SIZE).attr("text-anchor", "middle").attr("fill", labelColor).attr("font-size", CAPTION_FONT_SIZE).attr("font-weight", "700").text("Activity Risk");
41993
42859
  const fmt = (v) => {
41994
42860
  const r = Math.round(v * 100) / 100;
@@ -42144,7 +43010,7 @@ function renderScurveBlock(svg, data, args) {
42144
43010
  palette.textOnFillDark
42145
43011
  );
42146
43012
  const block = svg.append("g").attr("class", "pert-scurve-block").attr("data-pert-scurve", "");
42147
- block.append("rect").attr("class", "pert-scurve-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH10);
43013
+ block.append("rect").attr("class", "pert-scurve-rect").attr("x", x).attr("y", y).attr("width", width).attr("height", height).attr("rx", NODE_RADIUS2).attr("ry", NODE_RADIUS2).attr("fill", fill2).attr("stroke", chromeStroke).attr("stroke-width", NODE_STROKE_WIDTH);
42148
43014
  const hasTitle = typeof title === "string" && title.length > 0;
42149
43015
  if (hasTitle) {
42150
43016
  const titleText = title.replace(/\.$/, "");
@@ -42328,7 +43194,7 @@ function formatScurveDate(iso) {
42328
43194
  if (month < 0 || month > 11 || isNaN(day)) return iso;
42329
43195
  return `${SCURVE_MONTH_NAMES[month]} ${day}`;
42330
43196
  }
42331
- var d3Selection17, d3Shape9, DIAGRAM_PADDING11, NODE_FONT_SIZE5, NODE_CELL_FONT_SIZE2, NODE_RADIUS2, NODE_STROKE_WIDTH10, NODE_TOP_ROW_HEIGHT, NODE_BOTTOM_ROW_HEIGHT, EDGE_STROKE_WIDTH10, ARROWHEAD_W4, ARROWHEAD_H4, CONTAINER_RADIUS3, CONTAINER_LABEL_FONT_SIZE4, CONTAINER_HEADER_HEIGHT3, COLLAPSE_BAR_HEIGHT7, DURATION_FADE_OPACITY, PIN_ICON_W, PIN_ICON_H, LEGEND_PILL_HEIGHT, LEGEND_TOP_GAP, LEGEND_BOTTOM_GAP, FIELD_LEGEND_HEADER_BAND_HEIGHT, FIELD_LEGEND_CELL_VPAD, FIELD_LEGEND_LABEL_FONT_SIZE, FIELD_LEGEND_DESC_FONT_SIZE, FIELD_LEGEND_DESC_LINE_HEIGHT, FIELD_LEGEND_LABEL_DESC_GAP, FIELD_LEGEND_CELLS, lineGenerator6, FIELD_LEGEND_MIN_W, SUB_BULLET_INDENT, CAPTION_HEADER_BAND_HEIGHT, TORNADO_TOP_N, TORNADO_ROW_HEIGHT, TORNADO_NAME_COL_W, TORNADO_BAR_FONT_SIZE, TORNADO_BAR_HEIGHT, SUMMARY_MIN_W, SUMMARY_MAX_W, ANALYSIS_GAP, COL1_VSTACK_GAP, TORNADO_MIN_W, SCURVE_MIN_W, SCURVE_BOX_HEIGHT, SCURVE_PLOT_PADDING_X, SCURVE_PLOT_PADDING_RIGHT, SCURVE_PLOT_PADDING_BOTTOM, SCURVE_TICK_FONT_SIZE, SCURVE_PERCENTILE_RADIUS, DAYS_PER_UNIT, SCURVE_MONTH_NAMES, UNIT_TO_DAYS_LOCAL2;
43197
+ var d3Selection17, d3Shape9, DIAGRAM_PADDING11, NODE_FONT_SIZE5, NODE_CELL_FONT_SIZE2, NODE_RADIUS2, NODE_TOP_ROW_HEIGHT, NODE_BOTTOM_ROW_HEIGHT, ARROWHEAD_W4, ARROWHEAD_H4, DURATION_FADE_OPACITY, PIN_ICON_W, PIN_ICON_H, LEGEND_PILL_HEIGHT, LEGEND_TOP_GAP, LEGEND_BOTTOM_GAP, FIELD_LEGEND_HEADER_BAND_HEIGHT, FIELD_LEGEND_CELL_VPAD, FIELD_LEGEND_LABEL_FONT_SIZE, FIELD_LEGEND_DESC_FONT_SIZE, FIELD_LEGEND_DESC_LINE_HEIGHT, FIELD_LEGEND_LABEL_DESC_GAP, FIELD_LEGEND_CELLS, lineGenerator6, FIELD_LEGEND_MIN_W, SUB_BULLET_INDENT, CAPTION_HEADER_BAND_HEIGHT, TORNADO_TOP_N, TORNADO_ROW_HEIGHT, TORNADO_NAME_COL_W, TORNADO_BAR_FONT_SIZE, TORNADO_BAR_HEIGHT, SUMMARY_MIN_W, SUMMARY_MAX_W, ANALYSIS_GAP, COL1_VSTACK_GAP, TORNADO_MIN_W, SCURVE_MIN_W, SCURVE_BOX_HEIGHT, SCURVE_PLOT_PADDING_X, SCURVE_PLOT_PADDING_RIGHT, SCURVE_PLOT_PADDING_BOTTOM, SCURVE_TICK_FONT_SIZE, SCURVE_PERCENTILE_RADIUS, DAYS_PER_UNIT, SCURVE_MONTH_NAMES, UNIT_TO_DAYS_LOCAL2;
42332
43198
  var init_renderer11 = __esm({
42333
43199
  "src/pert/renderer.ts"() {
42334
43200
  "use strict";
@@ -42346,20 +43212,15 @@ var init_renderer11 = __esm({
42346
43212
  init_analyzer();
42347
43213
  init_layout11();
42348
43214
  init_internal();
43215
+ init_visual_conventions();
42349
43216
  DIAGRAM_PADDING11 = 20;
42350
43217
  NODE_FONT_SIZE5 = 13;
42351
43218
  NODE_CELL_FONT_SIZE2 = 11;
42352
43219
  NODE_RADIUS2 = 6;
42353
- NODE_STROKE_WIDTH10 = 1.5;
42354
43220
  NODE_TOP_ROW_HEIGHT = 26;
42355
43221
  NODE_BOTTOM_ROW_HEIGHT = 26;
42356
- EDGE_STROKE_WIDTH10 = 1.5;
42357
43222
  ARROWHEAD_W4 = 10;
42358
43223
  ARROWHEAD_H4 = 7;
42359
- CONTAINER_RADIUS3 = 8;
42360
- CONTAINER_LABEL_FONT_SIZE4 = 13;
42361
- CONTAINER_HEADER_HEIGHT3 = 28;
42362
- COLLAPSE_BAR_HEIGHT7 = 6;
42363
43224
  DURATION_FADE_OPACITY = 0.55;
42364
43225
  PIN_ICON_W = 13;
42365
43226
  PIN_ICON_H = 13;
@@ -45350,8 +46211,8 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45350
46211
  const sNodeFontSize = ctx.text(NODE_FONT_SIZE6);
45351
46212
  const sEdgeLabelFontSize = ctx.text(EDGE_LABEL_FONT_SIZE10);
45352
46213
  const sGroupLabelFontSize = ctx.text(GROUP_LABEL_FONT_SIZE3);
45353
- const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH11);
45354
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH11);
46214
+ const sEdgeStrokeWidth = ctx.structural(EDGE_STROKE_WIDTH);
46215
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
45355
46216
  const sArrowheadW = ctx.structural(ARROWHEAD_W5);
45356
46217
  const sArrowheadH = ctx.structural(ARROWHEAD_H5);
45357
46218
  const sPseudostateRadius = ctx.structural(PSEUDOSTATE_RADIUS);
@@ -45408,7 +46269,7 @@ function renderState(container, graph, layout, palette, isDark, onClickItem, exp
45408
46269
  const gy = group.y - sGroupExtraPadding - sGroupLabelFontSize - 4;
45409
46270
  const gw = group.width + sGroupExtraPadding * 2;
45410
46271
  const gh = group.height + sGroupExtraPadding * 2 + sGroupLabelFontSize + 4;
45411
- const fillColor = group.color ? mix(group.color, isDark ? palette.surface : palette.bg, 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
46272
+ const fillColor = group.color ? mix(group.color, themeBaseBg(palette, isDark), 10) : isDark ? palette.surface : mix(palette.border, palette.bg, 30);
45412
46273
  const strokeColor = group.color ?? palette.textMuted;
45413
46274
  const groupWrapper = contentG.append("g").attr("class", "st-group-wrapper").attr("data-line-number", String(group.lineNumber)).attr("data-group-id", group.id).attr("data-group-toggle", group.id).attr("tabindex", "0").attr("role", "button").attr("aria-expanded", "true").attr("aria-label", `Collapse group ${group.label}`).style("cursor", "pointer");
45414
46275
  groupWrapper.append("rect").attr("x", gx).attr("y", gy).attr("width", gw).attr("height", gh).attr("rx", 6).attr("fill", fillColor).attr("stroke", strokeColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("class", "st-group");
@@ -45628,7 +46489,7 @@ function renderStateForExport(content, theme, palette) {
45628
46489
  document.body.removeChild(container);
45629
46490
  }
45630
46491
  }
45631
- var d3Selection19, DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE3, EDGE_STROKE_WIDTH11, NODE_STROKE_WIDTH11, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
46492
+ var d3Selection19, DIAGRAM_PADDING12, MAX_SCALE7, NODE_FONT_SIZE6, EDGE_LABEL_FONT_SIZE10, GROUP_LABEL_FONT_SIZE3, ARROWHEAD_W5, ARROWHEAD_H5, PSEUDOSTATE_RADIUS, STATE_CORNER_RADIUS, GROUP_EXTRA_PADDING;
45632
46493
  var init_state_renderer = __esm({
45633
46494
  "src/graph/state-renderer.ts"() {
45634
46495
  "use strict";
@@ -45644,13 +46505,12 @@ var init_state_renderer = __esm({
45644
46505
  init_scaling();
45645
46506
  init_text_measure();
45646
46507
  init_note_box();
46508
+ init_visual_conventions();
45647
46509
  DIAGRAM_PADDING12 = 20;
45648
46510
  MAX_SCALE7 = 3;
45649
46511
  NODE_FONT_SIZE6 = 13;
45650
46512
  EDGE_LABEL_FONT_SIZE10 = 11;
45651
46513
  GROUP_LABEL_FONT_SIZE3 = 11;
45652
- EDGE_STROKE_WIDTH11 = 1.5;
45653
- NODE_STROKE_WIDTH11 = 1.5;
45654
46514
  ARROWHEAD_W5 = 10;
45655
46515
  ARROWHEAD_H5 = 7;
45656
46516
  PSEUDOSTATE_RADIUS = 10;
@@ -47363,14 +48223,14 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47363
48223
  const panelY = PADDING2;
47364
48224
  const textX = panelX + CARD_PADDING_X3;
47365
48225
  const clipId = "persona-clip";
47366
- defs.append("clipPath").attr("id", clipId).append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5);
48226
+ defs.append("clipPath").attr("id", clipId).append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS);
47367
48227
  const personaFill = shapeFill(palette, personaColor, isDark, { solid });
47368
48228
  const onPersonaText = contrastText(
47369
48229
  personaFill,
47370
48230
  palette.textOnFillLight,
47371
48231
  palette.textOnFillDark
47372
48232
  );
47373
- personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5).attr("fill", personaFill);
48233
+ personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS).attr("fill", personaFill);
47374
48234
  if (descLines.length > 0) {
47375
48235
  personaG.append("line").attr("x1", panelX + 1).attr("x2", panelX + panelWidth - silhouetteZone).attr("y1", panelY + titleRowH).attr("y2", panelY + titleRowH).attr("stroke", solid ? onPersonaText : personaColor).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
47376
48236
  }
@@ -47378,7 +48238,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47378
48238
  const silY = panelY + panelHeight / 2 - 6;
47379
48239
  const silClip = personaG.append("g").attr("clip-path", `url(#${clipId})`);
47380
48240
  renderPersonaSilhouette(silClip, silX, silY, personaColor, palette, 1.2);
47381
- personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS5).attr("fill", "none").attr("stroke", personaColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48241
+ personaG.append("rect").attr("x", panelX).attr("y", panelY).attr("width", panelWidth).attr("height", panelHeight).attr("rx", CARD_RADIUS).attr("fill", "none").attr("stroke", personaColor).attr("stroke-width", CARD_STROKE_WIDTH2);
47382
48242
  personaG.append("text").attr("x", textX).attr("y", panelY + CARD_PADDING_Y3 + FONT_SIZE_STEP).attr("font-size", FONT_SIZE_STEP).attr("font-weight", "500").attr("fill", onPersonaText).text(parsed.persona.name);
47383
48243
  for (let li = 0; li < descLines.length; li++) {
47384
48244
  const lineEl = personaG.append("text").attr("x", textX).attr("y", panelY + titleRowH + descLineH * (li + 1)).attr("font-size", FONT_SIZE_META).attr("fill", onPersonaText);
@@ -47618,7 +48478,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47618
48478
  { solid }
47619
48479
  );
47620
48480
  const rowStroke = stepColor ?? palette.textMuted;
47621
- itemG.append("rect").attr("x", listX).attr("y", itemY).attr("width", cardW).attr("height", COLLAPSED_CARD_H).attr("rx", CARD_RADIUS5).attr("fill", rowFill).attr("stroke", rowStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
48481
+ itemG.append("rect").attr("x", listX).attr("y", itemY).attr("width", cardW).attr("height", COLLAPSED_CARD_H).attr("rx", CARD_RADIUS).attr("fill", rowFill).attr("stroke", rowStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
47622
48482
  const faceCx = listX + CARD_PADDING_X3 + COLLAPSED_FACE_R;
47623
48483
  const faceCy = itemY + COLLAPSED_CARD_H / 2;
47624
48484
  if (step.score !== void 0) {
@@ -47781,7 +48641,7 @@ function renderJourneyMap(container, parsed, palette, isDark, options) {
47781
48641
  const scoreColor = scoreToColor(score, palette);
47782
48642
  const tintedBg = mix(scoreColor, palette.surface, 20);
47783
48643
  const g = overlayG.append("g").attr("class", "journey-thought-hover");
47784
- g.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", CARD_RADIUS5).attr("fill", tintedBg).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48644
+ g.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", CARD_RADIUS).attr("fill", tintedBg).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
47785
48645
  g.append("line").attr("x1", fcx).attr("y1", by + bh).attr("x2", fcx).attr("y2", fcy - FACE_RADIUS - 1).attr("stroke", scoreColor).attr("stroke-width", CARD_STROKE_WIDTH2);
47786
48646
  const centerX = bx + bw / 2;
47787
48647
  for (let i = 0; i < lines.length; i++) {
@@ -47938,7 +48798,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
47938
48798
  palette.textOnFillLight,
47939
48799
  palette.textOnFillDark
47940
48800
  );
47941
- stepG.append("rect").attr("x", cx).attr("y", cy).attr("width", sl.width).attr("height", sl.height).attr("rx", CARD_RADIUS5).attr("fill", cardFill).attr("stroke", cardStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
48801
+ stepG.append("rect").attr("x", cx).attr("y", cy).attr("width", sl.width).attr("height", sl.height).attr("rx", CARD_RADIUS).attr("fill", cardFill).attr("stroke", cardStroke).attr("stroke-width", CARD_STROKE_WIDTH2);
47942
48802
  const titleMaxW = sl.width - CARD_PADDING_X3 * 2;
47943
48803
  const titleLines = wrapTextToWidth(sl.step.title, FONT_SIZE_STEP, titleMaxW);
47944
48804
  for (let i = 0; i < titleLines.length; i++) {
@@ -47997,7 +48857,7 @@ function renderStepCard(parent, sl, palette, isDark, activeGroup, tagGroups, onN
47997
48857
  const stripFill = shapeFill(palette, stripColor, isDark, {
47998
48858
  ...solid !== void 0 && { solid }
47999
48859
  });
48000
- stepG.append("rect").attr("x", cx).attr("y", stripY).attr("width", sl.width).attr("height", TAG_STRIP_HEIGHT).attr("rx", CARD_RADIUS5).attr("fill", stripFill).attr("stroke", stripColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48860
+ stepG.append("rect").attr("x", cx).attr("y", stripY).attr("width", sl.width).attr("height", TAG_STRIP_HEIGHT).attr("rx", CARD_RADIUS).attr("fill", stripFill).attr("stroke", stripColor).attr("stroke-width", CARD_STROKE_WIDTH2);
48001
48861
  const stripTextColor = contrastText(
48002
48862
  stripFill,
48003
48863
  palette.textOnFillLight,
@@ -48113,7 +48973,7 @@ function renderJourneyMapForExport(content, theme, palette) {
48113
48973
  }
48114
48974
  return svgEl.outerHTML;
48115
48975
  }
48116
- var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_RADIUS5, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_HEIGHT3, CARD_STROKE_WIDTH2, CARD_META_LINE_HEIGHT3, CARD_GAP_INTERNAL, COLUMN_RADIUS2, COLUMN_HEADER_HEIGHT2, COLUMN_PADDING3, FONT_SIZE_TITLE, FONT_SIZE_PHASE, FONT_SIZE_STEP, FONT_SIZE_META, GRID_LINE_OPACITY, CURVE_STROKE_WIDTH, FACE_RADIUS, DIM_HOVER, TITLE_LINE_HEIGHT, EMOTION_LABEL_MAX_WIDTH, EMOTION_LABEL_FONT_SIZE, ICON_THUMBS_DOWN, ICON_THUMBS_UP, ICON_THOUGHT;
48976
+ var d3, d3Shape10, DIAGRAM_PADDING13, PADDING2, CARD_PADDING_X3, CARD_PADDING_Y3, CARD_HEADER_HEIGHT3, CARD_STROKE_WIDTH2, CARD_META_LINE_HEIGHT3, CARD_GAP_INTERNAL, COLUMN_RADIUS2, COLUMN_HEADER_HEIGHT2, COLUMN_PADDING3, FONT_SIZE_TITLE, FONT_SIZE_PHASE, FONT_SIZE_STEP, FONT_SIZE_META, GRID_LINE_OPACITY, CURVE_STROKE_WIDTH, FACE_RADIUS, DIM_HOVER, TITLE_LINE_HEIGHT, EMOTION_LABEL_MAX_WIDTH, EMOTION_LABEL_FONT_SIZE, ICON_THUMBS_DOWN, ICON_THUMBS_UP, ICON_THOUGHT;
48117
48977
  var init_renderer14 = __esm({
48118
48978
  "src/journey-map/renderer.ts"() {
48119
48979
  "use strict";
@@ -48128,9 +48988,9 @@ var init_renderer14 = __esm({
48128
48988
  init_tag_groups();
48129
48989
  init_scaling();
48130
48990
  init_text_measure();
48991
+ init_visual_conventions();
48131
48992
  DIAGRAM_PADDING13 = 20;
48132
48993
  PADDING2 = DIAGRAM_PADDING13;
48133
- CARD_RADIUS5 = 6;
48134
48994
  CARD_PADDING_X3 = 10;
48135
48995
  CARD_PADDING_Y3 = 6;
48136
48996
  CARD_HEADER_HEIGHT3 = 24;
@@ -48207,7 +49067,7 @@ function computeCycleLayout(parsed, options) {
48207
49067
  const hasDesc = !hideDescriptions && node.description.length > 0;
48208
49068
  const labelWidth2 = Math.max(
48209
49069
  MIN_NODE_WIDTH4,
48210
- measureText(node.label, LABEL_FONT_SIZE5) + NODE_PAD_X * 2
49070
+ measureText(node.label, LABEL_FONT_SIZE4) + NODE_PAD_X * 2
48211
49071
  );
48212
49072
  if (circleNodes) {
48213
49073
  return computeCircleNodeDims(node, hasDesc);
@@ -48424,7 +49284,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48424
49284
  let bestScore = Infinity;
48425
49285
  for (let w = minW; w <= MAX_NODE_WIDTH3; w += DESC_WIDTH_STEP) {
48426
49286
  const wrapped2 = wrapDescForWidth(description, w);
48427
- const h = HEADER_HEIGHT5 + wrapped2.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y;
49287
+ const h = HEADER_HEIGHT4 + wrapped2.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y;
48428
49288
  const ratio = w / h;
48429
49289
  const score = Math.abs(Math.log(ratio / DESC_TARGET_RATIO));
48430
49290
  if (score < bestScore) {
@@ -48436,7 +49296,7 @@ function chooseDescribedRectDims(description, labelWidth2) {
48436
49296
  const wrapped = wrapDescForWidth(description, minW);
48437
49297
  return {
48438
49298
  width: minW,
48439
- height: HEADER_HEIGHT5 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
49299
+ height: HEADER_HEIGHT4 + wrapped.length * DESC_LINE_HEIGHT7 + DESC_PAD_Y,
48440
49300
  wrappedDesc: wrapped
48441
49301
  };
48442
49302
  }
@@ -48449,7 +49309,7 @@ function wrapDescForWidth(description, nodeWidth) {
48449
49309
  );
48450
49310
  }
48451
49311
  function renderedDescNodeHeight(numLines, scale) {
48452
- const headerH = HEADER_HEIGHT5 * scale;
49312
+ const headerH = HEADER_HEIGHT4 * scale;
48453
49313
  const descFont = Math.max(
48454
49314
  RENDERER_DESC_FONT_MIN,
48455
49315
  Math.round(RENDERER_DESC_FONT * scale)
@@ -48784,7 +49644,7 @@ function buildEdgeArc(src, tgt, cx, cy, radius, isClockwise, arrowLength = 0) {
48784
49644
  const midAngle = srcExitAngle + dir * visibleSweep / 2;
48785
49645
  return { path, midAngle };
48786
49646
  }
48787
- var MIN_ARC_ANGLE, LABEL_FONT_SIZE5, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT5, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
49647
+ var MIN_ARC_ANGLE, LABEL_FONT_SIZE4, CIRCLE_LABEL_FONT_SIZE, DESC_FONT_SIZE6, EDGE_LABEL_FONT_SIZE11, MIN_NODE_WIDTH4, MAX_NODE_WIDTH3, DESC_MIN_WIDTH, DESC_WIDTH_STEP, DESC_TARGET_RATIO, PLAIN_NODE_HEIGHT, HEADER_HEIGHT4, DESC_LINE_HEIGHT7, DESC_PAD_Y, NODE_PAD_X, MIN_CIRCLE_RADIUS, CIRCLE_PAD, RENDERER_DESC_FONT, RENDERER_DESC_FONT_MIN, RENDERER_DESC_LINE_H, RENDERER_DESC_LINE_H_MIN, EDGE_LABEL_MAX_WIDTH;
48788
49648
  var init_layout14 = __esm({
48789
49649
  "src/cycle/layout.ts"() {
48790
49650
  "use strict";
@@ -48792,7 +49652,7 @@ var init_layout14 = __esm({
48792
49652
  init_wrapped_desc();
48793
49653
  init_text_measure();
48794
49654
  MIN_ARC_ANGLE = 15 * Math.PI / 180;
48795
- LABEL_FONT_SIZE5 = 13;
49655
+ LABEL_FONT_SIZE4 = 13;
48796
49656
  CIRCLE_LABEL_FONT_SIZE = 16;
48797
49657
  DESC_FONT_SIZE6 = 11;
48798
49658
  EDGE_LABEL_FONT_SIZE11 = 11;
@@ -48802,7 +49662,7 @@ var init_layout14 = __esm({
48802
49662
  DESC_WIDTH_STEP = 20;
48803
49663
  DESC_TARGET_RATIO = 1.6;
48804
49664
  PLAIN_NODE_HEIGHT = 50;
48805
- HEADER_HEIGHT5 = 36;
49665
+ HEADER_HEIGHT4 = 36;
48806
49666
  DESC_LINE_HEIGHT7 = 16;
48807
49667
  DESC_PAD_Y = 14;
48808
49668
  NODE_PAD_X = 20;
@@ -49382,6 +50242,16 @@ __export(resolver_exports, {
49382
50242
  isSubNationalUsExtent: () => isSubNationalUsExtent,
49383
50243
  resolveMap: () => resolveMap
49384
50244
  });
50245
+ function containerOvershoot(span, usOriented) {
50246
+ if (usOriented) return CONTAINER_OVERSHOOT_MAX;
50247
+ return Math.max(
50248
+ CONTAINER_OVERSHOOT_MIN,
50249
+ Math.min(
50250
+ CONTAINER_OVERSHOOT_MAX,
50251
+ CONTAINER_OVERSHOOT_MAX - CONTAINER_OVERSHOOT_DECAY * span
50252
+ )
50253
+ );
50254
+ }
49385
50255
  function bboxArea(b) {
49386
50256
  return (b[1][0] - b[0][0]) * (b[1][1] - b[0][1]);
49387
50257
  }
@@ -49878,7 +50748,7 @@ function resolveMap(parsed, data) {
49878
50748
  const containerUnion = unionExtent(containerBoxes, points);
49879
50749
  if (containerUnion)
49880
50750
  extent3 = pad(
49881
- clampContainerToCluster(containerUnion, points),
50751
+ clampContainerToCluster(containerUnion, points, usOriented),
49882
50752
  PAD_FRACTION
49883
50753
  );
49884
50754
  }
@@ -49962,17 +50832,21 @@ function mostCommonCountry(regions, poiCountries) {
49962
50832
  }
49963
50833
  return best;
49964
50834
  }
49965
- function clampContainerToCluster(container, points) {
50835
+ function clampContainerToCluster(container, points, usOriented) {
49966
50836
  const poi = unionExtent([], points);
49967
50837
  if (!poi) return container;
49968
50838
  let [[west, south], [east, north]] = container;
49969
50839
  const [[pWest, pSouth], [pEast, pNorth]] = poi;
49970
- south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
49971
- north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
49972
- if (east <= 180 && pEast <= 180) {
49973
- west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
49974
- east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
49975
- }
50840
+ const over = containerOvershoot(
50841
+ Math.max(pEast - pWest, pNorth - pSouth),
50842
+ usOriented
50843
+ );
50844
+ south = Math.max(south, pSouth - over);
50845
+ north = Math.min(north, pNorth + over);
50846
+ const wOver = pWest - over;
50847
+ const eOver = pEast + over;
50848
+ west = west >= -180 && west <= pWest ? Math.max(west, wOver) : wOver;
50849
+ east = east <= 180 && east >= pEast ? Math.min(east, eOver) : eOver;
49976
50850
  return [
49977
50851
  [west, south],
49978
50852
  [east, north]
@@ -49990,7 +50864,7 @@ function firstError(diags) {
49990
50864
  const e = diags.find((d) => d.severity === "error");
49991
50865
  return e ? formatDgmoError(e) : null;
49992
50866
  }
49993
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_DEG, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
50867
+ var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, CONTAINER_OVERSHOOT_MAX, CONTAINER_OVERSHOOT_MIN, CONTAINER_OVERSHOOT_DECAY, US_NATIONAL_LON_SPAN, CONUS_BBOX, US_SUBNATIONAL_AREA_FRACTION, REGION_ALIASES, US_STATE_POSTAL;
49994
50868
  var init_resolver2 = __esm({
49995
50869
  "src/map/resolver.ts"() {
49996
50870
  "use strict";
@@ -50003,7 +50877,9 @@ var init_resolver2 = __esm({
50003
50877
  WORLD_LAT_SOUTH = -58;
50004
50878
  WORLD_LAT_NORTH = 78;
50005
50879
  POI_ZOOM_FLOOR_DEG = 7;
50006
- CONTAINER_OVERSHOOT_DEG = 8;
50880
+ CONTAINER_OVERSHOOT_MAX = 8;
50881
+ CONTAINER_OVERSHOOT_MIN = 3;
50882
+ CONTAINER_OVERSHOOT_DECAY = 0.3;
50007
50883
  US_NATIONAL_LON_SPAN = 48;
50008
50884
  CONUS_BBOX = [
50009
50885
  [-125, 25],
@@ -50169,12 +51045,12 @@ function tierBand(maxSpanDeg) {
50169
51045
  }
50170
51046
  function labelBudget(width, height, band) {
50171
51047
  const bandCap = {
50172
- world: 7,
50173
- continental: 6,
50174
- regional: 5,
50175
- local: 4
51048
+ world: 10,
51049
+ continental: 9,
51050
+ regional: 7,
51051
+ local: 6
50176
51052
  };
50177
- const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 150);
51053
+ const area2 = Math.floor(Math.sqrt(Math.max(0, width * height)) / 105);
50178
51054
  return Math.max(0, Math.min(area2, bandCap[band]));
50179
51055
  }
50180
51056
  function waterEligible(tier, kind, band) {
@@ -50228,6 +51104,11 @@ function rectAround(cx, cy, lines, letterSpacing, font = FONT) {
50228
51104
  const h = (lines.length - 1) * lineHeight + font + 2 * PADY;
50229
51105
  return { x: cx - w / 2, y: cy - h / 2, w, h };
50230
51106
  }
51107
+ function rectDist2(px, py, x0, y0, x1, y1) {
51108
+ const dx = Math.max(x0 - px, 0, px - x1);
51109
+ const dy = Math.max(y0 - py, 0, py - y1);
51110
+ return dx * dx + dy * dy;
51111
+ }
50231
51112
  function rectFits(r, width, height) {
50232
51113
  return r.x >= 0 && r.y >= 0 && r.x + r.w <= width && r.y + r.h <= height;
50233
51114
  }
@@ -50246,6 +51127,7 @@ function placeContextLabels(args) {
50246
51127
  palette,
50247
51128
  project,
50248
51129
  collides,
51130
+ contentPoints,
50249
51131
  overLand
50250
51132
  } = args;
50251
51133
  void projection;
@@ -50316,8 +51198,17 @@ function placeContextLabels(args) {
50316
51198
  const [x0, y0, x1, y1] = c.bbox;
50317
51199
  const w = x1 - x0;
50318
51200
  const h = y1 - y0;
50319
- return { c, w, h, area: w * h };
50320
- }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort((a, b) => b.area - a.area);
51201
+ let dist = Infinity;
51202
+ if (contentPoints?.length) {
51203
+ for (const p of contentPoints) {
51204
+ const d = rectDist2(p[0], p[1], x0, y0, x1, y1);
51205
+ if (d < dist) dist = d;
51206
+ }
51207
+ }
51208
+ return { c, w, h, area: w * h, dist };
51209
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
51210
+ (a, b) => contentPoints?.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
51211
+ );
50321
51212
  const canvasLinear = Math.sqrt(Math.max(1, width * height));
50322
51213
  let ci = 0;
50323
51214
  for (const r of ranked) {
@@ -50334,7 +51225,7 @@ function placeContextLabels(args) {
50334
51225
  );
50335
51226
  const fontSize = Math.round(FONT + t * (COUNTRY_FONT_MAX - FONT));
50336
51227
  const fade = Math.round(t * COUNTRY_FADE_MAX);
50337
- const color = fade > 0 ? mix(countryColor, palette.bg, fade) : countryColor;
51228
+ const color = fade > 0 ? mix(countryColor, palette.bg, 100 - fade) : countryColor;
50338
51229
  const text = c.name;
50339
51230
  const tw = labelWidth(text, 0, fontSize);
50340
51231
  if (tw > w || fontSize + 2 * PADY > h) continue;
@@ -50347,6 +51238,9 @@ function placeContextLabels(args) {
50347
51238
  letterSpacing: 0,
50348
51239
  color,
50349
51240
  fontSize,
51241
+ // Multi-position dodging: carry the ordered interior positions through to the
51242
+ // commit loop. Invariant anchor === positions[0], so `cx/cy` is positions[0].
51243
+ ...c.positions ? { positions: c.positions } : {},
50350
51244
  // Band 1 (orientation-value ranking): above MINOR water (band 2, 2000+) but
50351
51245
  // below MAJOR water — oceans + major seas (band 0, ≤~16). So a big country
50352
51246
  // (US, Canada, Russia) outranks a minor sea/bay (Sargasso, Bahía de
@@ -50361,39 +51255,53 @@ function placeContextLabels(args) {
50361
51255
  const countryCount = candidates.reduce((n, c) => n + (c.italic ? 0 : 1), 0);
50362
51256
  const waterCap = budget - Math.min(2, countryCount);
50363
51257
  let waterPlaced = 0;
50364
- for (const cand of candidates) {
50365
- if (placed.length >= budget) break;
50366
- if (cand.italic && waterPlaced >= waterCap) continue;
51258
+ const gateAt = (cx, cy, cand) => {
50367
51259
  const rect = rectAround(
50368
- cand.cx,
50369
- cand.cy,
51260
+ cx,
51261
+ cy,
50370
51262
  cand.lines,
50371
51263
  cand.letterSpacing,
50372
51264
  cand.fontSize
50373
51265
  );
50374
- if (!rectFits(rect, width, height)) continue;
51266
+ if (!rectFits(rect, width, height)) return null;
50375
51267
  if (cand.italic && overLand) {
50376
51268
  const inset = 2;
50377
- const top = cand.cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
51269
+ const top = cy - (cand.lines.length - 1) / 2 * LINE_HEIGHT;
50378
51270
  const touchesLand = cand.lines.some((line11, li) => {
50379
51271
  const lw = labelWidth(line11, cand.letterSpacing);
50380
- const x0 = cand.cx - lw / 2 + inset;
50381
- const x1 = cand.cx + lw / 2 - inset;
50382
- const xs = [x0, (x0 + cand.cx) / 2, cand.cx, (cand.cx + x1) / 2, x1];
51272
+ const x0 = cx - lw / 2 + inset;
51273
+ const x1 = cx + lw / 2 - inset;
51274
+ const xs = [x0, (x0 + cx) / 2, cx, (cx + x1) / 2, x1];
50383
51275
  const base = top + li * LINE_HEIGHT;
50384
51276
  return [base, base - FONT * 0.4, base - FONT * 0.8].some(
50385
51277
  (y) => xs.some((x) => overLand(x, y))
50386
51278
  );
50387
51279
  });
50388
- if (touchesLand) continue;
51280
+ if (touchesLand) return null;
51281
+ }
51282
+ if (collides(rect)) return null;
51283
+ if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD)))
51284
+ return null;
51285
+ return rect;
51286
+ };
51287
+ for (const cand of candidates) {
51288
+ if (placed.length >= budget) break;
51289
+ if (cand.italic && waterPlaced >= waterCap) continue;
51290
+ const positions = cand.positions ?? [[cand.cx, cand.cy]];
51291
+ let chosen = null;
51292
+ for (const [px, py] of positions) {
51293
+ const rect = gateAt(px, py, cand);
51294
+ if (rect) {
51295
+ chosen = { x: px, y: py, rect };
51296
+ break;
51297
+ }
50389
51298
  }
50390
- if (collides(rect)) continue;
50391
- if (placedRects.some((r) => overlapsPadded(rect, r, CONTEXT_PAD))) continue;
50392
- placedRects.push(rect);
51299
+ if (!chosen) continue;
51300
+ placedRects.push(chosen.rect);
50393
51301
  if (cand.italic) waterPlaced++;
50394
51302
  placed.push({
50395
- x: cand.cx,
50396
- y: cand.cy,
51303
+ x: chosen.x,
51304
+ y: chosen.y,
50397
51305
  text: cand.text,
50398
51306
  anchor: "middle",
50399
51307
  color: cand.color,
@@ -50411,7 +51319,7 @@ function placeContextLabels(args) {
50411
51319
  }
50412
51320
  return placed;
50413
51321
  }
50414
- var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, KIND_ORDER;
51322
+ var FONT, LINE_HEIGHT, PADX, PADY, WATER_LETTER_SPACING, CONTEXT_PAD, EDGE_CLAMP_MARGIN, EDGE_CLAMP_OVERSHOOT, COUNTRY_FONT_MAX, COUNTRY_SIZE_FRAC_MIN, COUNTRY_SIZE_FRAC_MAX, COUNTRY_FADE_MAX, MAX_COUNTRY_POSITIONS, COUNTRY_POS_GRID, COUNTRY_POS_TOPN_MARGIN, KIND_ORDER;
50415
51323
  var init_context_labels = __esm({
50416
51324
  "src/map/context-labels.ts"() {
50417
51325
  "use strict";
@@ -50429,6 +51337,9 @@ var init_context_labels = __esm({
50429
51337
  COUNTRY_SIZE_FRAC_MIN = 0.06;
50430
51338
  COUNTRY_SIZE_FRAC_MAX = 0.32;
50431
51339
  COUNTRY_FADE_MAX = 45;
51340
+ MAX_COUNTRY_POSITIONS = 4;
51341
+ COUNTRY_POS_GRID = 5;
51342
+ COUNTRY_POS_TOPN_MARGIN = 3;
50432
51343
  KIND_ORDER = {
50433
51344
  ocean: 0,
50434
51345
  sea: 1,
@@ -50511,6 +51422,84 @@ function decodeLayer(topo) {
50511
51422
  decodeCache.set(topo, out);
50512
51423
  return out;
50513
51424
  }
51425
+ function countryLabelPositions(args) {
51426
+ const { geometry, bounds, project, width, height, curated } = args;
51427
+ const w0 = bounds[0][0];
51428
+ const s0 = bounds[0][1];
51429
+ const e0 = bounds[1][0];
51430
+ const n0 = bounds[1][1];
51431
+ if (![w0, s0, e0, n0].every(Number.isFinite) || e0 <= w0 || n0 <= s0) {
51432
+ return mkCurated(curated, project);
51433
+ }
51434
+ const N = COUNTRY_POS_GRID;
51435
+ const onLand = [];
51436
+ const kept = [];
51437
+ for (let i = 0; i < N; i++) {
51438
+ onLand[i] = [];
51439
+ const lon = w0 + (i + 0.5) / N * (e0 - w0);
51440
+ for (let j = 0; j < N; j++) {
51441
+ const lat = s0 + (j + 0.5) / N * (n0 - s0);
51442
+ const land = pointInGeometry(geometry, lon, lat);
51443
+ onLand[i][j] = land;
51444
+ if (!land) continue;
51445
+ const p = project(lon, lat);
51446
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) continue;
51447
+ if (p[0] < 0 || p[0] > width || p[1] < 0 || p[1] > height) continue;
51448
+ kept.push({ i, j, lon, lat, sx: p[0], sy: p[1] });
51449
+ }
51450
+ }
51451
+ if (!kept.length) return mkCurated(curated, project);
51452
+ const cx = kept.reduce((s, c) => s + c.sx, 0) / kept.length;
51453
+ const cy = kept.reduce((s, c) => s + c.sy, 0) / kept.length;
51454
+ const interiorness = (c) => {
51455
+ let n = 0;
51456
+ for (let di = -1; di <= 1; di++)
51457
+ for (let dj = -1; dj <= 1; dj++) {
51458
+ if (di === 0 && dj === 0) continue;
51459
+ const ni = c.i + di;
51460
+ const nj = c.j + dj;
51461
+ if (ni >= 0 && ni < N && nj >= 0 && nj < N && onLand[ni][nj]) n++;
51462
+ }
51463
+ return n;
51464
+ };
51465
+ const dist2ToCentre = (c) => (c.sx - cx) ** 2 + (c.sy - cy) ** 2;
51466
+ const pool = [...kept];
51467
+ pool.sort((a, b) => {
51468
+ const d = interiorness(b) - interiorness(a);
51469
+ return d !== 0 ? d : dist2ToCentre(a) - dist2ToCentre(b);
51470
+ });
51471
+ const ordered = [pool.shift()];
51472
+ while (ordered.length < MAX_COUNTRY_POSITIONS && pool.length) {
51473
+ let bestIdx = 0;
51474
+ let bestMin = -1;
51475
+ for (let k = 0; k < pool.length; k++) {
51476
+ const c = pool[k];
51477
+ let minD = Infinity;
51478
+ for (const o of ordered) {
51479
+ const d = (c.sx - o.sx) ** 2 + (c.sy - o.sy) ** 2;
51480
+ if (d < minD) minD = d;
51481
+ }
51482
+ if (minD > bestMin) {
51483
+ bestMin = minD;
51484
+ bestIdx = k;
51485
+ }
51486
+ }
51487
+ ordered.push(pool.splice(bestIdx, 1)[0]);
51488
+ }
51489
+ const grid = ordered.map((c) => ({
51490
+ lonLat: [c.lon, c.lat],
51491
+ screen: [c.sx, c.sy]
51492
+ }));
51493
+ const curatedPos = curated ? mkCurated(curated, project) : [];
51494
+ const out = [...curatedPos, ...grid].slice(0, MAX_COUNTRY_POSITIONS);
51495
+ return out;
51496
+ }
51497
+ function mkCurated(curated, project) {
51498
+ if (!curated) return [];
51499
+ const p = project(curated[0], curated[1]);
51500
+ if (!p || !Number.isFinite(p[0]) || !Number.isFinite(p[1])) return [];
51501
+ return [{ lonLat: [curated[0], curated[1]], screen: [p[0], p[1]] }];
51502
+ }
50514
51503
  function projectionFor(family, extent3) {
50515
51504
  switch (family) {
50516
51505
  case "albers-usa":
@@ -51338,6 +52327,8 @@ function layoutMap(resolvedIn, data, size, opts) {
51338
52327
  if (p[1] < minY) minY = p[1];
51339
52328
  if (p[1] > maxY) maxY = p[1];
51340
52329
  }
52330
+ r.bbox = [minX, minY, maxX, maxY];
52331
+ r.rings = rings;
51341
52332
  return { fill: r.fill, rings, minX, minY, maxX, maxY };
51342
52333
  });
51343
52334
  const fillAt = (x, y) => {
@@ -52528,6 +53519,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52528
53519
  for (const box of insets)
52529
53520
  obstacles.push({ x: box.x, y: box.y, w: box.w, h: box.h });
52530
53521
  const countryCandidates = [];
53522
+ const rawCountries = [];
52531
53523
  for (const f of worldLayer.values()) {
52532
53524
  const iso = typeof f.id === "string" ? f.id : String(f.id ?? "");
52533
53525
  if (!iso || regionById.has(iso)) continue;
@@ -52544,11 +53536,59 @@ function layoutMap(resolvedIn, data, size, opts) {
52544
53536
  if (!Number.isFinite(x0) || !Number.isFinite(x1)) continue;
52545
53537
  const anchorLngLat = WORLD_LABEL_ANCHORS[iso];
52546
53538
  const a = anchorLngLat ? project(anchorLngLat[0], anchorLngLat[1]) : path.centroid(f);
52547
- countryCandidates.push({
53539
+ rawCountries.push({
53540
+ f,
53541
+ iso,
52548
53542
  name: f.properties?.name ?? iso,
52549
53543
  bbox: [x0, y0, x1, y1],
52550
53544
  anchor: a && Number.isFinite(a[0]) ? [a[0], a[1]] : null,
52551
- curatedAnchor: !!anchorLngLat
53545
+ curatedLngLat: anchorLngLat ?? null,
53546
+ area: (x1 - x0) * (y1 - y0)
53547
+ });
53548
+ }
53549
+ const cBand = tierBand(Math.max(dLonSpan, dLatSpan));
53550
+ const cBudget = labelBudget(width, height, cBand);
53551
+ const contentPoints = markers.map((m) => [m.cx, m.cy]);
53552
+ const topN = cBudget + COUNTRY_POS_TOPN_MARGIN;
53553
+ const rankOrder = rawCountries.map((r, idx) => {
53554
+ let dist = Infinity;
53555
+ const [x0, y0, x1, y1] = r.bbox;
53556
+ for (const [px, py] of contentPoints) {
53557
+ const dx = Math.max(x0 - px, 0, px - x1);
53558
+ const dy = Math.max(y0 - py, 0, py - y1);
53559
+ const d = dx * dx + dy * dy;
53560
+ if (d < dist) dist = d;
53561
+ }
53562
+ return { idx, area: r.area, dist };
53563
+ }).filter((r) => Number.isFinite(r.area) && r.area > 0).sort(
53564
+ (a, b) => contentPoints.length ? a.dist - b.dist || b.area - a.area : b.area - a.area
53565
+ ).slice(0, topN);
53566
+ const genIdx = new Set(rankOrder.map((r) => r.idx));
53567
+ for (let i = 0; i < rawCountries.length; i++) {
53568
+ const r = rawCountries[i];
53569
+ let anchor = r.anchor;
53570
+ let positions;
53571
+ if (genIdx.has(i) && anchor) {
53572
+ const gb = (0, import_d3_geo2.geoBounds)(r.f);
53573
+ const gen = countryLabelPositions({
53574
+ geometry: r.f.geometry,
53575
+ bounds: gb,
53576
+ project,
53577
+ width,
53578
+ height,
53579
+ curated: r.curatedLngLat
53580
+ });
53581
+ if (gen.length) {
53582
+ positions = gen.map((p) => p.screen);
53583
+ anchor = positions[0];
53584
+ }
53585
+ }
53586
+ countryCandidates.push({
53587
+ name: r.name,
53588
+ bbox: r.bbox,
53589
+ anchor,
53590
+ curatedAnchor: !!r.curatedLngLat,
53591
+ ...positions ? { positions } : {}
52552
53592
  });
52553
53593
  }
52554
53594
  const framedStateContainers = (resolved.poiFrameContainers ?? []).some(
@@ -52582,6 +53622,7 @@ function layoutMap(resolvedIn, data, size, opts) {
52582
53622
  palette,
52583
53623
  project,
52584
53624
  collides,
53625
+ contentPoints,
52585
53626
  // Water labels must stay over open water — `fillAt` returns the ocean
52586
53627
  // backdrop colour off-land and a region fill on-land (lakes/states count
52587
53628
  // as land here, which is the safe side for an ocean name).
@@ -52835,7 +53876,7 @@ function ringToCoastPaths(ring, frame) {
52835
53876
  function coastlineOuterRings(regions, minExtent, frame) {
52836
53877
  const paths = [];
52837
53878
  for (const r of regions) {
52838
- const rings = parsePathRings(r.d);
53879
+ const rings = r.rings ?? parsePathRings(r.d);
52839
53880
  for (let i = 0; i < rings.length; i++) {
52840
53881
  const ring = rings[i];
52841
53882
  if (ring.length < 3) continue;
@@ -52899,6 +53940,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52899
53940
  const svg = d3Selection23.select(container).append("svg").attr("width", width).attr("height", height).attr("viewBox", `0 0 ${width} ${height}`).attr("preserveAspectRatio", "xMidYMin meet").attr("xmlns", "http://www.w3.org/2000/svg").style("font-family", FONT_FAMILY).style("background", layout.background);
52900
53941
  svg.append("rect").attr("width", width).attr("height", height).attr("fill", layout.background);
52901
53942
  const defs = svg.append("defs");
53943
+ const uid = mapInstanceCounter++;
53944
+ const nid = (base) => `${base}__m${uid}`;
52902
53945
  const arrowSize = (w) => Math.min(15, 7 + w * 0.95);
52903
53946
  const haloColor = palette.bg;
52904
53947
  const gRegions = svg.append("g").attr("class", "dgmo-map-regions");
@@ -52931,8 +53974,8 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52931
53974
  for (const r of layout.regions) drawRegion(gRegions, r, 0.5);
52932
53975
  if (layout.relief.length && layout.reliefHatch) {
52933
53976
  const h = layout.reliefHatch;
52934
- const rangeClipId = "dgmo-relief-clip";
52935
- const landClipId = "dgmo-relief-land";
53977
+ const rangeClipId = nid("dgmo-relief-clip");
53978
+ const landClipId = nid("dgmo-relief-land");
52936
53979
  const rangeClip = defs.append("clipPath").attr("id", rangeClipId);
52937
53980
  for (const s of layout.relief) rangeClip.append("path").attr("d", s.d);
52938
53981
  const landClip = defs.append("clipPath").attr("id", landClipId);
@@ -52945,7 +53988,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52945
53988
  }
52946
53989
  if (layout.coastlineStyle) {
52947
53990
  const cs = layout.coastlineStyle;
52948
- const maskId = "dgmo-map-water-mask";
53991
+ const maskId = nid("dgmo-map-water-mask");
52949
53992
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
52950
53993
  mask.append("rect").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "white");
52951
53994
  const landD = layout.regions.filter((r) => r.id !== "lake").map((r) => r.d).join(" ");
@@ -52963,7 +54006,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
52963
54006
  appendWaterLines(
52964
54007
  gWater,
52965
54008
  defs,
52966
- "dgmo-map-coast",
54009
+ nid("dgmo-map-coast"),
52967
54010
  // Pass the canvas frame so edges collinear with it (the antimeridian on a
52968
54011
  // world map, regional clipExtent cuts) don't get ringed as fake coast —
52969
54012
  // land runs cleanly to the render-area edge.
@@ -53000,7 +54043,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53000
54043
  (l) => l.poiId !== void 0 && !l.hidden
53001
54044
  );
53002
54045
  if (poiLabels.length) {
53003
- const patchBlurId = "dgmo-map-label-patch-blur";
54046
+ const patchBlurId = nid("dgmo-map-label-patch-blur");
53004
54047
  defs.append("filter").attr("id", patchBlurId).attr("x", "-50%").attr("y", "-50%").attr("width", "200%").attr("height", "200%").append("feGaussianBlur").attr("in", "SourceGraphic").attr("stdDeviation", 2.5);
53005
54048
  const PAD = 8;
53006
54049
  const buildPatch = (labels, maskId, decoCluster) => {
@@ -53028,14 +54071,22 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53028
54071
  if (decoCluster !== void 0)
53029
54072
  gPatch.attr("data-cluster-deco", decoCluster);
53030
54073
  for (const r of layout.regions) {
53031
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
53032
- for (const ring of parsePathRings(r.d))
53033
- for (const [px, py] of ring) {
53034
- if (px < minX) minX = px;
53035
- if (px > maxX) maxX = px;
53036
- if (py < minY) minY = py;
53037
- if (py > maxY) maxY = py;
53038
- }
54074
+ let minX, minY, maxX, maxY;
54075
+ if (r.bbox) {
54076
+ [minX, minY, maxX, maxY] = r.bbox;
54077
+ } else {
54078
+ minX = Infinity;
54079
+ minY = Infinity;
54080
+ maxX = -Infinity;
54081
+ maxY = -Infinity;
54082
+ for (const ring of parsePathRings(r.d))
54083
+ for (const [px, py] of ring) {
54084
+ if (px < minX) minX = px;
54085
+ if (px > maxX) maxX = px;
54086
+ if (py < minY) minY = py;
54087
+ if (py > maxY) maxY = py;
54088
+ }
54089
+ }
53039
54090
  const hit = blobRects.some(
53040
54091
  (b) => minX <= b.x1 && maxX >= b.x0 && minY <= b.y1 && maxY >= b.y0
53041
54092
  );
@@ -53045,7 +54096,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53045
54096
  if (clusterUi) {
53046
54097
  buildPatch(
53047
54098
  poiLabels.filter((l) => l.clusterMember === void 0),
53048
- "dgmo-map-label-patch"
54099
+ nid("dgmo-map-label-patch")
53049
54100
  );
53050
54101
  const byCluster = /* @__PURE__ */ new Map();
53051
54102
  for (const l of poiLabels) {
@@ -53056,9 +54107,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53056
54107
  }
53057
54108
  let ci = 0;
53058
54109
  for (const [cid, labs] of byCluster)
53059
- buildPatch(labs, `dgmo-map-label-patch-c${ci++}`, cid);
54110
+ buildPatch(labs, nid(`dgmo-map-label-patch-c${ci++}`), cid);
53060
54111
  } else {
53061
- buildPatch(poiLabels, "dgmo-map-label-patch");
54112
+ buildPatch(poiLabels, nid("dgmo-map-label-patch"));
53062
54113
  }
53063
54114
  }
53064
54115
  if (layout.insets.length) {
@@ -53067,7 +54118,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53067
54118
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
53068
54119
  insetG.append("path").attr("d", d).attr("fill", layout.background).attr("stroke", mix(palette.text, palette.bg, 55)).attr("stroke-width", 1).attr("stroke-linejoin", "round");
53069
54120
  if (box.contextLand) {
53070
- const clipId = `dgmo-map-inset-clip-${bi}`;
54121
+ const clipId = nid(`dgmo-map-inset-clip-${bi}`);
53071
54122
  defs.append("clipPath").attr("id", clipId).append("path").attr("d", d);
53072
54123
  insetG.append("path").attr("d", box.contextLand.d).attr("fill", box.contextLand.fill).attr("clip-path", `url(#${clipId})`);
53073
54124
  }
@@ -53075,7 +54126,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53075
54126
  for (const r of layout.insetRegions) drawRegion(insetG, r, 0.5);
53076
54127
  if (layout.coastlineStyle) {
53077
54128
  const cs = layout.coastlineStyle;
53078
- const maskId = "dgmo-map-inset-water-mask";
54129
+ const maskId = nid("dgmo-map-inset-water-mask");
53079
54130
  const mask = defs.append("mask").attr("id", maskId).attr("maskUnits", "userSpaceOnUse").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height);
53080
54131
  const reach = Math.max(0, ...cs.lines.map((l) => l.d + l.thickness));
53081
54132
  for (const box of layout.insets) {
@@ -53084,7 +54135,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53084
54135
  }
53085
54136
  layout.insets.forEach((box, bi) => {
53086
54137
  if (box.contextLand)
53087
- mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#dgmo-map-inset-clip-${bi})`);
54138
+ mask.append("path").attr("d", box.contextLand.d).attr("fill", "black").attr("clip-path", `url(#${nid(`dgmo-map-inset-clip-${bi}`)})`);
53088
54139
  });
53089
54140
  for (const r of layout.insetRegions)
53090
54141
  if (r.id !== "lake")
@@ -53092,7 +54143,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53092
54143
  for (const r of layout.insetRegions)
53093
54144
  if (r.id === "lake")
53094
54145
  mask.append("path").attr("d", r.d).attr("fill", "white");
53095
- const clipId = "dgmo-map-inset-water-clip";
54146
+ const clipId = nid("dgmo-map-inset-water-clip");
53096
54147
  const clip = defs.append("clipPath").attr("id", clipId);
53097
54148
  for (const box of layout.insets) {
53098
54149
  const d = box.points.map((p, i) => `${i ? "L" : "M"}${p[0]},${p[1]}`).join("") + "Z";
@@ -53102,7 +54153,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53102
54153
  appendWaterLines(
53103
54154
  gInsetWater,
53104
54155
  defs,
53105
- "dgmo-map-inset-coast",
54156
+ nid("dgmo-map-inset-coast"),
53106
54157
  coastlineOuterRings(layout.insetRegions, cs.minExtent),
53107
54158
  cs,
53108
54159
  layout.background
@@ -53131,7 +54182,7 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
53131
54182
  }
53132
54183
  wireSync(p, leg.lineNumber);
53133
54184
  if (leg.arrow) {
53134
- const id = `dgmo-map-arrow-${i}`;
54185
+ const id = nid(`dgmo-map-arrow-${i}`);
53135
54186
  const s = arrowSize(leg.width);
53136
54187
  defs.append("marker").attr("id", id).attr("viewBox", "0 0 10 10").attr("refX", 10).attr("refY", 5).attr("markerUnits", "userSpaceOnUse").attr("markerWidth", s).attr("markerHeight", s).attr("orient", "auto-start-reverse").append("path").attr("d", "M0,0L10,5L0,10z").attr("fill", leg.color);
53137
54188
  p.attr("marker-end", `url(#${id})`);
@@ -53327,7 +54378,7 @@ function emitText(g, x, y, text, anchor, color, halo, withHalo, fontSize, italic
53327
54378
  }
53328
54379
  return t;
53329
54380
  }
53330
- var d3Selection23, LABEL_FONT;
54381
+ var d3Selection23, LABEL_FONT, mapInstanceCounter;
53331
54382
  var init_renderer16 = __esm({
53332
54383
  "src/map/renderer.ts"() {
53333
54384
  "use strict";
@@ -53340,6 +54391,7 @@ var init_renderer16 = __esm({
53340
54391
  init_legend_band();
53341
54392
  init_layout15();
53342
54393
  LABEL_FONT = 11;
54394
+ mapInstanceCounter = 0;
53343
54395
  }
53344
54396
  });
53345
54397
 
@@ -54262,7 +55314,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54262
55314
  const tasksAll = [...allTasks(parsed)];
54263
55315
  if (tasksAll.length === 0 && parsed.phases.length === 0) return;
54264
55316
  const solid = parsed.options["solid-fill"] === "on";
54265
- const surfaceBg = isDark ? palette.surface : palette.bg;
55317
+ const surfaceBg = themeBaseBg(palette, isDark);
54266
55318
  const roleCount = Math.max(1, parsed.roles.length);
54267
55319
  const idealWidth = roleCount * ROLE_COL_MAX + TASK_LABEL_MAX + 2 * H_MARGIN;
54268
55320
  const ctx = exportDims ? ScaleContext.identity() : ScaleContext.from(width, idealWidth);
@@ -54271,7 +55323,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54271
55323
  const sHMargin = ctx.aesthetic(H_MARGIN);
54272
55324
  const sVMargin = ctx.aesthetic(V_MARGIN3);
54273
55325
  const sTitleAreaHeight = ctx.structural(TITLE_AREA_HEIGHT4);
54274
- const sHeaderHeight = ctx.structural(HEADER_HEIGHT6);
55326
+ const sHeaderHeight = ctx.structural(HEADER_HEIGHT5);
54275
55327
  const sRowHeight = ctx.structural(ROW_HEIGHT);
54276
55328
  const sPhaseHeight = ctx.structural(PHASE_HEIGHT);
54277
55329
  const sTaskLabelMin = ctx.structural(TASK_LABEL_MIN);
@@ -54296,7 +55348,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54296
55348
  const sLegendLetterFont = ctx.text(LEGEND_LETTER_FONT);
54297
55349
  const sViolationLineHeight = ctx.structural(VIOLATION_LINE_HEIGHT);
54298
55350
  const sStackTopGap = ctx.structural(STACK_TOP_GAP);
54299
- const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH12);
55351
+ const sNodeStrokeWidth = ctx.structural(NODE_STROKE_WIDTH);
54300
55352
  const sNodeRadius = ctx.structural(NODE_RADIUS3);
54301
55353
  const innerWidth = Math.max(0, width - 2 * sHMargin);
54302
55354
  let roleColW = Math.max(
@@ -54440,10 +55492,10 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
54440
55492
  const cx = roleX(i) + sColumnInset;
54441
55493
  const cw = roleColW - 2 * sColumnInset;
54442
55494
  const roleColor = parsed.roleColors[i] ?? autoAccent(i, palette);
54443
- const bodyFill = mix(roleColor, isDark ? palette.surface : palette.bg, 16);
55495
+ const bodyFill = mix(roleColor, themeBaseBg(palette, isDark), 16);
54444
55496
  const headerFill = mix(
54445
55497
  roleColor,
54446
- isDark ? palette.surface : palette.bg,
55498
+ themeBaseBg(palette, isDark),
54447
55499
  30
54448
55500
  );
54449
55501
  const colG = columnsG.append("g").attr("class", "raci-column").attr("data-role-id", roleId);
@@ -54842,7 +55894,7 @@ function parseQuotedSegments(message) {
54842
55894
  out.push({ text: message.slice(last), bold: false });
54843
55895
  return out;
54844
55896
  }
54845
- var d3Selection26, MARKER_LABELS, TITLE_AREA_HEIGHT4, H_MARGIN, V_MARGIN3, LEGEND_HEIGHT2, LEGEND_CHIP_GAP, LEGEND_CHIP_LABEL_MIN, LEGEND_LETTER_CHIP_W, TITLE_LEGEND_GAP, LEGEND_LABEL_FONT, LEGEND_LETTER_FONT, HEADER_HEIGHT6, ROW_HEIGHT, PHASE_HEIGHT, TASK_LABEL_MIN, TASK_LABEL_MAX, ROLE_COL_MIN, ROLE_COL_MAX, CELL_PAD, VIOLATION_LINE_HEIGHT, STACK_TOP_GAP, DESC_FONT, COLUMN_RADIUS3, COLUMN_INSET, COLUMN_BOTTOM_PAD, MARKER_FONT, MARKER_FONT_WEIGHT, LABEL_FONT2, ROLE_HEADER_FONT, PHASE_FONT, TINT_PCT, NODE_STROKE_WIDTH12, NODE_RADIUS3, AUTO_ACCENTS, SLICE_GAP, NAME_LINE_HEIGHT, DESC_LINE_HEIGHT9;
55897
+ var d3Selection26, MARKER_LABELS, TITLE_AREA_HEIGHT4, H_MARGIN, V_MARGIN3, LEGEND_HEIGHT2, LEGEND_CHIP_GAP, LEGEND_CHIP_LABEL_MIN, LEGEND_LETTER_CHIP_W, TITLE_LEGEND_GAP, LEGEND_LABEL_FONT, LEGEND_LETTER_FONT, HEADER_HEIGHT5, ROW_HEIGHT, PHASE_HEIGHT, TASK_LABEL_MIN, TASK_LABEL_MAX, ROLE_COL_MIN, ROLE_COL_MAX, CELL_PAD, VIOLATION_LINE_HEIGHT, STACK_TOP_GAP, DESC_FONT, COLUMN_RADIUS3, COLUMN_INSET, COLUMN_BOTTOM_PAD, MARKER_FONT, MARKER_FONT_WEIGHT, LABEL_FONT2, ROLE_HEADER_FONT, PHASE_FONT, TINT_PCT, NODE_RADIUS3, AUTO_ACCENTS, SLICE_GAP, NAME_LINE_HEIGHT, DESC_LINE_HEIGHT9;
54846
55898
  var init_renderer19 = __esm({
54847
55899
  "src/raci/renderer.ts"() {
54848
55900
  "use strict";
@@ -54854,6 +55906,7 @@ var init_renderer19 = __esm({
54854
55906
  init_variants();
54855
55907
  init_scaling();
54856
55908
  init_text_measure();
55909
+ init_visual_conventions();
54857
55910
  MARKER_LABELS = {
54858
55911
  raci: { R: "Responsible", A: "Accountable", C: "Consulted", I: "Informed" },
54859
55912
  rasci: {
@@ -54875,7 +55928,7 @@ var init_renderer19 = __esm({
54875
55928
  TITLE_LEGEND_GAP = 16;
54876
55929
  LEGEND_LABEL_FONT = 12;
54877
55930
  LEGEND_LETTER_FONT = 14;
54878
- HEADER_HEIGHT6 = 36;
55931
+ HEADER_HEIGHT5 = 36;
54879
55932
  ROW_HEIGHT = 36;
54880
55933
  PHASE_HEIGHT = 40;
54881
55934
  TASK_LABEL_MIN = 200;
@@ -54895,7 +55948,6 @@ var init_renderer19 = __esm({
54895
55948
  ROLE_HEADER_FONT = 12;
54896
55949
  PHASE_FONT = 13;
54897
55950
  TINT_PCT = 25;
54898
- NODE_STROKE_WIDTH12 = 1.5;
54899
55951
  NODE_RADIUS3 = 6;
54900
55952
  AUTO_ACCENTS = [
54901
55953
  "blue",
@@ -55495,11 +56547,11 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55495
56547
  const lines = splitParticipantLabel(
55496
56548
  p.label,
55497
56549
  labelTextWidth(PARTICIPANT_BOX_WIDTH),
55498
- LABEL_FONT_SIZE6
56550
+ LABEL_FONT_SIZE5
55499
56551
  );
55500
56552
  if (lines.length === 0) continue;
55501
56553
  const widest = Math.max(
55502
- ...lines.map((l) => measureText(l, LABEL_FONT_SIZE6))
56554
+ ...lines.map((l) => measureText(l, LABEL_FONT_SIZE5))
55503
56555
  );
55504
56556
  const labelWidth2 = widest + 10;
55505
56557
  uniformBoxWidth = Math.max(uniformBoxWidth, labelWidth2);
@@ -55533,7 +56585,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
55533
56585
  const sSelfCallWidth = ctx.structural(SELF_CALL_WIDTH);
55534
56586
  const sNoteTextWidthMax = sNoteMaxW - sNotePadH * 2 - sNoteFold;
55535
56587
  const sNoteLaneMax = sGap - sActivationWidth - sNoteGap;
55536
- const sLabelFontSize = ctx.text(LABEL_FONT_SIZE6);
56588
+ const sLabelFontSize = ctx.text(LABEL_FONT_SIZE5);
55537
56589
  const sLabelTextWidth = labelTextWidth(sBoxW);
55538
56590
  const participantIndexMap = /* @__PURE__ */ new Map();
55539
56591
  participants.forEach((p, i) => participantIndexMap.set(p.id, i));
@@ -56170,7 +57222,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56170
57222
  const groupTagColor = getTagColor(groupTagValue || void 0);
56171
57223
  const fillColor = groupTagColor ? mix(
56172
57224
  groupTagColor,
56173
- isDark ? palette.surface : palette.bg,
57225
+ themeBaseBg(palette, isDark),
56174
57226
  isDark ? 15 : 20
56175
57227
  ) : isDark ? palette.surface : palette.bg;
56176
57228
  const strokeColor = groupTagColor || palette.textMuted;
@@ -56224,7 +57276,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56224
57276
  participantG.append("title").text("Click to expand");
56225
57277
  const pFill = effectiveTagColor ? mix(
56226
57278
  effectiveTagColor,
56227
- isDark ? palette.surface : palette.bg,
57279
+ themeBaseBg(palette, isDark),
56228
57280
  isDark ? 30 : 40
56229
57281
  ) : isDark ? mix(palette.overlay, palette.surface, 50) : mix(palette.bg, palette.surface, 50);
56230
57282
  const pStroke = effectiveTagColor || palette.border;
@@ -56431,7 +57483,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
56431
57483
  const actTagValue = triggerMsg ? tagMap?.messages.get(triggerMsg.lineNumber) : void 0;
56432
57484
  const actTagColor = getTagColor(actTagValue);
56433
57485
  const actBaseColor = actTagColor || palette.primary;
56434
- svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", isDark ? palette.surface : palette.bg);
57486
+ svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", themeBaseBg(palette, isDark));
56435
57487
  const actFill = shapeFill(palette, actBaseColor, isDark, { solid });
56436
57488
  const actRect = svg.append("rect").attr("x", x).attr("y", y1).attr("width", sActivationWidth).attr("height", y2 - y1).attr("fill", actFill).attr("stroke", actBaseColor).attr("stroke-width", 1).attr("stroke-opacity", 0.5).attr("data-participant-id", act.participantId).attr("data-msg-lines", coveredLines.join(",")).attr("data-line-number", coveredLines[0] ?? "").attr("class", "activation");
56437
57489
  if (tagKey && actTagValue) {
@@ -56670,7 +57722,7 @@ function buildNoteMessageMap(elements) {
56670
57722
  walk(elements);
56671
57723
  return map;
56672
57724
  }
56673
- function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr, solid, boxW = W, boxH = H, labelTextW = labelTextWidth(W), labelFontSize2 = LABEL_FONT_SIZE6) {
57725
+ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tagAttr, solid, boxW = W, boxH = H, labelTextW = labelTextWidth(W), labelFontSize2 = LABEL_FONT_SIZE5) {
56674
57726
  const g = svg.append("g").attr("transform", `translate(${cx}, ${cy})`).attr("class", "participant").attr("data-participant-id", participant.id);
56675
57727
  if (tagAttr) {
56676
57728
  g.attr(`data-tag-${tagAttr.key}`, tagAttr.value);
@@ -56718,7 +57770,7 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
56718
57770
  });
56719
57771
  }
56720
57772
  }
56721
- var d3Selection27, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, LABEL_FONT_SIZE6, TOP_MARGIN, TITLE_HEIGHT8, PARTICIPANT_Y_OFFSET, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W2, NOTE_FOLD2, NOTE_PAD_H2, NOTE_PAD_V2, NOTE_FONT_SIZE2, NOTE_LINE_H2, NOTE_GAP2, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, ACTOR_LABEL_CLEARANCE, labelTextWidth, fill, stroke, SW, W, H;
57773
+ var d3Selection27, PARTICIPANT_GAP, PARTICIPANT_BOX_WIDTH, PARTICIPANT_BOX_HEIGHT, LABEL_FONT_SIZE5, TOP_MARGIN, TITLE_HEIGHT8, PARTICIPANT_Y_OFFSET, MESSAGE_START_OFFSET, LIFELINE_TAIL, ARROWHEAD_SIZE, NOTE_MAX_W2, NOTE_FOLD2, NOTE_PAD_H2, NOTE_PAD_V2, NOTE_FONT_SIZE2, NOTE_LINE_H2, NOTE_GAP2, ACTIVATION_WIDTH, SELF_CALL_HEIGHT, SELF_CALL_WIDTH, ACTOR_LABEL_CLEARANCE, labelTextWidth, fill, stroke, SW, W, H;
56722
57774
  var init_renderer20 = __esm({
56723
57775
  "src/sequence/renderer.ts"() {
56724
57776
  "use strict";
@@ -56739,7 +57791,7 @@ var init_renderer20 = __esm({
56739
57791
  PARTICIPANT_GAP = 160;
56740
57792
  PARTICIPANT_BOX_WIDTH = 120;
56741
57793
  PARTICIPANT_BOX_HEIGHT = 50;
56742
- LABEL_FONT_SIZE6 = 13;
57794
+ LABEL_FONT_SIZE5 = 13;
56743
57795
  TOP_MARGIN = 20;
56744
57796
  TITLE_HEIGHT8 = 30;
56745
57797
  PARTICIPANT_Y_OFFSET = 10;
@@ -58147,7 +59199,7 @@ function setupTimeline(container, parsed, palette, isDark, exportDims, activeTag
58147
59199
  const textColor = palette.text;
58148
59200
  const mutedColor = palette.border;
58149
59201
  const bgColor = palette.bg;
58150
- const bg = isDark ? palette.surface : palette.bg;
59202
+ const bg = themeBaseBg(palette, isDark);
58151
59203
  const colors = getSeriesColors(palette);
58152
59204
  const groupColorMap = /* @__PURE__ */ new Map();
58153
59205
  timelineGroups.forEach((grp, i) => {
@@ -58562,9 +59614,10 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
58562
59614
  drawLegend2();
58563
59615
  }
58564
59616
  }
58565
- function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
59617
+ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
58566
59618
  const {
58567
59619
  width,
59620
+ height,
58568
59621
  tooltip,
58569
59622
  solid,
58570
59623
  textColor,
@@ -58616,9 +59669,11 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
58616
59669
  const rowH = ctx.structural(28);
58617
59670
  const innerHeight = rowH * sorted.length;
58618
59671
  const usedHeight = margin.top + innerHeight + margin.bottom;
59672
+ const fitToContainer = !exportDims && height > 0 && usedHeight > height;
59673
+ const svgHeight = fitToContainer ? height : usedHeight;
58619
59674
  const xScale = d3Scale3.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
58620
- 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);
58621
- if (ctx.isBelowFloor) {
59675
+ 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);
59676
+ if (ctx.isBelowFloor && !fitToContainer) {
58622
59677
  svg.attr("width", "100%");
58623
59678
  }
58624
59679
  const g = svg.append("g").attr("transform", `translate(${margin.left},${margin.top})`);
@@ -60292,7 +61347,7 @@ function renderQuadrant(container, parsed, palette, isDark, onClickItem, exportD
60292
61347
  onClickItem
60293
61348
  );
60294
61349
  const chartG = svg.append("g").attr("transform", `translate(${margin.left}, ${margin.top})`);
60295
- const bg = isDark ? palette.surface : palette.bg;
61350
+ const bg = themeBaseBg(palette, isDark);
60296
61351
  const getQuadrantColor = (label, defaultIdx) => {
60297
61352
  return label?.color ?? defaultColors[defaultIdx % defaultColors.length];
60298
61353
  };
@@ -60629,18 +61684,22 @@ async function renderForExport(content, theme, palette, viewState, options) {
60629
61684
  palette,
60630
61685
  viewState,
60631
61686
  options,
60632
- exportMode
61687
+ exportMode,
61688
+ isDark: theme === "dark"
60633
61689
  };
60634
61690
  const handler = detectedType !== null ? DIAGRAM_EXPORT_HANDLERS[detectedType] : void 0;
60635
61691
  return (handler ?? exportVisualization)(ctx);
60636
61692
  }
61693
+ function ctxTagOverride(ctx) {
61694
+ return ctx.viewState?.tag ?? ctx.options?.tagGroup;
61695
+ }
60637
61696
  async function exportOrg(ctx) {
60638
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61697
+ const { content, theme, palette, viewState, exportMode } = ctx;
60639
61698
  const { parseOrg: parseOrg2 } = await Promise.resolve().then(() => (init_parser10(), parser_exports4));
60640
61699
  const { layoutOrg: layoutOrg2 } = await Promise.resolve().then(() => (init_layout(), layout_exports));
60641
61700
  const { collapseOrgTree: collapseOrgTree2 } = await Promise.resolve().then(() => (init_collapse(), collapse_exports));
60642
61701
  const { renderOrg: renderOrg2 } = await Promise.resolve().then(() => (init_renderer(), renderer_exports));
60643
- const isDark = theme === "dark";
61702
+ const isDark = ctx.isDark;
60644
61703
  const effectivePalette = await resolveExportPalette(theme, palette);
60645
61704
  const orgParsed = parseOrg2(content, effectivePalette);
60646
61705
  if (orgParsed.error) return "";
@@ -60648,7 +61707,7 @@ async function exportOrg(ctx) {
60648
61707
  const activeTagGroup = resolveActiveTagGroup(
60649
61708
  orgParsed.tagGroups,
60650
61709
  orgParsed.options["active-tag"],
60651
- viewState?.tag ?? options?.tagGroup
61710
+ ctxTagOverride(ctx)
60652
61711
  );
60653
61712
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60654
61713
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseOrgTree2(orgParsed, collapsedNodes) : { parsed: orgParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60681,12 +61740,12 @@ async function exportOrg(ctx) {
60681
61740
  return finalizeSvgExport(container, theme, effectivePalette);
60682
61741
  }
60683
61742
  async function exportSitemap(ctx) {
60684
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61743
+ const { content, theme, palette, viewState, exportMode } = ctx;
60685
61744
  const { parseSitemap: parseSitemap2 } = await Promise.resolve().then(() => (init_parser11(), parser_exports5));
60686
61745
  const { layoutSitemap: layoutSitemap2 } = await Promise.resolve().then(() => (init_layout2(), layout_exports2));
60687
61746
  const { collapseSitemapTree: collapseSitemapTree2 } = await Promise.resolve().then(() => (init_collapse2(), collapse_exports2));
60688
61747
  const { renderSitemap: renderSitemap2 } = await Promise.resolve().then(() => (init_renderer2(), renderer_exports2));
60689
- const isDark = theme === "dark";
61748
+ const isDark = ctx.isDark;
60690
61749
  const effectivePalette = await resolveExportPalette(theme, palette);
60691
61750
  const sitemapParsed = parseSitemap2(content, effectivePalette);
60692
61751
  if (sitemapParsed.error || sitemapParsed.roots.length === 0) return "";
@@ -60694,7 +61753,7 @@ async function exportSitemap(ctx) {
60694
61753
  const activeTagGroup = resolveActiveTagGroup(
60695
61754
  sitemapParsed.tagGroups,
60696
61755
  sitemapParsed.options["active-tag"],
60697
- viewState?.tag ?? options?.tagGroup
61756
+ ctxTagOverride(ctx)
60698
61757
  );
60699
61758
  const hiddenAttributes = viewState?.ha ? new Set(viewState.ha) : void 0;
60700
61759
  const { parsed: effectiveParsed, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseSitemapTree2(sitemapParsed, collapsedNodes) : { parsed: sitemapParsed, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60725,7 +61784,7 @@ async function exportSitemap(ctx) {
60725
61784
  return finalizeSvgExport(container, theme, effectivePalette);
60726
61785
  }
60727
61786
  async function exportKanban(ctx) {
60728
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61787
+ const { content, theme, palette, viewState, exportMode } = ctx;
60729
61788
  const { parseKanban: parseKanban2 } = await Promise.resolve().then(() => (init_parser13(), parser_exports7));
60730
61789
  const { renderKanban: renderKanban2 } = await Promise.resolve().then(() => (init_renderer3(), renderer_exports3));
60731
61790
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -60737,11 +61796,11 @@ async function exportKanban(ctx) {
60737
61796
  document.body.appendChild(container);
60738
61797
  const kanbanCollapsedLanes = viewState?.cl ? new Set(viewState.cl) : void 0;
60739
61798
  const kanbanCollapsedColumns = viewState?.cc ? new Set(viewState.cc) : void 0;
60740
- renderKanban2(container, kanbanParsed, effectivePalette, theme === "dark", {
61799
+ renderKanban2(container, kanbanParsed, effectivePalette, ctx.isDark, {
60741
61800
  activeTagGroup: resolveActiveTagGroup(
60742
61801
  kanbanParsed.tagGroups,
60743
61802
  kanbanParsed.options["active-tag"],
60744
- viewState?.tag ?? options?.tagGroup
61803
+ ctxTagOverride(ctx)
60745
61804
  ),
60746
61805
  currentSwimlaneGroup: viewState?.swim ?? null,
60747
61806
  ...kanbanCollapsedLanes !== void 0 && {
@@ -60774,7 +61833,7 @@ async function exportClass(ctx) {
60774
61833
  classParsed,
60775
61834
  classLayout,
60776
61835
  effectivePalette,
60777
- theme === "dark",
61836
+ ctx.isDark,
60778
61837
  void 0,
60779
61838
  { width: exportWidth, height: exportHeight },
60780
61839
  void 0,
@@ -60783,7 +61842,7 @@ async function exportClass(ctx) {
60783
61842
  return finalizeSvgExport(container, theme, effectivePalette);
60784
61843
  }
60785
61844
  async function exportEr(ctx) {
60786
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61845
+ const { content, theme, palette, viewState, exportMode } = ctx;
60787
61846
  const { parseERDiagram: parseERDiagram2 } = await Promise.resolve().then(() => (init_parser9(), parser_exports3));
60788
61847
  const { layoutERDiagram: layoutERDiagram2 } = await Promise.resolve().then(() => (init_layout4(), layout_exports4));
60789
61848
  const { renderERDiagram: renderERDiagram2 } = await Promise.resolve().then(() => (init_renderer5(), renderer_exports5));
@@ -60801,13 +61860,13 @@ async function exportEr(ctx) {
60801
61860
  erParsed,
60802
61861
  erLayout,
60803
61862
  effectivePalette,
60804
- theme === "dark",
61863
+ ctx.isDark,
60805
61864
  void 0,
60806
61865
  { width: exportWidth, height: exportHeight },
60807
61866
  resolveActiveTagGroup(
60808
61867
  erParsed.tagGroups,
60809
61868
  erParsed.options["active-tag"],
60810
- viewState?.tag ?? options?.tagGroup
61869
+ ctxTagOverride(ctx)
60811
61870
  ),
60812
61871
  viewState?.sem,
60813
61872
  exportMode
@@ -60815,7 +61874,7 @@ async function exportEr(ctx) {
60815
61874
  return finalizeSvgExport(container, theme, effectivePalette);
60816
61875
  }
60817
61876
  async function exportBoxesAndLines(ctx) {
60818
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61877
+ const { content, theme, palette, viewState, exportMode } = ctx;
60819
61878
  const { parseBoxesAndLines: parseBoxesAndLines2 } = await Promise.resolve().then(() => (init_parser18(), parser_exports12));
60820
61879
  const effectivePalette = await resolveExportPalette(theme, palette);
60821
61880
  const blParsed = parseBoxesAndLines2(content, effectivePalette);
@@ -60835,13 +61894,13 @@ async function exportBoxesAndLines(ctx) {
60835
61894
  const exportWidth = blLayout.width + PADDING3 * 2;
60836
61895
  const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
60837
61896
  const container = createExportContainer(exportWidth, exportHeight);
60838
- const blActiveTagGroup = viewState?.tag ?? options?.tagGroup;
61897
+ const blActiveTagGroup = ctxTagOverride(ctx);
60839
61898
  renderBoxesAndLinesForExport2(
60840
61899
  container,
60841
61900
  blParsed,
60842
61901
  blLayout,
60843
61902
  effectivePalette,
60844
- theme === "dark",
61903
+ ctx.isDark,
60845
61904
  {
60846
61905
  exportDims: { width: exportWidth, height: exportHeight },
60847
61906
  ...blActiveTagGroup !== void 0 && {
@@ -60856,12 +61915,12 @@ async function exportBoxesAndLines(ctx) {
60856
61915
  return finalizeSvgExport(container, theme, effectivePalette);
60857
61916
  }
60858
61917
  async function exportMindmap(ctx) {
60859
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61918
+ const { content, theme, palette, viewState, exportMode } = ctx;
60860
61919
  const { parseMindmap: parseMindmap2 } = await Promise.resolve().then(() => (init_parser19(), parser_exports13));
60861
61920
  const { layoutMindmap: layoutMindmap2 } = await Promise.resolve().then(() => (init_layout6(), layout_exports6));
60862
61921
  const { collapseMindmapTree: collapseMindmapTree2 } = await Promise.resolve().then(() => (init_collapse3(), collapse_exports3));
60863
61922
  const { renderMindmap: renderMindmap2 } = await Promise.resolve().then(() => (init_renderer7(), renderer_exports7));
60864
- const isDark = theme === "dark";
61923
+ const isDark = ctx.isDark;
60865
61924
  const effectivePalette = await resolveExportPalette(theme, palette);
60866
61925
  const mmParsed = parseMindmap2(content, effectivePalette);
60867
61926
  if (mmParsed.error) return "";
@@ -60869,7 +61928,7 @@ async function exportMindmap(ctx) {
60869
61928
  const activeTagGroup = resolveActiveTagGroup(
60870
61929
  mmParsed.tagGroups,
60871
61930
  mmParsed.options["active-tag"],
60872
- viewState?.tag ?? options?.tagGroup
61931
+ ctxTagOverride(ctx)
60873
61932
  );
60874
61933
  const hideDescriptions = mmParsed.options["no-descriptions"] === "true" || viewState?.hd === true;
60875
61934
  const { roots: effectiveRoots, hiddenCounts } = collapsedNodes && collapsedNodes.size > 0 ? collapseMindmapTree2(mmParsed.roots, collapsedNodes) : { roots: mmParsed.roots, hiddenCounts: /* @__PURE__ */ new Map() };
@@ -60919,7 +61978,7 @@ async function exportWireframe(ctx) {
60919
61978
  wireframeParsed,
60920
61979
  wireframeLayout,
60921
61980
  effectivePalette,
60922
- theme === "dark",
61981
+ ctx.isDark,
60923
61982
  void 0,
60924
61983
  { width: exportWidth, height: exportHeight },
60925
61984
  theme
@@ -60927,7 +61986,7 @@ async function exportWireframe(ctx) {
60927
61986
  return finalizeSvgExport(container, theme, effectivePalette);
60928
61987
  }
60929
61988
  async function exportC4(ctx) {
60930
- const { content, theme, palette, viewState, options, exportMode } = ctx;
61989
+ const { content, theme, palette, viewState, exportMode } = ctx;
60931
61990
  const { parseC4: parseC42 } = await Promise.resolve().then(() => (init_parser14(), parser_exports8));
60932
61991
  const {
60933
61992
  layoutC4Context: layoutC4Context2,
@@ -60939,9 +61998,9 @@ async function exportC4(ctx) {
60939
61998
  const effectivePalette = await resolveExportPalette(theme, palette);
60940
61999
  const c4Parsed = parseC42(content, effectivePalette);
60941
62000
  if (c4Parsed.error || c4Parsed.elements.length === 0) return "";
60942
- const c4Level = options?.c4Level ?? viewState?.c4l ?? "context";
60943
- const c4System = options?.c4System ?? viewState?.c4s;
60944
- const c4Container = options?.c4Container ?? viewState?.c4c;
62001
+ const c4Level = ctx.options?.c4Level ?? viewState?.c4l ?? "context";
62002
+ const c4System = ctx.options?.c4System ?? viewState?.c4s;
62003
+ const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
60945
62004
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
60946
62005
  if (c4Layout.nodes.length === 0) return "";
60947
62006
  const PADDING3 = 20;
@@ -60955,13 +62014,13 @@ async function exportC4(ctx) {
60955
62014
  c4Parsed,
60956
62015
  c4Layout,
60957
62016
  effectivePalette,
60958
- theme === "dark",
62017
+ ctx.isDark,
60959
62018
  void 0,
60960
62019
  { width: exportWidth, height: exportHeight },
60961
62020
  resolveActiveTagGroup(
60962
62021
  c4Parsed.tagGroups,
60963
62022
  c4Parsed.options["active-tag"],
60964
- viewState?.tag ?? options?.tagGroup
62023
+ ctxTagOverride(ctx)
60965
62024
  ),
60966
62025
  exportMode
60967
62026
  );
@@ -60982,14 +62041,14 @@ async function exportFlowchart(ctx) {
60982
62041
  fcParsed,
60983
62042
  layout,
60984
62043
  effectivePalette,
60985
- theme === "dark",
62044
+ ctx.isDark,
60986
62045
  void 0,
60987
62046
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
60988
62047
  );
60989
62048
  return finalizeSvgExport(container, theme, effectivePalette);
60990
62049
  }
60991
62050
  async function exportInfra(ctx) {
60992
- const { content, theme, palette, viewState, options } = ctx;
62051
+ const { content, theme, palette, viewState } = ctx;
60993
62052
  const { parseInfra: parseInfra2 } = await Promise.resolve().then(() => (init_parser15(), parser_exports9));
60994
62053
  const { computeInfra: computeInfra2 } = await Promise.resolve().then(() => (init_compute(), compute_exports));
60995
62054
  const { layoutInfra: layoutInfra2 } = await Promise.resolve().then(() => (init_layout10(), layout_exports10));
@@ -61002,7 +62061,7 @@ async function exportInfra(ctx) {
61002
62061
  const activeTagGroup = resolveActiveTagGroup(
61003
62062
  infraParsed.tagGroups,
61004
62063
  infraParsed.options["active-tag"],
61005
- viewState?.tag ?? options?.tagGroup
62064
+ ctxTagOverride(ctx)
61006
62065
  );
61007
62066
  const showInfraTitle = !!infraParsed.title && infraParsed.options["no-title"] !== "on";
61008
62067
  const titleOffset = showInfraTitle ? 40 : 0;
@@ -61020,7 +62079,7 @@ async function exportInfra(ctx) {
61020
62079
  container,
61021
62080
  infraLayout,
61022
62081
  effectivePalette,
61023
- theme === "dark",
62082
+ ctx.isDark,
61024
62083
  showInfraTitle ? infraParsed.title : null,
61025
62084
  showInfraTitle ? infraParsed.titleLineNumber : null,
61026
62085
  infraTagGroups,
@@ -61067,7 +62126,7 @@ async function exportPert(ctx) {
61067
62126
  pertResolved,
61068
62127
  pertLayout,
61069
62128
  effectivePalette,
61070
- theme === "dark",
62129
+ ctx.isDark,
61071
62130
  {
61072
62131
  title: pertParsed.title,
61073
62132
  exportDims: { width: exportW, height: exportH },
@@ -61080,7 +62139,7 @@ async function exportPert(ctx) {
61080
62139
  return finalizeSvgExport(container, theme, effectivePalette);
61081
62140
  }
61082
62141
  async function exportGantt(ctx) {
61083
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62142
+ const { content, theme, palette, viewState, exportMode } = ctx;
61084
62143
  const { parseGantt: parseGantt2 } = await Promise.resolve().then(() => (init_parser16(), parser_exports10));
61085
62144
  const { calculateSchedule: calculateSchedule2 } = await Promise.resolve().then(() => (init_calculator(), calculator_exports));
61086
62145
  const { renderGantt: renderGantt2 } = await Promise.resolve().then(() => (init_renderer12(), renderer_exports12));
@@ -61098,7 +62157,7 @@ async function exportGantt(ctx) {
61098
62157
  container,
61099
62158
  resolved,
61100
62159
  effectivePalette,
61101
- theme === "dark",
62160
+ ctx.isDark,
61102
62161
  {
61103
62162
  ...ganttCollapsedGroups !== void 0 && {
61104
62163
  collapsedGroups: ganttCollapsedGroups
@@ -61112,7 +62171,7 @@ async function exportGantt(ctx) {
61112
62171
  currentActiveGroup: resolveActiveTagGroup(
61113
62172
  resolved.tagGroups,
61114
62173
  resolved.options.activeTag ?? void 0,
61115
- viewState?.tag ?? options?.tagGroup
62174
+ ctxTagOverride(ctx)
61116
62175
  ),
61117
62176
  exportMode
61118
62177
  },
@@ -61135,7 +62194,7 @@ async function exportState(ctx) {
61135
62194
  stateParsed,
61136
62195
  layout,
61137
62196
  effectivePalette,
61138
- theme === "dark",
62197
+ ctx.isDark,
61139
62198
  void 0,
61140
62199
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61141
62200
  );
@@ -61155,7 +62214,7 @@ async function exportTechRadar(ctx) {
61155
62214
  container,
61156
62215
  radarParsed,
61157
62216
  effectivePalette,
61158
- theme === "dark",
62217
+ ctx.isDark,
61159
62218
  { width: RADAR_EXPORT_W, height: RADAR_EXPORT_H },
61160
62219
  viewState,
61161
62220
  exportMode
@@ -61172,13 +62231,13 @@ async function exportJourneyMap(ctx) {
61172
62231
  if (jmParsed.error || jmParsed.phases.length === 0 && jmParsed.steps.length === 0)
61173
62232
  return "";
61174
62233
  const jmLayout = layoutJourneyMap2(jmParsed, effectivePalette, {
61175
- isDark: theme === "dark"
62234
+ isDark: ctx.isDark
61176
62235
  });
61177
62236
  const container = createExportContainer(
61178
62237
  jmLayout.totalWidth,
61179
62238
  jmLayout.totalHeight
61180
62239
  );
61181
- renderJourneyMap2(container, jmParsed, effectivePalette, theme === "dark", {
62240
+ renderJourneyMap2(container, jmParsed, effectivePalette, ctx.isDark, {
61182
62241
  exportDims: { width: jmLayout.totalWidth, height: jmLayout.totalHeight },
61183
62242
  exportMode
61184
62243
  });
@@ -61196,7 +62255,7 @@ async function exportCycle(ctx) {
61196
62255
  container,
61197
62256
  cycleParsed,
61198
62257
  effectivePalette,
61199
- theme === "dark",
62258
+ ctx.isDark,
61200
62259
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT },
61201
62260
  viewState,
61202
62261
  exportMode
@@ -61233,7 +62292,7 @@ async function exportMap(ctx) {
61233
62292
  mapResolved,
61234
62293
  mapData,
61235
62294
  effectivePalette,
61236
- theme === "dark",
62295
+ ctx.isDark,
61237
62296
  dims
61238
62297
  );
61239
62298
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61250,7 +62309,7 @@ async function exportPyramid(ctx) {
61250
62309
  container,
61251
62310
  pyramidParsed,
61252
62311
  effectivePalette,
61253
- theme === "dark",
62312
+ ctx.isDark,
61254
62313
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61255
62314
  );
61256
62315
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61267,7 +62326,7 @@ async function exportRing(ctx) {
61267
62326
  container,
61268
62327
  ringParsed,
61269
62328
  effectivePalette,
61270
- theme === "dark",
62329
+ ctx.isDark,
61271
62330
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61272
62331
  );
61273
62332
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61284,7 +62343,7 @@ async function exportRaci(ctx) {
61284
62343
  container,
61285
62344
  raciParsed,
61286
62345
  effectivePalette,
61287
- theme === "dark",
62346
+ ctx.isDark,
61288
62347
  { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
61289
62348
  );
61290
62349
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61307,7 +62366,7 @@ async function exportSlope(ctx) {
61307
62366
  container,
61308
62367
  parsed,
61309
62368
  effectivePalette,
61310
- theme === "dark",
62369
+ ctx.isDark,
61311
62370
  void 0,
61312
62371
  dims
61313
62372
  );
@@ -61323,14 +62382,14 @@ async function exportArc(ctx) {
61323
62382
  container,
61324
62383
  parsed,
61325
62384
  effectivePalette,
61326
- theme === "dark",
62385
+ ctx.isDark,
61327
62386
  void 0,
61328
62387
  dims
61329
62388
  );
61330
62389
  return finalizeSvgExport(container, theme, effectivePalette);
61331
62390
  }
61332
62391
  async function exportTimeline(ctx) {
61333
- const { content, theme, palette, viewState, options, exportMode } = ctx;
62392
+ const { content, theme, palette, viewState, exportMode } = ctx;
61334
62393
  const parsed = parseTimeline(content, palette);
61335
62394
  if (parsed.error || parsed.timelineEvents.length === 0) return "";
61336
62395
  const effectivePalette = await resolveExportPalette(theme, palette);
@@ -61339,13 +62398,13 @@ async function exportTimeline(ctx) {
61339
62398
  container,
61340
62399
  parsed,
61341
62400
  effectivePalette,
61342
- theme === "dark",
62401
+ ctx.isDark,
61343
62402
  void 0,
61344
62403
  dims,
61345
62404
  resolveActiveTagGroup(
61346
62405
  parsed.timelineTagGroups,
61347
62406
  parsed.timelineActiveTag,
61348
- viewState?.tag ?? options?.tagGroup
62407
+ ctxTagOverride(ctx)
61349
62408
  ),
61350
62409
  viewState?.swim,
61351
62410
  void 0,
@@ -61364,7 +62423,7 @@ async function exportWordcloud(ctx) {
61364
62423
  container,
61365
62424
  parsed,
61366
62425
  effectivePalette,
61367
- theme === "dark",
62426
+ ctx.isDark,
61368
62427
  dims
61369
62428
  );
61370
62429
  return finalizeSvgExport(container, theme, effectivePalette);
@@ -61379,7 +62438,7 @@ async function exportVenn(ctx) {
61379
62438
  container,
61380
62439
  parsed,
61381
62440
  effectivePalette,
61382
- theme === "dark",
62441
+ ctx.isDark,
61383
62442
  void 0,
61384
62443
  dims
61385
62444
  );
@@ -61395,14 +62454,14 @@ async function exportQuadrant(ctx) {
61395
62454
  container,
61396
62455
  parsed,
61397
62456
  effectivePalette,
61398
- theme === "dark",
62457
+ ctx.isDark,
61399
62458
  void 0,
61400
62459
  dims
61401
62460
  );
61402
62461
  return finalizeSvgExport(container, theme, effectivePalette);
61403
62462
  }
61404
62463
  async function exportVisualization(ctx) {
61405
- const { content, theme, palette, viewState, options } = ctx;
62464
+ const { content, theme, palette, viewState } = ctx;
61406
62465
  const parsed = parseVisualization(content, palette);
61407
62466
  if (parsed.type !== "sequence") {
61408
62467
  if (parsed.error) {
@@ -61413,7 +62472,7 @@ async function exportVisualization(ctx) {
61413
62472
  }
61414
62473
  }
61415
62474
  const effectivePalette = await resolveExportPalette(theme, palette);
61416
- const isDark = theme === "dark";
62475
+ const isDark = ctx.isDark;
61417
62476
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
61418
62477
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
61419
62478
  const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
@@ -61421,7 +62480,7 @@ async function exportVisualization(ctx) {
61421
62480
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
61422
62481
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
61423
62482
  const collapsedGroups = viewState?.cg ? new Set(viewState.cg.map(Number).filter((n) => Number.isFinite(n))) : void 0;
61424
- const seqActiveTagGroup = viewState?.tag ?? options?.tagGroup;
62483
+ const seqActiveTagGroup = ctxTagOverride(ctx);
61425
62484
  renderSequenceDiagram2(
61426
62485
  container,
61427
62486
  seqParsed,
@@ -62195,7 +63254,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
62195
63254
 
62196
63255
  // src/auto/index.ts
62197
63256
  init_safe_href();
62198
- var VERSION = "0.30.0";
63257
+ var VERSION = "0.32.0";
62199
63258
  var DEFAULTS = {
62200
63259
  theme: "auto",
62201
63260
  palette: "slate",