@diagrammo/dgmo 0.86.0 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/dist/advanced.d.ts +6 -18
  2. package/dist/advanced.js +40 -37
  3. package/dist/block.js +6 -17
  4. package/dist/{chunk-FAV4RDVT.js → chunk-2A4DQAXG.js} +1 -1
  5. package/dist/{chunk-JIPUBFAM.js → chunk-5KGM44TG.js} +2 -2
  6. package/dist/{chunk-VEE777MD.js → chunk-5LD7SHXO.js} +3 -3
  7. package/dist/{chunk-QJ2JOLFN.js → chunk-6I5UN4FD.js} +2 -2
  8. package/dist/{chunk-ETBXIKK6.js → chunk-ABP5BUD6.js} +9 -430
  9. package/dist/{chunk-PKXKWHDW.js → chunk-AUS7V54P.js} +1 -1
  10. package/dist/{chunk-BF7JAFJI.js → chunk-B7CU2HW5.js} +2 -2
  11. package/dist/{chunk-X2VEJ5NT.js → chunk-BMUWF77V.js} +2 -2
  12. package/dist/{chunk-BP4BT5TH.js → chunk-CLQIXD2C.js} +1 -1
  13. package/dist/{chunk-QJFT5MAH.js → chunk-DSG6GBUQ.js} +2 -2
  14. package/dist/{chunk-EWSESGC2.js → chunk-EJS3K44P.js} +117 -78
  15. package/dist/{chunk-5JO443MB.js → chunk-HAH5X345.js} +1 -1
  16. package/dist/{chunk-HBCAY3KR.js → chunk-HTNLLT34.js} +1 -1
  17. package/dist/{chunk-HUMQIRJK.js → chunk-HTPZDZKQ.js} +2 -2
  18. package/dist/{chunk-VPVWG2GH.js → chunk-J7Q4VPBJ.js} +1 -1
  19. package/dist/{chunk-QPE646M2.js → chunk-JLETTWUJ.js} +30 -10
  20. package/dist/{chunk-3CQS3CIO.js → chunk-LL6D3MES.js} +1 -1
  21. package/dist/{chunk-ERYHC4KJ.js → chunk-LSQGJN5H.js} +1 -1
  22. package/dist/{chunk-AGO3PFGS.js → chunk-MVCPO6LI.js} +15 -10
  23. package/dist/{chunk-E6S2K6O2.js → chunk-N7XRBWXA.js} +2 -2
  24. package/dist/{chunk-WMIKC5XO.js → chunk-NPWVOFB3.js} +1 -1
  25. package/dist/{chunk-MYHPRQHR.js → chunk-O6MMQQLF.js} +3 -3
  26. package/dist/{chunk-GYORDNOM.js → chunk-P5V63L35.js} +1 -1
  27. package/dist/{chunk-BHZE36F7.js → chunk-PUVTFUNH.js} +1 -1
  28. package/dist/{chunk-TW6WMZFW.js → chunk-QT3KDDXK.js} +156 -7
  29. package/dist/{chunk-ZGHV4QLK.js → chunk-RJNZHFNG.js} +1 -1
  30. package/dist/{chunk-FWNJDD4U.js → chunk-S7W75MIM.js} +3 -20
  31. package/dist/{chunk-4YA5XW43.js → chunk-VW52W5F6.js} +981 -107
  32. package/dist/chunk-WXPBP4IT.js +438 -0
  33. package/dist/{chunk-C6FZZFYD.js → chunk-WZ23JJ7U.js} +1 -1
  34. package/dist/{chunk-LIYWJ6DV.js → chunk-XIFK423U.js} +108 -13
  35. package/dist/{chunk-4ELXLATB.js → chunk-XUDGZJTJ.js} +2 -2
  36. package/dist/{chunk-H6AFNCN3.js → chunk-XVSJYAK7.js} +1 -1
  37. package/dist/{chunk-LHBMQJC3.js → chunk-XWYR5T52.js} +2 -2
  38. package/dist/{chunk-QNKHJ5CK.js → chunk-YK4N6TBN.js} +4 -2
  39. package/dist/{chunk-XFMQSFS4.js → chunk-YMG7JDEY.js} +16 -6
  40. package/dist/chunk-Z6DXWULM.js +23 -0
  41. package/dist/{chunk-PBEH52A2.js → chunk-ZNNVI5SR.js} +1 -1
  42. package/dist/{flowchart-renderer-H72L7BEC.js → flowchart-renderer-THC6Y7EH.js} +7 -6
  43. package/dist/index.d.ts +68 -1
  44. package/dist/index.js +76 -7
  45. package/dist/{layout-QIPCNUSB.js → layout-BOVTLAOT.js} +7 -6
  46. package/dist/{layout-6JARW64V.js → layout-EHTYSMV4.js} +6 -5
  47. package/dist/{layout-YELNBVPC.js → layout-P6DTD4RT.js} +6 -5
  48. package/dist/{layout-T6TAB5OI.js → layout-RANVNAKI.js} +3 -3
  49. package/dist/{layout-FMKSENGL.js → layout-XUW444O5.js} +5 -4
  50. package/dist/{layout-search-AW5VBDCV.js → layout-search-VKSBUCDM.js} +57 -28
  51. package/dist/{renderer-AI2MD52D.js → renderer-222OTVQV.js} +3 -2
  52. package/dist/{renderer-UUWR7NGF.js → renderer-2KVQGGTT.js} +4 -3
  53. package/dist/{renderer-E5EKGIX2.js → renderer-2OOPQ4CH.js} +4 -3
  54. package/dist/{renderer-CZJ4AMYV.js → renderer-3FF4QVGW.js} +3 -2
  55. package/dist/{renderer-R6VJDZFO.js → renderer-4BHOELCE.js} +4 -3
  56. package/dist/{renderer-425QUIYO.js → renderer-4BIMRO4X.js} +4 -3
  57. package/dist/{renderer-5JVVJBAS.js → renderer-A4LMOBZL.js} +8 -7
  58. package/dist/{renderer-3AOOLHJU.js → renderer-A6ACVK4X.js} +3 -2
  59. package/dist/{renderer-ML2CLU25.js → renderer-DCFQDBGJ.js} +4 -3
  60. package/dist/{renderer-RH4U3UVM.js → renderer-LODEVN3V.js} +3 -2
  61. package/dist/{renderer-E7WF75GY.js → renderer-NHPKNNBD.js} +5 -4
  62. package/dist/{renderer-JEEGPLMM.js → renderer-R34GJL7U.js} +8 -7
  63. package/dist/{renderer-2BWRW23Z.js → renderer-RENDISO5.js} +1 -1
  64. package/dist/{renderer-NX4YKWCJ.js → renderer-RNCYODNF.js} +1 -1
  65. package/dist/{renderer-VKAJ52UO.js → renderer-SDUSBCJI.js} +3 -2
  66. package/dist/{renderer-RLSR7N6I.js → renderer-TPOZOAXR.js} +5 -4
  67. package/dist/{renderer-RUGMKHX5.js → renderer-WH2HWE5Z.js} +3 -2
  68. package/dist/{renderer-V6LLASGN.js → renderer-X5A5VIRD.js} +5 -4
  69. package/dist/{renderer-ADX4DWRJ.js → renderer-XUPLAO4B.js} +4 -2
  70. package/dist/{renderer-EWJS2RAZ.js → renderer-YDAVCRTP.js} +3 -2
  71. package/dist/{state-renderer-DDVJ43HR.js → state-renderer-5IQ4OJUI.js} +7 -6
  72. package/gallery/fixtures/journey-map.dgmo +28 -0
  73. package/gallery/fixtures/mindmap.dgmo +31 -0
  74. package/gallery/fixtures/pert.dgmo +27 -0
  75. package/gallery/fixtures/wireframe.dgmo +30 -0
  76. package/package.json +2 -2
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  wrapDescriptionLines
3
- } from "./chunk-WMIKC5XO.js";
3
+ } from "./chunk-NPWVOFB3.js";
4
4
  import {
5
5
  renderInlineText
6
- } from "./chunk-FWNJDD4U.js";
6
+ } from "./chunk-S7W75MIM.js";
7
7
  import {
8
8
  ScaleContext
9
9
  } from "./chunk-24YPR5EH.js";
