@diagrammo/dgmo 0.23.0 → 0.25.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.mjs CHANGED
@@ -741,7 +741,7 @@ function withTagAliases(base, aliases) {
741
741
  function isReservedKey(registry, key) {
742
742
  return registry.keys.has(key) || registry.tagAliases.has(key);
743
743
  }
744
- var SEQUENCE_REGISTRY, INFRA_REGISTRY, MAP_REGISTRY, ORG_REGISTRY, C4_REGISTRY, ER_REGISTRY, CLASS_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, WIREFRAME_REGISTRY;
744
+ 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;
745
745
  var init_reserved_key_registry = __esm({
746
746
  "src/utils/reserved-key-registry.ts"() {
747
747
  "use strict";
@@ -785,10 +785,6 @@ var init_reserved_key_registry = __esm({
785
785
  "description",
786
786
  "domain"
787
787
  ]);
788
- CLASS_REGISTRY = staticRegistry([
789
- "color",
790
- "description"
791
- ]);
792
788
  KANBAN_REGISTRY = staticRegistry([
793
789
  "color",
794
790
  "description",
@@ -864,7 +860,6 @@ var init_reserved_key_registry = __esm({
864
860
  "color",
865
861
  "description"
866
862
  ]);
867
- WIREFRAME_REGISTRY = staticRegistry([]);
868
863
  }
869
864
  });
870
865
 
@@ -4133,6 +4128,9 @@ var init_legend_layout = __esm({
4133
4128
  });
4134
4129
 
4135
4130
  // src/utils/legend-d3.ts
4131
+ function centerText(sel) {
4132
+ return sel.attr("dy", LEGEND_TEXT_DY);
4133
+ }
4136
4134
  function renderLegendD3(container, config, state, palette, isDark, callbacks, containerWidth) {
4137
4135
  const width = containerWidth ?? parseFloat(container.attr("width") || "800");
4138
4136
  let currentState = { ...state };
@@ -4215,21 +4213,21 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, _isDark, c
4215
4213
  const pill = capsule.pill;
4216
4214
  g.append("rect").attr("x", pill.x).attr("y", pill.y).attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", palette.bg);
4217
4215
  g.append("rect").attr("x", pill.x).attr("y", pill.y).attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", "none").attr("stroke", pillBorder).attr("stroke-width", 0.75);
4218
- g.append("text").attr("x", pill.x + pill.width / 2).attr("y", LEGEND_HEIGHT / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.text).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(capsule.groupName);
4216
+ g.append("text").attr("x", pill.x + pill.width / 2).attr("y", LEGEND_HEIGHT / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.text).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(capsule.groupName);
4219
4217
  if (capsule.gradient) {
4220
4218
  const gr = capsule.gradient;
4221
4219
  const gradId = `dgmo-legend-ramp-${capsule.groupName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
4222
4220
  const def = g.append("defs").append("linearGradient").attr("id", gradId);
4223
4221
  def.append("stop").attr("offset", "0%").attr("stop-color", mix(gr.hue, gr.base, 15));
4224
4222
  def.append("stop").attr("offset", "100%").attr("stop-color", gr.hue);
4225
- g.append("text").attr("x", gr.minX).attr("y", gr.textY).attr("dominant-baseline", "central").attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(gr.minText);
4223
+ g.append("text").attr("x", gr.minX).attr("y", gr.textY).call(centerText).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(gr.minText);
4226
4224
  g.append("rect").attr("class", "dgmo-legend-gradient-ramp").attr("data-ramp-min", gr.min).attr("data-ramp-max", gr.max).attr("x", gr.rampX).attr("y", gr.rampY).attr("width", gr.rampW).attr("height", gr.rampH).attr("rx", 2).attr("fill", `url(#${gradId})`);
4227
- g.append("text").attr("x", gr.maxX).attr("y", gr.textY).attr("dominant-baseline", "central").attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(gr.maxText);
4225
+ g.append("text").attr("x", gr.maxX).attr("y", gr.textY).call(centerText).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(gr.maxText);
4228
4226
  }
4229
4227
  for (const entry of capsule.entries) {
4230
4228
  const entryG = g.append("g").attr("data-legend-entry", entry.value.toLowerCase()).attr("data-series-name", entry.value).style("cursor", "pointer");
4231
4229
  entryG.append("circle").attr("cx", entry.dotCx).attr("cy", entry.dotCy).attr("r", LEGEND_DOT_R).attr("fill", entry.color);
4232
- entryG.append("text").attr("x", entry.textX).attr("y", entry.textY).attr("dominant-baseline", "central").attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("font-family", FONT_FAMILY).text(entry.displayValue ?? entry.value);
4230
+ entryG.append("text").attr("x", entry.textX).attr("y", entry.textY).call(centerText).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("font-family", FONT_FAMILY).text(entry.displayValue ?? entry.value);
4233
4231
  if (callbacks?.onEntryHover) {
4234
4232
  const groupName = capsule.groupName;
4235
4233
  const entryValue = entry.value;
@@ -4249,7 +4247,7 @@ function renderCapsule(parent, capsule, palette, groupBg, pillBorder, _isDark, c
4249
4247
  function renderPill(parent, pill, palette, groupBg, callbacks) {
4250
4248
  const g = parent.append("g").attr("transform", `translate(${pill.x},${pill.y})`).attr("data-legend-group", pill.groupName.toLowerCase()).style("cursor", "pointer");
4251
4249
  g.append("rect").attr("width", pill.width).attr("height", pill.height).attr("rx", pill.height / 2).attr("fill", groupBg);
4252
- g.append("text").attr("x", pill.width / 2).attr("y", pill.height / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(pill.groupName);
4250
+ g.append("text").attr("x", pill.width / 2).attr("y", pill.height / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(pill.groupName);
4253
4251
  if (callbacks?.onGroupToggle) {
4254
4252
  const cb = callbacks.onGroupToggle;
4255
4253
  const name = pill.groupName;
@@ -4272,7 +4270,7 @@ function renderControl(parent, ctrl, palette, _groupBg, pillBorder, _isDark, con
4272
4270
  textX = 8 + 14 + LEGEND_ENTRY_DOT_GAP + measureLegendText(ctrl.label, LEGEND_PILL_FONT_SIZE) / 2;
4273
4271
  }
4274
4272
  if (ctrl.label) {
4275
- g.append("text").attr("x", textX).attr("y", ctrl.height / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(ctrl.label);
4273
+ g.append("text").attr("x", textX).attr("y", ctrl.height / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_PILL_FONT_SIZE).attr("font-weight", 500).attr("fill", palette.textMuted).attr("pointer-events", "none").attr("font-family", FONT_FAMILY).text(ctrl.label);
4276
4274
  }
4277
4275
  if (ctrl.children) {
4278
4276
  let cx = ctrl.width + 4;
@@ -4282,7 +4280,7 @@ function renderControl(parent, ctrl, palette, _groupBg, pillBorder, _isDark, con
4282
4280
  "fill",
4283
4281
  child.isActive ? palette.primary ?? palette.text : "none"
4284
4282
  ).attr("stroke", pillBorder).attr("stroke-width", 0.75);
4285
- childG.append("text").attr("x", child.width / 2).attr("y", ctrl.height / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", child.isActive ? palette.bg : palette.textMuted).attr("font-family", FONT_FAMILY).text(child.label);
4283
+ childG.append("text").attr("x", child.width / 2).attr("y", ctrl.height / 2).attr("text-anchor", "middle").call(centerText).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", child.isActive ? palette.bg : palette.textMuted).attr("font-family", FONT_FAMILY).text(child.label);
4286
4284
  const configCtrl2 = configControls?.find((c) => c.id === ctrl.id);
4287
4285
  const configChild = configCtrl2?.children?.find((c) => c.id === child.id);
4288
4286
  if (configChild?.onClick) {
@@ -4336,7 +4334,7 @@ function renderControlsGroup(parent, layout, palette, groupBg, pillBorder, callb
4336
4334
  } else {
4337
4335
  entryG.append("circle").attr("cx", tl.dotCx).attr("cy", tl.dotCy).attr("r", LEGEND_TOGGLE_DOT_R).attr("fill", "none").attr("stroke", palette.textMuted).attr("stroke-width", 1);
4338
4336
  }
4339
- entryG.append("text").attr("x", tl.textX).attr("y", tl.textY).attr("dominant-baseline", "central").attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("opacity", tl.active ? 1 : LEGEND_TOGGLE_OFF_OPACITY).attr("font-family", FONT_FAMILY).text(tl.label);
4337
+ entryG.append("text").attr("x", tl.textX).attr("y", tl.textY).call(centerText).attr("font-size", LEGEND_ENTRY_FONT_SIZE).attr("fill", palette.textMuted).attr("opacity", tl.active ? 1 : LEGEND_TOGGLE_OFF_OPACITY).attr("font-family", FONT_FAMILY).text(tl.label);
4340
4338
  if (callbacks?.onControlsToggle && toggle) {
4341
4339
  const cb = callbacks.onControlsToggle;
4342
4340
  const id = tl.id;
@@ -4349,6 +4347,7 @@ function renderControlsGroup(parent, layout, palette, groupBg, pillBorder, callb
4349
4347
  }
4350
4348
  }
4351
4349
  }
4350
+ var LEGEND_TEXT_DY;
4352
4351
  var init_legend_d3 = __esm({
4353
4352
  "src/utils/legend-d3.ts"() {
4354
4353
  "use strict";
@@ -4356,6 +4355,7 @@ var init_legend_d3 = __esm({
4356
4355
  init_legend_layout();
4357
4356
  init_color_utils();
4358
4357
  init_fonts();
4358
+ LEGEND_TEXT_DY = "0.32em";
4359
4359
  }
4360
4360
  });
4361
4361
 
@@ -4606,7 +4606,6 @@ var init_arrows = __esm({
4606
4606
  var parser_exports = {};
4607
4607
  __export(parser_exports, {
4608
4608
  isSequenceBlock: () => isSequenceBlock,
4609
- isSequenceMessage: () => isSequenceMessage,
4610
4609
  isSequenceNote: () => isSequenceNote,
4611
4610
  isSequenceSection: () => isSequenceSection,
4612
4611
  looksLikeSequence: () => looksLikeSequence,
@@ -4622,9 +4621,6 @@ function isHardRemovedToken(remainder) {
4622
4621
  }
4623
4622
  return { removed: false };
4624
4623
  }
4625
- function isSequenceMessage(el) {
4626
- return el.kind === "message";
4627
- }
4628
4624
  function isSequenceBlock(el) {
4629
4625
  return el.kind === "block";
4630
4626
  }
@@ -26510,7 +26506,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26510
26506
  const VALUE_NAME = hasRamp ? parsed.boxMetric?.trim() || "Value" : null;
26511
26507
  const matchColorGroup = (v) => {
26512
26508
  const lv = v.trim().toLowerCase();
26513
- if (lv === "none") return null;
26509
+ if (lv === "" || lv === "none") return null;
26514
26510
  const tg = parsed.tagGroups.find((g) => g.name.toLowerCase() === lv);
26515
26511
  if (tg) return tg.name;
26516
26512
  if (lv === VALUE_NAME?.toLowerCase()) return VALUE_NAME;
@@ -26851,6 +26847,22 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26851
26847
  const tooltipText = fullText.length > 200 ? fullText.slice(0, 199) + "\u2026" : fullText;
26852
26848
  nodeG.append("title").text(tooltipText);
26853
26849
  }
26850
+ } else if (parsed.showValues && node.value !== void 0) {
26851
+ const valueLabel = parsed.boxMetric ? `${parsed.boxMetric}: ${node.value}` : String(node.value);
26852
+ const headerH = ln.height / 2;
26853
+ const sepY = -ln.height / 2 + headerH;
26854
+ const fitted = fitLabelToHeader(node.label, ln.width, 2);
26855
+ const labelLineH = fitted.fontSize * 1.3;
26856
+ const labelTotalH = fitted.lines.length * labelLineH;
26857
+ const headerCenterY = -ln.height / 2 + headerH / 2;
26858
+ for (let li = 0; li < fitted.lines.length; li++) {
26859
+ nodeG.append("text").attr("x", 0).attr(
26860
+ "y",
26861
+ headerCenterY - labelTotalH / 2 + labelLineH / 2 + li * labelLineH
26862
+ ).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", fitted.fontSize).attr("font-weight", "600").attr("fill", colors.text).text(fitted.lines[li]);
26863
+ }
26864
+ nodeG.append("line").attr("x1", -ln.width / 2).attr("y1", sepY).attr("x2", ln.width / 2).attr("y2", sepY).attr("stroke", colors.stroke).attr("stroke-opacity", 0.3).attr("stroke-width", 1);
26865
+ nodeG.append("text").attr("class", "bl-node-value").attr("x", 0).attr("y", (sepY + ln.height / 2) / 2).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("fill", colors.text).attr("opacity", 0.85).text(valueLabel);
26854
26866
  } else {
26855
26867
  const maxLabelLines = Math.max(
26856
26868
  2,
@@ -26863,21 +26875,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26863
26875
  nodeG.append("text").attr("x", 0).attr("y", -totalH / 2 + lineH / 2 + li * lineH).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", fitted.fontSize).attr("font-weight", "600").attr("fill", colors.text).text(fitted.lines[li]);
26864
26876
  }
26865
26877
  }
26866
- if (parsed.showValues && node.value !== void 0) {
26878
+ if (parsed.showValues && node.value !== void 0 && desc && desc.length > 0 && !hideDescriptions) {
26867
26879
  const valueText = String(node.value);
26868
- const descShown = !!(desc && desc.length > 0 && !hideDescriptions);
26869
- if (descShown) {
26870
- const padX = 6;
26871
- const padY = 5;
26872
- const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26873
- const bh = VALUE_FONT_SIZE + 4;
26874
- const bx = ln.width / 2 - bw - 4;
26875
- const by = -ln.height / 2 + 4;
26876
- nodeG.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
26877
- nodeG.append("text").attr("class", "bl-node-value").attr("x", bx + bw - padX).attr("y", by + padY).attr("text-anchor", "end").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", palette.textMuted).text(valueText);
26878
- } else {
26879
- nodeG.append("text").attr("class", "bl-node-value").attr("x", 0).attr("y", ln.height / 2 - VALUE_FONT_SIZE).attr("text-anchor", "middle").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", colors.text).attr("opacity", 0.8).text(valueText);
26880
- }
26880
+ const padX = 6;
26881
+ const padY = 5;
26882
+ const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26883
+ const bh = VALUE_FONT_SIZE + 4;
26884
+ const bx = Math.max(-ln.width / 2 + 4, ln.width / 2 - bw - 4);
26885
+ const by = -ln.height / 2 + 4;
26886
+ nodeG.append("rect").attr("x", bx).attr("y", by).attr("width", bw).attr("height", bh).attr("rx", 3).attr("fill", palette.bg).attr("opacity", 0.85);
26887
+ nodeG.append("text").attr("class", "bl-node-value").attr("x", bx + bw - padX).attr("y", by + padY).attr("text-anchor", "end").attr("dominant-baseline", "central").attr("font-size", VALUE_FONT_SIZE).attr("font-weight", "600").attr("fill", palette.textMuted).text(valueText);
26881
26888
  }
26882
26889
  }
26883
26890
  const hasDescriptions = parsed.nodes.some(
@@ -26971,7 +26978,7 @@ var init_renderer6 = __esm({
26971
26978
  init_wrapped_desc();
26972
26979
  init_scaling();
26973
26980
  DIAGRAM_PADDING6 = 20;
26974
- NODE_FONT_SIZE = 13;
26981
+ NODE_FONT_SIZE = 11;
26975
26982
  MIN_NODE_FONT_SIZE = 9;
26976
26983
  EDGE_LABEL_FONT_SIZE4 = 11;
26977
26984
  EDGE_STROKE_WIDTH5 = 1.5;
@@ -46708,7 +46715,11 @@ function resolveMap(parsed, data) {
46708
46715
  if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
46709
46716
  }
46710
46717
  const containerUnion = unionExtent(containerBoxes, points);
46711
- if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
46718
+ if (containerUnion)
46719
+ extent2 = pad(
46720
+ clampContainerToCluster(containerUnion, points),
46721
+ PAD_FRACTION
46722
+ );
46712
46723
  }
46713
46724
  if (isPoiOnly) {
46714
46725
  const cx = (extent2[0][0] + extent2[1][0]) / 2;
@@ -46789,6 +46800,22 @@ function mostCommonCountry(regions, poiCountries) {
46789
46800
  }
46790
46801
  return best;
46791
46802
  }
46803
+ function clampContainerToCluster(container, points) {
46804
+ const poi = unionExtent([], points);
46805
+ if (!poi) return container;
46806
+ let [[west, south], [east, north]] = container;
46807
+ const [[pWest, pSouth], [pEast, pNorth]] = poi;
46808
+ south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
46809
+ north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
46810
+ if (east <= 180 && pEast <= 180) {
46811
+ west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
46812
+ east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
46813
+ }
46814
+ return [
46815
+ [west, south],
46816
+ [east, north]
46817
+ ];
46818
+ }
46792
46819
  function pad(e, frac) {
46793
46820
  const dLon = (e[1][0] - e[0][0]) * frac || 1;
46794
46821
  const dLat = (e[1][1] - e[0][1]) * frac || 1;
@@ -46801,7 +46828,7 @@ function firstError(diags) {
46801
46828
  const e = diags.find((d) => d.severity === "error");
46802
46829
  return e ? formatDgmoError(e) : null;
46803
46830
  }
46804
- var WORLD_SPAN, MERCATOR_MAX_LAT, PAD_FRACTION, REGION_PAD_FRACTION, WORLD_LAT_SOUTH, WORLD_LAT_NORTH, POI_ZOOM_FLOOR_DEG, US_NATIONAL_LON_SPAN, REGION_ALIASES, US_STATE_POSTAL;
46831
+ 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, REGION_ALIASES, US_STATE_POSTAL;
46805
46832
  var init_resolver2 = __esm({
46806
46833
  "src/map/resolver.ts"() {
46807
46834
  "use strict";
@@ -46814,6 +46841,7 @@ var init_resolver2 = __esm({
46814
46841
  WORLD_LAT_SOUTH = -58;
46815
46842
  WORLD_LAT_NORTH = 78;
46816
46843
  POI_ZOOM_FLOOR_DEG = 7;
46844
+ CONTAINER_OVERSHOOT_DEG = 8;
46817
46845
  US_NATIONAL_LON_SPAN = 48;
46818
46846
  REGION_ALIASES = {
46819
46847
  // Common everyday names → the Natural-Earth display name actually shipped.
@@ -46892,6 +46920,55 @@ var init_resolver2 = __esm({
46892
46920
  }
46893
46921
  });
46894
46922
 
46923
+ // src/map/legend-band.ts
46924
+ function mapLegendGroups(legend) {
46925
+ const ramp = legend.ramp;
46926
+ const scoreGroup = ramp ? {
46927
+ name: ramp.metric?.trim() || "Value",
46928
+ entries: [],
46929
+ gradient: {
46930
+ min: ramp.min,
46931
+ max: ramp.max,
46932
+ hue: ramp.hue,
46933
+ base: ramp.base
46934
+ }
46935
+ } : null;
46936
+ const tagGroups = legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
46937
+ return [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
46938
+ }
46939
+ function mapLegendConfig(groups, mode) {
46940
+ return {
46941
+ groups,
46942
+ position: { placement: "top-center", titleRelation: "below-title" },
46943
+ mode,
46944
+ showEmptyGroups: false,
46945
+ showInactivePills: true
46946
+ };
46947
+ }
46948
+ function mapLegendTop(hasTitle, hasSubtitle) {
46949
+ return (hasTitle ? TITLE_Y + TITLE_FONT_SIZE : 0) + (hasSubtitle ? TITLE_FONT_SIZE : 0) + LEGEND_TOP_GAP2;
46950
+ }
46951
+ function mapLegendBand(legend, opts) {
46952
+ if (!legend) return 0;
46953
+ const groups = mapLegendGroups(legend);
46954
+ if (groups.length === 0) return 0;
46955
+ const config = mapLegendConfig(groups, opts.mode);
46956
+ const state = { activeGroup: legend.activeGroup };
46957
+ const { height } = computeLegendLayout(config, state, opts.width);
46958
+ if (height <= 0) return 0;
46959
+ return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
46960
+ }
46961
+ var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
46962
+ var init_legend_band = __esm({
46963
+ "src/map/legend-band.ts"() {
46964
+ "use strict";
46965
+ init_legend_layout();
46966
+ init_title_constants();
46967
+ LEGEND_TOP_GAP2 = 8;
46968
+ LEGEND_BOTTOM_GAP2 = 10;
46969
+ }
46970
+ });
46971
+
46895
46972
  // src/map/colorize.ts
46896
46973
  function assignColors(isos, adjacency) {
46897
46974
  const sorted = [...isos].sort();
@@ -47483,12 +47560,43 @@ function layoutMap(resolved, data, size, opts) {
47483
47560
  return tagFill(r.tags, activeGroup) ?? neutralFill;
47484
47561
  };
47485
47562
  const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
47563
+ let legend = null;
47564
+ if (!resolved.directives.noLegend) {
47565
+ const legendTagGroups = resolved.tagGroups.map((g) => ({
47566
+ name: g.name,
47567
+ entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
47568
+ }));
47569
+ if (legendTagGroups.length > 0 || hasRamp) {
47570
+ legend = {
47571
+ tagGroups: legendTagGroups,
47572
+ activeGroup,
47573
+ ...hasRamp && {
47574
+ ramp: {
47575
+ ...resolved.directives.regionMetric !== void 0 && {
47576
+ metric: resolved.directives.regionMetric
47577
+ },
47578
+ min: rampMin,
47579
+ max: rampMax,
47580
+ hue: rampHue,
47581
+ base: rampBase
47582
+ }
47583
+ }
47584
+ };
47585
+ }
47586
+ }
47486
47587
  const TITLE_GAP2 = 16;
47487
47588
  let topPad = FIT_PAD;
47488
47589
  if (resolved.title && resolved.pois.length > 0) {
47489
47590
  const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
47490
47591
  topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
47491
47592
  }
47593
+ const legendBand = mapLegendBand(legend, {
47594
+ width,
47595
+ mode: opts.legendMode ?? "preview",
47596
+ hasTitle: Boolean(resolved.title),
47597
+ hasSubtitle: Boolean(resolved.subtitle)
47598
+ });
47599
+ if (legendBand > topPad) topPad = legendBand;
47492
47600
  const fitBox = [
47493
47601
  [FIT_PAD, topPad],
47494
47602
  [
@@ -47506,7 +47614,7 @@ function layoutMap(resolved, data, size, opts) {
47506
47614
  const by0 = cb[0][1];
47507
47615
  const cw = cb[1][0] - bx0;
47508
47616
  const ch = cb[1][1] - by0;
47509
- const topReserve = resolved.title && resolved.pois.length > 0 ? topPad : 0;
47617
+ const topReserve = resolved.title && resolved.pois.length > 0 || legendBand > 0 ? topPad : 0;
47510
47618
  const ox = 0;
47511
47619
  const oy = topReserve;
47512
47620
  const sx = cw > 0 ? width / cw : 1;
@@ -48570,30 +48678,6 @@ function layoutMap(resolved, data, size, opts) {
48570
48678
  });
48571
48679
  labels.push(...contextLabels);
48572
48680
  }
48573
- let legend = null;
48574
- if (!resolved.directives.noLegend) {
48575
- const tagGroups = resolved.tagGroups.map((g) => ({
48576
- name: g.name,
48577
- entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
48578
- }));
48579
- if (tagGroups.length > 0 || hasRamp) {
48580
- legend = {
48581
- tagGroups,
48582
- activeGroup,
48583
- ...hasRamp && {
48584
- ramp: {
48585
- ...resolved.directives.regionMetric !== void 0 && {
48586
- metric: resolved.directives.regionMetric
48587
- },
48588
- min: rampMin,
48589
- max: rampMax,
48590
- hue: rampHue,
48591
- base: rampBase
48592
- }
48593
- }
48594
- };
48595
- }
48596
- }
48597
48681
  return {
48598
48682
  width,
48599
48683
  height,
@@ -48629,6 +48713,7 @@ var init_layout15 = __esm({
48629
48713
  init_label_layout();
48630
48714
  init_legend_constants();
48631
48715
  init_title_constants();
48716
+ init_legend_band();
48632
48717
  init_context_labels();
48633
48718
  FIT_PAD = 24;
48634
48719
  RAMP_FLOOR2 = 15;
@@ -48829,6 +48914,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
48829
48914
  // stretch-distorting. The in-app preview pane passes no exportDims → unset →
48830
48915
  // keeps the global stretch-fill.
48831
48916
  preferContain: exportDims?.preferContain ?? false,
48917
+ // Reserve the legend band for the mode actually drawn below (export shows
48918
+ // only the active group; preview keeps the inactive pills).
48919
+ legendMode: exportDims ? "export" : "preview",
48832
48920
  ...activeGroupOverride !== void 0 && {
48833
48921
  activeGroup: activeGroupOverride
48834
48922
  }
@@ -49120,30 +49208,12 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
49120
49208
  if (layout.legend) {
49121
49209
  const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
49122
49210
  const legendG = svg.append("g").attr("class", "dgmo-map-legend").attr("transform", `translate(0, ${legendY})`);
49123
- const ramp = layout.legend.ramp;
49124
- const scoreGroup = ramp ? {
49125
- name: ramp.metric?.trim() || "Value",
49126
- entries: [],
49127
- gradient: {
49128
- min: ramp.min,
49129
- max: ramp.max,
49130
- hue: ramp.hue,
49131
- base: ramp.base
49132
- }
49133
- } : null;
49134
- const tagGroups = layout.legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
49135
- const groups = [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
49211
+ const groups = mapLegendGroups(layout.legend);
49136
49212
  if (groups.length > 0) {
49137
- const config = {
49213
+ const config = mapLegendConfig(
49138
49214
  groups,
49139
- position: { placement: "top-center", titleRelation: "below-title" },
49140
- mode: exportDims ? "export" : "preview",
49141
- showEmptyGroups: false,
49142
- // Keep inactive siblings visible as pills so the user can click to flip
49143
- // the active colouring dimension (preview only — export shows just the
49144
- // active group).
49145
- showInactivePills: true
49146
- };
49215
+ exportDims ? "export" : "preview"
49216
+ );
49147
49217
  const state = { activeGroup: layout.legend.activeGroup };
49148
49218
  renderLegendD3(legendG, config, state, palette, isDark, void 0, width);
49149
49219
  }
@@ -49187,6 +49257,7 @@ var init_renderer16 = __esm({
49187
49257
  init_title_constants();
49188
49258
  init_color_utils();
49189
49259
  init_legend_d3();
49260
+ init_legend_band();
49190
49261
  init_layout15();
49191
49262
  LABEL_FONT = 11;
49192
49263
  }
@@ -49208,9 +49279,10 @@ function mapContentAspect(resolved, data, ref = REF) {
49208
49279
  const aspect = w / h;
49209
49280
  return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
49210
49281
  }
49211
- function mapExportDimensions(resolved, data, baseWidth = 1200) {
49212
- const raw = mapContentAspect(resolved, data);
49213
- const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49282
+ function mapExportDimensions(resolved, data, baseWidth = 1200, aspectOverride) {
49283
+ const useOverride = aspectOverride !== void 0 && Number.isFinite(aspectOverride) && aspectOverride > 0;
49284
+ const raw = useOverride ? aspectOverride : mapContentAspect(resolved, data);
49285
+ const clamped = useOverride ? raw : Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49214
49286
  const width = baseWidth;
49215
49287
  let height = Math.round(width / clamped);
49216
49288
  let chromeReserve = 0;
@@ -49223,7 +49295,7 @@ function mapExportDimensions(resolved, data, baseWidth = 1200) {
49223
49295
  height = Math.round(chromeReserve + MIN_MAP_BAND);
49224
49296
  floored = true;
49225
49297
  }
49226
- const preferContain = clamped !== raw || floored;
49298
+ const preferContain = useOverride ? floored : clamped !== raw || floored;
49227
49299
  return { width, height, preferContain };
49228
49300
  }
49229
49301
  var FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
@@ -54933,7 +55005,6 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
54933
55005
  function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
54934
55006
  const {
54935
55007
  width,
54936
- height,
54937
55008
  tooltip,
54938
55009
  solid,
54939
55010
  textColor,
@@ -54982,8 +55053,7 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
54982
55053
  const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
54983
55054
  const eraLabelY = eraReserve ? -(topScaleH + markerReserve + ERA_ROW_H / 2) : 0;
54984
55055
  const innerWidth = width - margin.left - margin.right;
54985
- const availInnerHeight = height - margin.top - margin.bottom;
54986
- const rowH = Math.min(ctx.structural(28), availInnerHeight / sorted.length);
55056
+ const rowH = ctx.structural(28);
54987
55057
  const innerHeight = rowH * sorted.length;
54988
55058
  const usedHeight = margin.top + innerHeight + margin.bottom;
54989
55059
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
@@ -57508,7 +57578,12 @@ async function renderForExport(content, theme, palette, viewState, options) {
57508
57578
  }
57509
57579
  }
57510
57580
  const mapResolved = resolveMap2(mapParsed, mapData);
57511
- const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
57581
+ const dims2 = mapExportDimensions2(
57582
+ mapResolved,
57583
+ mapData,
57584
+ EXPORT_WIDTH,
57585
+ options?.mapAspect
57586
+ );
57512
57587
  const container2 = createExportContainer(dims2.width, dims2.height);
57513
57588
  renderMapForExport2(
57514
57589
  container2,
@@ -59224,7 +59299,7 @@ pre.dgmo, code.language-dgmo, pre > code.language-dgmo,
59224
59299
 
59225
59300
  // src/auto/index.ts
59226
59301
  init_safe_href();
59227
- var VERSION = "0.23.0";
59302
+ var VERSION = "0.25.0";
59228
59303
  var DEFAULTS = {
59229
59304
  theme: "auto",
59230
59305
  palette: "nord",