@diagrammo/dgmo 0.37.0 → 0.38.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.
package/dist/auto.cjs CHANGED
@@ -602,7 +602,7 @@ function withTagAliases(base, aliases) {
602
602
  function isReservedKey(registry, key) {
603
603
  return registry.keys.has(key) || registry.tagAliases.has(key);
604
604
  }
605
- var SEQUENCE_REGISTRY, INFRA_REGISTRY, MAP_REGISTRY, ORG_REGISTRY, C4_REGISTRY, ER_REGISTRY, KANBAN_REGISTRY, SITEMAP_REGISTRY, GANTT_REGISTRY, PERT_REGISTRY, BOXES_AND_LINES_REGISTRY, TIMELINE_REGISTRY, MINDMAP_REGISTRY, TECH_RADAR_REGISTRY, CYCLE_REGISTRY, JOURNEY_MAP_REGISTRY, PYRAMID_REGISTRY, RING_REGISTRY, RACI_REGISTRY;
605
+ var SEQUENCE_REGISTRY, INFRA_REGISTRY, MAP_REGISTRY, ORG_REGISTRY, C4_REGISTRY, ER_REGISTRY, KANBAN_REGISTRY, SITEMAP_REGISTRY, GANTT_REGISTRY, PERT_REGISTRY, BOXES_AND_LINES_REGISTRY, TIMELINE_REGISTRY, MINDMAP_REGISTRY, TECH_RADAR_REGISTRY, CYCLE_REGISTRY, JOURNEY_MAP_REGISTRY, PYRAMID_REGISTRY, RING_REGISTRY, TREEMAP_REGISTRY, RACI_REGISTRY;
606
606
  var init_reserved_key_registry = __esm({
607
607
  "src/utils/reserved-key-registry.ts"() {
608
608
  "use strict";
@@ -721,6 +721,7 @@ var init_reserved_key_registry = __esm({
721
721
  "color",
722
722
  "description"
723
723
  ]);
724
+ TREEMAP_REGISTRY = staticRegistry(["heat"]);
724
725
  RACI_REGISTRY = staticRegistry([
725
726
  "color",
726
727
  "description"
@@ -1150,6 +1151,7 @@ var init_parsing = __esm({
1150
1151
  "journey-map",
1151
1152
  "pyramid",
1152
1153
  "ring",
1154
+ "treemap",
1153
1155
  "raci",
1154
1156
  "rasci",
1155
1157
  "daci",
@@ -11809,6 +11811,10 @@ var init_chart_types = __esm({
11809
11811
  id: "ring",
11810
11812
  description: "Concentric rings showing nested or hierarchical categories (read core-out)"
11811
11813
  },
11814
+ {
11815
+ id: "treemap",
11816
+ description: "Nested rectangles sized by value \u2014 show a hierarchy\u2019s proportions (budgets, disk usage, portfolios) with color by category, value heatmap, or branch"
11817
+ },
11812
11818
  {
11813
11819
  id: "map",
11814
11820
  description: "Geographic concept map: highlight/score regions, drop points of interest, connect with routes or edges"
@@ -18970,7 +18976,7 @@ function parseMap(content, palette) {
18970
18976
  continue;
18971
18977
  }
18972
18978
  if (DIRECTIVE_SET.has(firstWord) && !trimmed.slice(firstWord.length).trimStart().startsWith(":")) {
18973
- handleDirective(
18979
+ handleDirective2(
18974
18980
  firstWord,
18975
18981
  trimmed.slice(firstWord.length).trim(),
18976
18982
  lineNumber
@@ -19009,7 +19015,7 @@ function parseMap(content, palette) {
19009
19015
  }
19010
19016
  }
19011
19017
  return result;
19012
- function handleDirective(key, value, line11) {
19018
+ function handleDirective2(key, value, line11) {
19013
19019
  const d = result.directives;
19014
19020
  const dup = (have) => {
19015
19021
  if (have !== void 0)
@@ -22879,6 +22885,344 @@ var init_parser25 = __esm({
22879
22885
  }
22880
22886
  });
22881
22887
 
22888
+ // src/treemap/parser.ts
22889
+ var parser_exports20 = {};
22890
+ __export(parser_exports20, {
22891
+ TREEMAP_DIAGNOSTIC_CODES: () => TREEMAP_DIAGNOSTIC_CODES,
22892
+ parseTreemap: () => parseTreemap
22893
+ });
22894
+ function isFullyQuoted(s) {
22895
+ return s.length >= 2 && (s.startsWith('"') && s.endsWith('"') || s.startsWith("'") && s.endsWith("'"));
22896
+ }
22897
+ function parseTreemap(content, palette) {
22898
+ const options = {
22899
+ heatColors: [],
22900
+ noValues: false,
22901
+ noPercent: false,
22902
+ noHeaders: false,
22903
+ noLegend: false
22904
+ };
22905
+ const result = {
22906
+ type: "treemap",
22907
+ title: null,
22908
+ titleLineNumber: null,
22909
+ roots: [],
22910
+ tagGroups: [],
22911
+ hasHeat: false,
22912
+ defaultColorMode: "branch",
22913
+ options,
22914
+ diagnostics: [],
22915
+ error: null
22916
+ };
22917
+ const fail = makeFail(result);
22918
+ const pushError = (line11, message, code) => {
22919
+ const diag = makeDgmoError(line11, message, "error", code);
22920
+ result.diagnostics.push(diag);
22921
+ if (!result.error) result.error = formatDgmoError(diag);
22922
+ };
22923
+ const pushWarning = (line11, message, code) => {
22924
+ result.diagnostics.push(makeDgmoError(line11, message, "warning", code));
22925
+ };
22926
+ if (!content?.trim()) {
22927
+ return fail(0, "No content provided");
22928
+ }
22929
+ const lines = content.split("\n");
22930
+ let contentStarted = false;
22931
+ let nodeCounter = 0;
22932
+ let currentTagGroup = null;
22933
+ const aliasMap = /* @__PURE__ */ new Map();
22934
+ const indentStack = [];
22935
+ for (let i = 0; i < lines.length; i++) {
22936
+ const line11 = lines[i];
22937
+ const lineNumber = i + 1;
22938
+ const trimmed = line11.trim();
22939
+ if (!trimmed) {
22940
+ currentTagGroup = null;
22941
+ continue;
22942
+ }
22943
+ if (trimmed.startsWith("//")) continue;
22944
+ if (!contentStarted) {
22945
+ const firstLine = parseFirstLine(trimmed);
22946
+ if (firstLine && result.titleLineNumber === null && i === 0) {
22947
+ if (firstLine.chartType !== "treemap") {
22948
+ let msg = `Expected chart type "treemap", got "${firstLine.chartType}"`;
22949
+ const hint = suggest(firstLine.chartType, ["treemap"]);
22950
+ if (hint) msg += `. ${hint}`;
22951
+ return fail(lineNumber, msg);
22952
+ }
22953
+ result.title = firstLine.title ?? null;
22954
+ result.titleLineNumber = lineNumber;
22955
+ continue;
22956
+ }
22957
+ }
22958
+ const tagBlockMatch = matchTagBlockHeading(trimmed);
22959
+ if (tagBlockMatch) {
22960
+ emitTagLegacyDiagnostic(tagBlockMatch, lineNumber, result.diagnostics);
22961
+ if (contentStarted) {
22962
+ pushError(
22963
+ lineNumber,
22964
+ "Tag groups must appear before treemap content",
22965
+ "E_TAG_DECLARED_AFTER_CONTENT"
22966
+ );
22967
+ continue;
22968
+ }
22969
+ currentTagGroup = {
22970
+ name: tagBlockMatch.name,
22971
+ ...tagBlockMatch.alias !== void 0 && {
22972
+ alias: tagBlockMatch.alias
22973
+ },
22974
+ entries: [],
22975
+ lineNumber
22976
+ };
22977
+ if (tagBlockMatch.alias) {
22978
+ aliasMap.set(
22979
+ tagBlockMatch.alias.toLowerCase(),
22980
+ tagAttrKey(tagBlockMatch.name)
22981
+ );
22982
+ }
22983
+ aliasMap.set(
22984
+ tagAttrKey(tagBlockMatch.name),
22985
+ tagAttrKey(tagBlockMatch.name)
22986
+ );
22987
+ result.tagGroups.push(currentTagGroup);
22988
+ continue;
22989
+ }
22990
+ if (currentTagGroup && !contentStarted) {
22991
+ const indent2 = measureIndent(line11);
22992
+ if (indent2 > 0) {
22993
+ const { text: cleanEntry, isDefault } = stripDefaultModifier(trimmed);
22994
+ const { label, color } = extractColor(
22995
+ cleanEntry,
22996
+ palette,
22997
+ result.diagnostics,
22998
+ lineNumber
22999
+ );
23000
+ if (isDefault) {
23001
+ currentTagGroup.defaultValue = label;
23002
+ } else if (currentTagGroup.entries.length === 0) {
23003
+ currentTagGroup.defaultValue = label;
23004
+ }
23005
+ currentTagGroup.entries.push({
23006
+ value: label,
23007
+ color: color ?? AUTO_TAG_COLOR_SENTINEL,
23008
+ lineNumber
23009
+ });
23010
+ continue;
23011
+ }
23012
+ currentTagGroup = null;
23013
+ }
23014
+ if (!contentStarted && measureIndent(line11) === 0) {
23015
+ if (handleDirective(trimmed, options, result, pushWarning, lineNumber)) {
23016
+ continue;
23017
+ }
23018
+ }
23019
+ contentStarted = true;
23020
+ currentTagGroup = null;
23021
+ const indent = measureIndent(line11);
23022
+ const node = parseNodeLine3(
23023
+ trimmed,
23024
+ lineNumber,
23025
+ ++nodeCounter,
23026
+ aliasMap,
23027
+ result.diagnostics,
23028
+ pushWarning
23029
+ );
23030
+ if (node.heat !== void 0) result.hasHeat = true;
23031
+ attachNode4(node, indent, indentStack, result);
23032
+ }
23033
+ finalizeAutoTagColors(result.tagGroups, palette);
23034
+ const allNodes = [];
23035
+ const collectAll = (nodes) => {
23036
+ for (const n of nodes) {
23037
+ allNodes.push(n);
23038
+ collectAll(n.children);
23039
+ }
23040
+ };
23041
+ collectAll(result.roots);
23042
+ for (const n of allNodes) {
23043
+ const node = n;
23044
+ const isBranch = node.children.length > 0;
23045
+ if (isBranch) {
23046
+ if (node.value !== void 0) {
23047
+ pushWarning(
23048
+ node.lineNumber,
23049
+ `"${node.label}" is a branch \u2014 its trailing number is ignored; the area is the auto-sum of its children`,
23050
+ TREEMAP_DIAGNOSTIC_CODES.BRANCH_VALUE_IGNORED
23051
+ );
23052
+ delete node.value;
23053
+ }
23054
+ } else if (node.value === void 0) {
23055
+ pushWarning(
23056
+ node.lineNumber,
23057
+ `Leaf "${node.label}" has no value \u2014 it renders with zero area`,
23058
+ TREEMAP_DIAGNOSTIC_CODES.LEAF_NO_VALUE
23059
+ );
23060
+ node.value = 0;
23061
+ } else if (node.value < 0) {
23062
+ pushError(
23063
+ node.lineNumber,
23064
+ `Leaf "${node.label}" has a negative value (${node.value}) \u2014 treemap sizes must be \u2265 0`,
23065
+ TREEMAP_DIAGNOSTIC_CODES.NEGATIVE_VALUE
23066
+ );
23067
+ }
23068
+ }
23069
+ if (result.hasHeat || options.heatLabel !== void 0) {
23070
+ result.hasHeat = true;
23071
+ }
23072
+ if (result.tagGroups.length > 0) {
23073
+ validateTagValues(allNodes, result.tagGroups, pushWarning, suggest);
23074
+ validateTagGroupNames(result.tagGroups, pushWarning);
23075
+ cascadeTagMetadata(result.roots, result.tagGroups);
23076
+ }
23077
+ result.defaultColorMode = resolveDefaultColorMode(result);
23078
+ if (result.roots.length === 0 && !result.error) {
23079
+ const diag = makeDgmoError(1, "No nodes found in treemap");
23080
+ result.diagnostics.push(diag);
23081
+ result.error = formatDgmoError(diag);
23082
+ }
23083
+ return result;
23084
+ }
23085
+ function handleDirective(trimmed, options, result, pushWarning, lineNumber) {
23086
+ const depthMatch = trimmed.match(/^depth\s+(-?\d+)\s*$/i);
23087
+ if (depthMatch) {
23088
+ const n = parseInt(depthMatch[1], 10);
23089
+ if (n >= 1) options.maxDepth = n;
23090
+ else pushWarning(lineNumber, '"depth" must be \u2265 1 \u2014 ignored');
23091
+ return true;
23092
+ }
23093
+ if (/^other-below\b/i.test(trimmed)) {
23094
+ pushWarning(
23095
+ lineNumber,
23096
+ '"other-below" is no longer supported \u2014 remove this line'
23097
+ );
23098
+ return true;
23099
+ }
23100
+ const heatMatch = trimmed.match(/^heat\s+(.+)$/i);
23101
+ if (heatMatch) {
23102
+ const { label, low, high } = peelRampColors(heatMatch[1].trim());
23103
+ options.heatLabel = label || "Value";
23104
+ const colors = [];
23105
+ if (low !== void 0) colors.push(low);
23106
+ if (high !== void 0) colors.push(high);
23107
+ options.heatColors = colors;
23108
+ result.hasHeat = true;
23109
+ return true;
23110
+ }
23111
+ const noMatch = trimmed.match(/^no-(values|percent|headers|legend)\s*$/i);
23112
+ if (noMatch) {
23113
+ const key = noMatch[1].toLowerCase();
23114
+ if (key === "values") options.noValues = true;
23115
+ else if (key === "percent") options.noPercent = true;
23116
+ else if (key === "headers") options.noHeaders = true;
23117
+ else if (key === "legend") options.noLegend = true;
23118
+ return true;
23119
+ }
23120
+ return false;
23121
+ }
23122
+ function resolveDefaultColorMode(result) {
23123
+ if (result.tagGroups.length > 0) return "tag";
23124
+ if (result.hasHeat) return "heat";
23125
+ return "branch";
23126
+ }
23127
+ function parseNodeLine3(trimmed, lineNumber, counter, aliasMap, diagnostics, pushWarning) {
23128
+ if (trimmed.includes("|")) {
23129
+ diagnostics.push(
23130
+ makeDgmoError(
23131
+ lineNumber,
23132
+ pipeOperatorRemovedMessage(),
23133
+ "error",
23134
+ METADATA_DIAGNOSTIC_CODES.PIPE_OPERATOR_REMOVED
23135
+ )
23136
+ );
23137
+ }
23138
+ const registry = withTagAliases(TREEMAP_REGISTRY, new Set(aliasMap.keys()));
23139
+ const split = splitNameAndMeta(
23140
+ trimmed,
23141
+ registry,
23142
+ aliasMap,
23143
+ void 0,
23144
+ diagnostics,
23145
+ lineNumber
23146
+ );
23147
+ warnUnknownMetaKeys(
23148
+ split.meta,
23149
+ registry,
23150
+ (msg) => diagnostics.push(makeDgmoError(lineNumber, msg, "warning")),
23151
+ split.name
23152
+ );
23153
+ const metadata = { ...split.meta };
23154
+ let heat;
23155
+ if ("heat" in metadata) {
23156
+ const h = parseFloat(metadata["heat"].replace(/_/g, ""));
23157
+ if (Number.isFinite(h)) heat = h;
23158
+ delete metadata["heat"];
23159
+ }
23160
+ const rawName = split.name;
23161
+ let label;
23162
+ let value;
23163
+ if (isFullyQuoted(rawName)) {
23164
+ label = stripQuotes(rawName);
23165
+ } else {
23166
+ const vm = rawName.match(VALUE_TOKEN_RE);
23167
+ if (vm) {
23168
+ const labelPart = vm[1].trim();
23169
+ const token = vm[2];
23170
+ if (token.includes(",")) {
23171
+ pushWarning(lineNumber, dataCommaRemovedMessage(labelPart));
23172
+ }
23173
+ const cleaned = token.replace(/[_,]/g, "");
23174
+ const parsed = parseFloat(cleaned);
23175
+ if (Number.isFinite(parsed)) {
23176
+ value = parsed;
23177
+ label = isFullyQuoted(labelPart) ? stripQuotes(labelPart) : labelPart;
23178
+ } else {
23179
+ label = rawName;
23180
+ }
23181
+ } else {
23182
+ label = rawName;
23183
+ }
23184
+ }
23185
+ return {
23186
+ id: `treemap-node-${counter}`,
23187
+ label,
23188
+ ...value !== void 0 && { value },
23189
+ ...heat !== void 0 && { heat },
23190
+ metadata,
23191
+ children: [],
23192
+ lineNumber
23193
+ };
23194
+ }
23195
+ function attachNode4(node, indent, indentStack, result) {
23196
+ while (indentStack.length > 0) {
23197
+ const top = indentStack[indentStack.length - 1];
23198
+ if (top.indent < indent) break;
23199
+ indentStack.pop();
23200
+ }
23201
+ if (indentStack.length > 0) {
23202
+ const parent = indentStack[indentStack.length - 1].node;
23203
+ parent.children.push(node);
23204
+ } else {
23205
+ result.roots.push(node);
23206
+ }
23207
+ indentStack.push({ node, indent });
23208
+ }
23209
+ var TREEMAP_DIAGNOSTIC_CODES, VALUE_TOKEN_RE;
23210
+ var init_parser26 = __esm({
23211
+ "src/treemap/parser.ts"() {
23212
+ "use strict";
23213
+ init_diagnostics();
23214
+ init_tag_groups();
23215
+ init_parsing();
23216
+ init_reserved_key_registry();
23217
+ TREEMAP_DIAGNOSTIC_CODES = {
23218
+ NEGATIVE_VALUE: "E_TREEMAP_NEGATIVE_VALUE",
23219
+ LEAF_NO_VALUE: "W_TREEMAP_LEAF_NO_VALUE",
23220
+ BRANCH_VALUE_IGNORED: "W_TREEMAP_BRANCH_VALUE_IGNORED"
23221
+ };
23222
+ VALUE_TOKEN_RE = /^(.+?)\s+(-?\d[\d_,.]*)$/;
23223
+ }
23224
+ });
23225
+
22882
23226
  // src/raci/variants.ts
22883
23227
  function inferVariant(markers) {
22884
23228
  let hasD = false;
@@ -23133,8 +23477,8 @@ var init_variants = __esm({
23133
23477
  });
23134
23478
 
23135
23479
  // src/raci/parser.ts
23136
- var parser_exports20 = {};
23137
- __export(parser_exports20, {
23480
+ var parser_exports21 = {};
23481
+ __export(parser_exports21, {
23138
23482
  allTasks: () => allTasks,
23139
23483
  parseRaci: () => parseRaci
23140
23484
  });
@@ -23655,7 +23999,7 @@ function* allTasks(parsed) {
23655
23999
  }
23656
24000
  }
23657
24001
  var RACI_CHART_TYPE_IDS, CHART_TYPE_VARIANT_LOCK, VARIANT_LOCK_DIRECTIVES, KNOWN_OPTIONS10, KNOWN_BOOLEANS7, REMOVED_BOOLEANS3, PHASE_RE2, PHASE_PALETTE_COLOR_RE, ROLE_ASSIGNMENT_RE;
23658
- var init_parser26 = __esm({
24002
+ var init_parser27 = __esm({
23659
24003
  "src/raci/parser.ts"() {
23660
24004
  "use strict";
23661
24005
  init_diagnostics();
@@ -23901,6 +24245,7 @@ var init_chart_type_registry = __esm({
23901
24245
  init_parser24();
23902
24246
  init_parser25();
23903
24247
  init_parser26();
24248
+ init_parser27();
23904
24249
  CHART_TYPE_REGISTRY = [
23905
24250
  // ── Structured diagrams ───────────────────────────────────
23906
24251
  {
@@ -24041,6 +24386,9 @@ var init_chart_type_registry = __esm({
24041
24386
  { id: "cycle", category: "visualization", parse: parseCycle },
24042
24387
  { id: "pyramid", category: "visualization", parse: parsePyramid },
24043
24388
  { id: "ring", category: "visualization", parse: parseRing },
24389
+ // Treemap: squarified hierarchy. No measure/minDims — a treemap fills whatever
24390
+ // rectangle it's given and has no intrinsic aspect/size (F11).
24391
+ { id: "treemap", category: "visualization", parse: parseTreemap },
24044
24392
  // ── Geographic map (own parser → resolver → layout → renderer) ──
24045
24393
  { id: "map", category: "visualization", parse: parseMap }
24046
24394
  ];
@@ -26773,10 +27121,10 @@ async function renderSitemapForExport(content, theme, palette) {
26773
27121
  const parsed = parseSitemap2(content, effectivePalette);
26774
27122
  if (parsed.error || parsed.roots.length === 0) return "";
26775
27123
  const sitemapLayout = layoutSitemap2(parsed, void 0, null, void 0, true);
26776
- const PADDING3 = 20;
27124
+ const PADDING4 = 20;
26777
27125
  const titleOffset = parsed.title ? 30 : 0;
26778
- const exportWidth = sitemapLayout.width + PADDING3 * 2;
26779
- const exportHeight = sitemapLayout.height + PADDING3 * 2 + titleOffset;
27126
+ const exportWidth = sitemapLayout.width + PADDING4 * 2;
27127
+ const exportHeight = sitemapLayout.height + PADDING4 * 2 + titleOffset;
26780
27128
  const container = document.createElement("div");
26781
27129
  container.style.width = `${exportWidth}px`;
26782
27130
  container.style.height = `${exportHeight}px`;
@@ -31804,6 +32152,10 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
31804
32152
  const reserveEdgeLabels = opts?.reserveEdgeLabels ?? false;
31805
32153
  const onProgress = opts?.onProgress;
31806
32154
  const tick = onProgress ? () => new Promise((r) => setTimeout(r)) : () => void 0;
32155
+ const YIELD_AFTER_MS = 150;
32156
+ const YIELD_EVERY_MS = 30;
32157
+ const searchStart = performance.now();
32158
+ let lastYield = searchStart;
31807
32159
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
31808
32160
  if (collapseInfo) {
31809
32161
  const missing = /* @__PURE__ */ new Set();
@@ -32233,7 +32585,11 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
32233
32585
  const step = async (phase) => {
32234
32586
  if (!onProgress) return;
32235
32587
  onProgress(++progressDone, progressTotal, phase);
32236
- await tick();
32588
+ const now = performance.now();
32589
+ if (now - searchStart > YIELD_AFTER_MS && now - lastYield > YIELD_EVERY_MS) {
32590
+ lastYield = now;
32591
+ await tick();
32592
+ }
32237
32593
  };
32238
32594
  const pool = [];
32239
32595
  for (const cfg of configs) {
@@ -50623,7 +50979,7 @@ function computeEdgeLabelPosition(midAngle, radius, cx, cy, lineCount, labelPxW,
50623
50979
  };
50624
50980
  }
50625
50981
  function fitToCanvas(nodes, edges, parsed, _cx, _cy, radius, width, height, _isClockwise) {
50626
- const PADDING3 = 30;
50982
+ const PADDING4 = 30;
50627
50983
  let contentMinX = Infinity, contentMaxX = -Infinity;
50628
50984
  let contentMinY = Infinity, contentMaxY = -Infinity;
50629
50985
  for (const n of nodes) {
@@ -50665,13 +51021,13 @@ function fitToCanvas(nodes, edges, parsed, _cx, _cy, radius, width, height, _isC
50665
51021
  contentMinY = Math.min(contentMinY, le.labelY - 12);
50666
51022
  contentMaxY = Math.max(contentMaxY, le.labelY + (lineCount - 1) * 15);
50667
51023
  }
50668
- const overflowX = Math.max(0, PADDING3 - contentMinX) + Math.max(0, contentMaxX - (width - PADDING3));
50669
- const overflowY = Math.max(0, PADDING3 - contentMinY) + Math.max(0, contentMaxY - (height - PADDING3));
51024
+ const overflowX = Math.max(0, PADDING4 - contentMinX) + Math.max(0, contentMaxX - (width - PADDING4));
51025
+ const overflowY = Math.max(0, PADDING4 - contentMinY) + Math.max(0, contentMaxY - (height - PADDING4));
50670
51026
  if (overflowX <= 0 && overflowY <= 0) return null;
50671
51027
  const contentW = contentMaxX - contentMinX;
50672
51028
  const contentH = contentMaxY - contentMinY;
50673
- const availW = width - 2 * PADDING3;
50674
- const availH = height - 2 * PADDING3;
51029
+ const availW = width - 2 * PADDING4;
51030
+ const availH = height - 2 * PADDING4;
50675
51031
  const shrink = Math.min(availW / contentW, availH / contentH);
50676
51032
  const newRadius = Math.max(80, radius * shrink);
50677
51033
  return { radius: newRadius };
@@ -50869,7 +51225,7 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
50869
51225
  }
50870
51226
  edgeLabelInfo.push({ le, edge });
50871
51227
  }
50872
- const HEADER_H = 36 * layout.scale;
51228
+ const HEADER_H2 = 36 * layout.scale;
50873
51229
  const scaledNodeFont = Math.max(6, Math.round(NODE_FONT_SIZE7 * layout.scale));
50874
51230
  const scaledEdgeLabelFont = Math.max(
50875
51231
  6,
@@ -50933,10 +51289,10 @@ function renderCycle(container, parsed, palette, isDark, onClickItem, exportDims
50933
51289
  const rx = 6;
50934
51290
  nodeG.append("rect").attr("x", ln.x - nodeW / 2).attr("y", ln.y - nodeH / 2).attr("width", nodeW).attr("height", nodeH).attr("rx", rx).attr("ry", rx).attr("fill", fillColor).attr("stroke", solidColor).attr("stroke-width", 2);
50935
51291
  if (hasDesc) {
50936
- const headerCenterY = ln.y - nodeH / 2 + HEADER_H / 2;
51292
+ const headerCenterY = ln.y - nodeH / 2 + HEADER_H2 / 2;
50937
51293
  const labelText = nodeG.append("text").attr("x", ln.x).attr("y", headerCenterY + scaledNodeFont / 3).attr("text-anchor", "middle").attr("fill", textColor).attr("font-family", FONT_FAMILY).attr("font-size", scaledNodeFont).attr("font-weight", "600");
50938
51294
  renderInlineText(labelText, node.label, palette, scaledNodeFont);
50939
- const sepY = ln.y - nodeH / 2 + HEADER_H;
51295
+ const sepY = ln.y - nodeH / 2 + HEADER_H2;
50940
51296
  nodeG.append("line").attr("x1", ln.x - nodeW / 2).attr("y1", sepY).attr("x2", ln.x + nodeW / 2).attr("y2", sepY).attr("stroke", solid ? descColor : solidColor).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
50941
51297
  const descStartY = sepY + 4 + scaledDescFont;
50942
51298
  const descPadX = Math.max(8, 12 * layout.scale);
@@ -55748,7 +56104,9 @@ function mapContentAspect(resolved, data, ref = REF) {
55748
56104
  function mapExportDimensions(resolved, data, baseWidth = 1200, aspectOverride) {
55749
56105
  const useOverride = aspectOverride !== void 0 && Number.isFinite(aspectOverride) && aspectOverride > 0;
55750
56106
  const raw = useOverride ? aspectOverride : mapContentAspect(resolved, data);
55751
- const clamped = useOverride ? raw : Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
56107
+ const isGlobal = !useOverride && buildMapProjection(resolved, data).fitIsGlobal;
56108
+ const target = isGlobal ? raw / GLOBAL_HEIGHT_BOOST : raw;
56109
+ const clamped = useOverride ? raw : Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, target));
55752
56110
  const width = baseWidth;
55753
56111
  let height = Math.round(width / clamped);
55754
56112
  let chromeReserve = 0;
@@ -55761,10 +56119,10 @@ function mapExportDimensions(resolved, data, baseWidth = 1200, aspectOverride) {
55761
56119
  height = Math.round(chromeReserve + MIN_MAP_BAND);
55762
56120
  floored = true;
55763
56121
  }
55764
- const preferContain = useOverride ? floored : clamped !== raw || floored;
56122
+ const preferContain = useOverride ? floored : clamped !== target || floored;
55765
56123
  return { width, height, preferContain };
55766
56124
  }
55767
- var import_d3_geo3, FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
56125
+ var import_d3_geo3, FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, GLOBAL_HEIGHT_BOOST, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
55768
56126
  var init_dimensions = __esm({
55769
56127
  "src/map/dimensions.ts"() {
55770
56128
  "use strict";
@@ -55775,6 +56133,7 @@ var init_dimensions = __esm({
55775
56133
  TITLE_GAP = 16;
55776
56134
  ASPECT_MAX = 3;
55777
56135
  ASPECT_MIN = 0.9;
56136
+ GLOBAL_HEIGHT_BOOST = 1.12;
55778
56137
  MIN_MAP_BAND = 200;
55779
56138
  FALLBACK_ASPECT = 1.5;
55780
56139
  REF = 1e3;
@@ -56564,9 +56923,469 @@ var init_renderer18 = __esm({
56564
56923
  }
56565
56924
  });
56566
56925
 
56567
- // src/raci/renderer.ts
56926
+ // src/treemap/layout.ts
56927
+ function toDatum(node) {
56928
+ if (node.children.length === 0) {
56929
+ return {
56930
+ ref: node,
56931
+ label: node.label,
56932
+ value: node.value ?? 0,
56933
+ ...node.heat !== void 0 && { heat: node.heat },
56934
+ lineNumber: node.lineNumber
56935
+ };
56936
+ }
56937
+ return {
56938
+ ref: node,
56939
+ label: node.label,
56940
+ ...node.heat !== void 0 && { heat: node.heat },
56941
+ lineNumber: node.lineNumber,
56942
+ children: node.children.map((c) => toDatum(c))
56943
+ };
56944
+ }
56945
+ function datumHeat(node) {
56946
+ if (node.data.heat !== void 0) return node.data.heat;
56947
+ const xs = [];
56948
+ for (const l of node.leaves()) {
56949
+ if (l.data.heat !== void 0) xs.push(l.data.heat);
56950
+ }
56951
+ if (xs.length === 0) return void 0;
56952
+ return xs.reduce((a, b) => a + b, 0) / xs.length;
56953
+ }
56954
+ function layoutTreemap(roots, opts) {
56955
+ const maxDepth = opts.maxDepth ?? Infinity;
56956
+ const rootDatum = {
56957
+ label: "__root",
56958
+ children: roots.map((r) => toDatum(r))
56959
+ };
56960
+ const h = (0, import_d3_hierarchy2.hierarchy)(rootDatum).sum((d) => d.children?.length ? 0 : d.value ?? 0).sort((a, b) => (b.value ?? 0) - (a.value ?? 0));
56961
+ const hroot = (0, import_d3_hierarchy2.treemap)().tile(import_d3_hierarchy2.treemapSquarify).size([opts.width, opts.height]).paddingOuter(opts.paddingOuter ?? 3).paddingTop(opts.headerH > 0 ? opts.headerH : opts.paddingInner ?? 2).paddingInner(opts.paddingInner ?? 2).round(true)(h);
56962
+ const total = hroot.value ?? 0;
56963
+ (hroot.children ?? []).forEach((top, i) => {
56964
+ top.each((d) => {
56965
+ d._topIndex = i;
56966
+ });
56967
+ });
56968
+ const cells = [];
56969
+ for (const d of hroot.descendants()) {
56970
+ if (d.depth === 0) continue;
56971
+ if (d.depth > maxDepth) continue;
56972
+ const hasChildren = !!d.children && d.children.length > 0;
56973
+ const isCollapsed = hasChildren && d.depth >= maxDepth;
56974
+ const isContainer = hasChildren && !isCollapsed;
56975
+ const path = [];
56976
+ let p = d;
56977
+ while (p && p.depth > 0) {
56978
+ path.unshift(p.data.label);
56979
+ p = p.parent;
56980
+ }
56981
+ const heatVal = datumHeat(d);
56982
+ cells.push({
56983
+ node: d.data.ref ?? null,
56984
+ label: d.data.label,
56985
+ x0: d.x0,
56986
+ y0: d.y0,
56987
+ x1: d.x1,
56988
+ y1: d.y1,
56989
+ depth: d.depth,
56990
+ value: d.value ?? 0,
56991
+ isContainer,
56992
+ isCollapsed,
56993
+ topIndex: d._topIndex ?? 0,
56994
+ pctOfRoot: total > 0 ? (d.value ?? 0) / total : 0,
56995
+ pctOfParent: d.parent && (d.parent.value ?? 0) > 0 ? (d.value ?? 0) / (d.parent.value ?? 1) : 1,
56996
+ ...heatVal !== void 0 && { heat: heatVal },
56997
+ path,
56998
+ ...d.data.lineNumber !== void 0 && { lineNumber: d.data.lineNumber }
56999
+ });
57000
+ }
57001
+ return { cells, total };
57002
+ }
57003
+ var import_d3_hierarchy2;
57004
+ var init_layout16 = __esm({
57005
+ "src/treemap/layout.ts"() {
57006
+ "use strict";
57007
+ import_d3_hierarchy2 = require("d3-hierarchy");
57008
+ }
57009
+ });
57010
+
57011
+ // src/treemap/renderer.ts
56568
57012
  var renderer_exports19 = {};
56569
57013
  __export(renderer_exports19, {
57014
+ compactNumber: () => compactNumber2,
57015
+ renderTreemap: () => renderTreemap,
57016
+ renderTreemapForExport: () => renderTreemapForExport
57017
+ });
57018
+ function renderTreemapForExport(container, parsed, palette, isDark, exportDims, options) {
57019
+ renderTreemap(container, parsed, palette, isDark, exportDims, {
57020
+ exportMode: true,
57021
+ ...options
57022
+ });
57023
+ }
57024
+ function renderTreemap(container, parsed, palette, isDark, exportDims, options = {}) {
57025
+ if (parsed.error || parsed.roots.length === 0) return;
57026
+ void isDark;
57027
+ d3Selection26.select(container).selectAll(":not([data-d3-tooltip])").remove();
57028
+ const width = exportDims?.width ?? container.clientWidth;
57029
+ const height = exportDims?.height ?? container.clientHeight;
57030
+ if (width <= 0 || height <= 0) return;
57031
+ const mode = resolveColorMode(parsed, options.colorMode);
57032
+ const opts = parsed.options;
57033
+ const exportMode = options.exportMode ?? false;
57034
+ const seriesColorsTop = getSeriesColors(palette);
57035
+ const showTitle = !!parsed.title;
57036
+ const titleH = showTitle ? TITLE_BAND : 0;
57037
+ const heat = buildHeatScale(parsed, palette);
57038
+ const legend = !opts.noLegend ? buildLegend(mode, parsed, heat, seriesColorsTop, options.colorOffset ?? 0) : null;
57039
+ const legendReserve = legend ? getMaxLegendReservedHeight(
57040
+ { groups: legend.groups, position: LEGEND_POSITION, mode: "preview" },
57041
+ width
57042
+ ) + LEGEND_GROUP_GAP : 0;
57043
+ const headerH = opts.noHeaders ? 0 : HEADER_H;
57044
+ const areaX = PADDING3;
57045
+ const areaY = titleH + legendReserve + PADDING3;
57046
+ const areaW = Math.max(1, width - PADDING3 * 2);
57047
+ const areaH = Math.max(1, height - areaY - PADDING3);
57048
+ const layout = layoutTreemap(parsed.roots, {
57049
+ width: areaW,
57050
+ height: areaH,
57051
+ headerH,
57052
+ ...options.maxDepth !== void 0 && { maxDepth: options.maxDepth }
57053
+ });
57054
+ const svg = d3Selection26.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").attr("class", "dgmo-treemap").style("font-family", FONT_FAMILY);
57055
+ svg.append("style").text(
57056
+ ".dgmo-treemap-focus{opacity:.3;transition:opacity .12s}.dgmo-treemap-cell:hover .dgmo-treemap-focus{opacity:.7}"
57057
+ );
57058
+ svg.append("rect").attr("width", width).attr("height", height).attr("fill", palette.bg);
57059
+ if (showTitle) {
57060
+ const title = svg.append("text").attr("class", "chart-title").attr("x", width / 2).attr("y", TITLE_Y).attr("text-anchor", "middle").attr("fill", palette.text).attr("font-family", FONT_FAMILY).attr("font-size", TITLE_FONT_SIZE).attr("font-weight", TITLE_FONT_WEIGHT).text(parsed.title);
57061
+ if (parsed.titleLineNumber !== null) {
57062
+ title.attr("data-line-number", parsed.titleLineNumber);
57063
+ }
57064
+ }
57065
+ const root = svg.append("g").attr("class", "dgmo-treemap-cells").attr("transform", `translate(${areaX},${areaY})`);
57066
+ const seriesColors2 = seriesColorsTop;
57067
+ const colorOffset = options.colorOffset ?? 0;
57068
+ const activeGroup = parsed.tagGroups.length > 0 ? parsed.tagGroups[0].name : null;
57069
+ const rootIndexByLabel = new Map(parsed.roots.map((r, i) => [r.label, i]));
57070
+ const colorOf = (cell) => {
57071
+ if (mode === "heat") {
57072
+ return cell.heat !== void 0 && heat ? heat.scale(cell.heat) : MUTED_FILL;
57073
+ }
57074
+ if (mode === "tag") {
57075
+ if (!cell.node) return MUTED_FILL;
57076
+ return resolveTagColor(cell.node.metadata, parsed.tagGroups, activeGroup) ?? MUTED_FILL;
57077
+ }
57078
+ const topLabel = cell.path[0] ?? cell.label;
57079
+ const idx = (rootIndexByLabel.get(topLabel) ?? cell.topIndex) + colorOffset;
57080
+ const hue = seriesColors2[idx % seriesColors2.length];
57081
+ if (cell.depth <= 1) return hue;
57082
+ const keepPct = Math.max(55, 100 - (cell.depth - 1) * 18);
57083
+ return mix(hue, palette.bg, keepPct);
57084
+ };
57085
+ for (const cell of layout.cells) {
57086
+ const w = Math.max(0, cell.x1 - cell.x0);
57087
+ const h = Math.max(0, cell.y1 - cell.y0);
57088
+ if (w <= 0 || h <= 0) continue;
57089
+ const baseColor = colorOf(cell);
57090
+ const fill2 = cell.isContainer ? baseColor : mix(baseColor, palette.bg, LEAF_MUTE_PCT);
57091
+ const drillable = cell.isContainer || cell.isCollapsed;
57092
+ const g = root.append("g").attr("class", "dgmo-treemap-cell").attr("transform", `translate(${cell.x0},${cell.y0})`).attr("data-node-path", cell.path.join(" / "));
57093
+ if (cell.lineNumber !== void 0) {
57094
+ g.attr("data-line-number", cell.lineNumber);
57095
+ }
57096
+ if (cell.heat !== void 0) g.attr("data-heat", cell.heat);
57097
+ if (cell.node && activeGroup) {
57098
+ const key = tagAttrKey(activeGroup);
57099
+ const tagVal = cell.node.metadata[key];
57100
+ if (tagVal) g.attr(`data-tag-${key}`, tagVal);
57101
+ }
57102
+ const rect = g.append("rect").attr(
57103
+ "class",
57104
+ cell.isContainer ? "dgmo-treemap-rect branch" : "dgmo-treemap-rect"
57105
+ ).attr("width", w).attr("height", h).attr("rx", 2).attr("fill", fill2).attr("fill-opacity", 1).attr("stroke", palette.bg).attr("stroke-width", cell.isContainer ? 1 : 0);
57106
+ if (drillable && options.onClickItem && cell.lineNumber !== void 0) {
57107
+ const ln = cell.lineNumber;
57108
+ rect.style("cursor", "pointer").on("click", () => options.onClickItem(ln));
57109
+ }
57110
+ const ink = contrastText(
57111
+ fill2,
57112
+ palette.textOnFillLight,
57113
+ palette.textOnFillDark
57114
+ );
57115
+ if (cell.isContainer && headerH > 0 && w > 34 && h > headerH) {
57116
+ const valParts = [];
57117
+ if (!opts.noValues) valParts.push(compactNumber2(cell.value));
57118
+ if (!opts.noPercent) valParts.push(formatPct(cell.pctOfRoot));
57119
+ const valStr = valParts.join(" \xB7 ");
57120
+ const ICON_RESERVE = drillable ? 16 : 4;
57121
+ const showVal = valStr.length > 0 && w > 110;
57122
+ const valW = showVal ? measureText(valStr, 10.5) : 0;
57123
+ const labelMax = w - 8 - ICON_RESERVE - (showVal ? valW + 10 : 0);
57124
+ g.append("text").attr("class", "dgmo-treemap-header").attr("x", 6).attr("y", 13).attr("font-size", 11).attr("font-weight", 700).attr("fill", ink).text(clipLabel(cell.label, Math.max(0, labelMax), 11));
57125
+ if (showVal) {
57126
+ g.append("text").attr("class", "dgmo-treemap-header-value").attr("x", w - ICON_RESERVE - 4).attr("y", 13).attr("text-anchor", "end").attr("font-size", 10.5).attr("fill", ink).attr("opacity", 0.75).text(valStr);
57127
+ }
57128
+ }
57129
+ const PAD = Math.max(4, Math.min(14, Math.floor(Math.min(w, h) / 5)));
57130
+ const MIN_FS = 7;
57131
+ if (!cell.isContainer && w >= 2 * PAD + 8 && h >= 2 * PAD + 8) {
57132
+ const maxW = w - 2 * PAD;
57133
+ let fs = clamp3(Math.round(Math.min(w / 3.6, h / 3.4)), MIN_FS, 72);
57134
+ const nameW = measureText(cell.label, fs) * 1.06;
57135
+ if (nameW > maxW) fs = Math.max(MIN_FS, Math.floor(fs * maxW / nameW));
57136
+ const tg = clamp3((fs - MIN_FS) / (72 - MIN_FS), 0, 1);
57137
+ const nameOpacity = 0.95 - tg * 0.5;
57138
+ const vfs = clamp3(Math.round(fs * 0.42), 11, 28);
57139
+ const valOpacity = Math.max(0.45, nameOpacity - 0.1);
57140
+ let y = PAD + Math.round(fs * 0.78);
57141
+ g.append("text").attr("class", "dgmo-treemap-label").attr("x", PAD).attr("y", y).attr("font-size", fs).attr("font-weight", 600).attr("fill", ink).attr("opacity", nameOpacity).text(clipLabel(cell.label, maxW, fs));
57142
+ const valParts = [
57143
+ opts.noValues ? "" : compactNumber2(cell.value),
57144
+ opts.noPercent ? "" : formatPct(cell.pctOfRoot)
57145
+ ].filter(Boolean);
57146
+ const combined = valParts.join(" \xB7 ");
57147
+ const nameBaseY = y;
57148
+ const addLineAt = (text, yy, size, opacity) => {
57149
+ g.append("text").attr("class", "dgmo-treemap-value").attr("x", PAD).attr("y", yy).attr("font-size", size).attr("fill", ink).attr("opacity", opacity).text(text);
57150
+ };
57151
+ if (combined) {
57152
+ const fitsW = measureText(combined, vfs) <= maxW;
57153
+ const roomBelow = h - PAD > y + vfs + 6;
57154
+ if (fitsW && roomBelow) {
57155
+ addLineAt(combined, y + vfs + 6, vfs, valOpacity);
57156
+ } else if (fitsW) {
57157
+ const ivfs = clamp3(Math.round(fs * 0.5), 11, Math.max(11, vfs));
57158
+ const rendered = clipLabel(cell.label, maxW, fs);
57159
+ const nW = measureText(rendered, fs) * 1.12;
57160
+ const gap = Math.max(12, Math.round(fs * 0.25));
57161
+ if (PAD + nW + gap + measureText(combined, ivfs) <= w - PAD) {
57162
+ g.append("text").attr("class", "dgmo-treemap-value").attr("x", PAD + nW + gap).attr("y", nameBaseY).attr("font-size", ivfs).attr("fill", ink).attr("opacity", valOpacity).text(combined);
57163
+ }
57164
+ } else if (valParts.length === 2) {
57165
+ if (h - PAD > y + vfs + 6) {
57166
+ y += vfs + 6;
57167
+ addLineAt(valParts[0], y, vfs, valOpacity);
57168
+ }
57169
+ const pfs = Math.max(10, Math.round(vfs * 0.9));
57170
+ if (h - PAD > y + pfs + 4) {
57171
+ y += pfs + 4;
57172
+ addLineAt(valParts[1], y, pfs, valOpacity);
57173
+ }
57174
+ } else if (h - PAD > y + vfs + 6) {
57175
+ addLineAt(combined, y + vfs + 6, vfs, valOpacity);
57176
+ }
57177
+ }
57178
+ }
57179
+ if (drillable && w > 30 && h > 22) {
57180
+ drawFocusIcon(g, w, ink, 9);
57181
+ }
57182
+ }
57183
+ if (legend) {
57184
+ const legendG = svg.append("g").attr("class", "dgmo-treemap-legend").attr("transform", `translate(0, ${titleH})`);
57185
+ renderIntegratedLegend(legendG, {
57186
+ groups: legend.groups,
57187
+ palette: {
57188
+ bg: palette.bg,
57189
+ surface: palette.surface,
57190
+ text: palette.text,
57191
+ textMuted: palette.textMuted,
57192
+ primary: palette.primary
57193
+ },
57194
+ isDark,
57195
+ width,
57196
+ mode: exportMode ? "export" : "preview",
57197
+ position: LEGEND_POSITION,
57198
+ activeGroup: legend.activeGroup,
57199
+ // The color-mode switcher is the legend itself: each applicable mode is a
57200
+ // group pill; the active one is the open capsule, the rest are clickable
57201
+ // pills that switch mode. Export shows only the active group.
57202
+ showInactivePills: !exportMode,
57203
+ showEmptyGroups: !exportMode,
57204
+ ...options.onSelectMode !== void 0 && {
57205
+ callbacks: {
57206
+ onGroupToggle: (name) => {
57207
+ const m = legend.modeByName.get(name);
57208
+ if (m) options.onSelectMode(m);
57209
+ }
57210
+ }
57211
+ }
57212
+ });
57213
+ }
57214
+ }
57215
+ function buildLegend(activeMode, parsed, heat, seriesColors2, colorOffset) {
57216
+ const groups = [];
57217
+ const modeByName = /* @__PURE__ */ new Map();
57218
+ let activeGroup = null;
57219
+ if (parsed.tagGroups.length > 0) {
57220
+ const tg = parsed.tagGroups[0];
57221
+ const used = /* @__PURE__ */ new Set();
57222
+ const collect = (nodes) => {
57223
+ for (const n of nodes) {
57224
+ const v = n.metadata[tagAttrKey(tg.name)];
57225
+ if (v) used.add(v.toLowerCase());
57226
+ collect(n.children);
57227
+ }
57228
+ };
57229
+ collect(parsed.roots);
57230
+ groups.push({
57231
+ name: tg.name,
57232
+ entries: tg.entries.filter((e) => used.has(e.value.toLowerCase())).map((e) => ({ value: e.value, color: e.color }))
57233
+ });
57234
+ modeByName.set(tg.name, "tag");
57235
+ if (activeMode === "tag") activeGroup = tg.name;
57236
+ }
57237
+ if (heat) {
57238
+ const name = parsed.options.heatLabel ?? "Value";
57239
+ groups.push({
57240
+ name,
57241
+ entries: [],
57242
+ gradient: {
57243
+ min: heat.min,
57244
+ max: heat.max,
57245
+ low: heat.stops[0],
57246
+ high: heat.stops[heat.stops.length - 1]
57247
+ }
57248
+ });
57249
+ modeByName.set(name, "heat");
57250
+ if (activeMode === "heat") activeGroup = name;
57251
+ }
57252
+ groups.push({
57253
+ name: "Branch",
57254
+ entries: parsed.roots.map((r, i) => ({
57255
+ value: r.label,
57256
+ color: seriesColors2[(i + colorOffset) % seriesColors2.length]
57257
+ }))
57258
+ });
57259
+ modeByName.set("Branch", "branch");
57260
+ if (activeMode === "branch" || activeGroup === null) activeGroup = "Branch";
57261
+ return { groups, activeGroup, modeByName };
57262
+ }
57263
+ function buildHeatScale(parsed, palette) {
57264
+ if (!parsed.hasHeat) return null;
57265
+ const values = [];
57266
+ const collect = (nodes) => {
57267
+ for (const n of nodes) {
57268
+ if (typeof n.heat === "number") values.push(n.heat);
57269
+ collect(n.children);
57270
+ }
57271
+ };
57272
+ collect(parsed.roots);
57273
+ if (values.length === 0) return null;
57274
+ const min = Math.min(...values);
57275
+ const max = Math.max(...values);
57276
+ const signed = min < 0 && max > 0;
57277
+ const neutral = palette.surface;
57278
+ const explicit = parsed.options.heatColors.map((c) => resolveColor(c, palette) ?? c).filter((c) => !!c);
57279
+ let stops;
57280
+ let domain;
57281
+ if (explicit.length >= 2) {
57282
+ stops = [explicit[0], neutral, explicit[1]];
57283
+ const mid = signed ? 0 : (min + max) / 2;
57284
+ domain = [min, mid, max];
57285
+ } else if (explicit.length === 1) {
57286
+ stops = [neutral, explicit[0]];
57287
+ domain = [min, max];
57288
+ } else if (signed) {
57289
+ stops = [palette.colors.red, neutral, palette.colors.green];
57290
+ domain = [min, 0, max];
57291
+ } else {
57292
+ stops = [neutral, palette.primary];
57293
+ domain = [min, max];
57294
+ }
57295
+ if (domain[0] === domain[domain.length - 1]) {
57296
+ const last = stops[stops.length - 1];
57297
+ return { scale: () => last, min, max, stops, signed };
57298
+ }
57299
+ const linear = (0, import_d3_scale.scaleLinear)().domain(domain).range(stops).clamp(true);
57300
+ return { scale: (v) => toHex(linear(v)), min, max, stops, signed };
57301
+ }
57302
+ function toHex(c) {
57303
+ if (c.startsWith("#")) return c;
57304
+ const m = c.match(/rgba?\(([^)]+)\)/);
57305
+ if (!m) return c;
57306
+ const parts = m[1].split(",").map((s) => Math.round(parseFloat(s)));
57307
+ const h = (n) => Math.max(0, Math.min(255, n || 0)).toString(16).padStart(2, "0");
57308
+ return `#${h(parts[0] ?? 0)}${h(parts[1] ?? 0)}${h(parts[2] ?? 0)}`;
57309
+ }
57310
+ function resolveColorMode(parsed, override) {
57311
+ let mode = override ?? parsed.defaultColorMode;
57312
+ if (mode === "tag" && parsed.tagGroups.length === 0) {
57313
+ mode = parsed.hasHeat ? "heat" : "branch";
57314
+ }
57315
+ if (mode === "heat" && !parsed.hasHeat) mode = "branch";
57316
+ return mode;
57317
+ }
57318
+ function drawFocusIcon(g, w, ink, cy) {
57319
+ const r = 4.5;
57320
+ const cx = w - r - 5;
57321
+ const fi = g.append("g").attr("class", "dgmo-treemap-focus").attr("data-export-ignore", "true");
57322
+ fi.append("rect").attr("x", cx - 9).attr("y", cy - 9).attr("width", 18).attr("height", 18).attr("fill", "transparent");
57323
+ fi.append("circle").attr("cx", cx).attr("cy", cy).attr("r", r).attr("fill", "none").attr("stroke", ink).attr("stroke-width", 1.25);
57324
+ fi.append("circle").attr("cx", cx).attr("cy", cy).attr("r", 1.4).attr("fill", ink);
57325
+ }
57326
+ function clamp3(x, lo, hi) {
57327
+ return Math.max(lo, Math.min(hi, x));
57328
+ }
57329
+ function clipLabel(s, maxWidth, fs) {
57330
+ if (measureText(s, fs) <= maxWidth) return s;
57331
+ let lo = 0;
57332
+ let hi = s.length;
57333
+ while (lo < hi) {
57334
+ const mid = Math.ceil((lo + hi) / 2);
57335
+ if (measureText(s.slice(0, mid) + "\u2026", fs) <= maxWidth) lo = mid;
57336
+ else hi = mid - 1;
57337
+ }
57338
+ return lo <= 0 ? "" : s.slice(0, lo) + "\u2026";
57339
+ }
57340
+ function compactNumber2(v) {
57341
+ const abs = Math.abs(v);
57342
+ if (abs >= 1e9) return strip(v / 1e9) + "B";
57343
+ if (abs >= 1e6) return strip(v / 1e6) + "M";
57344
+ if (abs >= 1e3) return strip(v / 1e3) + "k";
57345
+ return strip(Math.round(v * 100) / 100);
57346
+ }
57347
+ function strip(n) {
57348
+ return parseFloat(
57349
+ n.toFixed(n < 10 && !Number.isInteger(n) ? 1 : 0)
57350
+ ).toString();
57351
+ }
57352
+ function formatPct(frac) {
57353
+ const pct = frac * 100;
57354
+ if (pct >= 10) return `${Math.round(pct)}%`;
57355
+ if (pct >= 1) return `${pct.toFixed(0)}%`;
57356
+ return `${pct.toFixed(1)}%`;
57357
+ }
57358
+ var d3Selection26, import_d3_scale, PADDING3, HEADER_H, TITLE_BAND, MUTED_FILL, LEAF_MUTE_PCT, LEGEND_POSITION;
57359
+ var init_renderer19 = __esm({
57360
+ "src/treemap/renderer.ts"() {
57361
+ "use strict";
57362
+ d3Selection26 = __toESM(require("d3-selection"), 1);
57363
+ import_d3_scale = require("d3-scale");
57364
+ init_fonts();
57365
+ init_colors();
57366
+ init_color_utils();
57367
+ init_tag_groups();
57368
+ init_text_measure();
57369
+ init_legend_integration();
57370
+ init_legend_layout();
57371
+ init_legend_constants();
57372
+ init_title_constants();
57373
+ init_layout16();
57374
+ PADDING3 = 12;
57375
+ HEADER_H = 18;
57376
+ TITLE_BAND = 36;
57377
+ MUTED_FILL = "#cbd5e1";
57378
+ LEAF_MUTE_PCT = 50;
57379
+ LEGEND_POSITION = {
57380
+ placement: "top-center",
57381
+ titleRelation: "below-title"
57382
+ };
57383
+ }
57384
+ });
57385
+
57386
+ // src/raci/renderer.ts
57387
+ var renderer_exports20 = {};
57388
+ __export(renderer_exports20, {
56570
57389
  MARKER_LABELS: () => MARKER_LABELS,
56571
57390
  renderRaci: () => renderRaci,
56572
57391
  renderRaciForExport: () => renderRaciForExport
@@ -56642,7 +57461,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
56642
57461
  const hideLegend = opts.hideLegend ?? false;
56643
57462
  const collapsedPhases = opts.collapsedPhases ?? /* @__PURE__ */ new Set();
56644
57463
  const hideTitle = (opts.hideTitle ?? false) || parsed.options["no-title"] === "on";
56645
- d3Selection26.select(container).selectAll(":not([data-d3-tooltip])").remove();
57464
+ d3Selection27.select(container).selectAll(":not([data-d3-tooltip])").remove();
56646
57465
  const width = exportDims?.width ?? container.clientWidth;
56647
57466
  const height = exportDims?.height ?? container.clientHeight;
56648
57467
  if (width <= 0 || height <= 0) return;
@@ -56749,7 +57568,7 @@ function renderRaci(container, parsed, palette, isDark, handlers, exportDims) {
56749
57568
  const colBottomY = cursorY + sColumnBottomPad;
56750
57569
  const totalHeight = colBottomY + sVMargin;
56751
57570
  const svgHeight = exportDims ? totalHeight : Math.max(height, totalHeight);
56752
- const svg = d3Selection26.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", width).attr("height", svgHeight).attr("viewBox", `0 0 ${width} ${svgHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY).style("background", "transparent");
57571
+ const svg = d3Selection27.select(container).append("svg").attr("xmlns", "http://www.w3.org/2000/svg").attr("width", width).attr("height", svgHeight).attr("viewBox", `0 0 ${width} ${svgHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("font-family", FONT_FAMILY).style("background", "transparent");
56753
57572
  if (ctx.isBelowFloor) {
56754
57573
  svg.attr("width", "100%");
56755
57574
  }
@@ -57229,15 +58048,15 @@ function parseQuotedSegments(message) {
57229
58048
  out.push({ text: message.slice(last), bold: false });
57230
58049
  return out;
57231
58050
  }
57232
- 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;
57233
- var init_renderer19 = __esm({
58051
+ var d3Selection27, 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;
58052
+ var init_renderer20 = __esm({
57234
58053
  "src/raci/renderer.ts"() {
57235
58054
  "use strict";
57236
- d3Selection26 = __toESM(require("d3-selection"), 1);
58055
+ d3Selection27 = __toESM(require("d3-selection"), 1);
57237
58056
  init_fonts();
57238
58057
  init_title_constants();
57239
58058
  init_color_utils();
57240
- init_parser26();
58059
+ init_parser27();
57241
58060
  init_variants();
57242
58061
  init_scaling();
57243
58062
  init_text_measure();
@@ -57494,8 +58313,8 @@ var init_tag_resolution = __esm({
57494
58313
  });
57495
58314
 
57496
58315
  // src/sequence/renderer.ts
57497
- var renderer_exports20 = {};
57498
- __export(renderer_exports20, {
58316
+ var renderer_exports21 = {};
58317
+ __export(renderer_exports21, {
57499
58318
  applyGroupOrdering: () => applyGroupOrdering,
57500
58319
  applyPositionOverrides: () => applyPositionOverrides,
57501
58320
  buildNoteMessageMap: () => buildNoteMessageMap,
@@ -57835,7 +58654,7 @@ function applyGroupOrdering(participants, groups, messages = []) {
57835
58654
  return result;
57836
58655
  }
57837
58656
  function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateToLine, options) {
57838
- d3Selection27.select(container).selectAll("*").remove();
58657
+ d3Selection28.select(container).selectAll("*").remove();
57839
58658
  const { title, options: parsedOptions } = parsed;
57840
58659
  const solid = parsedOptions["solid-fill"] === "on";
57841
58660
  const effectiveCollapsedGroups = /* @__PURE__ */ new Set();
@@ -58486,7 +59305,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
58486
59305
  offsetX = leftMargin + Math.max(0, (svgWidth - totalWidth) / 2);
58487
59306
  buildParticipantX();
58488
59307
  }
58489
- const svg = d3Selection27.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
59308
+ const svg = d3Selection28.select(container).append("svg").attr("width", "100%").attr("height", totalHeight).attr("viewBox", `0 0 ${svgWidth} ${totalHeight}`).attr("preserveAspectRatio", "xMidYMin meet").attr("class", "sequence-diagram").style("font-family", FONT_FAMILY);
58490
59309
  const defs = svg.append("defs");
58491
59310
  defs.append("marker").attr("id", "seq-arrowhead").attr("viewBox", `0 0 ${sArrowheadSize} ${sArrowheadSize}`).attr("refX", sArrowheadSize).attr("refY", sArrowheadSize / 2).attr("markerWidth", sArrowheadSize).attr("markerHeight", sArrowheadSize).attr("orient", "auto").append("polygon").attr(
58492
59311
  "points",
@@ -59098,12 +59917,12 @@ function renderParticipant(svg, participant, cx, cy, palette, isDark, color, tag
59098
59917
  });
59099
59918
  }
59100
59919
  }
59101
- 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;
59102
- var init_renderer20 = __esm({
59920
+ var d3Selection28, 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;
59921
+ var init_renderer21 = __esm({
59103
59922
  "src/sequence/renderer.ts"() {
59104
59923
  "use strict";
59105
59924
  init_tag_groups();
59106
- d3Selection27 = __toESM(require("d3-selection"), 1);
59925
+ d3Selection28 = __toESM(require("d3-selection"), 1);
59107
59926
  init_color_utils();
59108
59927
  init_inline_markdown();
59109
59928
  init_fonts();
@@ -62992,6 +63811,7 @@ var DIAGRAM_EXPORT_HANDLERS = {
62992
63811
  map: exportMap,
62993
63812
  pyramid: exportPyramid,
62994
63813
  ring: exportRing,
63814
+ treemap: exportTreemap,
62995
63815
  raci: exportRaci,
62996
63816
  rasci: exportRaci,
62997
63817
  daci: exportRaci,
@@ -63049,10 +63869,10 @@ async function exportOrg(ctx) {
63049
63869
  false
63050
63870
  // expandAllLegend off — collapsed-by-default per §1.3
63051
63871
  );
63052
- const PADDING3 = 20;
63872
+ const PADDING4 = 20;
63053
63873
  const titleOffset = effectiveParsed.title ? 30 : 0;
63054
- const exportWidth = orgLayout.width + PADDING3 * 2;
63055
- const exportHeight = orgLayout.height + PADDING3 * 2 + titleOffset;
63874
+ const exportWidth = orgLayout.width + PADDING4 * 2;
63875
+ const exportHeight = orgLayout.height + PADDING4 * 2 + titleOffset;
63056
63876
  const container = createExportContainer(exportWidth, exportHeight);
63057
63877
  renderOrg2(
63058
63878
  container,
@@ -63094,10 +63914,10 @@ async function exportSitemap(ctx) {
63094
63914
  hiddenAttributes,
63095
63915
  true
63096
63916
  );
63097
- const PADDING3 = 20;
63917
+ const PADDING4 = 20;
63098
63918
  const titleOffset = effectiveParsed.title ? 30 : 0;
63099
- const exportWidth = sitemapLayout.width + PADDING3 * 2;
63100
- const exportHeight = sitemapLayout.height + PADDING3 * 2 + titleOffset;
63919
+ const exportWidth = sitemapLayout.width + PADDING4 * 2;
63920
+ const exportHeight = sitemapLayout.height + PADDING4 * 2 + titleOffset;
63101
63921
  const container = createExportContainer(exportWidth, exportHeight);
63102
63922
  renderSitemap2(
63103
63923
  container,
@@ -63153,10 +63973,10 @@ async function exportClass(ctx) {
63153
63973
  const classParsed = parseClassDiagram2(content, effectivePalette);
63154
63974
  if (classParsed.error || classParsed.classes.length === 0) return "";
63155
63975
  const classLayout = layoutClassDiagram2(classParsed);
63156
- const PADDING3 = 20;
63976
+ const PADDING4 = 20;
63157
63977
  const titleOffset = classParsed.title ? 40 : 0;
63158
- const exportWidth = classLayout.width + PADDING3 * 2;
63159
- const exportHeight = classLayout.height + PADDING3 * 2 + titleOffset;
63978
+ const exportWidth = classLayout.width + PADDING4 * 2;
63979
+ const exportHeight = classLayout.height + PADDING4 * 2 + titleOffset;
63160
63980
  const container = createExportContainer(exportWidth, exportHeight);
63161
63981
  renderClassDiagram2(
63162
63982
  container,
@@ -63180,10 +64000,10 @@ async function exportEr(ctx) {
63180
64000
  const erParsed = parseERDiagram2(content, effectivePalette);
63181
64001
  if (erParsed.error || erParsed.tables.length === 0) return "";
63182
64002
  const erLayout = layoutERDiagram2(erParsed);
63183
- const PADDING3 = 20;
64003
+ const PADDING4 = 20;
63184
64004
  const titleOffset = erParsed.title ? 40 : 0;
63185
- const exportWidth = erLayout.width + PADDING3 * 2;
63186
- const exportHeight = erLayout.height + PADDING3 * 2 + titleOffset;
64005
+ const exportWidth = erLayout.width + PADDING4 * 2;
64006
+ const exportHeight = erLayout.height + PADDING4 * 2 + titleOffset;
63187
64007
  const container = createExportContainer(exportWidth, exportHeight);
63188
64008
  renderERDiagram2(
63189
64009
  container,
@@ -63219,10 +64039,10 @@ async function exportBoxesAndLines(ctx) {
63219
64039
  const { renderBoxesAndLinesForExport: renderBoxesAndLinesForExport2 } = await Promise.resolve().then(() => (init_renderer6(), renderer_exports6));
63220
64040
  const { layoutBoxesAndLines: layoutBoxesAndLines2 } = await Promise.resolve().then(() => (init_layout5(), layout_exports5));
63221
64041
  const blLayout = await layoutBoxesAndLines2(blParsed);
63222
- const PADDING3 = 20;
64042
+ const PADDING4 = 20;
63223
64043
  const titleOffset = blParsed.title ? 40 : 0;
63224
- const exportWidth = blLayout.width + PADDING3 * 2;
63225
- const exportHeight = blLayout.height + PADDING3 * 2 + titleOffset;
64044
+ const exportWidth = blLayout.width + PADDING4 * 2;
64045
+ const exportHeight = blLayout.height + PADDING4 * 2 + titleOffset;
63226
64046
  const container = createExportContainer(exportWidth, exportHeight);
63227
64047
  const blActiveTagGroup = ctxTagOverride(ctx);
63228
64048
  renderBoxesAndLinesForExport2(
@@ -63269,10 +64089,10 @@ async function exportMindmap(ctx) {
63269
64089
  activeTagGroup,
63270
64090
  hideDescriptions
63271
64091
  });
63272
- const PADDING3 = 20;
64092
+ const PADDING4 = 20;
63273
64093
  const titleOffset = effectiveParsed.title ? 30 : 0;
63274
- const exportWidth = mmLayout.width + PADDING3 * 2;
63275
- const exportHeight = mmLayout.height + PADDING3 * 2 + titleOffset;
64094
+ const exportWidth = mmLayout.width + PADDING4 * 2;
64095
+ const exportHeight = mmLayout.height + PADDING4 * 2 + titleOffset;
63276
64096
  const container = createExportContainer(exportWidth, exportHeight);
63277
64097
  const colorByDepth = viewState?.cbd === true;
63278
64098
  renderMindmap2(
@@ -63333,10 +64153,10 @@ async function exportC4(ctx) {
63333
64153
  const c4Container = ctx.options?.c4Container ?? viewState?.c4c;
63334
64154
  const c4Layout = c4Level === "deployment" ? layoutC4Deployment2(c4Parsed) : c4Level === "components" && c4System && c4Container ? layoutC4Components2(c4Parsed, c4System, c4Container) : c4Level === "containers" && c4System ? layoutC4Containers2(c4Parsed, c4System) : layoutC4Context2(c4Parsed);
63335
64155
  if (c4Layout.nodes.length === 0) return "";
63336
- const PADDING3 = 20;
64156
+ const PADDING4 = 20;
63337
64157
  const titleOffset = c4Parsed.title ? 40 : 0;
63338
- const exportWidth = c4Layout.width + PADDING3 * 2;
63339
- const exportHeight = c4Layout.height + PADDING3 * 2 + titleOffset;
64158
+ const exportWidth = c4Layout.width + PADDING4 * 2;
64159
+ const exportHeight = c4Layout.height + PADDING4 * 2 + titleOffset;
63340
64160
  const container = createExportContainer(exportWidth, exportHeight);
63341
64161
  const renderFn = c4Level === "deployment" || c4Level === "components" && c4System && c4Container || c4Level === "containers" && c4System ? renderC4Containers2 : renderC4Context2;
63342
64162
  renderFn(
@@ -63652,30 +64472,44 @@ async function exportRing(ctx) {
63652
64472
  const ringParsed = parseRing2(content);
63653
64473
  if (ringParsed.error || ringParsed.layers.length === 0) return "";
63654
64474
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
63655
- renderRingForExport2(
64475
+ renderRingForExport2(container, ringParsed, effectivePalette, ctx.isDark, {
64476
+ width: EXPORT_WIDTH,
64477
+ height: EXPORT_HEIGHT
64478
+ });
64479
+ return finalizeSvgExport(container, theme, effectivePalette);
64480
+ }
64481
+ async function exportTreemap(ctx) {
64482
+ const { content, theme, palette } = ctx;
64483
+ const { parseTreemap: parseTreemap2 } = await Promise.resolve().then(() => (init_parser26(), parser_exports20));
64484
+ const { renderTreemapForExport: renderTreemapForExport2 } = await Promise.resolve().then(() => (init_renderer19(), renderer_exports19));
64485
+ const effectivePalette = await resolveExportPalette(theme, palette);
64486
+ const treemapParsed = parseTreemap2(content, effectivePalette);
64487
+ if (treemapParsed.error || treemapParsed.roots.length === 0) return "";
64488
+ const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
64489
+ renderTreemapForExport2(
63656
64490
  container,
63657
- ringParsed,
64491
+ treemapParsed,
63658
64492
  effectivePalette,
63659
64493
  ctx.isDark,
63660
- { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
64494
+ {
64495
+ width: EXPORT_WIDTH,
64496
+ height: EXPORT_HEIGHT
64497
+ }
63661
64498
  );
63662
64499
  return finalizeSvgExport(container, theme, effectivePalette);
63663
64500
  }
63664
64501
  async function exportRaci(ctx) {
63665
64502
  const { content, theme, palette } = ctx;
63666
- const { parseRaci: parseRaci2 } = await Promise.resolve().then(() => (init_parser26(), parser_exports20));
63667
- const { renderRaciForExport: renderRaciForExport2 } = await Promise.resolve().then(() => (init_renderer19(), renderer_exports19));
64503
+ const { parseRaci: parseRaci2 } = await Promise.resolve().then(() => (init_parser27(), parser_exports21));
64504
+ const { renderRaciForExport: renderRaciForExport2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
63668
64505
  const effectivePalette = await resolveExportPalette(theme, palette);
63669
64506
  const raciParsed = parseRaci2(content, effectivePalette);
63670
64507
  if (raciParsed.error) return "";
63671
64508
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
63672
- renderRaciForExport2(
63673
- container,
63674
- raciParsed,
63675
- effectivePalette,
63676
- ctx.isDark,
63677
- { width: EXPORT_WIDTH, height: EXPORT_HEIGHT }
63678
- );
64509
+ renderRaciForExport2(container, raciParsed, effectivePalette, ctx.isDark, {
64510
+ width: EXPORT_WIDTH,
64511
+ height: EXPORT_HEIGHT
64512
+ });
63679
64513
  return finalizeSvgExport(container, theme, effectivePalette);
63680
64514
  }
63681
64515
  function beginVizExport() {
@@ -63764,14 +64598,7 @@ async function exportVenn(ctx) {
63764
64598
  if (parsed.error || parsed.vennSets.length < 2) return "";
63765
64599
  const effectivePalette = await resolveExportPalette(theme, palette);
63766
64600
  const { container, dims } = beginVizExport();
63767
- renderVenn(
63768
- container,
63769
- parsed,
63770
- effectivePalette,
63771
- ctx.isDark,
63772
- void 0,
63773
- dims
63774
- );
64601
+ renderVenn(container, parsed, effectivePalette, ctx.isDark, void 0, dims);
63775
64602
  return finalizeSvgExport(container, theme, effectivePalette);
63776
64603
  }
63777
64604
  async function exportQuadrant(ctx) {
@@ -63805,7 +64632,7 @@ async function exportVisualization(ctx) {
63805
64632
  const isDark = ctx.isDark;
63806
64633
  const container = createExportContainer(EXPORT_WIDTH, EXPORT_HEIGHT);
63807
64634
  const { parseSequenceDgmo: parseSequenceDgmo2 } = await Promise.resolve().then(() => (init_parser7(), parser_exports));
63808
- const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer20(), renderer_exports20));
64635
+ const { renderSequenceDiagram: renderSequenceDiagram2 } = await Promise.resolve().then(() => (init_renderer21(), renderer_exports21));
63809
64636
  const seqParsed = parseSequenceDgmo2(content, effectivePalette);
63810
64637
  if (seqParsed.error || seqParsed.participants.length === 0) return "";
63811
64638
  const collapsedSections = viewState?.cs ? new Set(viewState.cs) : void 0;
@@ -64016,6 +64843,7 @@ var CHART_TYPES = /* @__PURE__ */ new Set([
64016
64843
  "journey-map",
64017
64844
  "pyramid",
64018
64845
  "ring",
64846
+ "treemap",
64019
64847
  "raci",
64020
64848
  "rasci",
64021
64849
  "daci",
@@ -64207,6 +65035,8 @@ var ATTRIBUTE_KEYS = /* @__PURE__ */ new Set([
64207
65035
  "value",
64208
65036
  "label",
64209
65037
  "style",
65038
+ // Treemap color-by-value metric key
65039
+ "heat",
64210
65040
  // Infra node behavior + edge colon-keys (§4) come from the single-source
64211
65041
  // directives registry — the parser accepts them only as `key: value` node
64212
65042
  // properties, so they render as `propertyName`. The dual-use top-level SLO
@@ -64584,7 +65414,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
64584
65414
 
64585
65415
  // src/auto/index.ts
64586
65416
  init_safe_href();
64587
- var VERSION = "0.37.0";
65417
+ var VERSION = "0.38.0";
64588
65418
  var DEFAULTS = {
64589
65419
  theme: "auto",
64590
65420
  palette: "slate",