@@ -842,7 +842,7 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
842
842
  }
843
843
  msgToLastStep.set(step.messageIndex, si);
844
844
  });
845
- const findAssociatedLastStep = (note) => {
845
+ const closestPrecedingMsgIndex = (note) => {
846
846
  let closestMsgIndex = -1;
847
847
  let closestLine = -1;
848
848
  for (let mi = 0; mi < messages.length; mi++) {
@@ -852,9 +852,15 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
852
852
  closestMsgIndex = mi;
853
853
  }
854
854
  }
855
- if (closestMsgIndex >= 0 && hiddenMsgIndices.has(closestMsgIndex)) {
856
- return -1;
857
- }
855
+ return closestMsgIndex;
856
+ };
857
+ const isNoteHiddenByCollapse = (note) => {
858
+ const closestMsgIndex = closestPrecedingMsgIndex(note);
859
+ return closestMsgIndex >= 0 && hiddenMsgIndices.has(closestMsgIndex);
860
+ };
861
+ const findAssociatedLastStep = (note) => {
862
+ if (isNoteHiddenByCollapse(note)) return -1;
863
+ const closestMsgIndex = closestPrecedingMsgIndex(note);
858
864
  if (closestMsgIndex < 0) return -1;
859
865
  return msgToLastStep.get(closestMsgIndex) ?? -1;
860
866
  };
