@diagrammo/dgmo 0.23.0 → 0.24.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/index.js CHANGED
@@ -26585,7 +26585,7 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26585
26585
  const VALUE_NAME = hasRamp ? parsed.boxMetric?.trim() || "Value" : null;
26586
26586
  const matchColorGroup = (v) => {
26587
26587
  const lv = v.trim().toLowerCase();
26588
- if (lv === "none") return null;
26588
+ if (lv === "" || lv === "none") return null;
26589
26589
  const tg = parsed.tagGroups.find((g) => g.name.toLowerCase() === lv);
26590
26590
  if (tg) return tg.name;
26591
26591
  if (lv === VALUE_NAME?.toLowerCase()) return VALUE_NAME;
@@ -26926,6 +26926,22 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26926
26926
  const tooltipText = fullText.length > 200 ? fullText.slice(0, 199) + "\u2026" : fullText;
26927
26927
  nodeG.append("title").text(tooltipText);
26928
26928
  }
26929
+ } else if (parsed.showValues && node.value !== void 0) {
26930
+ const valueLabel = parsed.boxMetric ? `${parsed.boxMetric}: ${node.value}` : String(node.value);
26931
+ const headerH = ln.height / 2;
26932
+ const sepY = -ln.height / 2 + headerH;
26933
+ const fitted = fitLabelToHeader(node.label, ln.width, 2);
26934
+ const labelLineH = fitted.fontSize * 1.3;
26935
+ const labelTotalH = fitted.lines.length * labelLineH;
26936
+ const headerCenterY = -ln.height / 2 + headerH / 2;
26937
+ for (let li = 0; li < fitted.lines.length; li++) {
26938
+ nodeG.append("text").attr("x", 0).attr(
26939
+ "y",
26940
+ headerCenterY - labelTotalH / 2 + labelLineH / 2 + li * labelLineH
26941
+ ).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]);
26942
+ }
26943
+ 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);
26944
+ 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);
26929
26945
  } else {
26930
26946
  const maxLabelLines = Math.max(
26931
26947
  2,
@@ -26938,21 +26954,16 @@ function renderBoxesAndLines(container, parsed, layout, palette, isDark, options
26938
26954
  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]);
26939
26955
  }
26940
26956
  }
26941
- if (parsed.showValues && node.value !== void 0) {
26957
+ if (parsed.showValues && node.value !== void 0 && desc && desc.length > 0 && !hideDescriptions) {
26942
26958
  const valueText = String(node.value);
26943
- const descShown = !!(desc && desc.length > 0 && !hideDescriptions);
26944
- if (descShown) {
26945
- const padX = 6;
26946
- const padY = 5;
26947
- const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26948
- const bh = VALUE_FONT_SIZE + 4;
26949
- const bx = ln.width / 2 - bw - 4;
26950
- const by = -ln.height / 2 + 4;
26951
- 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);
26952
- 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);
26953
- } else {
26954
- 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);
26955
- }
26959
+ const padX = 6;
26960
+ const padY = 5;
26961
+ const bw = valueText.length * VALUE_FONT_SIZE * CHAR_WIDTH_RATIO2 + 8;
26962
+ const bh = VALUE_FONT_SIZE + 4;
26963
+ const bx = Math.max(-ln.width / 2 + 4, ln.width / 2 - bw - 4);
26964
+ const by = -ln.height / 2 + 4;
26965
+ 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);
26966
+ 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);
26956
26967
  }
26957
26968
  }
