libpetri 2.0.0 → 2.1.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.
@@ -0,0 +1,8 @@
1
+ import {
2
+ dotExport
3
+ } from "./chunk-B2CV5M3L.js";
4
+ import "./chunk-FN773SSE.js";
5
+ export {
6
+ dotExport
7
+ };
8
+ //# sourceMappingURL=dot-exporter-WJMCJEFK.js.map
@@ -13,7 +13,7 @@ import { P as PetriNet } from '../petri-net-BDrj4XZE.js';
13
13
  */
14
14
  type RankDir = 'TB' | 'BT' | 'LR' | 'RL';
15
15
  type NodeShape = 'circle' | 'doublecircle' | 'box' | 'diamond' | 'ellipse' | 'record';
16
- type EdgeLineStyle = 'solid' | 'dashed' | 'bold';
16
+ type EdgeLineStyle = 'solid' | 'dashed' | 'bold' | 'invis';
17
17
  type ArrowHead = 'normal' | 'odot' | 'none' | 'diamond' | 'dot';
18
18
  interface GraphNode {
19
19
  readonly id: string;
@@ -38,7 +38,7 @@ interface GraphEdge {
38
38
  readonly arrowhead: ArrowHead;
39
39
  readonly penwidth?: number;
40
40
  /** The arc type that produced this edge (for semantic queries). */
41
- readonly arcType: 'input' | 'output' | 'inhibitor' | 'read' | 'reset' | 'reset-output';
41
+ readonly arcType: 'input' | 'output' | 'inhibitor' | 'read' | 'reset' | 'reset-output' | 'ghost';
42
42
  readonly attrs?: Readonly<Record<string, string>>;
43
43
  }
44
44
  /**
@@ -8,7 +8,7 @@ import {
8
8
  nodeStyle,
9
9
  renderDot,
10
10
  sanitize
11
- } from "../chunk-4L6JVKH4.js";
11
+ } from "../chunk-B2CV5M3L.js";
12
12
  import "../chunk-FN773SSE.js";
13
13
  export {
14
14
  DEFAULT_DOT_CONFIG,
@@ -0,0 +1,73 @@
1
+ import {
2
+ elkLayout,
3
+ foldOrphans,
4
+ parseLibpetriDot,
5
+ replicateShared,
6
+ writeBack
7
+ } from "./chunk-RNWTYK5B.js";
8
+
9
+ // src/viewer/render.ts
10
+ import { instance as vizInstance } from "@viz-js/viz";
11
+ var vizPromise = null;
12
+ function getViz() {
13
+ if (!vizPromise) vizPromise = vizInstance();
14
+ return vizPromise;
15
+ }
16
+ async function renderDotToSvg(dotSource) {
17
+ const viz = await getViz();
18
+ return viz.renderSVGElement(dotSource, { engine: "dot" });
19
+ }
20
+ function fnv1a(input) {
21
+ let h = 2166136261;
22
+ for (let i = 0; i < input.length; i++) {
23
+ h ^= input.charCodeAt(i);
24
+ h = Math.imul(h, 16777619);
25
+ }
26
+ return (h >>> 0).toString(16);
27
+ }
28
+ var PINNED_DOT_CACHE_CAP = 16;
29
+ var pinnedDotCache = /* @__PURE__ */ new Map();
30
+ function getCachedPinnedDot(key) {
31
+ const hit = pinnedDotCache.get(key);
32
+ if (hit !== void 0) {
33
+ pinnedDotCache.delete(key);
34
+ pinnedDotCache.set(key, hit);
35
+ }
36
+ return hit;
37
+ }
38
+ function setCachedPinnedDot(key, value) {
39
+ pinnedDotCache.set(key, value);
40
+ while (pinnedDotCache.size > PINNED_DOT_CACHE_CAP) {
41
+ const oldest = pinnedDotCache.keys().next().value;
42
+ if (oldest === void 0) break;
43
+ pinnedDotCache.delete(oldest);
44
+ }
45
+ }
46
+ function _clearElkLayoutCache() {
47
+ pinnedDotCache.clear();
48
+ }
49
+ async function renderDotToSvgWithElkLayout(dotSource) {
50
+ const key = fnv1a(dotSource);
51
+ let pinnedDot = getCachedPinnedDot(key);
52
+ if (pinnedDot === void 0) {
53
+ const graph = replicateShared(
54
+ foldOrphans(parseLibpetriDot(dotSource), 0.7),
55
+ { max: Infinity }
56
+ );
57
+ const layout = await elkLayout(graph);
58
+ pinnedDot = writeBack(graph, layout);
59
+ setCachedPinnedDot(key, pinnedDot);
60
+ }
61
+ const viz = await getViz();
62
+ return viz.renderSVGElement(pinnedDot, {
63
+ engine: "nop",
64
+ yInvert: true
65
+ });
66
+ }
67
+ export {
68
+ _clearElkLayoutCache,
69
+ getViz,
70
+ renderDotToSvg,
71
+ renderDotToSvgWithElkLayout
72
+ };
73
+ //# sourceMappingURL=render-QK57X4TP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/viewer/render.ts"],"sourcesContent":["/**\n * DOT → SVG rendering for the canonical libpetri viewer.\n *\n * Two render paths:\n *\n * renderDotToSvg(dot) — plain Graphviz `dot` engine.\n * renderDotToSvgWithElkLayout(dot) — C0 pipeline: parse → fold →\n * replicate → ELK → writeBack →\n * Graphviz `neato` with `nop=1`\n * (pin mode). Cached by DOT hash.\n *\n * The ELK path is the default for {@link mount}; the plain-Graphviz path\n * remains as a fallback for callers that want stock layout (or for\n * environments where elkjs isn't installed).\n *\n * @module viewer/render\n */\n\nimport { instance as vizInstance } from '@viz-js/viz';\nimport {\n foldOrphans,\n parseLibpetriDot,\n replicateShared,\n} from './layout/preprocess.js';\nimport { elkLayout, writeBack } from './layout/elk-place.js';\n\ntype Viz = Awaited<ReturnType<typeof vizInstance>>;\n\nlet vizPromise: Promise<Viz> | null = null;\n\n/** Memoize the viz.js instance so the wasm loads only once per page. */\nexport function getViz(): Promise<Viz> {\n if (!vizPromise) vizPromise = vizInstance();\n return vizPromise;\n}\n\n/**\n * Render a DOT source string to an SVGSVGElement using the plain `dot`\n * engine. Deterministic across runs; libpetri exporters emit byte-stable\n * DOT per spec EXP-014 so the same DOT yields the same SVG.\n */\nexport async function renderDotToSvg(dotSource: string): Promise<SVGSVGElement> {\n const viz = await getViz();\n return viz.renderSVGElement(dotSource, { engine: 'dot' });\n}\n\n// ---- C0 / ELK pin-mode cache ---------------------------------------------\n\n/** FNV-1a 32-bit hash. Fast enough that a SHA isn't worth the import. */\nfunction fnv1a(input: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < input.length; i++) {\n h ^= input.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(16);\n}\n\nconst PINNED_DOT_CACHE_CAP = 16;\nconst pinnedDotCache = new Map<string, string>();\n\nfunction getCachedPinnedDot(key: string): string | undefined {\n const hit = pinnedDotCache.get(key);\n if (hit !== undefined) {\n // LRU touch: re-insert moves to most-recent position in Map ordering.\n pinnedDotCache.delete(key);\n pinnedDotCache.set(key, hit);\n }\n return hit;\n}\n\nfunction setCachedPinnedDot(key: string, value: string): void {\n pinnedDotCache.set(key, value);\n while (pinnedDotCache.size > PINNED_DOT_CACHE_CAP) {\n const oldest = pinnedDotCache.keys().next().value;\n if (oldest === undefined) break;\n pinnedDotCache.delete(oldest);\n }\n}\n\n/** Test helper — clears the pinned-DOT LRU cache between mounts. */\nexport function _clearElkLayoutCache(): void {\n pinnedDotCache.clear();\n}\n\n/**\n * Run the C0 layout pipeline against a libpetri DOT source and return the\n * rendered SVG. The expensive steps (preprocess + ELK layout + DOT rewrite)\n * are cached keyed on the input DOT's hash, so re-mounts on the same net\n * structure skip everything except the Graphviz pin-mode draw.\n *\n * Per-tick marking updates do NOT call this — they toggle classes on the\n * already-mounted SVG. ELK only runs when the net structure changes.\n */\nexport async function renderDotToSvgWithElkLayout(\n dotSource: string,\n): Promise<SVGSVGElement> {\n const key = fnv1a(dotSource);\n let pinnedDot = getCachedPinnedDot(key);\n if (pinnedDot === undefined) {\n const graph = replicateShared(\n foldOrphans(parseLibpetriDot(dotSource), 0.7),\n { max: Infinity },\n );\n const layout = await elkLayout(graph);\n pinnedDot = writeBack(graph, layout);\n setCachedPinnedDot(key, pinnedDot);\n }\n const viz = await getViz();\n return viz.renderSVGElement(pinnedDot, {\n engine: 'nop',\n yInvert: true,\n });\n}\n"],"mappings":";;;;;;;;;AAkBA,SAAS,YAAY,mBAAmB;AAUxC,IAAI,aAAkC;AAG/B,SAAS,SAAuB;AACrC,MAAI,CAAC,WAAY,cAAa,YAAY;AAC1C,SAAO;AACT;AAOA,eAAsB,eAAe,WAA2C;AAC9E,QAAM,MAAM,MAAM,OAAO;AACzB,SAAO,IAAI,iBAAiB,WAAW,EAAE,QAAQ,MAAM,CAAC;AAC1D;AAKA,SAAS,MAAM,OAAuB;AACpC,MAAI,IAAI;AACR,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,SAAK,MAAM,WAAW,CAAC;AACvB,QAAI,KAAK,KAAK,GAAG,QAAU;AAAA,EAC7B;AACA,UAAQ,MAAM,GAAG,SAAS,EAAE;AAC9B;AAEA,IAAM,uBAAuB;AAC7B,IAAM,iBAAiB,oBAAI,IAAoB;AAE/C,SAAS,mBAAmB,KAAiC;AAC3D,QAAM,MAAM,eAAe,IAAI,GAAG;AAClC,MAAI,QAAQ,QAAW;AAErB,mBAAe,OAAO,GAAG;AACzB,mBAAe,IAAI,KAAK,GAAG;AAAA,EAC7B;AACA,SAAO;AACT;AAEA,SAAS,mBAAmB,KAAa,OAAqB;AAC5D,iBAAe,IAAI,KAAK,KAAK;AAC7B,SAAO,eAAe,OAAO,sBAAsB;AACjD,UAAM,SAAS,eAAe,KAAK,EAAE,KAAK,EAAE;AAC5C,QAAI,WAAW,OAAW;AAC1B,mBAAe,OAAO,MAAM;AAAA,EAC9B;AACF;AAGO,SAAS,uBAA6B;AAC3C,iBAAe,MAAM;AACvB;AAWA,eAAsB,4BACpB,WACwB;AACxB,QAAM,MAAM,MAAM,SAAS;AAC3B,MAAI,YAAY,mBAAmB,GAAG;AACtC,MAAI,cAAc,QAAW;AAC3B,UAAM,QAAQ;AAAA,MACZ,YAAY,iBAAiB,SAAS,GAAG,GAAG;AAAA,MAC5C,EAAE,KAAK,SAAS;AAAA,IAClB;AACA,UAAM,SAAS,MAAM,UAAU,KAAK;AACpC,gBAAY,UAAU,OAAO,MAAM;AACnC,uBAAmB,KAAK,SAAS;AAAA,EACnC;AACA,QAAM,MAAM,MAAM,OAAO;AACzB,SAAO,IAAI,iBAAiB,WAAW;AAAA,IACrC,QAAQ;AAAA,IACR,SAAS;AAAA,EACX,CAAC;AACH;","names":[]}
@@ -82,6 +82,17 @@ declare function discoverClusters(svg: SVGSVGElement): Map<string, ClusterDescri
82
82
  */
83
83
  declare function colorForPrefix(prefix: string): string;
84
84
 
85
+ interface VisibilityState {
86
+ /** Cluster short names currently visible. */
87
+ readonly visibleClusters: ReadonlySet<string>;
88
+ /**
89
+ * When false, hide every replica/shared place (`g.node.petri-replica`)
90
+ * regardless of which cluster it belongs to. The corresponding sidebar
91
+ * checkbox is labelled "Shared places".
92
+ */
93
+ readonly includeSharedPlaces: boolean;
94
+ }
95
+
85
96
  /**
86
97
  * CSS custom properties exposed by the canonical viewer stylesheet.
87
98
  *
@@ -96,7 +107,7 @@ declare function colorForPrefix(prefix: string): string;
96
107
  * @module viewer/styles
97
108
  */
98
109
  /** CSS custom property names that the canonical stylesheet honours. */
99
- declare const VIEWER_CSS_VARIABLES: readonly ["--lpv-bg", "--lpv-header-bg", "--lpv-border", "--lpv-text", "--lpv-muted", "--lpv-cluster-bg", "--lpv-cluster-bg-collapsed", "--lpv-cluster-stroke-width", "--lpv-cluster-stroke-dash", "--lpv-cluster-fill-tint", "--lpv-dim-opacity", "--lpv-active-filter-outline", "--lpv-legend-bg", "--lpv-chip-bg", "--lpv-chip-active-bg"];
110
+ declare const VIEWER_CSS_VARIABLES: readonly ["--lpv-bg", "--lpv-header-bg", "--lpv-border", "--lpv-text", "--lpv-muted", "--lpv-cluster-bg", "--lpv-cluster-bg-collapsed", "--lpv-cluster-stroke-width", "--lpv-cluster-stroke-dash", "--lpv-cluster-fill-tint", "--lpv-dim-opacity", "--lpv-active-filter-outline", "--lpv-legend-bg", "--lpv-chip-bg", "--lpv-chip-active-bg", "--lpv-sidebar-bg", "--lpv-sidebar-border", "--lpv-sidebar-text", "--lpv-sidebar-muted", "--lpv-sidebar-chip-bg", "--lpv-sidebar-chip-hover-bg", "--lpv-sidebar-chip-off-opacity", "--lpv-shared-glyph-color", "--lpv-replica-fill", "--lpv-replica-stroke", "--lpv-highlight-stroke", "--lpv-highlight-glow", "--lpv-neighbor-stroke", "--lpv-faded-node-opacity", "--lpv-faded-edge-opacity", "--lpv-faded-cluster-opacity"];
100
111
 
101
112
  /**
102
113
  * Canonical libpetri Petri-net diagram viewer.
@@ -152,6 +163,18 @@ interface ViewerHandle {
152
163
  fit(): void;
153
164
  /** Reports whether `graphId` is inside a currently-collapsed cluster. */
154
165
  isInsideCollapsedCluster(graphId: string): boolean;
166
+ /**
167
+ * Highlight a node and every DOM copy of the same logical place; walk
168
+ * junctions to surface real neighbors. Pass `null` to clear. C0-only.
169
+ */
170
+ highlight(nodeId: string | null): void;
171
+ /** The node id currently highlighted, or `null`. */
172
+ readonly highlightedNodeId: string | null;
173
+ /**
174
+ * Set the cluster-visibility state: which clusters render, and whether
175
+ * directed-reachable orphans render. C0-only.
176
+ */
177
+ setVisibility(state: VisibilityState): void;
155
178
  /** Tear down: dispose panzoom and detach listeners. */
156
179
  dispose(): void;
157
180
  }
@@ -172,6 +195,14 @@ interface MountOptions {
172
195
  readonly onClusterFilter?: (prefix: string | null) => void;
173
196
  /** When true, append a legend sidebar + filter chip strip inside `container`. */
174
197
  readonly chrome?: boolean;
198
+ /**
199
+ * Layout strategy. `'elk'` (default) runs the C0 pipeline — parse → fold
200
+ * → replicate → ELK → Graphviz `neato` pin mode — and tags replica
201
+ * nodes for the click-all-copies + ⇄ overlay. `'graphviz'` runs the
202
+ * plain Graphviz `dot` engine and skips replica tagging. The result is
203
+ * cached on the DOT hash; re-mounts on identical DOT skip the pipeline.
204
+ */
205
+ readonly layout?: 'elk' | 'graphviz';
175
206
  }
176
207
  /**
177
208
  * Render a DOT source, mount the resulting SVG into `container`, wire pan/zoom,