libpetri 3.0.0 → 3.0.1
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/README.md +49 -1
- package/dist/chunk-6NH64RCU.js +1016 -0
- package/dist/chunk-6NH64RCU.js.map +1 -0
- package/dist/{chunk-5W6SVYPD.js → chunk-JZIEWVAV.js} +839 -44
- package/dist/chunk-JZIEWVAV.js.map +1 -0
- package/dist/debug/index.d.ts +2 -2
- package/dist/doclet/index.d.ts +12 -3
- package/dist/doclet/index.js +5 -1
- package/dist/doclet/index.js.map +1 -1
- package/dist/doclet/resources/petrinet-diagrams.css +21 -0
- package/dist/doclet/resources/petrinet-diagrams.js +3575 -3573
- package/dist/{render-ZGZEZ5RK.js → elk-place-YVNQFGXI.js} +3 -258
- package/dist/elk-place-YVNQFGXI.js.map +1 -0
- package/dist/{event-store-Df_sAVQ_.d.ts → event-store-BFX_yJ8I.d.ts} +1 -1
- package/dist/export/index.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/pan-zoom-Cp51IkDl.d.ts +33 -0
- package/dist/{petri-net-UQBBkvLl.d.ts → petri-net-WSScMyDL.d.ts} +20 -1
- package/dist/preprocess-FN3F75JR.js +193 -0
- package/dist/preprocess-FN3F75JR.js.map +1 -0
- package/dist/render-QOHGDWNE.js +78 -0
- package/dist/render-QOHGDWNE.js.map +1 -0
- package/dist/render-dom/index.d.ts +28 -29
- package/dist/render-dom/index.js +14 -21
- package/dist/render-dom/index.js.map +1 -1
- package/dist/verification/index.d.ts +269 -4
- package/dist/verification/index.js +7 -1
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.d.ts +24 -32
- package/dist/viewer/index.js +9 -1003
- package/dist/viewer/index.js.map +1 -1
- package/dist/viewer/viewer.css +21 -0
- package/dist/viewer/viewer.iife.js +3575 -3573
- package/package.json +2 -2
- package/dist/chunk-5W6SVYPD.js.map +0 -1
- package/dist/render-ZGZEZ5RK.js.map +0 -1
package/dist/viewer/index.js
CHANGED
|
@@ -1,1008 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
};
|
|
9
|
-
function attachPanzoom(svg, options, previous) {
|
|
10
|
-
if (previous) {
|
|
11
|
-
try {
|
|
12
|
-
previous.dispose();
|
|
13
|
-
} catch {
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
const opts = { ...DEFAULT_PANZOOM_OPTS, ...options };
|
|
17
|
-
return panzoom(svg, opts);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// src/viewer/cluster-overlay.ts
|
|
21
|
-
function discoverClusters(svg) {
|
|
22
|
-
const out = /* @__PURE__ */ new Map();
|
|
23
|
-
const clusterGroups = svg.querySelectorAll("g.cluster");
|
|
24
|
-
if (!clusterGroups.length) return out;
|
|
25
|
-
const allNodes = Array.from(svg.querySelectorAll("g.node"));
|
|
26
|
-
for (const g of Array.from(clusterGroups)) {
|
|
27
|
-
const titleEl = directChild(g, "title");
|
|
28
|
-
if (!titleEl) continue;
|
|
29
|
-
const raw = titleEl.textContent ?? "";
|
|
30
|
-
if (!raw.startsWith("cluster_")) continue;
|
|
31
|
-
const prefix = raw.slice("cluster_".length);
|
|
32
|
-
if (out.has(prefix)) continue;
|
|
33
|
-
const rect = clusterRect(g);
|
|
34
|
-
let nodeCount = 0;
|
|
35
|
-
for (const node of allNodes) {
|
|
36
|
-
if (node.getAttribute("data-instance")) continue;
|
|
37
|
-
const nodeTitle = directChild(node, "title");
|
|
38
|
-
if (!nodeTitle) continue;
|
|
39
|
-
const id = (nodeTitle.textContent ?? "").trim();
|
|
40
|
-
if (nodeBelongsToCluster(id, node, prefix, rect)) {
|
|
41
|
-
node.setAttribute("data-instance", prefix);
|
|
42
|
-
nodeCount++;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
out.set(prefix, {
|
|
46
|
-
prefix,
|
|
47
|
-
group: g,
|
|
48
|
-
nodeCount,
|
|
49
|
-
color: colorForPrefix(prefix)
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
return out;
|
|
53
|
-
}
|
|
54
|
-
function nodeBelongsToCluster(nodeId, node, prefix, rect) {
|
|
55
|
-
if (nodeBelongsToPrefix(nodeId, prefix)) return true;
|
|
56
|
-
if (rect) {
|
|
57
|
-
const c = nodeCenter(node);
|
|
58
|
-
if (c && c.x >= rect.minX && c.x <= rect.maxX && c.y >= rect.minY && c.y <= rect.maxY) {
|
|
59
|
-
return true;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
function nodeBelongsToPrefix(nodeId, prefix) {
|
|
65
|
-
return nodeId === prefix || nodeId.startsWith(`${prefix}/`) || nodeId.startsWith(`p_${prefix}_`) || nodeId.startsWith(`t_${prefix}_`) || nodeId.startsWith(`j_${prefix}_`);
|
|
66
|
-
}
|
|
67
|
-
function clusterRect(group) {
|
|
68
|
-
const border = directChild(group, "polygon") ?? directChild(group, "path");
|
|
69
|
-
if (!border) return null;
|
|
70
|
-
const raw = border.getAttribute("points") ?? border.getAttribute("d") ?? "";
|
|
71
|
-
return boundsOf(raw);
|
|
72
|
-
}
|
|
73
|
-
function nodeCenter(node) {
|
|
74
|
-
const ellipse = directChild(node, "ellipse");
|
|
75
|
-
if (ellipse) {
|
|
76
|
-
const x = parseFloat(ellipse.getAttribute("cx") ?? "");
|
|
77
|
-
const y = parseFloat(ellipse.getAttribute("cy") ?? "");
|
|
78
|
-
if (!Number.isNaN(x) && !Number.isNaN(y)) return { x, y };
|
|
79
|
-
}
|
|
80
|
-
const shape = directChild(node, "polygon") ?? directChild(node, "path");
|
|
81
|
-
if (shape) {
|
|
82
|
-
const bounds = boundsOf(
|
|
83
|
-
shape.getAttribute("points") ?? shape.getAttribute("d") ?? ""
|
|
84
|
-
);
|
|
85
|
-
if (bounds) {
|
|
86
|
-
return {
|
|
87
|
-
x: (bounds.minX + bounds.maxX) / 2,
|
|
88
|
-
y: (bounds.minY + bounds.maxY) / 2
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
function boundsOf(coords) {
|
|
95
|
-
const nums = coords.match(/-?\d+(?:\.\d+)?/g);
|
|
96
|
-
if (!nums || nums.length < 2) return null;
|
|
97
|
-
let minX = Infinity;
|
|
98
|
-
let minY = Infinity;
|
|
99
|
-
let maxX = -Infinity;
|
|
100
|
-
let maxY = -Infinity;
|
|
101
|
-
for (let i = 0; i + 1 < nums.length; i += 2) {
|
|
102
|
-
const x = parseFloat(nums[i]);
|
|
103
|
-
const y = parseFloat(nums[i + 1]);
|
|
104
|
-
if (x < minX) minX = x;
|
|
105
|
-
if (x > maxX) maxX = x;
|
|
106
|
-
if (y < minY) minY = y;
|
|
107
|
-
if (y > maxY) maxY = y;
|
|
108
|
-
}
|
|
109
|
-
if (minX === Infinity) return null;
|
|
110
|
-
return { minX, minY, maxX, maxY };
|
|
111
|
-
}
|
|
112
|
-
function nodeClusterMap(svg) {
|
|
113
|
-
const map = /* @__PURE__ */ new Map();
|
|
114
|
-
svg.querySelectorAll("g.node[data-instance]").forEach((node) => {
|
|
115
|
-
const di = node.getAttribute("data-instance");
|
|
116
|
-
const title = directChild(node, "title");
|
|
117
|
-
const id = (title?.textContent ?? "").trim();
|
|
118
|
-
if (di && id) map.set(id, di);
|
|
119
|
-
});
|
|
120
|
-
return map;
|
|
121
|
-
}
|
|
122
|
-
function endpointInCluster(id, prefix, clusterMap) {
|
|
123
|
-
return nodeBelongsToPrefix(id, prefix) || clusterMap.get(id) === prefix;
|
|
124
|
-
}
|
|
125
|
-
function colorForPrefix(prefix) {
|
|
126
|
-
let h = 2166136261;
|
|
127
|
-
for (let i = 0; i < prefix.length; i++) {
|
|
128
|
-
h ^= prefix.charCodeAt(i);
|
|
129
|
-
h = h * 16777619 >>> 0;
|
|
130
|
-
}
|
|
131
|
-
let hue = Math.floor((h / 4294967296 * 360 + h % 360 * 0.61803) % 360);
|
|
132
|
-
if (hue < 0) hue += 360;
|
|
133
|
-
return `hsl(${hue}, 60%, 45%)`;
|
|
134
|
-
}
|
|
135
|
-
function paintClusterBorders(clusters) {
|
|
136
|
-
for (const c of clusters.values()) {
|
|
137
|
-
const border = directChild(c.group, "polygon") ?? directChild(c.group, "path");
|
|
138
|
-
if (border) {
|
|
139
|
-
border.setAttribute("stroke", c.color);
|
|
140
|
-
border.setAttribute("stroke-width", "2.2");
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
function setClusterCollapsed(cluster, collapsed) {
|
|
145
|
-
const g = cluster.group;
|
|
146
|
-
const isCollapsed = g.classList.contains("cluster-collapsed");
|
|
147
|
-
const root = g.ownerSVGElement;
|
|
148
|
-
if (!root) return;
|
|
149
|
-
if (collapsed && !isCollapsed) {
|
|
150
|
-
g.classList.add("cluster-collapsed");
|
|
151
|
-
const hidden = [];
|
|
152
|
-
root.querySelectorAll(`g.node[data-instance="${cssEscape(cluster.prefix)}"]`).forEach((node) => {
|
|
153
|
-
node.classList.add("petri-collapsed-inside");
|
|
154
|
-
hidden.push(node);
|
|
155
|
-
});
|
|
156
|
-
const clusterMap = nodeClusterMap(root);
|
|
157
|
-
root.querySelectorAll("g.edge").forEach((edge) => {
|
|
158
|
-
const titleEl = directChild(edge, "title");
|
|
159
|
-
if (!titleEl) return;
|
|
160
|
-
const t = (titleEl.textContent ?? "").trim();
|
|
161
|
-
const arrow = t.indexOf("->");
|
|
162
|
-
if (arrow < 0) return;
|
|
163
|
-
const from = t.slice(0, arrow);
|
|
164
|
-
const to = t.slice(arrow + 2);
|
|
165
|
-
if (endpointInCluster(from, cluster.prefix, clusterMap) && endpointInCluster(to, cluster.prefix, clusterMap)) {
|
|
166
|
-
edge.classList.add("petri-collapsed-inside");
|
|
167
|
-
hidden.push(edge);
|
|
168
|
-
}
|
|
169
|
-
});
|
|
170
|
-
g._petriHiddenSiblings = hidden;
|
|
171
|
-
if (!g._petriCollapsedBadge) {
|
|
172
|
-
const label = directChild(g, "text");
|
|
173
|
-
if (label) {
|
|
174
|
-
const badge = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
175
|
-
badge.setAttribute("x", label.getAttribute("x") ?? "0");
|
|
176
|
-
const labelY = parseFloat(label.getAttribute("y") ?? "0");
|
|
177
|
-
badge.setAttribute("y", String(labelY + 16));
|
|
178
|
-
badge.setAttribute("text-anchor", label.getAttribute("text-anchor") ?? "middle");
|
|
179
|
-
badge.setAttribute("font-size", "11");
|
|
180
|
-
badge.setAttribute("fill", "#9ca3af");
|
|
181
|
-
badge.setAttribute("class", "cluster-collapsed-badge");
|
|
182
|
-
badge.textContent = `(${cluster.nodeCount} internal node${cluster.nodeCount === 1 ? "" : "s"})`;
|
|
183
|
-
g.appendChild(badge);
|
|
184
|
-
g._petriCollapsedBadge = badge;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
} else if (!collapsed && isCollapsed) {
|
|
188
|
-
g.classList.remove("cluster-collapsed");
|
|
189
|
-
if (g._petriHiddenSiblings) {
|
|
190
|
-
g._petriHiddenSiblings.forEach((el) => el.classList.remove("petri-collapsed-inside"));
|
|
191
|
-
g._petriHiddenSiblings = void 0;
|
|
192
|
-
}
|
|
193
|
-
if (g._petriCollapsedBadge && g._petriCollapsedBadge.parentNode === g) {
|
|
194
|
-
g.removeChild(g._petriCollapsedBadge);
|
|
195
|
-
g._petriCollapsedBadge = null;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
function applyFilter(svg, prefix) {
|
|
200
|
-
if (prefix == null) {
|
|
201
|
-
svg.removeAttribute("data-active-filter");
|
|
202
|
-
svg.classList.remove("has-active-filter");
|
|
203
|
-
svg.querySelectorAll("g.node, g.edge").forEach((el) => el.classList.remove("petri-dimmed"));
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
svg.setAttribute("data-active-filter", prefix);
|
|
207
|
-
svg.classList.add("has-active-filter");
|
|
208
|
-
const clusterMap = nodeClusterMap(svg);
|
|
209
|
-
svg.querySelectorAll("g.node").forEach((node) => {
|
|
210
|
-
const di = node.getAttribute("data-instance");
|
|
211
|
-
const match = !!di && (di === prefix || di.indexOf(prefix + "_") === 0 || di.indexOf(prefix + "/") === 0);
|
|
212
|
-
if (match) node.classList.remove("petri-dimmed");
|
|
213
|
-
else node.classList.add("petri-dimmed");
|
|
214
|
-
});
|
|
215
|
-
svg.querySelectorAll("g.edge").forEach((edge) => {
|
|
216
|
-
const titleEl = directChild(edge, "title");
|
|
217
|
-
if (!titleEl) return;
|
|
218
|
-
const titleText = titleEl.textContent ?? "";
|
|
219
|
-
const arrow = titleText.indexOf("->");
|
|
220
|
-
if (arrow < 0) {
|
|
221
|
-
edge.classList.remove("petri-dimmed");
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
const from = titleText.slice(0, arrow);
|
|
225
|
-
const to = titleText.slice(arrow + 2);
|
|
226
|
-
const matches = (name) => name.indexOf(prefix + "/") >= 0 || name.indexOf(prefix + "_") >= 0 || name === prefix || clusterMap.get(name) === prefix;
|
|
227
|
-
if (matches(from) || matches(to)) edge.classList.remove("petri-dimmed");
|
|
228
|
-
else edge.classList.add("petri-dimmed");
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
function isInsideCollapsedCluster(svg, clusters, collapsedPrefixes, graphId) {
|
|
232
|
-
if (collapsedPrefixes.size === 0) return false;
|
|
233
|
-
if (!svg) return false;
|
|
234
|
-
const clusterMap = nodeClusterMap(svg);
|
|
235
|
-
for (const prefix of collapsedPrefixes) {
|
|
236
|
-
if (!clusters.has(prefix)) continue;
|
|
237
|
-
if (endpointInCluster(graphId, prefix, clusterMap)) return true;
|
|
238
|
-
}
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
function cssEscape(s) {
|
|
242
|
-
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") return CSS.escape(s);
|
|
243
|
-
return s.replace(/[^a-zA-Z0-9_-]/g, (c) => `\\${c}`);
|
|
244
|
-
}
|
|
245
|
-
function directChild(parent, tagName) {
|
|
246
|
-
const lc = tagName.toLowerCase();
|
|
247
|
-
for (const child of Array.from(parent.children)) {
|
|
248
|
-
if (child.tagName.toLowerCase() === lc) return child;
|
|
249
|
-
}
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// src/viewer/c0-annotations.ts
|
|
254
|
-
function ownTitle(g) {
|
|
255
|
-
for (const child of Array.from(g.children)) {
|
|
256
|
-
if (child.tagName === "title") return child.textContent ?? "";
|
|
257
|
-
}
|
|
258
|
-
return "";
|
|
259
|
-
}
|
|
260
|
-
function enclosingClusterShortName(node) {
|
|
261
|
-
const cluster = node.parentElement?.closest("g.cluster");
|
|
262
|
-
if (!cluster) return "";
|
|
263
|
-
return ownTitle(cluster).replace(/^cluster_/, "");
|
|
264
|
-
}
|
|
265
|
-
function annotateSvgForC0(svg) {
|
|
266
|
-
const nodes = Array.from(svg.querySelectorAll("g.node"));
|
|
267
|
-
for (const node of nodes) {
|
|
268
|
-
const id = ownTitle(node);
|
|
269
|
-
if (!id) continue;
|
|
270
|
-
node.setAttribute("data-id", id);
|
|
271
|
-
const replicaM = /__rep__([A-Za-z0-9_]+)$/.exec(id);
|
|
272
|
-
if (replicaM) {
|
|
273
|
-
node.setAttribute("data-instance", replicaM[1]);
|
|
274
|
-
} else if (!node.hasAttribute("data-instance")) {
|
|
275
|
-
const inst = enclosingClusterShortName(node);
|
|
276
|
-
if (inst) node.setAttribute("data-instance", inst);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
const nodeIndex = /* @__PURE__ */ new Map();
|
|
280
|
-
for (const n of nodes) {
|
|
281
|
-
const id = n.getAttribute("data-id");
|
|
282
|
-
if (id) nodeIndex.set(id, n);
|
|
283
|
-
}
|
|
284
|
-
for (const edge of Array.from(svg.querySelectorAll("g.edge"))) {
|
|
285
|
-
const title = ownTitle(edge);
|
|
286
|
-
const arrow = title.indexOf("->");
|
|
287
|
-
if (arrow <= 0) continue;
|
|
288
|
-
const src = title.slice(0, arrow).trim();
|
|
289
|
-
const dst = title.slice(arrow + 2).trim();
|
|
290
|
-
edge.setAttribute("data-src", src);
|
|
291
|
-
edge.setAttribute("data-dst", dst);
|
|
292
|
-
const srcInst = nodeIndex.get(src)?.getAttribute("data-instance") ?? "";
|
|
293
|
-
const dstInst = nodeIndex.get(dst)?.getAttribute("data-instance") ?? "";
|
|
294
|
-
edge.setAttribute("data-src-cluster", srcInst);
|
|
295
|
-
edge.setAttribute("data-dst-cluster", dstInst);
|
|
296
|
-
edge.classList.remove("intra-cluster", "cross-cluster");
|
|
297
|
-
if (srcInst !== "" && srcInst === dstInst) {
|
|
298
|
-
edge.classList.add("intra-cluster");
|
|
299
|
-
edge.setAttribute("data-cluster", srcInst);
|
|
300
|
-
} else {
|
|
301
|
-
edge.classList.add("cross-cluster");
|
|
302
|
-
edge.removeAttribute("data-cluster");
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// src/viewer/chrome/sidebar.ts
|
|
308
|
-
var SIDEBAR_CLASS = "libpetri-sidebar";
|
|
309
|
-
var CHIP_CLASS = "libpetri-sidebar-chip";
|
|
310
|
-
var CHIP_OFF_CLASS = "is-off";
|
|
311
|
-
function mountSidebar(container, clusters, callbacks) {
|
|
312
|
-
const visibleClusters = /* @__PURE__ */ new Set();
|
|
313
|
-
for (const prefix of clusters.keys()) visibleClusters.add(prefix);
|
|
314
|
-
let includeSharedPlaces = true;
|
|
315
|
-
let highlightMode = true;
|
|
316
|
-
const emit = () => {
|
|
317
|
-
callbacks.onVisibilityChange({
|
|
318
|
-
visibleClusters: new Set(visibleClusters),
|
|
319
|
-
includeSharedPlaces
|
|
320
|
-
});
|
|
321
|
-
};
|
|
322
|
-
const root = document.createElement("aside");
|
|
323
|
-
root.className = SIDEBAR_CLASS;
|
|
324
|
-
const title = document.createElement("h4");
|
|
325
|
-
title.className = "libpetri-sidebar-title";
|
|
326
|
-
title.textContent = `Subnets (${clusters.size})`;
|
|
327
|
-
root.appendChild(title);
|
|
328
|
-
const actions = document.createElement("div");
|
|
329
|
-
actions.className = "libpetri-sidebar-actions";
|
|
330
|
-
const showAll = document.createElement("button");
|
|
331
|
-
showAll.type = "button";
|
|
332
|
-
showAll.textContent = "show all";
|
|
333
|
-
const hideAll = document.createElement("button");
|
|
334
|
-
hideAll.type = "button";
|
|
335
|
-
hideAll.textContent = "hide all";
|
|
336
|
-
actions.appendChild(showAll);
|
|
337
|
-
actions.appendChild(hideAll);
|
|
338
|
-
root.appendChild(actions);
|
|
339
|
-
const sharedLabel = document.createElement("label");
|
|
340
|
-
sharedLabel.className = "libpetri-sidebar-checkbox";
|
|
341
|
-
const sharedCb = document.createElement("input");
|
|
342
|
-
sharedCb.type = "checkbox";
|
|
343
|
-
sharedCb.checked = includeSharedPlaces;
|
|
344
|
-
sharedLabel.appendChild(sharedCb);
|
|
345
|
-
sharedLabel.appendChild(document.createTextNode(" Shared places"));
|
|
346
|
-
sharedCb.addEventListener("change", () => {
|
|
347
|
-
includeSharedPlaces = sharedCb.checked;
|
|
348
|
-
emit();
|
|
349
|
-
});
|
|
350
|
-
root.appendChild(sharedLabel);
|
|
351
|
-
const hlLabel = document.createElement("label");
|
|
352
|
-
hlLabel.className = "libpetri-sidebar-checkbox";
|
|
353
|
-
const hlCb = document.createElement("input");
|
|
354
|
-
hlCb.type = "checkbox";
|
|
355
|
-
hlCb.checked = highlightMode;
|
|
356
|
-
hlLabel.appendChild(hlCb);
|
|
357
|
-
hlLabel.appendChild(document.createTextNode(" Click node \u2192 highlight"));
|
|
358
|
-
hlCb.addEventListener("change", () => {
|
|
359
|
-
highlightMode = hlCb.checked;
|
|
360
|
-
callbacks.onHighlightModeChange(highlightMode);
|
|
361
|
-
});
|
|
362
|
-
root.appendChild(hlLabel);
|
|
363
|
-
const hint = document.createElement("div");
|
|
364
|
-
hint.className = "libpetri-sidebar-hint";
|
|
365
|
-
hint.innerHTML = "Click chip: toggle.<br>Shift-click: isolate.<br>Cmd/Ctrl-click: multi-select.<br>Click empty: clear highlight.";
|
|
366
|
-
root.appendChild(hint);
|
|
367
|
-
const chipList = document.createElement("ul");
|
|
368
|
-
chipList.className = "libpetri-sidebar-chips";
|
|
369
|
-
const chipMap = /* @__PURE__ */ new Map();
|
|
370
|
-
for (const cluster of clusters.values()) {
|
|
371
|
-
const li = document.createElement("li");
|
|
372
|
-
li.className = CHIP_CLASS;
|
|
373
|
-
li.style.borderLeftColor = cluster.color;
|
|
374
|
-
li.dataset.prefix = cluster.prefix;
|
|
375
|
-
const dot = document.createElement("span");
|
|
376
|
-
dot.className = "libpetri-sidebar-chip-dot";
|
|
377
|
-
dot.style.background = cluster.color;
|
|
378
|
-
const name = document.createElement("span");
|
|
379
|
-
name.className = "libpetri-sidebar-chip-name";
|
|
380
|
-
name.textContent = cluster.prefix;
|
|
381
|
-
const count = document.createElement("span");
|
|
382
|
-
count.className = "libpetri-sidebar-chip-count";
|
|
383
|
-
count.textContent = String(cluster.nodeCount);
|
|
384
|
-
li.appendChild(dot);
|
|
385
|
-
li.appendChild(name);
|
|
386
|
-
li.appendChild(count);
|
|
387
|
-
li.addEventListener("click", (ev) => {
|
|
388
|
-
const prefix = cluster.prefix;
|
|
389
|
-
if (ev.shiftKey) {
|
|
390
|
-
const allOff = [...clusters.keys()].every(
|
|
391
|
-
(k) => k === prefix ? visibleClusters.has(k) : !visibleClusters.has(k)
|
|
392
|
-
);
|
|
393
|
-
visibleClusters.clear();
|
|
394
|
-
if (allOff) for (const k of clusters.keys()) visibleClusters.add(k);
|
|
395
|
-
else visibleClusters.add(prefix);
|
|
396
|
-
} else if (ev.ctrlKey || ev.metaKey) {
|
|
397
|
-
const anyHidden = [...clusters.keys()].some((k) => !visibleClusters.has(k));
|
|
398
|
-
if (!anyHidden) {
|
|
399
|
-
visibleClusters.clear();
|
|
400
|
-
visibleClusters.add(prefix);
|
|
401
|
-
} else if (visibleClusters.has(prefix)) {
|
|
402
|
-
visibleClusters.delete(prefix);
|
|
403
|
-
} else {
|
|
404
|
-
visibleClusters.add(prefix);
|
|
405
|
-
}
|
|
406
|
-
} else {
|
|
407
|
-
if (visibleClusters.has(prefix)) visibleClusters.delete(prefix);
|
|
408
|
-
else visibleClusters.add(prefix);
|
|
409
|
-
}
|
|
410
|
-
refreshChips();
|
|
411
|
-
emit();
|
|
412
|
-
});
|
|
413
|
-
chipList.appendChild(li);
|
|
414
|
-
chipMap.set(cluster.prefix, li);
|
|
415
|
-
}
|
|
416
|
-
root.appendChild(chipList);
|
|
417
|
-
function refreshChips() {
|
|
418
|
-
for (const [prefix, li] of chipMap) {
|
|
419
|
-
li.classList.toggle(CHIP_OFF_CLASS, !visibleClusters.has(prefix));
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
showAll.addEventListener("click", () => {
|
|
423
|
-
visibleClusters.clear();
|
|
424
|
-
for (const k of clusters.keys()) visibleClusters.add(k);
|
|
425
|
-
includeSharedPlaces = true;
|
|
426
|
-
sharedCb.checked = true;
|
|
427
|
-
refreshChips();
|
|
428
|
-
emit();
|
|
429
|
-
});
|
|
430
|
-
hideAll.addEventListener("click", () => {
|
|
431
|
-
visibleClusters.clear();
|
|
432
|
-
includeSharedPlaces = false;
|
|
433
|
-
sharedCb.checked = false;
|
|
434
|
-
refreshChips();
|
|
435
|
-
emit();
|
|
436
|
-
});
|
|
437
|
-
container.appendChild(root);
|
|
438
|
-
emit();
|
|
439
|
-
return {
|
|
440
|
-
root,
|
|
441
|
-
dispose() {
|
|
442
|
-
if (root.parentNode === container) container.removeChild(root);
|
|
443
|
-
}
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// src/viewer/highlight.ts
|
|
448
|
-
var HIGHLIGHT_CLASSES = ["is-highlight", "is-neighbor", "is-faded"];
|
|
449
|
-
function clearHighlightClasses(svg) {
|
|
450
|
-
for (const cls of HIGHLIGHT_CLASSES) {
|
|
451
|
-
for (const el of Array.from(svg.querySelectorAll("." + cls))) {
|
|
452
|
-
el.classList.remove(cls);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
function findAllCopies(svg, focusId) {
|
|
457
|
-
const focus = svg.querySelector(`g.node[data-id="${cssEscape2(focusId)}"]`);
|
|
458
|
-
if (!focus) return /* @__PURE__ */ new Set([focusId]);
|
|
459
|
-
const copies = /* @__PURE__ */ new Set([focusId]);
|
|
460
|
-
const origLabel = focus.getAttribute("data-replica-of") ?? (focusId.startsWith("p_") ? focusId.slice(2) : null);
|
|
461
|
-
if (origLabel) {
|
|
462
|
-
const orig = svg.querySelector(`g.node[data-id="${cssEscape2("p_" + origLabel)}"]`);
|
|
463
|
-
if (orig) {
|
|
464
|
-
const id = orig.getAttribute("data-id");
|
|
465
|
-
if (id) copies.add(id);
|
|
466
|
-
}
|
|
467
|
-
for (const sibling of Array.from(
|
|
468
|
-
svg.querySelectorAll(`g.node[data-replica-of="${cssEscape2(origLabel)}"]`)
|
|
469
|
-
)) {
|
|
470
|
-
const id = sibling.getAttribute("data-id");
|
|
471
|
-
if (id) copies.add(id);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
return copies;
|
|
475
|
-
}
|
|
476
|
-
function applyHighlight(svg, focusId) {
|
|
477
|
-
clearHighlightClasses(svg);
|
|
478
|
-
if (focusId === null) return;
|
|
479
|
-
const copies = findAllCopies(svg, focusId);
|
|
480
|
-
for (const id of copies) {
|
|
481
|
-
const n = svg.querySelector(`g.node[data-id="${cssEscape2(id)}"]`);
|
|
482
|
-
if (n && !n.classList.contains("is-hidden")) n.classList.add("is-highlight");
|
|
483
|
-
}
|
|
484
|
-
const neighbors = /* @__PURE__ */ new Set();
|
|
485
|
-
const pathJunctions = /* @__PURE__ */ new Set();
|
|
486
|
-
const pathEdges = [];
|
|
487
|
-
const queue = Array.from(copies);
|
|
488
|
-
const visited = new Set(copies);
|
|
489
|
-
const allEdges = Array.from(svg.querySelectorAll("g.edge"));
|
|
490
|
-
while (queue.length > 0) {
|
|
491
|
-
const nodeId = queue.shift();
|
|
492
|
-
for (const e of allEdges) {
|
|
493
|
-
if (e.classList.contains("is-hidden")) continue;
|
|
494
|
-
const s = e.getAttribute("data-src");
|
|
495
|
-
const d = e.getAttribute("data-dst");
|
|
496
|
-
if (s !== nodeId && d !== nodeId) continue;
|
|
497
|
-
const otherId = s === nodeId ? d : s;
|
|
498
|
-
if (!otherId) continue;
|
|
499
|
-
pathEdges.push(e);
|
|
500
|
-
if (visited.has(otherId)) continue;
|
|
501
|
-
visited.add(otherId);
|
|
502
|
-
if (otherId.startsWith("j_")) {
|
|
503
|
-
pathJunctions.add(otherId);
|
|
504
|
-
queue.push(otherId);
|
|
505
|
-
} else {
|
|
506
|
-
neighbors.add(otherId);
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
for (const e of pathEdges) e.classList.add("is-highlight");
|
|
511
|
-
for (const e of allEdges) {
|
|
512
|
-
if (e.classList.contains("is-hidden") || e.classList.contains("is-highlight")) continue;
|
|
513
|
-
e.classList.add("is-faded");
|
|
514
|
-
}
|
|
515
|
-
for (const n of Array.from(svg.querySelectorAll("g.node"))) {
|
|
516
|
-
if (n.classList.contains("is-hidden")) continue;
|
|
517
|
-
const id = n.getAttribute("data-id") ?? "";
|
|
518
|
-
if (copies.has(id)) continue;
|
|
519
|
-
if (neighbors.has(id)) n.classList.add("is-neighbor");
|
|
520
|
-
else if (pathJunctions.has(id)) continue;
|
|
521
|
-
else n.classList.add("is-faded");
|
|
522
|
-
}
|
|
523
|
-
for (const c of Array.from(
|
|
524
|
-
svg.querySelectorAll("g.cluster:not(.cluster-orchestrator)")
|
|
525
|
-
)) {
|
|
526
|
-
if (c.classList.contains("is-hidden")) continue;
|
|
527
|
-
c.classList.add("is-faded");
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
function cssEscape2(value) {
|
|
531
|
-
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// src/viewer/replica-tagging.ts
|
|
535
|
-
var REPLICA_RE = /^(p_[A-Za-z0-9_]+)__rep__/;
|
|
536
|
-
function semanticName(placeId) {
|
|
537
|
-
return placeId.replace(/^p_/, "");
|
|
538
|
-
}
|
|
539
|
-
function tagReplicas(svg) {
|
|
540
|
-
const originalsWithCopies = /* @__PURE__ */ new Set();
|
|
541
|
-
const allNodes = Array.from(svg.querySelectorAll("g.node"));
|
|
542
|
-
for (const g of allNodes) {
|
|
543
|
-
const title = g.querySelector("title")?.textContent ?? "";
|
|
544
|
-
const m = REPLICA_RE.exec(title);
|
|
545
|
-
if (m) originalsWithCopies.add(m[1]);
|
|
546
|
-
}
|
|
547
|
-
for (const g of allNodes) {
|
|
548
|
-
const title = g.querySelector("title")?.textContent ?? "";
|
|
549
|
-
const replicaMatch = REPLICA_RE.exec(title);
|
|
550
|
-
let semantic = null;
|
|
551
|
-
if (replicaMatch) {
|
|
552
|
-
semantic = semanticName(replicaMatch[1]);
|
|
553
|
-
} else if (originalsWithCopies.has(title)) {
|
|
554
|
-
semantic = semanticName(title);
|
|
555
|
-
}
|
|
556
|
-
if (semantic === null) continue;
|
|
557
|
-
g.classList.add("petri-replica");
|
|
558
|
-
g.setAttribute("data-replica-of", semantic);
|
|
559
|
-
appendSharedGlyph(g);
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
563
|
-
var GLYPH_CLASS = "libpetri-replica-glyph";
|
|
564
|
-
function appendSharedGlyph(g) {
|
|
565
|
-
if (g.querySelector(`text.${GLYPH_CLASS}`)) return;
|
|
566
|
-
const shape = g.querySelector("ellipse, circle");
|
|
567
|
-
if (!shape) return;
|
|
568
|
-
let cx = 0, cy = 0, rx = 0, ry = 0;
|
|
569
|
-
if (shape.tagName === "ellipse") {
|
|
570
|
-
cx = parseFloat(shape.getAttribute("cx") ?? "0");
|
|
571
|
-
cy = parseFloat(shape.getAttribute("cy") ?? "0");
|
|
572
|
-
rx = parseFloat(shape.getAttribute("rx") ?? "0");
|
|
573
|
-
ry = parseFloat(shape.getAttribute("ry") ?? "0");
|
|
574
|
-
} else {
|
|
575
|
-
cx = parseFloat(shape.getAttribute("cx") ?? "0");
|
|
576
|
-
cy = parseFloat(shape.getAttribute("cy") ?? "0");
|
|
577
|
-
rx = parseFloat(shape.getAttribute("r") ?? "0");
|
|
578
|
-
ry = rx;
|
|
579
|
-
}
|
|
580
|
-
const text = document.createElementNS(SVG_NS, "text");
|
|
581
|
-
text.setAttribute("class", GLYPH_CLASS);
|
|
582
|
-
text.setAttribute("x", (cx + rx + 2).toFixed(2));
|
|
583
|
-
text.setAttribute("y", (cy - ry + 2).toFixed(2));
|
|
584
|
-
text.setAttribute("text-anchor", "start");
|
|
585
|
-
text.setAttribute("font-size", "9");
|
|
586
|
-
text.textContent = "\u21C4";
|
|
587
|
-
g.appendChild(text);
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
// src/viewer/visibility.ts
|
|
591
|
-
function computeVisibleOrphans(svg, visibleClusters) {
|
|
592
|
-
if (visibleClusters.size === 0) return /* @__PURE__ */ new Set();
|
|
593
|
-
const orphanNodes = Array.from(
|
|
594
|
-
svg.querySelectorAll("g.node:not([data-instance])")
|
|
595
|
-
);
|
|
596
|
-
const orphanIds = /* @__PURE__ */ new Set();
|
|
597
|
-
for (const n of orphanNodes) {
|
|
598
|
-
const id = n.getAttribute("data-id");
|
|
599
|
-
if (id) orphanIds.add(id);
|
|
600
|
-
}
|
|
601
|
-
const fwdAdj = /* @__PURE__ */ new Map();
|
|
602
|
-
const bwdAdj = /* @__PURE__ */ new Map();
|
|
603
|
-
const link = (m, k, v) => {
|
|
604
|
-
let s = m.get(k);
|
|
605
|
-
if (!s) {
|
|
606
|
-
s = /* @__PURE__ */ new Set();
|
|
607
|
-
m.set(k, s);
|
|
608
|
-
}
|
|
609
|
-
s.add(v);
|
|
610
|
-
};
|
|
611
|
-
for (const e of Array.from(svg.querySelectorAll("g.edge"))) {
|
|
612
|
-
const s = e.getAttribute("data-src") ?? "";
|
|
613
|
-
const d = e.getAttribute("data-dst") ?? "";
|
|
614
|
-
if (orphanIds.has(s) && orphanIds.has(d)) {
|
|
615
|
-
link(fwdAdj, s, d);
|
|
616
|
-
link(bwdAdj, d, s);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
const seedUpstream = /* @__PURE__ */ new Set();
|
|
620
|
-
const seedDownstream = /* @__PURE__ */ new Set();
|
|
621
|
-
for (const e of Array.from(
|
|
622
|
-
svg.querySelectorAll("g.edge.cross-cluster")
|
|
623
|
-
)) {
|
|
624
|
-
const s = e.getAttribute("data-src") ?? "";
|
|
625
|
-
const d = e.getAttribute("data-dst") ?? "";
|
|
626
|
-
const sCl = e.getAttribute("data-src-cluster") ?? "";
|
|
627
|
-
const dCl = e.getAttribute("data-dst-cluster") ?? "";
|
|
628
|
-
if (orphanIds.has(s) && visibleClusters.has(dCl)) seedUpstream.add(s);
|
|
629
|
-
if (orphanIds.has(d) && visibleClusters.has(sCl)) seedDownstream.add(d);
|
|
630
|
-
}
|
|
631
|
-
const visible = /* @__PURE__ */ new Set();
|
|
632
|
-
const bfs = (seeds, adj) => {
|
|
633
|
-
const q = [];
|
|
634
|
-
for (const s of seeds) {
|
|
635
|
-
visible.add(s);
|
|
636
|
-
q.push(s);
|
|
637
|
-
}
|
|
638
|
-
while (q.length) {
|
|
639
|
-
const cur = q.shift();
|
|
640
|
-
const next = adj.get(cur);
|
|
641
|
-
if (!next) continue;
|
|
642
|
-
for (const x of next) {
|
|
643
|
-
if (!visible.has(x)) {
|
|
644
|
-
visible.add(x);
|
|
645
|
-
q.push(x);
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
}
|
|
649
|
-
};
|
|
650
|
-
bfs(seedUpstream, bwdAdj);
|
|
651
|
-
bfs(seedDownstream, fwdAdj);
|
|
652
|
-
return visible;
|
|
653
|
-
}
|
|
654
|
-
function applyVisibility(svg, state) {
|
|
655
|
-
annotateSvgForC0(svg);
|
|
656
|
-
const { visibleClusters, includeSharedPlaces } = state;
|
|
657
|
-
for (const c of Array.from(
|
|
658
|
-
svg.querySelectorAll("g.cluster:not(.cluster-orchestrator)")
|
|
659
|
-
)) {
|
|
660
|
-
const titleEl = c.querySelector(":scope > title");
|
|
661
|
-
const shortName = (titleEl?.textContent ?? "").replace(/^cluster_/, "");
|
|
662
|
-
c.classList.toggle("is-hidden", !visibleClusters.has(shortName));
|
|
663
|
-
}
|
|
664
|
-
for (const n of Array.from(svg.querySelectorAll("g.node[data-instance]"))) {
|
|
665
|
-
const inst = n.getAttribute("data-instance") ?? "";
|
|
666
|
-
n.classList.toggle("is-hidden", !visibleClusters.has(inst));
|
|
667
|
-
}
|
|
668
|
-
const visOrphans = computeVisibleOrphans(svg, visibleClusters);
|
|
669
|
-
for (const n of Array.from(
|
|
670
|
-
svg.querySelectorAll("g.node:not([data-instance])")
|
|
671
|
-
)) {
|
|
672
|
-
const id = n.getAttribute("data-id") ?? "";
|
|
673
|
-
n.classList.toggle("is-hidden", !visOrphans.has(id));
|
|
674
|
-
}
|
|
675
|
-
if (!includeSharedPlaces) {
|
|
676
|
-
for (const n of Array.from(svg.querySelectorAll("g.node.petri-replica"))) {
|
|
677
|
-
n.classList.add("is-hidden");
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
const nodeById = /* @__PURE__ */ new Map();
|
|
681
|
-
for (const n of Array.from(svg.querySelectorAll("g.node"))) {
|
|
682
|
-
const id = n.getAttribute("data-id");
|
|
683
|
-
if (id) nodeById.set(id, n);
|
|
684
|
-
}
|
|
685
|
-
for (const e of Array.from(svg.querySelectorAll("g.edge"))) {
|
|
686
|
-
const s = e.getAttribute("data-src") ?? "";
|
|
687
|
-
const d = e.getAttribute("data-dst") ?? "";
|
|
688
|
-
const sHidden = nodeById.get(s)?.classList.contains("is-hidden") ?? false;
|
|
689
|
-
const dHidden = nodeById.get(d)?.classList.contains("is-hidden") ?? false;
|
|
690
|
-
e.classList.toggle("is-hidden", sHidden || dHidden);
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// src/viewer/styles.ts
|
|
695
|
-
var VIEWER_CSS_VARIABLES = [
|
|
696
|
-
"--lpv-bg",
|
|
697
|
-
"--lpv-header-bg",
|
|
698
|
-
"--lpv-border",
|
|
699
|
-
"--lpv-text",
|
|
700
|
-
"--lpv-muted",
|
|
701
|
-
"--lpv-cluster-bg",
|
|
702
|
-
"--lpv-cluster-bg-collapsed",
|
|
703
|
-
"--lpv-cluster-stroke-width",
|
|
704
|
-
"--lpv-cluster-stroke-dash",
|
|
705
|
-
"--lpv-cluster-fill-tint",
|
|
706
|
-
"--lpv-dim-opacity",
|
|
707
|
-
"--lpv-active-filter-outline",
|
|
708
|
-
"--lpv-legend-bg",
|
|
709
|
-
"--lpv-chip-bg",
|
|
710
|
-
"--lpv-chip-active-bg",
|
|
711
|
-
// C0 sidebar + replica + highlight (Stage 5)
|
|
712
|
-
"--lpv-sidebar-bg",
|
|
713
|
-
"--lpv-sidebar-border",
|
|
714
|
-
"--lpv-sidebar-text",
|
|
715
|
-
"--lpv-sidebar-muted",
|
|
716
|
-
"--lpv-sidebar-chip-bg",
|
|
717
|
-
"--lpv-sidebar-chip-hover-bg",
|
|
718
|
-
"--lpv-sidebar-chip-off-opacity",
|
|
719
|
-
"--lpv-shared-glyph-color",
|
|
720
|
-
"--lpv-replica-fill",
|
|
721
|
-
"--lpv-replica-stroke",
|
|
722
|
-
"--lpv-highlight-stroke",
|
|
723
|
-
"--lpv-highlight-glow",
|
|
724
|
-
"--lpv-neighbor-stroke",
|
|
725
|
-
"--lpv-faded-node-opacity",
|
|
726
|
-
"--lpv-faded-edge-opacity",
|
|
727
|
-
"--lpv-faded-cluster-opacity"
|
|
728
|
-
];
|
|
729
|
-
|
|
730
|
-
// src/viewer/dot-flatten.ts
|
|
731
|
-
var CLUSTER_OPEN = /^\s*subgraph\s+cluster_[A-Za-z0-9_]+\s*\{\s*$/;
|
|
732
|
-
var ONLY_CLOSE_BRACE = /^\s*\}\s*$/;
|
|
733
|
-
function flattenClusters(dot) {
|
|
734
|
-
const lines = dot.split("\n");
|
|
735
|
-
const out = [];
|
|
736
|
-
let clusterDepth = 0;
|
|
737
|
-
for (const line of lines) {
|
|
738
|
-
if (CLUSTER_OPEN.test(line)) {
|
|
739
|
-
clusterDepth++;
|
|
740
|
-
continue;
|
|
741
|
-
}
|
|
742
|
-
if (clusterDepth > 0) {
|
|
743
|
-
if (ONLY_CLOSE_BRACE.test(line)) {
|
|
744
|
-
clusterDepth--;
|
|
745
|
-
continue;
|
|
746
|
-
}
|
|
747
|
-
if (line.includes("[") || line.includes("->")) {
|
|
748
|
-
out.push(stripClusterEdgeAttrs(line));
|
|
749
|
-
}
|
|
750
|
-
continue;
|
|
751
|
-
}
|
|
752
|
-
if (line.includes("->") && (line.includes("ltail=") || line.includes("lhead="))) {
|
|
753
|
-
out.push(stripClusterEdgeAttrs(line));
|
|
754
|
-
continue;
|
|
755
|
-
}
|
|
756
|
-
out.push(line);
|
|
757
|
-
}
|
|
758
|
-
return out.join("\n");
|
|
759
|
-
}
|
|
760
|
-
function stripClusterEdgeAttrs(line) {
|
|
761
|
-
return line.replace(/,\s*ltail="cluster_[^"]*"/g, "").replace(/,\s*lhead="cluster_[^"]*"/g, "").replace(/\[\s*ltail="cluster_[^"]*"\s*,\s*/g, "[").replace(/\[\s*lhead="cluster_[^"]*"\s*,\s*/g, "[").replace(/\[\s*ltail="cluster_[^"]*"\s*\]/g, "[]").replace(/\[\s*lhead="cluster_[^"]*"\s*\]/g, "[]");
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// src/viewer/index.ts
|
|
765
|
-
async function mount(dotSource, container, opts = {}) {
|
|
766
|
-
const previousHandle = opts.previousHandle ?? null;
|
|
767
|
-
const preservedCollapsed = previousHandle ? new Set(previousHandle.collapsedPrefixes) : /* @__PURE__ */ new Set();
|
|
768
|
-
const preservedFilter = previousHandle?.activeFilter ?? null;
|
|
769
|
-
const subnetsMode = opts.subnets ?? previousHandle?.subnets ?? "show";
|
|
770
|
-
if (previousHandle) {
|
|
771
|
-
previousHandle.dispose();
|
|
772
|
-
}
|
|
773
|
-
const renderModule = await import("../render-ZGZEZ5RK.js");
|
|
774
|
-
const useElk = (opts.layout ?? "elk") === "elk";
|
|
775
|
-
const renderedDot = subnetsMode === "hide" ? flattenClusters(dotSource) : dotSource;
|
|
776
|
-
const elkConfig = {
|
|
777
|
-
clusterLayout: opts.clusterLayout,
|
|
778
|
-
leafPacking: opts.leafPacking
|
|
779
|
-
};
|
|
780
|
-
const svg = useElk ? await renderModule.renderDotToSvgWithElkLayout(renderedDot, elkConfig) : await renderModule.renderDotToSvg(renderedDot);
|
|
781
|
-
container.innerHTML = "";
|
|
782
|
-
container.appendChild(svg);
|
|
783
|
-
container.classList.add("libpetri-viewer");
|
|
784
|
-
const panzoomInstance = attachPanzoom(svg, opts.panzoom);
|
|
785
|
-
const clusters = discoverClusters(svg);
|
|
786
|
-
paintClusterBorders(clusters);
|
|
787
|
-
const layoutMode = opts.layout ?? "elk";
|
|
788
|
-
if (layoutMode === "elk") {
|
|
789
|
-
tagReplicas(svg);
|
|
790
|
-
annotateSvgForC0(svg);
|
|
791
|
-
}
|
|
792
|
-
const collapsedPrefixes = /* @__PURE__ */ new Set();
|
|
793
|
-
let activeFilter = null;
|
|
794
|
-
let highlightedNodeId = null;
|
|
795
|
-
let disposed = false;
|
|
796
|
-
let chromeRoot = null;
|
|
797
|
-
let sidebarHandle = null;
|
|
798
|
-
let highlightModeEnabled = true;
|
|
799
|
-
function ensureChrome() {
|
|
800
|
-
if (!opts.chrome) return;
|
|
801
|
-
if (chromeRoot && chromeRoot.parentNode === container) return;
|
|
802
|
-
chromeRoot = document.createElement("div");
|
|
803
|
-
chromeRoot.className = "libpetri-viewer-chrome";
|
|
804
|
-
chromeRoot.style.position = "absolute";
|
|
805
|
-
chromeRoot.style.inset = "0";
|
|
806
|
-
chromeRoot.style.pointerEvents = "none";
|
|
807
|
-
const controls = document.createElement("div");
|
|
808
|
-
controls.className = "diagram-controls";
|
|
809
|
-
controls.style.pointerEvents = "auto";
|
|
810
|
-
const resetBtn = document.createElement("button");
|
|
811
|
-
resetBtn.type = "button";
|
|
812
|
-
resetBtn.className = "diagram-btn btn-reset";
|
|
813
|
-
resetBtn.title = "Reset view";
|
|
814
|
-
resetBtn.textContent = "Reset";
|
|
815
|
-
resetBtn.addEventListener("click", () => handle.fit());
|
|
816
|
-
const fsBtn = document.createElement("button");
|
|
817
|
-
fsBtn.type = "button";
|
|
818
|
-
fsBtn.className = "diagram-btn btn-fullscreen";
|
|
819
|
-
fsBtn.title = "Toggle fullscreen";
|
|
820
|
-
fsBtn.textContent = "Fullscreen";
|
|
821
|
-
fsBtn.addEventListener("click", () => toggleFullscreen(container, fsBtn));
|
|
822
|
-
const subnetsBtn = document.createElement("button");
|
|
823
|
-
subnetsBtn.type = "button";
|
|
824
|
-
subnetsBtn.className = "diagram-btn btn-subnets";
|
|
825
|
-
subnetsBtn.title = subnetsMode === "show" ? "Hide subnet groupings" : "Show subnet groupings";
|
|
826
|
-
subnetsBtn.textContent = subnetsMode === "show" ? "Flat view" : "Subnets view";
|
|
827
|
-
subnetsBtn.addEventListener("click", () => {
|
|
828
|
-
void handle.toggleSubnets();
|
|
829
|
-
});
|
|
830
|
-
controls.appendChild(subnetsBtn);
|
|
831
|
-
controls.appendChild(resetBtn);
|
|
832
|
-
controls.appendChild(fsBtn);
|
|
833
|
-
chromeRoot.appendChild(controls);
|
|
834
|
-
if (getComputedStyle(container).position === "static") {
|
|
835
|
-
container.style.position = "relative";
|
|
836
|
-
}
|
|
837
|
-
container.appendChild(chromeRoot);
|
|
838
|
-
if (layoutMode === "elk" && subnetsMode === "show" && clusters.size > 0) {
|
|
839
|
-
sidebarHandle = mountSidebar(container, clusters, {
|
|
840
|
-
onVisibilityChange: (state) => handle.setVisibility(state),
|
|
841
|
-
onHighlightModeChange: (enabled) => {
|
|
842
|
-
highlightModeEnabled = enabled;
|
|
843
|
-
if (!enabled) handle.highlight(null);
|
|
844
|
-
}
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
}
|
|
848
|
-
function toggleFullscreen(host, btn) {
|
|
849
|
-
const isFs = host.classList.toggle("diagram-fullscreen");
|
|
850
|
-
btn.textContent = isFs ? "Exit fullscreen" : "Fullscreen";
|
|
851
|
-
requestAnimationFrame(() => handle.fit());
|
|
852
|
-
}
|
|
853
|
-
const handle = {
|
|
854
|
-
svg,
|
|
855
|
-
panzoom: panzoomInstance,
|
|
856
|
-
clusters,
|
|
857
|
-
get collapsedPrefixes() {
|
|
858
|
-
return collapsedPrefixes;
|
|
859
|
-
},
|
|
860
|
-
get activeFilter() {
|
|
861
|
-
return activeFilter;
|
|
862
|
-
},
|
|
863
|
-
get subnets() {
|
|
864
|
-
return subnetsMode;
|
|
865
|
-
},
|
|
866
|
-
setSubnets(mode) {
|
|
867
|
-
if (mode === subnetsMode) return Promise.resolve(handle);
|
|
868
|
-
return mount(dotSource, container, {
|
|
869
|
-
...opts,
|
|
870
|
-
previousHandle: handle,
|
|
871
|
-
subnets: mode
|
|
872
|
-
}).then((next) => {
|
|
873
|
-
container.dispatchEvent(
|
|
874
|
-
new CustomEvent("libpetri-viewer:remount", {
|
|
875
|
-
bubbles: true,
|
|
876
|
-
detail: { handle: next, subnets: mode }
|
|
877
|
-
})
|
|
878
|
-
);
|
|
879
|
-
return next;
|
|
880
|
-
});
|
|
881
|
-
},
|
|
882
|
-
toggleSubnets() {
|
|
883
|
-
return handle.setSubnets(subnetsMode === "show" ? "hide" : "show");
|
|
884
|
-
},
|
|
885
|
-
collapse(prefix) {
|
|
886
|
-
if (disposed) return;
|
|
887
|
-
const cluster = clusters.get(prefix);
|
|
888
|
-
if (!cluster) return;
|
|
889
|
-
if (!collapsedPrefixes.has(prefix)) {
|
|
890
|
-
setClusterCollapsed(cluster, true);
|
|
891
|
-
collapsedPrefixes.add(prefix);
|
|
892
|
-
opts.onClusterCollapse?.(prefix, true);
|
|
893
|
-
}
|
|
894
|
-
},
|
|
895
|
-
expand(prefix) {
|
|
896
|
-
if (disposed) return;
|
|
897
|
-
const cluster = clusters.get(prefix);
|
|
898
|
-
if (!cluster) return;
|
|
899
|
-
if (collapsedPrefixes.has(prefix)) {
|
|
900
|
-
setClusterCollapsed(cluster, false);
|
|
901
|
-
collapsedPrefixes.delete(prefix);
|
|
902
|
-
opts.onClusterCollapse?.(prefix, false);
|
|
903
|
-
}
|
|
904
|
-
},
|
|
905
|
-
collapseAll() {
|
|
906
|
-
if (disposed) return;
|
|
907
|
-
for (const cluster of clusters.values()) {
|
|
908
|
-
if (!collapsedPrefixes.has(cluster.prefix)) {
|
|
909
|
-
setClusterCollapsed(cluster, true);
|
|
910
|
-
collapsedPrefixes.add(cluster.prefix);
|
|
911
|
-
opts.onClusterCollapse?.(cluster.prefix, true);
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
|
-
},
|
|
915
|
-
expandAll() {
|
|
916
|
-
if (disposed) return;
|
|
917
|
-
for (const prefix of Array.from(collapsedPrefixes)) {
|
|
918
|
-
const cluster = clusters.get(prefix);
|
|
919
|
-
if (!cluster) continue;
|
|
920
|
-
setClusterCollapsed(cluster, false);
|
|
921
|
-
collapsedPrefixes.delete(prefix);
|
|
922
|
-
opts.onClusterCollapse?.(prefix, false);
|
|
923
|
-
}
|
|
924
|
-
},
|
|
925
|
-
filter(prefix) {
|
|
926
|
-
if (disposed) return;
|
|
927
|
-
activeFilter = prefix;
|
|
928
|
-
applyFilter(svg, prefix);
|
|
929
|
-
opts.onClusterFilter?.(prefix);
|
|
930
|
-
},
|
|
931
|
-
resetZoom() {
|
|
932
|
-
if (disposed) return;
|
|
933
|
-
try {
|
|
934
|
-
panzoomInstance.moveTo(0, 0);
|
|
935
|
-
panzoomInstance.zoomAbs(0, 0, 1);
|
|
936
|
-
} catch {
|
|
937
|
-
}
|
|
938
|
-
},
|
|
939
|
-
fit() {
|
|
940
|
-
if (disposed) return;
|
|
941
|
-
try {
|
|
942
|
-
panzoomInstance.zoomAbs(0, 0, 1);
|
|
943
|
-
panzoomInstance.moveTo(0, 0);
|
|
944
|
-
} catch {
|
|
945
|
-
}
|
|
946
|
-
},
|
|
947
|
-
isInsideCollapsedCluster(graphId) {
|
|
948
|
-
return isInsideCollapsedCluster(svg, clusters, collapsedPrefixes, graphId);
|
|
949
|
-
},
|
|
950
|
-
get highlightedNodeId() {
|
|
951
|
-
return highlightedNodeId;
|
|
952
|
-
},
|
|
953
|
-
highlight(nodeId) {
|
|
954
|
-
if (disposed) return;
|
|
955
|
-
if (layoutMode !== "elk") return;
|
|
956
|
-
highlightedNodeId = nodeId;
|
|
957
|
-
applyHighlight(svg, nodeId);
|
|
958
|
-
},
|
|
959
|
-
setVisibility(state) {
|
|
960
|
-
if (disposed) return;
|
|
961
|
-
if (layoutMode !== "elk") return;
|
|
962
|
-
applyVisibility(svg, state);
|
|
963
|
-
},
|
|
964
|
-
dispose() {
|
|
965
|
-
if (disposed) return;
|
|
966
|
-
disposed = true;
|
|
967
|
-
try {
|
|
968
|
-
panzoomInstance.dispose();
|
|
969
|
-
} catch {
|
|
970
|
-
}
|
|
971
|
-
sidebarHandle?.dispose();
|
|
972
|
-
sidebarHandle = null;
|
|
973
|
-
if (chromeRoot && chromeRoot.parentNode === container) {
|
|
974
|
-
container.removeChild(chromeRoot);
|
|
975
|
-
}
|
|
976
|
-
chromeRoot = null;
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
|
-
for (const prefix of preservedCollapsed) {
|
|
980
|
-
handle.collapse(prefix);
|
|
981
|
-
}
|
|
982
|
-
if (preservedFilter !== null) {
|
|
983
|
-
handle.filter(preservedFilter);
|
|
984
|
-
}
|
|
985
|
-
if (layoutMode === "elk") {
|
|
986
|
-
svg.addEventListener("click", (ev) => {
|
|
987
|
-
if (!highlightModeEnabled) return;
|
|
988
|
-
const target = ev.target;
|
|
989
|
-
if (!(target instanceof Element)) return;
|
|
990
|
-
const nodeG = target.closest("g.node");
|
|
991
|
-
if (!nodeG) {
|
|
992
|
-
handle.highlight(null);
|
|
993
|
-
return;
|
|
994
|
-
}
|
|
995
|
-
const id = nodeG.getAttribute("data-id");
|
|
996
|
-
if (!id) return;
|
|
997
|
-
handle.highlight(id === highlightedNodeId ? null : id);
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
ensureChrome();
|
|
1001
|
-
requestAnimationFrame(() => handle.fit());
|
|
1002
|
-
return handle;
|
|
1003
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_PANZOOM_OPTS,
|
|
3
|
+
VERSION,
|
|
4
|
+
VIEWER_CSS_VARIABLES,
|
|
5
|
+
colorForPrefix,
|
|
6
|
+
discoverClusters,
|
|
7
|
+
mount
|
|
8
|
+
} from "../chunk-6NH64RCU.js";
|
|
1004
9
|
export {
|
|
1005
10
|
DEFAULT_PANZOOM_OPTS,
|
|
11
|
+
VERSION,
|
|
1006
12
|
VIEWER_CSS_VARIABLES,
|
|
1007
13
|
colorForPrefix,
|
|
1008
14
|
discoverClusters,
|