26958
26969
  const hasDescriptions = parsed.nodes.some(
@@ -27046,7 +27057,7 @@ var init_renderer6 = __esm({
27046
27057
  init_wrapped_desc();
27047
27058
  init_scaling();
27048
27059
  DIAGRAM_PADDING6 = 20;
27049
- NODE_FONT_SIZE = 13;
27060
+ NODE_FONT_SIZE = 11;
27050
27061
  MIN_NODE_FONT_SIZE = 9;
27051
27062
  EDGE_LABEL_FONT_SIZE4 = 11;
27052
27063
  EDGE_STROKE_WIDTH5 = 1.5;
@@ -46783,7 +46794,11 @@ function resolveMap(parsed, data) {
46783
46794
  if (bb && !isWholeSphere(bb)) containerBoxes.push(bb);
46784
46795
  }
46785
46796
  const containerUnion = unionExtent(containerBoxes, points);
46786
- if (containerUnion) extent2 = pad(containerUnion, PAD_FRACTION);
46797
+ if (containerUnion)
46798
+ extent2 = pad(
46799
+ clampContainerToCluster(containerUnion, points),
46800
+ PAD_FRACTION
46801
+ );
46787
46802
  }
46788
46803
  if (isPoiOnly) {
46789
46804
  const cx = (extent2[0][0] + extent2[1][0]) / 2;
@@ -46864,6 +46879,22 @@ function mostCommonCountry(regions, poiCountries) {
46864
46879
  }
46865
46880
  return best;
46866
46881
  }
46882
+ function clampContainerToCluster(container, points) {
46883
+ const poi = unionExtent([], points);
46884
+ if (!poi) return container;
46885
+ let [[west, south], [east, north]] = container;
46886
+ const [[pWest, pSouth], [pEast, pNorth]] = poi;
46887
+ south = Math.max(south, pSouth - CONTAINER_OVERSHOOT_DEG);
46888
+ north = Math.min(north, pNorth + CONTAINER_OVERSHOOT_DEG);
46889
+ if (east <= 180 && pEast <= 180) {
46890
+ west = Math.max(west, pWest - CONTAINER_OVERSHOOT_DEG);
46891
+ east = Math.min(east, pEast + CONTAINER_OVERSHOOT_DEG);
46892
+ }
46893
+ return [
46894
+ [west, south],
46895
+ [east, north]
46896
+ ];
46897
+ }
46867
46898
  function pad(e, frac) {
46868
46899
  const dLon = (e[1][0] - e[0][0]) * frac || 1;
46869
46900
  const dLat = (e[1][1] - e[0][1]) * frac || 1;
@@ -46876,7 +46907,7 @@ function firstError(diags) {
46876
46907
  const e = diags.find((d) => d.severity === "error");
46877
46908
  return e ? formatDgmoError(e) : null;
46878
46909
  }
46879
- 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;
46910
+ 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;
46880
46911
  var init_resolver2 = __esm({
46881
46912
  "src/map/resolver.ts"() {
46882
46913
  "use strict";
@@ -46889,6 +46920,7 @@ var init_resolver2 = __esm({
46889
46920
  WORLD_LAT_SOUTH = -58;
46890
46921
  WORLD_LAT_NORTH = 78;
46891
46922
  POI_ZOOM_FLOOR_DEG = 7;
46923
+ CONTAINER_OVERSHOOT_DEG = 8;
46892
46924
  US_NATIONAL_LON_SPAN = 48;
46893
46925
  REGION_ALIASES = {
46894
46926
  // Common everyday names → the Natural-Earth display name actually shipped.
@@ -46967,6 +46999,55 @@ var init_resolver2 = __esm({
46967
46999
  }
46968
47000
  });
46969
47001
 
47002
+ // src/map/legend-band.ts
47003
+ function mapLegendGroups(legend) {
47004
+ const ramp = legend.ramp;
47005
+ const scoreGroup = ramp ? {
47006
+ name: ramp.metric?.trim() || "Value",
47007
+ entries: [],
47008
+ gradient: {
47009
+ min: ramp.min,
47010
+ max: ramp.max,
47011
+ hue: ramp.hue,
47012
+ base: ramp.base
47013
+ }
47014
+ } : null;
47015
+ const tagGroups = legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
47016
+ return [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
47017
+ }
47018
+ function mapLegendConfig(groups, mode) {
47019
+ return {
47020
+ groups,
47021
+ position: { placement: "top-center", titleRelation: "below-title" },
47022
+ mode,
47023
+ showEmptyGroups: false,
47024
+ showInactivePills: true
47025
+ };
47026
+ }
47027
+ function mapLegendTop(hasTitle, hasSubtitle) {
47028
+ return (hasTitle ? TITLE_Y + TITLE_FONT_SIZE : 0) + (hasSubtitle ? TITLE_FONT_SIZE : 0) + LEGEND_TOP_GAP2;
47029
+ }
47030
+ function mapLegendBand(legend, opts) {
47031
+ if (!legend) return 0;
47032
+ const groups = mapLegendGroups(legend);
47033
+ if (groups.length === 0) return 0;
47034
+ const config = mapLegendConfig(groups, opts.mode);
47035
+ const state = { activeGroup: legend.activeGroup };
47036
+ const { height } = computeLegendLayout(config, state, opts.width);
47037
+ if (height <= 0) return 0;
47038
+ return mapLegendTop(opts.hasTitle, opts.hasSubtitle) + height + LEGEND_BOTTOM_GAP2;
47039
+ }
47040
+ var LEGEND_TOP_GAP2, LEGEND_BOTTOM_GAP2;
47041
+ var init_legend_band = __esm({
47042
+ "src/map/legend-band.ts"() {
47043
+ "use strict";
47044
+ init_legend_layout();
47045
+ init_title_constants();
47046
+ LEGEND_TOP_GAP2 = 8;
47047
+ LEGEND_BOTTOM_GAP2 = 10;
47048
+ }
47049
+ });
47050
+
46970
47051
  // src/map/colorize.ts
46971
47052
  function assignColors(isos, adjacency) {
46972
47053
  const sorted = [...isos].sort();
@@ -47558,12 +47639,43 @@ function layoutMap(resolved, data, size, opts) {
47558
47639
  return tagFill(r.tags, activeGroup) ?? neutralFill;
47559
47640
  };
47560
47641
  const regionById = new Map(resolved.regions.map((r) => [r.iso, r]));
47642
+ let legend = null;
47643
+ if (!resolved.directives.noLegend) {
47644
+ const legendTagGroups = resolved.tagGroups.map((g) => ({
47645
+ name: g.name,
47646
+ entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
47647
+ }));
47648
+ if (legendTagGroups.length > 0 || hasRamp) {
47649
+ legend = {
47650
+ tagGroups: legendTagGroups,
47651
+ activeGroup,
47652
+ ...hasRamp && {
47653
+ ramp: {
47654
+ ...resolved.directives.regionMetric !== void 0 && {
47655
+ metric: resolved.directives.regionMetric
47656
+ },
47657
+ min: rampMin,
47658
+ max: rampMax,
47659
+ hue: rampHue,
47660
+ base: rampBase
47661
+ }
47662
+ }
47663
+ };
47664
+ }
47665
+ }
47561
47666
  const TITLE_GAP2 = 16;
47562
47667
  let topPad = FIT_PAD;
47563
47668
  if (resolved.title && resolved.pois.length > 0) {
47564
47669
  const bannerBottom = (resolved.subtitle ? TITLE_Y + TITLE_FONT_SIZE : TITLE_Y) + TITLE_FONT_SIZE / 2;
47565
47670
  topPad = Math.max(FIT_PAD, bannerBottom + TITLE_GAP2);
47566
47671
  }
47672
+ const legendBand = mapLegendBand(legend, {
47673
+ width,
47674
+ mode: opts.legendMode ?? "preview",
47675
+ hasTitle: Boolean(resolved.title),
47676
+ hasSubtitle: Boolean(resolved.subtitle)
47677
+ });
47678
+ if (legendBand > topPad) topPad = legendBand;
47567
47679
  const fitBox = [
47568
47680
  [FIT_PAD, topPad],
47569
47681
  [
@@ -47581,7 +47693,7 @@ function layoutMap(resolved, data, size, opts) {
47581
47693
  const by0 = cb[0][1];
47582
47694
  const cw = cb[1][0] - bx0;
47583
47695
  const ch = cb[1][1] - by0;
47584
- const topReserve = resolved.title && resolved.pois.length > 0 ? topPad : 0;
47696
+ const topReserve = resolved.title && resolved.pois.length > 0 || legendBand > 0 ? topPad : 0;
47585
47697
  const ox = 0;
47586
47698
  const oy = topReserve;
47587
47699
  const sx = cw > 0 ? width / cw : 1;
@@ -48645,30 +48757,6 @@ function layoutMap(resolved, data, size, opts) {
48645
48757
  });
48646
48758
  labels.push(...contextLabels);
48647
48759
  }
48648
- let legend = null;
48649
- if (!resolved.directives.noLegend) {
48650
- const tagGroups = resolved.tagGroups.map((g) => ({
48651
- name: g.name,
48652
- entries: g.entries.map((e) => ({ value: e.value, color: e.color }))
48653
- }));
48654
- if (tagGroups.length > 0 || hasRamp) {
48655
- legend = {
48656
- tagGroups,
48657
- activeGroup,
48658
- ...hasRamp && {
48659
- ramp: {
48660
- ...resolved.directives.regionMetric !== void 0 && {
48661
- metric: resolved.directives.regionMetric
48662
- },
48663
- min: rampMin,
48664
- max: rampMax,
48665
- hue: rampHue,
48666
- base: rampBase
48667
- }
48668
- }
48669
- };
48670
- }
48671
- }
48672
48760
  return {
48673
48761
  width,
48674
48762
  height,
@@ -48704,6 +48792,7 @@ var init_layout15 = __esm({
48704
48792
  init_label_layout();
48705
48793
  init_legend_constants();
48706
48794
  init_title_constants();
48795
+ init_legend_band();
48707
48796
  init_context_labels();
48708
48797
  FIT_PAD = 24;
48709
48798
  RAMP_FLOOR2 = 15;
@@ -48904,6 +48993,9 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
48904
48993
  // stretch-distorting. The in-app preview pane passes no exportDims → unset →
48905
48994
  // keeps the global stretch-fill.
48906
48995
  preferContain: exportDims?.preferContain ?? false,
48996
+ // Reserve the legend band for the mode actually drawn below (export shows
48997
+ // only the active group; preview keeps the inactive pills).
48998
+ legendMode: exportDims ? "export" : "preview",
48907
48999
  ...activeGroupOverride !== void 0 && {
48908
49000
  activeGroup: activeGroupOverride
48909
49001
  }
@@ -49195,30 +49287,12 @@ function renderMap(container, resolved, data, palette, isDark, onClickItem, expo
49195
49287
  if (layout.legend) {
49196
49288
  const legendY = (layout.title ? TITLE_Y + TITLE_FONT_SIZE : 0) + (layout.subtitle ? TITLE_FONT_SIZE : 0) + 8;
49197
49289
  const legendG = svg.append("g").attr("class", "dgmo-map-legend").attr("transform", `translate(0, ${legendY})`);
49198
- const ramp = layout.legend.ramp;
49199
- const scoreGroup = ramp ? {
49200
- name: ramp.metric?.trim() || "Value",
49201
- entries: [],
49202
- gradient: {
49203
- min: ramp.min,
49204
- max: ramp.max,
49205
- hue: ramp.hue,
49206
- base: ramp.base
49207
- }
49208
- } : null;
49209
- const tagGroups = layout.legend.tagGroups.filter((g) => g.entries.length > 0).map((g) => ({ name: g.name, entries: [...g.entries] }));
49210
- const groups = [...scoreGroup ? [scoreGroup] : [], ...tagGroups];
49290
+ const groups = mapLegendGroups(layout.legend);
49211
49291
  if (groups.length > 0) {
49212
- const config = {
49292
+ const config = mapLegendConfig(
49213
49293
  groups,
49214
- position: { placement: "top-center", titleRelation: "below-title" },
49215
- mode: exportDims ? "export" : "preview",
49216
- showEmptyGroups: false,
49217
- // Keep inactive siblings visible as pills so the user can click to flip
49218
- // the active colouring dimension (preview only — export shows just the
49219
- // active group).
49220
- showInactivePills: true
49221
- };
49294
+ exportDims ? "export" : "preview"
49295
+ );
49222
49296
  const state = { activeGroup: layout.legend.activeGroup };
49223
49297
  renderLegendD3(legendG, config, state, palette, isDark, void 0, width);
49224
49298
  }
@@ -49262,6 +49336,7 @@ var init_renderer16 = __esm({
49262
49336
  init_title_constants();
49263
49337
  init_color_utils();
49264
49338
  init_legend_d3();
49339
+ init_legend_band();
49265
49340
  init_layout15();
49266
49341
  LABEL_FONT = 11;
49267
49342
  }
@@ -49283,9 +49358,10 @@ function mapContentAspect(resolved, data, ref = REF) {
49283
49358
  const aspect = w / h;
49284
49359
  return Number.isFinite(aspect) && aspect > 0 ? aspect : FALLBACK_ASPECT;
49285
49360
  }
49286
- function mapExportDimensions(resolved, data, baseWidth = 1200) {
49287
- const raw = mapContentAspect(resolved, data);
49288
- const clamped = Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49361
+ function mapExportDimensions(resolved, data, baseWidth = 1200, aspectOverride) {
49362
+ const useOverride = aspectOverride !== void 0 && Number.isFinite(aspectOverride) && aspectOverride > 0;
49363
+ const raw = useOverride ? aspectOverride : mapContentAspect(resolved, data);
49364
+ const clamped = useOverride ? raw : Math.max(ASPECT_MIN, Math.min(ASPECT_MAX, raw));
49289
49365
  const width = baseWidth;
49290
49366
  let height = Math.round(width / clamped);
49291
49367
  let chromeReserve = 0;
@@ -49298,7 +49374,7 @@ function mapExportDimensions(resolved, data, baseWidth = 1200) {
49298
49374
  height = Math.round(chromeReserve + MIN_MAP_BAND);
49299
49375
  floored = true;
49300
49376
  }
49301
- const preferContain = clamped !== raw || floored;
49377
+ const preferContain = useOverride ? floored : clamped !== raw || floored;
49302
49378
  return { width, height, preferContain };
49303
49379
  }
49304
49380
  var FIT_PAD2, TITLE_GAP, ASPECT_MAX, ASPECT_MIN, MIN_MAP_BAND, FALLBACK_ASPECT, REF;
@@ -55008,7 +55084,6 @@ function renderTimelineTagLegendOverlay(container, parsed, palette, isDark, setu
55008
55084
  function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, setup, hovers, onClickItem, _exportDims, _swimlaneTagGroup, _activeTagGroup, _onTagStateChange, _viewMode) {
55009
55085
  const {
55010
55086
  width,
55011
- height,
55012
55087
  tooltip,
55013
55088
  solid,
55014
55089
  textColor,
@@ -55057,8 +55132,7 @@ function renderTimelineHorizontalTimeSort(container, parsed, palette, isDark, se
55057
55132
  const markerLabelY = markerReserve ? -(topScaleH + MARKER_ROW_H / 2) : 0;
55058
55133
  const eraLabelY = eraReserve ? -(topScaleH + markerReserve + ERA_ROW_H / 2) : 0;
55059
55134
  const innerWidth = width - margin.left - margin.right;
55060
- const availInnerHeight = height - margin.top - margin.bottom;
55061
- const rowH = Math.min(ctx.structural(28), availInnerHeight / sorted.length);
55135
+ const rowH = ctx.structural(28);
55062
55136
  const innerHeight = rowH * sorted.length;
55063
55137
  const usedHeight = margin.top + innerHeight + margin.bottom;
55064
55138
  const xScale = d3Scale2.scaleLinear().domain([minDate - datePadding, maxDate + datePadding]).range([0, innerWidth]);
@@ -57583,7 +57657,12 @@ async function renderForExport(content, theme, palette, viewState, options) {
57583
57657
  }
57584
57658
  }
57585
57659
  const mapResolved = resolveMap2(mapParsed, mapData);
57586
- const dims2 = mapExportDimensions2(mapResolved, mapData, EXPORT_WIDTH);
57660
+ const dims2 = mapExportDimensions2(
57661
+ mapResolved,
57662
+ mapData,
57663
+ EXPORT_WIDTH,
57664
+ options?.mapAspect
57665
+ );
57587
57666
  const container2 = createExportContainer(dims2.width, dims2.height);
57588
57667
  renderMapForExport2(
57589
57668
  container2,
@@ -58727,6 +58806,134 @@ function extractInfraCounts(content) {
58727
58806
  return { nodes: parsed.nodes.length };
58728
58807
  }
58729
58808
 
58809
+ // src/utils/svg-embed.ts
58810
+ function normalizeSvgForEmbed(input) {
58811
+ let svg = input;
58812
+ const rootMatch = svg.match(/<svg[^>]*>/);
58813
+ const rootTag = rootMatch?.[0] ?? "";
58814
+ if (rootTag && !rootTag.includes("viewBox")) {
58815
+ const wh = rootTag.match(/width="(\d+)"[^>]*height="(\d+)"/);
58816
+ if (wh) {
58817
+ svg = svg.replace(/<svg/, `<svg viewBox="0 0 ${wh[1]} ${wh[2]}"`);
58818
+ }
58819
+ }
58820
+ const tight = computeBBox(svg);
58821
+ if (tight && tight.width > 0 && tight.height > 0) {
58822
+ const pad2 = 16;
58823
+ const vb = `${tight.x - pad2} ${tight.y - pad2} ${tight.width + pad2 * 2} ${tight.height + pad2 * 2}`;
58824
+ svg = svg.replace(/(<svg[^>]*?)viewBox="[^"]*"/, `$1viewBox="${vb}"`);
58825
+ }
58826
+ svg = svg.replace(/(<svg[^>]*?) width="[^"]*"/g, "$1");
58827
+ svg = svg.replace(/(<svg[^>]*?) height="[^"]*"/g, "$1");
58828
+ svg = svg.replace(/(<svg[^>]*?style="[^"]*?)background:[^;"]*;?\s*/g, "$1");
58829
+ svg = svg.replace(/<svg\s{2,}/g, "<svg ");
58830
+ return svg;
58831
+ }
58832
+ function getEmbedSvgViewBox(svg) {
58833
+ const tight = computeBBox(svg);
58834
+ if (!tight || tight.width <= 0 || tight.height <= 0) return null;
58835
+ const pad2 = 16;
58836
+ return {
58837
+ x: tight.x - pad2,
58838
+ y: tight.y - pad2,
58839
+ width: tight.width + pad2 * 2,
58840
+ height: tight.height + pad2 * 2
58841
+ };
58842
+ }
58843
+ function computeBBox(svg) {
58844
+ const xs = [];
58845
+ const ys = [];
58846
+ function push(x, y) {
58847
+ if (Number.isFinite(x) && Number.isFinite(y)) {
58848
+ xs.push(x);
58849
+ ys.push(y);
58850
+ }
58851
+ }
58852
+ function attr(tag, name) {
58853
+ const m = tag.match(new RegExp(`\\b${name}="([^"]*)"`));
58854
+ if (!m) return null;
58855
+ const n = parseFloat(m[1]);
58856
+ return Number.isFinite(n) ? n : null;
58857
+ }
58858
+ for (const m of svg.matchAll(/<rect\b[^>]*?\/?>/g)) {
58859
+ const tag = m[0];
58860
+ const x = attr(tag, "x");
58861
+ const y = attr(tag, "y");
58862
+ const w = attr(tag, "width");
58863
+ const h = attr(tag, "height");
58864
+ if (x !== null && y !== null && w !== null && h !== null) {
58865
+ push(x, y);
58866
+ push(x + w, y + h);
58867
+ }
58868
+ }
58869
+ for (const m of svg.matchAll(/<line\b[^>]*?\/?>/g)) {
58870
+ const tag = m[0];
58871
+ const x1 = attr(tag, "x1");
58872
+ const y1 = attr(tag, "y1");
58873
+ const x2 = attr(tag, "x2");
58874
+ const y2 = attr(tag, "y2");
58875
+ if (x1 !== null && y1 !== null && x2 !== null && y2 !== null) {
58876
+ push(x1, y1);
58877
+ push(x2, y2);
58878
+ }
58879
+ }
58880
+ for (const m of svg.matchAll(/<circle\b[^>]*?\/?>/g)) {
58881
+ const tag = m[0];
58882
+ const cx = attr(tag, "cx");
58883
+ const cy = attr(tag, "cy");
58884
+ const r = attr(tag, "r");
58885
+ if (cx !== null && cy !== null && r !== null) {
58886
+ push(cx - r, cy - r);
58887
+ push(cx + r, cy + r);
58888
+ }
58889
+ }
58890
+ for (const m of svg.matchAll(/<ellipse\b[^>]*?\/?>/g)) {
58891
+ const tag = m[0];
58892
+ const cx = attr(tag, "cx");
58893
+ const cy = attr(tag, "cy");
58894
+ const rx = attr(tag, "rx");
58895
+ const ry = attr(tag, "ry");
58896
+ if (cx !== null && cy !== null && rx !== null && ry !== null) {
58897
+ push(cx - rx, cy - ry);
58898
+ push(cx + rx, cy + ry);
58899
+ }
58900
+ }
58901
+ for (const m of svg.matchAll(/<text\b([^>]*?)>([\s\S]*?)<\/text>/g)) {
58902
+ const tag = `<text${m[1]}>`;
58903
+ const text = m[2].replace(/<[^>]+>/g, "");
58904
+ const x = attr(tag, "x");
58905
+ const y = attr(tag, "y");
58906
+ if (x !== null && y !== null) {
58907
+ const w = text.length * 7;
58908
+ push(x - w / 2, y - 14);
58909
+ push(x + w / 2, y + 4);
58910
+ }
58911
+ }
58912
+ for (const m of svg.matchAll(/<path\b[^>]*?\bd="([^"]+)"/g)) {
58913
+ const d = m[1];
58914
+ const nums = d.match(/-?\d+(?:\.\d+)?/g);
58915
+ if (!nums) continue;
58916
+ for (let i = 0; i + 1 < nums.length; i += 2) {
58917
+ push(parseFloat(nums[i]), parseFloat(nums[i + 1]));
58918
+ }
58919
+ }
58920
+ for (const m of svg.matchAll(
58921
+ /<(?:polygon|polyline)\b[^>]*?\bpoints="([^"]+)"/g
58922
+ )) {
58923
+ const nums = m[1].match(/-?\d+(?:\.\d+)?/g);
58924
+ if (!nums) continue;
58925
+ for (let i = 0; i + 1 < nums.length; i += 2) {
58926
+ push(parseFloat(nums[i]), parseFloat(nums[i + 1]));
58927
+ }
58928
+ }
58929
+ if (xs.length === 0 || ys.length === 0) return null;
58930
+ const minX = Math.min(...xs);
58931
+ const maxX = Math.max(...xs);
58932
+ const minY = Math.min(...ys);
58933
+ const maxY = Math.max(...ys);
58934
+ return { x: minX, y: minY, width: maxX - minX, height: maxY - minY };
58935
+ }
58936
+
58730
58937
  // src/map/completion.ts
58731
58938
  var fold2 = (s) => s.normalize("NFD").replace(/\p{Diacritic}/gu, "").toLowerCase().trim();
58732
58939
  var groupThousands = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
@@ -58850,8 +59057,10 @@ export {
58850
59057
  decodeDiagramUrl2 as decodeDiagramUrl,
58851
59058
  encodeDiagramUrl2 as encodeDiagramUrl,
58852
59059
  formatDgmoError,
59060
+ getEmbedSvgViewBox,
58853
59061
  getMinDimensions,
58854
59062
  getPalette,
59063
+ normalizeSvgForEmbed,
58855
59064
  palettes,
58856
59065
  render2 as render,
58857
59066
  themes,