@diagrammo/dgmo 0.86.0 → 0.87.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/advanced.d.ts +0 -18
- package/dist/advanced.js +36 -35
- package/dist/block.js +5 -17
- package/dist/{chunk-FAV4RDVT.js → chunk-2A4DQAXG.js} +1 -1
- package/dist/{chunk-JIPUBFAM.js → chunk-5KGM44TG.js} +2 -2
- package/dist/{chunk-QJ2JOLFN.js → chunk-6I5UN4FD.js} +2 -2
- package/dist/{chunk-PKXKWHDW.js → chunk-AUS7V54P.js} +1 -1
- package/dist/{chunk-BF7JAFJI.js → chunk-B7CU2HW5.js} +2 -2
- package/dist/{chunk-X2VEJ5NT.js → chunk-BMUWF77V.js} +2 -2
- package/dist/{chunk-BP4BT5TH.js → chunk-CLQIXD2C.js} +1 -1
- package/dist/{chunk-QJFT5MAH.js → chunk-DSG6GBUQ.js} +2 -2
- package/dist/{chunk-EWSESGC2.js → chunk-EJS3K44P.js} +117 -78
- package/dist/{chunk-VEE777MD.js → chunk-FBKPSU3A.js} +3 -3
- package/dist/{chunk-5JO443MB.js → chunk-HAH5X345.js} +1 -1
- package/dist/{chunk-HBCAY3KR.js → chunk-HTNLLT34.js} +1 -1
- package/dist/{chunk-HUMQIRJK.js → chunk-HTPZDZKQ.js} +2 -2
- package/dist/{chunk-VPVWG2GH.js → chunk-J7Q4VPBJ.js} +1 -1
- package/dist/{chunk-3CQS3CIO.js → chunk-LL6D3MES.js} +1 -1
- package/dist/{chunk-QPE646M2.js → chunk-LPXC4I4R.js} +18 -1
- package/dist/{chunk-ERYHC4KJ.js → chunk-LSQGJN5H.js} +1 -1
- package/dist/{chunk-E6S2K6O2.js → chunk-N7XRBWXA.js} +2 -2
- package/dist/{chunk-WMIKC5XO.js → chunk-NPWVOFB3.js} +1 -1
- package/dist/{chunk-MYHPRQHR.js → chunk-O6MMQQLF.js} +3 -3
- package/dist/{chunk-GYORDNOM.js → chunk-P5V63L35.js} +1 -1
- package/dist/{chunk-4YA5XW43.js → chunk-PJQZWJ5I.js} +50 -28
- package/dist/{chunk-BHZE36F7.js → chunk-PUVTFUNH.js} +1 -1
- package/dist/{chunk-ETBXIKK6.js → chunk-Q2U5635V.js} +1 -1
- package/dist/{chunk-TW6WMZFW.js → chunk-QT3KDDXK.js} +156 -7
- package/dist/{chunk-ZGHV4QLK.js → chunk-RJNZHFNG.js} +1 -1
- package/dist/{chunk-FWNJDD4U.js → chunk-S7W75MIM.js} +3 -20
- package/dist/{chunk-C6FZZFYD.js → chunk-WZ23JJ7U.js} +1 -1
- package/dist/{chunk-LIYWJ6DV.js → chunk-XIFK423U.js} +108 -13
- package/dist/{chunk-4ELXLATB.js → chunk-XUDGZJTJ.js} +2 -2
- package/dist/{chunk-H6AFNCN3.js → chunk-XVSJYAK7.js} +1 -1
- package/dist/{chunk-LHBMQJC3.js → chunk-XWYR5T52.js} +2 -2
- package/dist/{chunk-QNKHJ5CK.js → chunk-YK4N6TBN.js} +4 -2
- package/dist/{chunk-XFMQSFS4.js → chunk-YMG7JDEY.js} +16 -6
- package/dist/chunk-Z6DXWULM.js +23 -0
- package/dist/{chunk-PBEH52A2.js → chunk-ZNNVI5SR.js} +1 -1
- package/dist/{flowchart-renderer-H72L7BEC.js → flowchart-renderer-THC6Y7EH.js} +7 -6
- package/dist/index.d.ts +68 -1
- package/dist/index.js +75 -7
- package/dist/{layout-QIPCNUSB.js → layout-BOVTLAOT.js} +7 -6
- package/dist/{layout-6JARW64V.js → layout-EHTYSMV4.js} +6 -5
- package/dist/{layout-YELNBVPC.js → layout-P6DTD4RT.js} +6 -5
- package/dist/{layout-T6TAB5OI.js → layout-RANVNAKI.js} +3 -3
- package/dist/{layout-FMKSENGL.js → layout-XUW444O5.js} +5 -4
- package/dist/{layout-search-AW5VBDCV.js → layout-search-VKSBUCDM.js} +57 -28
- package/dist/{renderer-AI2MD52D.js → renderer-222OTVQV.js} +3 -2
- package/dist/{renderer-UUWR7NGF.js → renderer-2KVQGGTT.js} +4 -3
- package/dist/{renderer-E5EKGIX2.js → renderer-2OOPQ4CH.js} +4 -3
- package/dist/{renderer-RLSR7N6I.js → renderer-3CTUWCO7.js} +5 -4
- package/dist/{renderer-CZJ4AMYV.js → renderer-3FF4QVGW.js} +3 -2
- package/dist/{renderer-R6VJDZFO.js → renderer-4BHOELCE.js} +4 -3
- package/dist/{renderer-425QUIYO.js → renderer-4BIMRO4X.js} +4 -3
- package/dist/{renderer-5JVVJBAS.js → renderer-A4LMOBZL.js} +8 -7
- package/dist/{renderer-3AOOLHJU.js → renderer-A6ACVK4X.js} +3 -2
- package/dist/{renderer-ML2CLU25.js → renderer-DCFQDBGJ.js} +4 -3
- package/dist/{renderer-ADX4DWRJ.js → renderer-ELYPDPNF.js} +3 -2
- package/dist/{renderer-RH4U3UVM.js → renderer-LODEVN3V.js} +3 -2
- package/dist/{renderer-E7WF75GY.js → renderer-NHPKNNBD.js} +5 -4
- package/dist/{renderer-JEEGPLMM.js → renderer-R34GJL7U.js} +8 -7
- package/dist/{renderer-2BWRW23Z.js → renderer-RENDISO5.js} +1 -1
- package/dist/{renderer-VKAJ52UO.js → renderer-SDUSBCJI.js} +3 -2
- package/dist/{renderer-RUGMKHX5.js → renderer-WH2HWE5Z.js} +3 -2
- package/dist/{renderer-V6LLASGN.js → renderer-X5A5VIRD.js} +5 -4
- package/dist/{renderer-EWJS2RAZ.js → renderer-YDAVCRTP.js} +3 -2
- package/dist/{state-renderer-DDVJ43HR.js → state-renderer-5IQ4OJUI.js} +7 -6
- package/gallery/fixtures/journey-map.dgmo +28 -0
- package/gallery/fixtures/mindmap.dgmo +31 -0
- package/gallery/fixtures/pert.dgmo +27 -0
- package/gallery/fixtures/wireframe.dgmo +30 -0
- package/package.json +2 -2
|
@@ -1,26 +1,10 @@
|
|
|
1
|
+
import {
|
|
2
|
+
safeHref
|
|
3
|
+
} from "./chunk-Z6DXWULM.js";
|
|
1
4
|
import {
|
|
2
5
|
MONO_FAMILY
|
|
3
6
|
} from "./chunk-ICLB27PZ.js";
|
|
4
7
|
|
|
5
|
-
// src/utils/safe-href.ts
|
|
6
|
-
var ALLOWED_SCHEMES = ["http", "https", "mailto"];
|
|
7
|
-
var SCHEME_RE = /^([a-z][a-z0-9+.-]*):/i;
|
|
8
|
-
var LEADING_TRIM_RE = /^[\x00-\x20]+/;
|
|
9
|
-
function safeHref(url) {
|
|
10
|
-
if (typeof url !== "string") return null;
|
|
11
|
-
const trimmed = url.replace(LEADING_TRIM_RE, "");
|
|
12
|
-
if (trimmed.length === 0) return null;
|
|
13
|
-
const match = SCHEME_RE.exec(trimmed);
|
|
14
|
-
if (!match) {
|
|
15
|
-
return url;
|
|
16
|
-
}
|
|
17
|
-
const scheme = match[1].toLowerCase();
|
|
18
|
-
if (ALLOWED_SCHEMES.includes(scheme)) {
|
|
19
|
-
return url;
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
8
|
// src/utils/inline-markdown.ts
|
|
25
9
|
function parseInlineMarkdown(text) {
|
|
26
10
|
const spans = [];
|
|
@@ -88,7 +72,6 @@ function renderInlineText(textEl, text, palette, fontSize) {
|
|
|
88
72
|
}
|
|
89
73
|
|
|
90
74
|
export {
|
|
91
|
-
safeHref,
|
|
92
75
|
parseInlineMarkdown,
|
|
93
76
|
truncateBareUrl,
|
|
94
77
|
stripInlineMarkdown,
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
NODE_HEIGHT,
|
|
4
4
|
NODE_WIDTH,
|
|
5
5
|
placeEdgeLabels
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QT3KDDXK.js";
|
|
7
7
|
import {
|
|
8
8
|
buildPlacedNotes
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-HTNLLT34.js";
|
|
10
10
|
import {
|
|
11
11
|
noteCanvasShift
|
|
12
12
|
} from "./chunk-6EK55ART.js";
|
|
@@ -22,6 +22,9 @@ import {
|
|
|
22
22
|
var MARGIN = 40;
|
|
23
23
|
var MAX_PARALLEL_EDGES = 5;
|
|
24
24
|
var PARALLEL_SPACING = 22;
|
|
25
|
+
var DETOUR_TOLERANCE = 2 * PARALLEL_SPACING;
|
|
26
|
+
var MIN_PORT_GAP = 16;
|
|
27
|
+
var PORT_CORNER_CLEAR = 6;
|
|
25
28
|
var DESC_NODE_WIDTH = 140;
|
|
26
29
|
var DESC_FONT_SIZE = 10;
|
|
27
30
|
var DESC_LINE_HEIGHT = 1.4;
|
|
@@ -104,7 +107,7 @@ function computeNodeSize(node, reserveValueRow) {
|
|
|
104
107
|
return { width: w, height: Math.max(NODE_HEIGHT, totalHeight) };
|
|
105
108
|
}
|
|
106
109
|
async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
107
|
-
const { layoutBoxesAndLinesSearch } = await import("./layout-search-
|
|
110
|
+
const { layoutBoxesAndLinesSearch } = await import("./layout-search-VKSBUCDM.js");
|
|
108
111
|
const searchOpts = {
|
|
109
112
|
...layoutOptions?.hideDescriptions !== void 0 && {
|
|
110
113
|
hideDescriptions: layoutOptions.hideDescriptions
|
|
@@ -117,13 +120,6 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
|
117
120
|
},
|
|
118
121
|
...layoutOptions?.onProgress !== void 0 && {
|
|
119
122
|
onProgress: layoutOptions.onProgress
|
|
120
|
-
},
|
|
121
|
-
// Spread into the label-reserving relayout below too, via `...searchOpts`:
|
|
122
|
-
// that second search has its OWN clock, so a caller that disabled the
|
|
123
|
-
// deadline must have it disabled for both passes or the fixture that needs
|
|
124
|
-
// two passes is exactly the one still racing.
|
|
125
|
-
...layoutOptions?.budgetMs !== void 0 && {
|
|
126
|
-
budgetMs: layoutOptions.budgetMs
|
|
127
123
|
}
|
|
128
124
|
};
|
|
129
125
|
let topConfigs;
|
|
@@ -134,7 +130,7 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
|
134
130
|
}
|
|
135
131
|
});
|
|
136
132
|
const labelOpts = { groups: parsed.groups };
|
|
137
|
-
let chosen = applyParallelEdgeOffsets(searched);
|
|
133
|
+
let chosen = applyParallelEdgeOffsets(spreadCrowdedPorts(searched));
|
|
138
134
|
let placed = placeEdgeLabels(chosen, labelOpts);
|
|
139
135
|
if (placed.unresolved.length > 0) {
|
|
140
136
|
const relaid = await layoutBoxesAndLinesSearch(parsed, collapseInfo, {
|
|
@@ -145,7 +141,7 @@ async function layoutBoxesAndLines(parsed, collapseInfo, layoutOptions) {
|
|
|
145
141
|
// first search surfaced no dagre candidates.
|
|
146
142
|
...topConfigs !== void 0 && topConfigs.length > 0 && { configs: topConfigs }
|
|
147
143
|
});
|
|
148
|
-
const relaidChosen = applyParallelEdgeOffsets(relaid);
|
|
144
|
+
const relaidChosen = applyParallelEdgeOffsets(spreadCrowdedPorts(relaid));
|
|
149
145
|
const relaidPlaced = placeEdgeLabels(relaidChosen, labelOpts);
|
|
150
146
|
if (relaidPlaced.unresolved.length < placed.unresolved.length) {
|
|
151
147
|
placed = relaidPlaced;
|
|
@@ -242,6 +238,102 @@ function attachNotes(layout, parsed, collapsedNotes) {
|
|
|
242
238
|
height: bbMaxY + shiftY + MARGIN
|
|
243
239
|
};
|
|
244
240
|
}
|
|
241
|
+
function spreadCrowdedPorts(layout) {
|
|
242
|
+
const boxes = /* @__PURE__ */ new Map();
|
|
243
|
+
for (const n of layout.nodes)
|
|
244
|
+
boxes.set(n.label, { x: n.x, y: n.y, w: n.width, h: n.height });
|
|
245
|
+
for (const g of layout.groups)
|
|
246
|
+
if (g.collapsed)
|
|
247
|
+
boxes.set("__group_" + g.label, {
|
|
248
|
+
x: g.x,
|
|
249
|
+
y: g.y,
|
|
250
|
+
w: g.width,
|
|
251
|
+
h: g.height
|
|
252
|
+
});
|
|
253
|
+
const faces = /* @__PURE__ */ new Map();
|
|
254
|
+
const onSide = (box, p) => {
|
|
255
|
+
const E = 1;
|
|
256
|
+
const l = box.x - box.w / 2;
|
|
257
|
+
const r = box.x + box.w / 2;
|
|
258
|
+
const t = box.y - box.h / 2;
|
|
259
|
+
const b = box.y + box.h / 2;
|
|
260
|
+
if (p.x < l - E || p.x > r + E || p.y < t - E || p.y > b + E) return null;
|
|
261
|
+
if (Math.abs(p.y - t) <= E) return "top";
|
|
262
|
+
if (Math.abs(p.y - b) <= E) return "bottom";
|
|
263
|
+
if (Math.abs(p.x - l) <= E) return "left";
|
|
264
|
+
if (Math.abs(p.x - r) <= E) return "right";
|
|
265
|
+
return null;
|
|
266
|
+
};
|
|
267
|
+
layout.edges.forEach((e, i) => {
|
|
268
|
+
if (e.source === e.target || e.points.length < 2) return;
|
|
269
|
+
for (const last of [false, true]) {
|
|
270
|
+
const id = last ? e.target : e.source;
|
|
271
|
+
const box = boxes.get(id);
|
|
272
|
+
const p = last ? e.points[e.points.length - 1] : e.points[0];
|
|
273
|
+
if (!box) continue;
|
|
274
|
+
const side = onSide(box, p);
|
|
275
|
+
if (!side) continue;
|
|
276
|
+
const horiz = side === "top" || side === "bottom";
|
|
277
|
+
const key = `${id}\0${side}`;
|
|
278
|
+
let f = faces.get(key);
|
|
279
|
+
if (!f) faces.set(key, f = { box, horiz, ends: [] });
|
|
280
|
+
f.ends.push({ edge: i, last, along: horiz ? p.x : p.y });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
const moved = /* @__PURE__ */ new Map();
|
|
284
|
+
for (const { box, horiz, ends } of faces.values()) {
|
|
285
|
+
if (ends.length < 2) continue;
|
|
286
|
+
ends.sort((a, b) => a.along - b.along || a.edge - b.edge);
|
|
287
|
+
let crowded = false;
|
|
288
|
+
for (let k = 1; k < ends.length; k++)
|
|
289
|
+
if (ends[k].along - ends[k - 1].along < MIN_PORT_GAP) crowded = true;
|
|
290
|
+
if (!crowded) continue;
|
|
291
|
+
const half = (horiz ? box.w : box.h) / 2 - PORT_CORNER_CLEAR;
|
|
292
|
+
const ctr = horiz ? box.x : box.y;
|
|
293
|
+
if (half <= 0) continue;
|
|
294
|
+
const gap = Math.min(MIN_PORT_GAP, 2 * half / (ends.length - 1));
|
|
295
|
+
const mean = ends.reduce((acc, e) => acc + e.along, 0) / ends.length;
|
|
296
|
+
const span = gap * (ends.length - 1);
|
|
297
|
+
const first = Math.max(
|
|
298
|
+
ctr - half,
|
|
299
|
+
Math.min(ctr + half - span, mean - span / 2)
|
|
300
|
+
);
|
|
301
|
+
ends.forEach((e, k) => {
|
|
302
|
+
moved.set(`${e.edge}:${e.last}`, { along: first + k * gap, horiz });
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (moved.size === 0) return layout;
|
|
306
|
+
return {
|
|
307
|
+
...layout,
|
|
308
|
+
edges: layout.edges.map((e, i) => {
|
|
309
|
+
const a = moved.get(`${i}:false`);
|
|
310
|
+
const b = moved.get(`${i}:true`);
|
|
311
|
+
if (a === void 0 && b === void 0) return e;
|
|
312
|
+
const pts = e.points.map((p) => ({ x: p.x, y: p.y }));
|
|
313
|
+
const shift = (idx, to) => {
|
|
314
|
+
if (to.horiz) pts[idx].x = to.along;
|
|
315
|
+
else pts[idx].y = to.along;
|
|
316
|
+
};
|
|
317
|
+
if (a !== void 0) shift(0, a);
|
|
318
|
+
if (b !== void 0) shift(pts.length - 1, b);
|
|
319
|
+
return { ...e, points: pts };
|
|
320
|
+
})
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function routeDetour(points) {
|
|
324
|
+
const s = points[0];
|
|
325
|
+
const t = points[points.length - 1];
|
|
326
|
+
if (!s || !t) return 0;
|
|
327
|
+
const dx = t.x - s.x;
|
|
328
|
+
const dy = t.y - s.y;
|
|
329
|
+
const len = Math.hypot(dx, dy);
|
|
330
|
+
let worst = 0;
|
|
331
|
+
for (const p of points) {
|
|
332
|
+
const d = len < 1e-6 ? Math.hypot(p.x - s.x, p.y - s.y) : Math.abs((p.x - s.x) * dy - (p.y - s.y) * dx) / len;
|
|
333
|
+
if (d > worst) worst = d;
|
|
334
|
+
}
|
|
335
|
+
return worst;
|
|
336
|
+
}
|
|
245
337
|
function applyParallelEdgeOffsets(layout) {
|
|
246
338
|
const groups = /* @__PURE__ */ new Map();
|
|
247
339
|
layout.edges.forEach((e, i) => {
|
|
@@ -262,8 +354,11 @@ function applyParallelEdgeOffsets(layout) {
|
|
|
262
354
|
layout.edges.length
|
|
263
355
|
).fill(void 0);
|
|
264
356
|
for (const [key, idxs] of groups) {
|
|
265
|
-
const
|
|
357
|
+
const kept = idxs.slice(0, MAX_PARALLEL_EDGES);
|
|
266
358
|
for (const drop of idxs.slice(MAX_PARALLEL_EDGES)) count[drop] = 0;
|
|
359
|
+
const capped = kept.filter(
|
|
360
|
+
(idx) => routeDetour(layout.edges[idx].points) <= DETOUR_TOLERANCE
|
|
361
|
+
);
|
|
267
362
|
if (capped.length < 2) continue;
|
|
268
363
|
const sep = key.indexOf("\0");
|
|
269
364
|
const ca = centre.get(key.slice(0, sep));
|
|
@@ -3,14 +3,14 @@ import {
|
|
|
3
3
|
} from "./chunk-26EVL454.js";
|
|
4
4
|
import {
|
|
5
5
|
layoutGraph
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-ZNNVI5SR.js";
|
|
7
7
|
import {
|
|
8
8
|
NOTE_BADGE_RADIUS,
|
|
9
9
|
noteConnectorPoints,
|
|
10
10
|
renderNoteBadge,
|
|
11
11
|
renderNoteBox,
|
|
12
12
|
renderNoteConnector
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-HAH5X345.js";
|
|
14
14
|
import {
|
|
15
15
|
appendArrowheadMarkers
|
|
16
16
|
} from "./chunk-XBJJP2HN.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
layoutClassDiagram
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-CLQIXD2C.js";
|
|
4
4
|
import {
|
|
5
5
|
NOTE_BADGE_RADIUS,
|
|
6
6
|
noteConnectorPoints,
|
|
7
7
|
renderNoteBadge,
|
|
8
8
|
renderNoteBox,
|
|
9
9
|
renderNoteConnector
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-HAH5X345.js";
|
|
11
11
|
import {
|
|
12
12
|
ScaleContext
|
|
13
13
|
} from "./chunk-24YPR5EH.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
wrapDescriptionLines
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NPWVOFB3.js";
|
|
4
4
|
import {
|
|
5
5
|
renderInlineText
|
|
6
|
-
} from "./chunk-
|
|
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
|
|
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
|
-
|
|
856
|
-
|
|
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
|
+
};
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
2
|
renderFlowchart,
|
|
3
3
|
renderFlowchartForExport
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XUDGZJTJ.js";
|
|
5
5
|
import "./chunk-26EVL454.js";
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-
|
|
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-
|
|
10
|
+
import "./chunk-RJNZHFNG.js";
|
|
11
11
|
import "./chunk-5DH3KYFH.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
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,12 @@ 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-
|
|
17
|
+
} from "./chunk-PJQZWJ5I.js";
|
|
15
18
|
import {
|
|
16
19
|
renderErrorCard
|
|
17
20
|
} from "./chunk-D4M25W6R.js";
|
|
@@ -34,7 +37,7 @@ import "./chunk-KMU65FYM.js";
|
|
|
34
37
|
import "./chunk-EPDBWATV.js";
|
|
35
38
|
import "./chunk-7UDAIP6W.js";
|
|
36
39
|
import "./chunk-2CW5ZTNH.js";
|
|
37
|
-
import "./chunk-
|
|
40
|
+
import "./chunk-LPXC4I4R.js";
|
|
38
41
|
import "./chunk-YRYKTYMQ.js";
|
|
39
42
|
import "./chunk-24YPR5EH.js";
|
|
40
43
|
import {
|
|
@@ -124,6 +127,50 @@ import {
|
|
|
124
127
|
} from "./chunk-BYU54C3S.js";
|
|
125
128
|
import "./chunk-5WRI5ZAA.js";
|
|
126
129
|
|
|
130
|
+
// src/utils/sanitize-svg.ts
|
|
131
|
+
var XLINK_NS = "http://www.w3.org/1999/xlink";
|
|
132
|
+
var REMOVED_TAGS = "script, foreignObject, iframe, object, embed, frame, base, link, meta";
|
|
133
|
+
var ANIMATION_TAGS = new Set(
|
|
134
|
+
["animate", "set", "animateTransform", "animateMotion"].map(
|
|
135
|
+
(t) => t.toLowerCase()
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
var ANIMATED_URL_ATTRS = /* @__PURE__ */ new Set(["href", "xlink:href"]);
|
|
139
|
+
function isUrlAnimation(node) {
|
|
140
|
+
if (!ANIMATION_TAGS.has(node.localName.toLowerCase())) return false;
|
|
141
|
+
const target = (node.getAttribute("attributeName") ?? "").trim().toLowerCase();
|
|
142
|
+
return ANIMATED_URL_ATTRS.has(target);
|
|
143
|
+
}
|
|
144
|
+
function scrubElement(node) {
|
|
145
|
+
for (const attr of Array.from(node.attributes)) {
|
|
146
|
+
if (attr.name.toLowerCase().startsWith("on")) {
|
|
147
|
+
node.removeAttribute(attr.name);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
if (node.hasAttribute("href")) {
|
|
151
|
+
const safe = safeHref(node.getAttribute("href"));
|
|
152
|
+
if (safe === null) node.removeAttribute("href");
|
|
153
|
+
}
|
|
154
|
+
if (node.hasAttributeNS(XLINK_NS, "href")) {
|
|
155
|
+
const v = node.getAttributeNS(XLINK_NS, "href");
|
|
156
|
+
if (safeHref(v) === null) {
|
|
157
|
+
node.removeAttributeNS(XLINK_NS, "href");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function sanitizeSvgInPlace(root) {
|
|
162
|
+
const dangerous = root.querySelectorAll(REMOVED_TAGS);
|
|
163
|
+
dangerous.forEach((n) => n.remove());
|
|
164
|
+
const all = [root, ...Array.from(root.querySelectorAll("*"))];
|
|
165
|
+
for (const node of all) {
|
|
166
|
+
if (isUrlAnimation(node)) {
|
|
167
|
+
node.remove();
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
scrubElement(node);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
127
174
|
// src/charts-d3/interactions.ts
|
|
128
175
|
var NS = "http://www.w3.org/2000/svg";
|
|
129
176
|
var STYLE_ID = "dgmo-chart-interactions-style";
|
|
@@ -134,7 +181,8 @@ function ensureStyle(svg, muted) {
|
|
|
134
181
|
style.id = STYLE_ID;
|
|
135
182
|
style.textContent = `
|
|
136
183
|
.dgmo-series,.dgmo-datum,.dgmo-tick,.dgmo-ptlabel{transition:opacity .12s ease}
|
|
137
|
-
|
|
184
|
+
[data-legend-entry]{transition:opacity .12s ease}
|
|
185
|
+
.dgmo-series.dgmo-dim,.dgmo-datum.dgmo-dim,.dgmo-ptlabel.dgmo-dim,[data-legend-entry].dgmo-dim{opacity:.18}
|
|
138
186
|
.dgmo-tick.dgmo-faded{opacity:.22}
|
|
139
187
|
.dgmo-datum{cursor:pointer}
|
|
140
188
|
.dgmo-axis-label{cursor:pointer}
|
|
@@ -177,6 +225,9 @@ function attachDataChartInteractions(svg, opts = {}) {
|
|
|
177
225
|
svg.querySelectorAll(".dgmo-series")
|
|
178
226
|
);
|
|
179
227
|
const datums = Array.from(svg.querySelectorAll(".dgmo-datum"));
|
|
228
|
+
const legendEntries = Array.from(
|
|
229
|
+
svg.querySelectorAll(".chart-legend [data-legend-entry]")
|
|
230
|
+
);
|
|
180
231
|
const circles = Array.from(
|
|
181
232
|
svg.querySelectorAll(".dgmo-pt")
|
|
182
233
|
);
|
|
@@ -247,6 +298,15 @@ function attachDataChartInteractions(svg, opts = {}) {
|
|
|
247
298
|
const ptLabels = Array.from(
|
|
248
299
|
svg.querySelectorAll(".dgmo-ptlabel")
|
|
249
300
|
);
|
|
301
|
+
const dimLegendExcept = (el) => {
|
|
302
|
+
const name = el?.getAttribute("data-series-name") ?? el?.getAttribute("data-emph-key") ?? null;
|
|
303
|
+
const paired = name != null && legendEntries.some((e) => e.getAttribute("data-series-name") === name);
|
|
304
|
+
for (const e of legendEntries)
|
|
305
|
+
e.classList.toggle(
|
|
306
|
+
"dgmo-dim",
|
|
307
|
+
paired && e.getAttribute("data-series-name") !== name
|
|
308
|
+
);
|
|
309
|
+
};
|
|
250
310
|
const dimDatumsExcept = (el) => {
|
|
251
311
|
for (const d of datums) d.classList.toggle("dgmo-dim", d !== el);
|
|
252
312
|
const line = el?.getAttribute("data-line-number");
|
|
@@ -255,6 +315,7 @@ function attachDataChartInteractions(svg, opts = {}) {
|
|
|
255
315
|
"dgmo-dim",
|
|
256
316
|
line == null || l.getAttribute("data-line-number") !== line
|
|
257
317
|
);
|
|
318
|
+
dimLegendExcept(el);
|
|
258
319
|
};
|
|
259
320
|
const dimByKey = (el) => {
|
|
260
321
|
const key = el.getAttribute("data-emph-key");
|
|
@@ -263,11 +324,13 @@ function attachDataChartInteractions(svg, opts = {}) {
|
|
|
263
324
|
if (key) d.classList.toggle("dgmo-dim", dk !== key);
|
|
264
325
|
else d.classList.toggle("dgmo-dim", d !== el);
|
|
265
326
|
}
|
|
327
|
+
dimLegendExcept(el);
|
|
266
328
|
};
|
|
267
329
|
const clearDim = () => {
|
|
268
330
|
for (const d of datums) d.classList.remove("dgmo-dim");
|
|
269
331
|
for (const g of seriesGroups) g.classList.remove("dgmo-dim");
|
|
270
332
|
for (const l of ptLabels) l.classList.remove("dgmo-dim");
|
|
333
|
+
for (const e of legendEntries) e.classList.remove("dgmo-dim");
|
|
271
334
|
reapplyAxisPin();
|
|
272
335
|
};
|
|
273
336
|
let axisPin = null;
|
|
@@ -513,9 +576,6 @@ function attachDataChartInteractions(svg, opts = {}) {
|
|
|
513
576
|
svg.addEventListener("mousemove", onSvgMove);
|
|
514
577
|
svg.addEventListener("mouseleave", onLeave);
|
|
515
578
|
svg.addEventListener("click", onClick);
|
|
516
|
-
const legendEntries = Array.from(
|
|
517
|
-
svg.querySelectorAll(".chart-legend [data-legend-entry]")
|
|
518
|
-
);
|
|
519
579
|
const emphasizeSeries = (name) => {
|
|
520
580
|
for (const g of seriesGroups) {
|
|
521
581
|
const sn = g.getAttribute("data-series-name");
|
|
@@ -637,7 +697,14 @@ function mountD3DataChart(container, content, opts = {}) {
|
|
|
637
697
|
interaction.destroy();
|
|
638
698
|
interaction = null;
|
|
639
699
|
}
|
|
640
|
-
|
|
700
|
+
const holder = document.createElement("div");
|
|
701
|
+
holder.innerHTML = svg;
|
|
702
|
+
const parsed = holder.querySelector("svg");
|
|
703
|
+
container.innerHTML = "";
|
|
704
|
+
if (parsed) {
|
|
705
|
+
sanitizeSvgInPlace(parsed);
|
|
706
|
+
container.append(parsed);
|
|
707
|
+
}
|
|
641
708
|
const el = container.querySelector("svg");
|
|
642
709
|
if (el) {
|
|
643
710
|
const pal = paletteOf(current);
|
|
@@ -750,6 +817,7 @@ export {
|
|
|
750
817
|
palettes,
|
|
751
818
|
render2 as render,
|
|
752
819
|
resolvePaletteOrFallback,
|
|
820
|
+
sanitizeSvgInPlace,
|
|
753
821
|
searchMapLocations,
|
|
754
822
|
supportsD3DataChart,
|
|
755
823
|
themes,
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
computeNodeSize,
|
|
3
3
|
layoutBoxesAndLines
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
6
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-XIFK423U.js";
|
|
5
|
+
import "./chunk-QT3KDDXK.js";
|
|
6
|
+
import "./chunk-HTNLLT34.js";
|
|
7
7
|
import "./chunk-6EK55ART.js";
|
|
8
|
-
import "./chunk-
|
|
8
|
+
import "./chunk-RJNZHFNG.js";
|
|
9
9
|
import "./chunk-5DH3KYFH.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
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-
|
|
4
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-2A4DQAXG.js";
|
|
4
|
+
import "./chunk-HTNLLT34.js";
|
|
5
5
|
import "./chunk-6EK55ART.js";
|
|
6
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-RJNZHFNG.js";
|
|
7
7
|
import "./chunk-5DH3KYFH.js";
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
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";
|