@@ -942,6 +948,10 @@ function renderSequenceDiagram(container, parsed, palette, isDark, _onNavigateTo
942
948
  for (let i = 0; i < els.length; i++) {
943
949
  const el = els[i];
944
950
  if (isSequenceNote(el)) {
951
+ if (isNoteHiddenByCollapse(el)) {
952
+ while (i + 1 < els.length && isSequenceNote(els[i + 1])) i++;
953
+ continue;
954
+ }
945
955
  const firstOffset = noteOffsetBelow(el);
946
956
  let totalExtent = firstOffset;
947
957
  let j = i;
@@ -0,0 +1,23 @@
1
+ // src/utils/safe-href.ts
2
+ var ALLOWED_SCHEMES = ["http", "https", "mailto"];
3
+ var SCHEME_RE = /^([a-z][a-z0-9+.-]*):/i;
4
+ var LEADING_TRIM_RE = /^[\x00-\x20]+/;
5
+ var URL_STRIP_RE = /[\x09\x0A\x0D]/g;
6
+ function safeHref(url) {
7
+ if (typeof url !== "string") return null;
8
+ const trimmed = url.replace(URL_STRIP_RE, "").replace(LEADING_TRIM_RE, "");
9
+ if (trimmed.length === 0) return null;
10
+ const match = SCHEME_RE.exec(trimmed);
11
+ if (!match) {
12
+ return url;
13
+ }
14
+ const scheme = match[1].toLowerCase();
15
+ if (ALLOWED_SCHEMES.includes(scheme)) {
16
+ return url;
17
+ }
18
+ return null;
19
+ }
20
+
21
+ export {
22
+ safeHref
23
+ };
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-6EK55ART.js";
5
5
  import {
6
6
  noteBoxSize
7
- } from "./chunk-ZGHV4QLK.js";
7
+ } from "./chunk-RJNZHFNG.js";
8
8
  import {
9
9
  resolveNotes
10
10
  } from "./chunk-O753JUEZ.js";
@@ -1,16 +1,17 @@
1
1
  import {
2
2
  renderFlowchart,
3
3
  renderFlowchartForExport
4
- } from "./chunk-4ELXLATB.js";
4
+ } from "./chunk-XUDGZJTJ.js";
5
5
  import "./chunk-26EVL454.js";
6
- import "./chunk-PBEH52A2.js";
7
- import "./chunk-5JO443MB.js";
6
+ import "./chunk-ZNNVI5SR.js";
7
+ import "./chunk-HAH5X345.js";
8
8
  import "./chunk-XBJJP2HN.js";
9
9
  import "./chunk-6EK55ART.js";
10
- import "./chunk-ZGHV4QLK.js";
10
+ import "./chunk-RJNZHFNG.js";
11
11
  import "./chunk-5DH3KYFH.js";
12
- import "./chunk-WMIKC5XO.js";
13
- import "./chunk-FWNJDD4U.js";
12
+ import "./chunk-NPWVOFB3.js";
13
+ import "./chunk-S7W75MIM.js";
14
+ import "./chunk-Z6DXWULM.js";
14
15
  import "./chunk-EPDBWATV.js";
15
16
  import "./chunk-24YPR5EH.js";
16
17
  import "./chunk-XCKNFFKM.js";
package/dist/index.d.ts CHANGED
@@ -102,6 +102,73 @@ declare function attachDataChartInteractions(svg: SVGSVGElement, opts?: DataChar
102
102
  declare const D3_DATA_CHART_TYPES: Set<string>;
103
103
  declare function supportsD3DataChart(type: string): boolean;
104
104
 
105
+ /**
106
+ * The supported sanitizing boundary for renderer output that is about to land
107
+ * in a live document.
108
+ *
109
+ * Renderer output is trusted only as far as the renderer that produced it, and
110
+ * every host — the desktop app, the web editor, Obsidian, the script-tag
111
+ * drop-ins, this library's own `mountD3DataChart` — puts that output into the
112
+ * DOM. This module is what they call first. It lives here, beside `safe-href`,
113
+ * rather than inside `src/auto/shared.ts` where it started: that module is the
114
+ * browser-embed bundle's private toolbox, reachable only from the two IIFE
115
+ * script-tag builds, so a host importing `@diagrammo/dgmo` could not call the
116
+ * sanitizer at all however much it wanted to.
117
+ *
118
+ * 🔴 **Parse into a DETACHED holder, take the `<svg>`, sanitize, insert.** All
119
+ * four steps, in that order:
120
+ *
121
+ * ```ts
122
+ * const holder = document.createElement('div');
123
+ * holder.innerHTML = svg; // inert: nothing here is connected yet
124
+ * const svgEl = holder.querySelector('svg');
125
+ * if (svgEl) {
126
+ * sanitizeSvgInPlace(svgEl);
127
+ * container.replaceChildren(svgEl);
128
+ * }
129
+ * ```
130
+ *
131
+ * Sanitizing *after* assigning into a live container is too late: connecting
132
+ * the subtree is what creates a nested browsing context for an `<iframe>` and
133
+ * runs a custom element's `connectedCallback`, both synchronously inside the
134
+ * assignment. And taking only the `<svg>` is half the safety — moving every
135
+ * child of the holder across would connect whatever a hostile document put
136
+ * *beside* the diagram. `auto/index.ts` and `element/index.ts` have always done
137
+ * both; `mount.ts` learned them on diagrammo/diagrammo#884.
138
+ *
139
+ * 🔴 **What this does NOT do**, so a caller is not misled about the contract:
140
+ *
141
+ * - It does not parse CSS. An `@import` or a `url()` inside a `<style>`
142
+ * element passes through untouched.
143
+ * - It removes the elements listed in `REMOVED_TAGS` and nothing else. That
144
+ * set is the script-execution and remote-content surface as of
145
+ * diagrammo/diagrammo#884; an element neither it nor the attribute scrub
146
+ * below names survives, so a caller sanitizing *arbitrary* HTML rather than
147
+ * dgmo renderer output needs its own allowlist on top.
148
+ * - It does not stop a resource load that an attribute alone starts —
149
+ * `<img src>` fetches whether or not the node is connected. What protects
150
+ * that case is the `on*` strip, not the detached holder.
151
+ * - It is no substitute for escaping where a renderer interpolates author
152
+ * text (see `src/embed/escape.ts`).
153
+ *
154
+ * Browser-only — it needs a live DOM. There is nothing to sanitize on the
155
+ * rasterising path, which never builds a document.
156
+ */
157
+ /**
158
+ * Strip script-execution surface from a freshly parsed SVG tree before it
159
+ * lands in the live DOM. This is the safety net that lets us build SVG from
160
+ * markup without trusting renderer output to be fully sanitized.
161
+ *
162
+ * Removes every element in `REMOVED_TAGS`, any SMIL element animating an
163
+ * `href`, any `on*` event-handler attribute, and any `href`/`xlink:href`
164
+ * failing the `safeHref` allowlist.
165
+ *
166
+ * Mutates `root` and returns nothing. Call it on a detached holder — see the
167
+ * module comment above for why the order matters, and for what this does not
168
+ * cover.
169
+ */
170
+ declare function sanitizeSvgInPlace(root: Element): void;
171
+
105
172
  /**
106
173
  * The full diagnostic catalog, sorted by code. Every coded diagnostic
107
174
  * dgmo can emit — its severity, owning chart type, canonical message,
@@ -206,4 +273,4 @@ interface DecodedDiagramUrl {
206
273
  */
207
274
  declare function decodeDiagramUrl(url: string): DecodedDiagramUrl | null;
208
275
 
209
- export { CompactViewState, D3_DATA_CHART_TYPES, type DataChartInteractionOpts, type DecodedDiagramUrl, DgmoError, DiagnosticSpec, type EncodeDiagramUrlOptions, MapDataSource, type MountD3Opts, type MountedD3Chart, type NormalizeSvgForEmbedOptions, PaletteConfig, type RenderOptions, type RenderResult, Theme, attachDataChartInteractions, decodeDiagramUrl, defaultEmbedBackground, encodeDiagramUrl, getDiagnosticSpec, getEmbedSvgViewBox, listDiagnosticCodes, mountD3DataChart, normalizeSvgForEmbed, render, supportsD3DataChart };
276
+ export { CompactViewState, D3_DATA_CHART_TYPES, type DataChartInteractionOpts, type DecodedDiagramUrl, DgmoError, DiagnosticSpec, type EncodeDiagramUrlOptions, MapDataSource, type MountD3Opts, type MountedD3Chart, type NormalizeSvgForEmbedOptions, PaletteConfig, type RenderOptions, type RenderResult, Theme, attachDataChartInteractions, decodeDiagramUrl, defaultEmbedBackground, encodeDiagramUrl, getDiagnosticSpec, getEmbedSvgViewBox, listDiagnosticCodes, mountD3DataChart, normalizeSvgForEmbed, render, sanitizeSvgInPlace, supportsD3DataChart };
package/dist/index.js CHANGED
@@ -9,9 +9,13 @@ import {
9
9
  searchMapLocations,
10
10
  themes
11
11
  } from "./chunk-SQAG3UXH.js";
12
+ import {
13
+ safeHref
14
+ } from "./chunk-Z6DXWULM.js";
12
15
  import {
13
16
  render
14
- } from "./chunk-4YA5XW43.js";
17
+ } from "./chunk-VW52W5F6.js";
18
+ import "./chunk-ABP5BUD6.js";
15
19
  import {
16
20
  renderErrorCard
17
21
  } from "./chunk-D4M25W6R.js";
@@ -34,7 +38,7 @@ import "./chunk-KMU65FYM.js";
34
38
  import "./chunk-EPDBWATV.js";
35
39
  import "./chunk-7UDAIP6W.js";
36
40
  import "./chunk-2CW5ZTNH.js";
37
- import "./chunk-QPE646M2.js";
41
+ import "./chunk-JLETTWUJ.js";
38
42
  import "./chunk-YRYKTYMQ.js";
39
43
  import "./chunk-24YPR5EH.js";
40
44
  import {
@@ -124,6 +128,50 @@ import {
124
128
  } from "./chunk-BYU54C3S.js";
125
129
  import "./chunk-5WRI5ZAA.js";
126
130
 
131
+ // src/utils/sanitize-svg.ts
132
+ var XLINK_NS = "http://www.w3.org/1999/xlink";
133
+ var REMOVED_TAGS = "script, foreignObject, iframe, object, embed, frame, base, link, meta";
134
+ var ANIMATION_TAGS = new Set(
135
+ ["animate", "set", "animateTransform", "animateMotion"].map(
136
+ (t) => t.toLowerCase()
137
+ )
138
+ );
139
+ var ANIMATED_URL_ATTRS = /* @__PURE__ */ new Set(["href", "xlink:href"]);
140
+ function isUrlAnimation(node) {
141
+ if (!ANIMATION_TAGS.has(node.localName.toLowerCase())) return false;
142
+ const target = (node.getAttribute("attributeName") ?? "").trim().toLowerCase();
143
+ return ANIMATED_URL_ATTRS.has(target);
144
+ }
145
+ function scrubElement(node) {
146
+ for (const attr of Array.from(node.attributes)) {
147
+ if (attr.name.toLowerCase().startsWith("on")) {
148
+ node.removeAttribute(attr.name);
149
+ }
150
+ }
151
+ if (node.hasAttribute("href")) {
152
+ const safe = safeHref(node.getAttribute("href"));
153
+ if (safe === null) node.removeAttribute("href");
154
+ }
155
+ if (node.hasAttributeNS(XLINK_NS, "href")) {
156
+ const v = node.getAttributeNS(XLINK_NS, "href");
157
+ if (safeHref(v) === null) {
158
+ node.removeAttributeNS(XLINK_NS, "href");
159
+ }
160
+ }
161
+ }
162
+ function sanitizeSvgInPlace(root) {
163
+ const dangerous = root.querySelectorAll(REMOVED_TAGS);
164
+ dangerous.forEach((n) => n.remove());
165
+ const all = [root, ...Array.from(root.querySelectorAll("*"))];
166
+ for (const node of all) {
167
+ if (isUrlAnimation(node)) {
168
+ node.remove();
169
+ continue;
170
+ }
171
+ scrubElement(node);
172
+ }
173
+ }
174
+
127
175
  // src/charts-d3/interactions.ts
128
176
  var NS = "http://www.w3.org/2000/svg";
129
177
  var STYLE_ID = "dgmo-chart-interactions-style";
@@ -134,7 +182,8 @@ function ensureStyle(svg, muted) {
134
182
  style.id = STYLE_ID;
135
183
  style.textContent = `
136
184
  .dgmo-series,.dgmo-datum,.dgmo-tick,.dgmo-ptlabel{transition:opacity .12s ease}
137
- .dgmo-series.dgmo-dim,.dgmo-datum.dgmo-dim,.dgmo-ptlabel.dgmo-dim{opacity:.18}
185
+ [data-legend-entry]{transition:opacity .12s ease}
186
+ .dgmo-series.dgmo-dim,.dgmo-datum.dgmo-dim,.dgmo-ptlabel.dgmo-dim,[data-legend-entry].dgmo-dim{opacity:.18}
138
187
  .dgmo-tick.dgmo-faded{opacity:.22}
139
188
  .dgmo-datum{cursor:pointer}
140
189
  .dgmo-axis-label{cursor:pointer}
@@ -177,6 +226,9 @@ function attachDataChartInteractions(svg, opts = {}) {
177
226
  svg.querySelectorAll(".dgmo-series")
178
227
  );
179
228
  const datums = Array.from(svg.querySelectorAll(".dgmo-datum"));
229
+ const legendEntries = Array.from(
230
+ svg.querySelectorAll(".chart-legend [data-legend-entry]")
231
+ );
180
232
  const circles = Array.from(
181
233
  svg.querySelectorAll(".dgmo-pt")
182
234
  );
@@ -247,6 +299,15 @@ function attachDataChartInteractions(svg, opts = {}) {
247
299
  const ptLabels = Array.from(
248
300
  svg.querySelectorAll(".dgmo-ptlabel")
249
301
  );
302
+ const dimLegendExcept = (el) => {
303
+ const name = el?.getAttribute("data-series-name") ?? el?.getAttribute("data-emph-key") ?? null;
304
+ const paired = name != null && legendEntries.some((e) => e.getAttribute("data-series-name") === name);
305
+ for (const e of legendEntries)
306
+ e.classList.toggle(
307
+ "dgmo-dim",
308
+ paired && e.getAttribute("data-series-name") !== name
309
+ );
310
+ };
250
311
  const dimDatumsExcept = (el) => {
251
312
  for (const d of datums) d.classList.toggle("dgmo-dim", d !== el);
252
313
  const line = el?.getAttribute("data-line-number");
@@ -255,6 +316,7 @@ function attachDataChartInteractions(svg, opts = {}) {
255
316
  "dgmo-dim",
256
317
  line == null || l.getAttribute("data-line-number") !== line
257
318
  );
319
+ dimLegendExcept(el);
258
320
  };
259
321
  const dimByKey = (el) => {
260
322
  const key = el.getAttribute("data-emph-key");
@@ -263,11 +325,13 @@ function attachDataChartInteractions(svg, opts = {}) {
263
325
  if (key) d.classList.toggle("dgmo-dim", dk !== key);
264
326
  else d.classList.toggle("dgmo-dim", d !== el);
265
327
  }
328
+ dimLegendExcept(el);
266
329
  };
267
330
  const clearDim = () => {
268
331
  for (const d of datums) d.classList.remove("dgmo-dim");
269
332
  for (const g of seriesGroups) g.classList.remove("dgmo-dim");
270
333
  for (const l of ptLabels) l.classList.remove("dgmo-dim");
334
+ for (const e of legendEntries) e.classList.remove("dgmo-dim");
271
335
  reapplyAxisPin();
272
336
  };
273
337
  let axisPin = null;
@@ -513,9 +577,6 @@ function attachDataChartInteractions(svg, opts = {}) {
513
577
  svg.addEventListener("mousemove", onSvgMove);
514
578
  svg.addEventListener("mouseleave", onLeave);
515
579
  svg.addEventListener("click", onClick);
516
- const legendEntries = Array.from(
517
- svg.querySelectorAll(".chart-legend [data-legend-entry]")
518
- );
519
580
  const emphasizeSeries = (name) => {
520
581
  for (const g of seriesGroups) {
521
582
  const sn = g.getAttribute("data-series-name");
@@ -637,7 +698,14 @@ function mountD3DataChart(container, content, opts = {}) {
637
698
  interaction.destroy();
638
699
  interaction = null;
639
700
  }
640
- container.innerHTML = svg;
701
+ const holder = document.createElement("div");
702
+ holder.innerHTML = svg;
703
+ const parsed = holder.querySelector("svg");
704
+ container.innerHTML = "";
705
+ if (parsed) {
706
+ sanitizeSvgInPlace(parsed);
707
+ container.append(parsed);
708
+ }
641
709
  const el = container.querySelector("svg");
642
710
  if (el) {
643
711
  const pal = paletteOf(current);
@@ -750,6 +818,7 @@ export {
750
818
  palettes,
751
819
  render2 as render,
752
820
  resolvePaletteOrFallback,
821
+ sanitizeSvgInPlace,
753
822
  searchMapLocations,
754
823
  supportsD3DataChart,
755
824
  themes,
@@ -1,14 +1,15 @@
1
1
  import {
2
2
  computeNodeSize,
3
3
  layoutBoxesAndLines
4
- } from "./chunk-LIYWJ6DV.js";
5
- import "./chunk-TW6WMZFW.js";
6
- import "./chunk-HBCAY3KR.js";
4
+ } from "./chunk-XIFK423U.js";
5
+ import "./chunk-QT3KDDXK.js";
6
+ import "./chunk-HTNLLT34.js";
7
7
  import "./chunk-6EK55ART.js";
8
- import "./chunk-ZGHV4QLK.js";
8
+ import "./chunk-RJNZHFNG.js";
9
9
  import "./chunk-5DH3KYFH.js";
10
- import "./chunk-WMIKC5XO.js";
11
- import "./chunk-FWNJDD4U.js";
10
+ import "./chunk-NPWVOFB3.js";
11
+ import "./chunk-S7W75MIM.js";
12
+ import "./chunk-Z6DXWULM.js";
12
13
  import "./chunk-ICLB27PZ.js";
13
14
  import "./chunk-KAO7JL4I.js";
14
15
  import "./chunk-O753JUEZ.js";
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  layoutERDiagram
3
- } from "./chunk-FAV4RDVT.js";
4
- import "./chunk-HBCAY3KR.js";
3
+ } from "./chunk-2A4DQAXG.js";
4
+ import "./chunk-HTNLLT34.js";
5
5
  import "./chunk-6EK55ART.js";
6
- import "./chunk-ZGHV4QLK.js";
6
+ import "./chunk-RJNZHFNG.js";
7
7
  import "./chunk-5DH3KYFH.js";
8
- import "./chunk-WMIKC5XO.js";
9
- import "./chunk-FWNJDD4U.js";
8
+ import "./chunk-NPWVOFB3.js";
9
+ import "./chunk-S7W75MIM.js";
10
+ import "./chunk-Z6DXWULM.js";
10
11
  import "./chunk-ICLB27PZ.js";
11
12
  import "./chunk-KAO7JL4I.js";
12
13
  import "./chunk-O753JUEZ.js";
@@ -1,12 +1,13 @@
1
1
  import {
2
2
  layoutClassDiagram
3
- } from "./chunk-BP4BT5TH.js";
4
- import "./chunk-HBCAY3KR.js";
3
+ } from "./chunk-CLQIXD2C.js";
4
+ import "./chunk-HTNLLT34.js";
5
5
  import "./chunk-6EK55ART.js";
6
- import "./chunk-ZGHV4QLK.js";
6
+ import "./chunk-RJNZHFNG.js";
7
7
  import "./chunk-5DH3KYFH.js";
8
- import "./chunk-WMIKC5XO.js";
9
- import "./chunk-FWNJDD4U.js";
8
+ import "./chunk-NPWVOFB3.js";
9
+ import "./chunk-S7W75MIM.js";
10
+ import "./chunk-Z6DXWULM.js";
10
11
  import "./chunk-ICLB27PZ.js";
11
12
  import "./chunk-KAO7JL4I.js";
12
13
  import "./chunk-O753JUEZ.js";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  layoutSwimlane
3
- } from "./chunk-PKXKWHDW.js";
4
- import "./chunk-EWSESGC2.js";
5
- import "./chunk-TW6WMZFW.js";
3
+ } from "./chunk-AUS7V54P.js";
4
+ import "./chunk-EJS3K44P.js";
5
+ import "./chunk-QT3KDDXK.js";
6
6
  import "./chunk-KAO7JL4I.js";
7
7
  import "./chunk-5WRI5ZAA.js";
8
8
  export {
@@ -1,11 +1,12 @@
1
1
  import {
2
2
  layoutGraph
3
- } from "./chunk-PBEH52A2.js";
3
+ } from "./chunk-ZNNVI5SR.js";
4
4
  import "./chunk-6EK55ART.js";
5
- import "./chunk-ZGHV4QLK.js";
5
+ import "./chunk-RJNZHFNG.js";
6
6
  import "./chunk-5DH3KYFH.js";
7
- import "./chunk-WMIKC5XO.js";
8
- import "./chunk-FWNJDD4U.js";
7
+ import "./chunk-NPWVOFB3.js";
8
+ import "./chunk-S7W75MIM.js";
9
+ import "./chunk-Z6DXWULM.js";
9
10
  import "./chunk-ICLB27PZ.js";
10
11
  import "./chunk-KAO7JL4I.js";
11
12
  import "./chunk-NXMESY4J.js";
@@ -1,22 +1,23 @@
1
1
  import {
2
2
  computeNodeSize
3
- } from "./chunk-LIYWJ6DV.js";
3
+ } from "./chunk-XIFK423U.js";
4
4
  import {
5
5
  clipEndpointsToNodes,
6
6
  groupedTierCandidates
7
- } from "./chunk-EWSESGC2.js";
7
+ } from "./chunk-EJS3K44P.js";
8
8
  import {
9
9
  EDGE_LABEL_FONT_SIZE,
10
10
  NODE_HEIGHT,
11
11
  NODE_WIDTH,
12
12
  measureEdgeLabel
13
- } from "./chunk-TW6WMZFW.js";
14
- import "./chunk-HBCAY3KR.js";
13
+ } from "./chunk-QT3KDDXK.js";
14
+ import "./chunk-HTNLLT34.js";
15
15
  import "./chunk-6EK55ART.js";
16
- import "./chunk-ZGHV4QLK.js";
16
+ import "./chunk-RJNZHFNG.js";
17
17
  import "./chunk-5DH3KYFH.js";
18
- import "./chunk-WMIKC5XO.js";
19
- import "./chunk-FWNJDD4U.js";
18
+ import "./chunk-NPWVOFB3.js";
19
+ import "./chunk-S7W75MIM.js";
20
+ import "./chunk-Z6DXWULM.js";
20
21
  import "./chunk-ICLB27PZ.js";
21
22
  import "./chunk-KAO7JL4I.js";
22
23
  import "./chunk-O753JUEZ.js";
@@ -813,7 +814,8 @@ var ESCALATE_THRESHOLD = 4;
813
814
  var ESCALATE_MAX_N = 45;
814
815
  var ESCALATE_SEEDS = 18;
815
816
  var ESCALATE_REFINE = 10;
816
- var DEFAULT_SEARCH_BUDGET_MS = 5e3;
817
+ var SEARCH_WORK_CAP = 36e3;
818
+ var MIN_SEARCH_CANDIDATES = 6;
817
819
  function rng2(s) {
818
820
  return () => {
819
821
  s |= 0;
@@ -910,7 +912,7 @@ function countSplineCrossings(layout, floor = Infinity) {
910
912
  if (g.collapsed) center.set("__group_" + g.label, { x: g.x, y: g.y });
911
913
  const polys = flatPolys(layout);
912
914
  const edges = layout.edges;
913
- const R = 34;
915
+ const R2 = 34 * 34;
914
916
  let total = 0;
915
917
  for (let a = 0; a < polys.length; a++)
916
918
  for (let b = a + 1; b < polys.length; b++) {
@@ -928,6 +930,8 @@ function countSplineCrossings(layout, floor = Infinity) {
928
930
  for (let i = 1; i < ap.length; i++) {
929
931
  const a0 = ap[i - 1], a1 = ap[i];
930
932
  const axMin = a0.x < a1.x ? a0.x : a1.x, axMax = a0.x > a1.x ? a0.x : a1.x, ayMin = a0.y < a1.y ? a0.y : a1.y, ayMax = a0.y > a1.y ? a0.y : a1.y;
933
+ if (axMax < B.x0 || axMin > B.x1 || ayMax < B.y0 || ayMin > B.y1)
934
+ continue;
931
935
  for (let j = 1; j < bp.length; j++) {
932
936
  const b0 = bp[j - 1], b1 = bp[j];
933
937
  if (axMax < (b0.x < b1.x ? b0.x : b1.x)) continue;
@@ -936,9 +940,9 @@ function countSplineCrossings(layout, floor = Infinity) {
936
940
  if ((b0.y > b1.y ? b0.y : b1.y) < ayMin) continue;
937
941
  const p = segPoint(a0, a1, b0, b1);
938
942
  if (!p) continue;
939
- if (sh0 && Math.hypot(p.x - sh0.x, p.y - sh0.y) < R) continue;
940
- if (sh1 && Math.hypot(p.x - sh1.x, p.y - sh1.y) < R) continue;
941
- if (!hits.some((h) => Math.hypot(h.x - p.x, h.y - p.y) < 6))
943
+ if (sh0 && (p.x - sh0.x) ** 2 + (p.y - sh0.y) ** 2 < R2) continue;
944
+ if (sh1 && (p.x - sh1.x) ** 2 + (p.y - sh1.y) ** 2 < R2) continue;
945
+ if (!hits.some((h) => (h.x - p.x) ** 2 + (h.y - p.y) ** 2 < 36))
942
946
  hits.push(p);
943
947
  }
944
948
  }
@@ -947,19 +951,25 @@ function countSplineCrossings(layout, floor = Infinity) {
947
951
  }
948
952
  return total;
949
953
  }
950
- function pointSegDist(p, a, b) {
954
+ function pointSegDist2(p, a, b) {
951
955
  const dx = b.x - a.x, dy = b.y - a.y;
952
956
  const len2 = dx * dx + dy * dy;
953
- if (len2 < 1e-9) return Math.hypot(p.x - a.x, p.y - a.y);
954
- let t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2;
955
- t = Math.max(0, Math.min(1, t));
956
- return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy));
957
+ let qx = p.x - a.x, qy = p.y - a.y;
958
+ if (len2 >= 1e-9) {
959
+ let t = (qx * dx + qy * dy) / len2;
960
+ t = t < 0 ? 0 : t > 1 ? 1 : t;
961
+ qx -= t * dx;
962
+ qy -= t * dy;
963
+ }
964
+ return qx * qx + qy * qy;
957
965
  }
958
- function distToPoly(p, poly) {
959
- let m = Infinity;
960
- for (let i = 1; i < poly.length; i++)
961
- m = Math.min(m, pointSegDist(p, poly[i - 1], poly[i]));
962
- return m;
966
+ function nearPoly(p, poly, dist) {
967
+ if (p.x < poly.x0 - dist || p.x > poly.x1 + dist || p.y < poly.y0 - dist || p.y > poly.y1 + dist)
968
+ return false;
969
+ const d2 = dist * dist, pts = poly.pts;
970
+ for (let i = 1; i < pts.length; i++)
971
+ if (pointSegDist2(p, pts[i - 1], pts[i]) < d2) return true;
972
+ return false;
963
973
  }
964
974
  function pointRectDist(p, r) {
965
975
  const dx = Math.max(r.x - r.w / 2 - p.x, 0, p.x - (r.x + r.w / 2));
@@ -1010,7 +1020,7 @@ function detectEdgeOverlaps(layout, opts) {
1010
1020
  };
1011
1021
  for (const p of A.pts) {
1012
1022
  const nearShared = shr0 !== void 0 && pointRectDist(p, shr0) < nodeClear || shr1 !== void 0 && pointRectDist(p, shr1) < nodeClear;
1013
- const covered = !nearShared && distToPoly(p, B.pts) < dist;
1023
+ const covered = !nearShared && nearPoly(p, B, dist);
1014
1024
  if (covered) {
1015
1025
  if (run.length)
1016
1026
  runLen += Math.hypot(
@@ -1434,8 +1444,6 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
1434
1444
  const YIELD_EVERY_MS = 30;
1435
1445
  const searchStart = performance.now();
1436
1446
  let lastYield = searchStart;
1437
- const budgetMs = opts?.budgetMs ?? DEFAULT_SEARCH_BUDGET_MS;
1438
- const overBudget = () => budgetMs > 0 && performance.now() - searchStart > budgetMs;
1439
1447
  const collapsedGroupLabels = /* @__PURE__ */ new Set();
1440
1448
  if (collapseInfo) {
1441
1449
  const missing = /* @__PURE__ */ new Set();
@@ -1694,10 +1702,16 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
1694
1702
  };
1695
1703
  }
1696
1704
  const n = parsed.nodes.length;
1697
- const seedCount = opts?.seeds ?? (n <= 12 ? 80 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
1705
+ const seedCount = opts?.seeds ?? (n <= 12 ? 20 : n <= 22 ? 40 : n <= 35 ? 22 : 10);
1698
1706
  const REFINE_K = opts?.refineK ?? 6;
1699
1707
  const lambda = opts?.lambda ?? DEFAULT_LAMBDA;
1700
1708
  const prev = opts?.previousPositions;
1709
+ const searchSize = Math.max(1, n + parsed.edges.length);
1710
+ const maxCandidates = Math.max(
1711
+ MIN_SEARCH_CANDIDATES,
1712
+ Math.floor(SEARCH_WORK_CAP / Math.pow(searchSize, 1.5))
1713
+ );
1714
+ let attempts = 0;
1701
1715
  const configs = [];
1702
1716
  if (opts?.configs) {
1703
1717
  configs.push(...opts.configs);
@@ -1737,7 +1751,15 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
1737
1751
  };
1738
1752
  const pool = [];
1739
1753
  const cfgOf = /* @__PURE__ */ new Map();
1754
+ const perfect = [];
1755
+ let seedsChecked = false;
1740
1756
  for (const cfg of configs) {
1757
+ if (!opts?.configs && cfg.seed !== void 0 && !seedsChecked) {
1758
+ seedsChecked = true;
1759
+ for (const lay of pool) if (badness(lay, 0) === 0) perfect.push(lay);
1760
+ if (perfect.length) break;
1761
+ }
1762
+ attempts++;
1741
1763
  try {
1742
1764
  const lay = place(cfg);
1743
1765
  pool.push(lay);
@@ -1745,7 +1767,7 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
1745
1767
  } catch {
1746
1768
  }
1747
1769
  await step("Optimizing layout");
1748
- if (pool.length && overBudget()) break;
1770
+ if (pool.length && attempts >= maxCandidates) break;
1749
1771
  }
1750
1772
  if (!pool.length) {
1751
1773
  const plain = {
@@ -1813,6 +1835,12 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
1813
1835
  if (cfg) topConfigs.push(cfg);
1814
1836
  await step("Refining layout");
1815
1837
  }
1838
+ for (const lay of perfect) {
1839
+ if (pool.indexOf(lay) < refineK) continue;
1840
+ consider(lay);
1841
+ const cfg = cfgOf.get(lay);
1842
+ if (cfg) topConfigs.push(cfg);
1843
+ }
1816
1844
  if (!opts?.configs && bestBad >= ESCALATE_THRESHOLD && n <= ESCALATE_MAX_N) {
1817
1845
  const extra = [];
1818
1846
  for (let s = seedCount; s < seedCount + ESCALATE_SEEDS; s++) {
@@ -1822,13 +1850,14 @@ async function layoutBoxesAndLinesSearch(parsed, collapseInfo, opts) {
1822
1850
  ranksep: 60,
1823
1851
  seed: s
1824
1852
  };
1853
+ attempts++;
1825
1854
  try {
1826
1855
  const lay = place(cfg);
1827
1856
  extra.push(lay);
1828
1857
  cfgOf.set(lay, cfg);
1829
1858
  } catch {
1830
1859
  }
1831
- if (overBudget()) break;
1860
+ if (attempts >= maxCandidates) break;
1832
1861
  }
1833
1862
  const extraKey = /* @__PURE__ */ new Map();
1834
1863
  for (const lay of extra)
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  renderGoal,
3
3
  renderGoalForExport
4
- } from "./chunk-H6AFNCN3.js";
4
+ } from "./chunk-XVSJYAK7.js";
5
5
  import "./chunk-LNO3ASX5.js";
6
- import "./chunk-FWNJDD4U.js";
6
+ import "./chunk-S7W75MIM.js";
7
+ import "./chunk-Z6DXWULM.js";
7
8
  import "./chunk-VLC6WNRD.js";
8
9
  import "./chunk-ICLB27PZ.js";
9
10
  import "./chunk-KAO7JL4I.js";