libpetri 1.8.5 → 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.
- package/README.md +47 -0
- package/dist/{chunk-B2D5DMTO.js → chunk-B2CV5M3L.js} +228 -9
- package/dist/chunk-B2CV5M3L.js.map +1 -0
- package/dist/chunk-H62Z76FY.js +1346 -0
- package/dist/chunk-H62Z76FY.js.map +1 -0
- package/dist/chunk-RNWTYK5B.js +419 -0
- package/dist/chunk-RNWTYK5B.js.map +1 -0
- package/dist/chunk-SXK2Z45Z.js +50 -0
- package/dist/chunk-SXK2Z45Z.js.map +1 -0
- package/dist/debug/index.d.ts +50 -3
- package/dist/debug/index.js +64 -6
- package/dist/debug/index.js.map +1 -1
- package/dist/doclet/index.d.ts +152 -31
- package/dist/doclet/index.js +458 -57
- package/dist/doclet/index.js.map +1 -1
- package/dist/doclet/resources/petrinet-diagrams.css +640 -7
- package/dist/doclet/resources/petrinet-diagrams.js +7238 -114
- package/dist/dot-exporter-WJMCJEFK.js +8 -0
- package/dist/{event-store-BnyHh3TF.d.ts → event-store-2zkXeQkd.d.ts} +1 -1
- package/dist/export/index.d.ts +18 -4
- package/dist/export/index.js +1 -1
- package/dist/index.d.ts +41 -5
- package/dist/index.js +1221 -35
- package/dist/index.js.map +1 -1
- package/dist/petri-net-BDrj4XZE.d.ts +1461 -0
- package/dist/render-QK57X4TP.js +73 -0
- package/dist/render-QK57X4TP.js.map +1 -0
- package/dist/verification/index.d.ts +3 -144
- package/dist/verification/index.js +30 -1214
- package/dist/verification/index.js.map +1 -1
- package/dist/viewer/index.d.ts +227 -0
- package/dist/viewer/index.js +877 -0
- package/dist/viewer/index.js.map +1 -0
- package/dist/viewer/layout/index.d.ts +200 -0
- package/dist/viewer/layout/index.js +15 -0
- package/dist/viewer/layout/index.js.map +1 -0
- package/dist/viewer/viewer-static.iife.js +3 -0
- package/dist/viewer/viewer.css +759 -0
- package/dist/viewer/viewer.iife.js +7243 -0
- package/package.json +28 -8
- package/dist/chunk-B2D5DMTO.js.map +0 -1
- package/dist/chunk-VQ4XMJTD.js +0 -107
- package/dist/chunk-VQ4XMJTD.js.map +0 -1
- package/dist/dot-exporter-3CVCH6J4.js +0 -8
- package/dist/petri-net-D-GN9g_D.d.ts +0 -570
- /package/dist/{dot-exporter-3CVCH6J4.js.map → dot-exporter-WJMCJEFK.js.map} +0 -0
|
@@ -0,0 +1,759 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
* Canonical libpetri Petri-net diagram viewer stylesheet.
|
|
3
|
+
*
|
|
4
|
+
* Source of truth: typescript/src/viewer/resources/viewer.css.
|
|
5
|
+
* This file is the union of the old petrinet-diagrams.css (Javadoc taglet)
|
|
6
|
+
* and the cluster-overlay rules from debug-ui/src/styles.css. It is
|
|
7
|
+
* shipped as `petrinet-diagrams.css` to all consumers via the build
|
|
8
|
+
* script (scripts/build-viewer.sh). Edit HERE, not in the consumer dirs.
|
|
9
|
+
*
|
|
10
|
+
* All visual constants surface as CSS custom properties (`--lpv-*`) so
|
|
11
|
+
* individual consumers can theme without forking. Defaults match the
|
|
12
|
+
* doclet palette + the user's "clusters must be visually obvious"
|
|
13
|
+
* feedback (deeper tint, thicker stroke, more prominent dash).
|
|
14
|
+
* ============================================================ */
|
|
15
|
+
|
|
16
|
+
:root,
|
|
17
|
+
.petrinet-diagram,
|
|
18
|
+
.libpetri-viewer {
|
|
19
|
+
--lpv-bg: #fafafa;
|
|
20
|
+
--lpv-header-bg: #f0f0f0;
|
|
21
|
+
--lpv-border: #ddd;
|
|
22
|
+
--lpv-text: #111827;
|
|
23
|
+
--lpv-muted: #6b7280;
|
|
24
|
+
|
|
25
|
+
/* Cluster visuals — user feedback: clusters must be visually obvious.
|
|
26
|
+
Bumped the cluster background from a near-white tint to a saturated
|
|
27
|
+
indigo wash, thickened the stroke, and lengthened the dashes. */
|
|
28
|
+
--lpv-cluster-bg: #eef2ff;
|
|
29
|
+
--lpv-cluster-bg-collapsed: #cbd5e1;
|
|
30
|
+
--lpv-cluster-stroke-width: 2.2;
|
|
31
|
+
--lpv-cluster-stroke-dash: 6 3;
|
|
32
|
+
--lpv-cluster-fill-tint: rgba(99, 102, 241, 0.08);
|
|
33
|
+
|
|
34
|
+
--lpv-dim-opacity: 0.2;
|
|
35
|
+
--lpv-active-filter-outline: rgba(245, 158, 11, 0.4);
|
|
36
|
+
|
|
37
|
+
--lpv-legend-bg: rgba(255, 255, 255, 0.95);
|
|
38
|
+
--lpv-chip-bg: #ffffff;
|
|
39
|
+
--lpv-chip-active-bg: #111827;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* ============================================================
|
|
43
|
+
* Top-level diagram container
|
|
44
|
+
* Kept for backward compatibility with the existing Javadoc/Rustdoc
|
|
45
|
+
* taglet markup. Live consumers (debug-ui, dev-preview) don't use the
|
|
46
|
+
* outer .petrinet-diagram chrome; their styling lives in debug-ui CSS.
|
|
47
|
+
* ============================================================ */
|
|
48
|
+
.petrinet-diagram {
|
|
49
|
+
margin: 1.5em 0;
|
|
50
|
+
border: 1px solid var(--lpv-border);
|
|
51
|
+
border-radius: 8px;
|
|
52
|
+
background: var(--lpv-bg);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.petrinet-diagram h4 {
|
|
56
|
+
margin: 0;
|
|
57
|
+
padding: 12px 16px;
|
|
58
|
+
background: var(--lpv-header-bg);
|
|
59
|
+
border-bottom: 1px solid var(--lpv-border);
|
|
60
|
+
border-radius: 8px 8px 0 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.petrinet-diagram .diagram-container {
|
|
64
|
+
position: relative;
|
|
65
|
+
padding: 16px;
|
|
66
|
+
height: 70vh;
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.petrinet-diagram .diagram-viewport {
|
|
71
|
+
width: 100%;
|
|
72
|
+
height: 100%;
|
|
73
|
+
cursor: grab;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.petrinet-diagram .diagram-viewport:active {
|
|
77
|
+
cursor: grabbing;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.petrinet-diagram .diagram-container svg {
|
|
81
|
+
display: block;
|
|
82
|
+
width: 100%;
|
|
83
|
+
height: 100%;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/* 2.0 mount target — the host div emitted by the Java/Rust/TS doclet
|
|
87
|
+
taglets. Needs the same sizing constraints as the legacy
|
|
88
|
+
.diagram-container, otherwise the host grows to the Graphviz-emitted
|
|
89
|
+
SVG intrinsic size and produces a 9000+ px tall column. With this
|
|
90
|
+
rule the SVG fills the constrained host and viewBox auto-fits. */
|
|
91
|
+
.petrinet-diagram-viewer {
|
|
92
|
+
display: block;
|
|
93
|
+
position: relative;
|
|
94
|
+
width: 100%;
|
|
95
|
+
height: 70vh;
|
|
96
|
+
min-height: 400px;
|
|
97
|
+
max-height: 700px;
|
|
98
|
+
overflow: hidden;
|
|
99
|
+
background: var(--lpv-bg);
|
|
100
|
+
border: 1px solid var(--lpv-border);
|
|
101
|
+
border-radius: 4px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Override Graphviz's explicit pt width/height on the rendered SVG.
|
|
105
|
+
Without this the SVG retains its intrinsic 5021pt × 7203pt size,
|
|
106
|
+
defeating the host height constraint. */
|
|
107
|
+
.petrinet-diagram-viewer > svg {
|
|
108
|
+
display: block;
|
|
109
|
+
width: 100% !important;
|
|
110
|
+
height: 100% !important;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.petrinet-diagram .diagram-controls,
|
|
114
|
+
.libpetri-viewer .diagram-controls {
|
|
115
|
+
position: absolute;
|
|
116
|
+
top: 8px;
|
|
117
|
+
right: 8px;
|
|
118
|
+
display: flex;
|
|
119
|
+
gap: 4px;
|
|
120
|
+
z-index: 10;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.petrinet-diagram .diagram-btn,
|
|
124
|
+
.libpetri-viewer .diagram-btn {
|
|
125
|
+
padding: 6px 10px;
|
|
126
|
+
border: 1px solid #ccc;
|
|
127
|
+
border-radius: 4px;
|
|
128
|
+
background: white;
|
|
129
|
+
cursor: pointer;
|
|
130
|
+
font-size: 14px;
|
|
131
|
+
min-width: 32px;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.petrinet-diagram .diagram-btn:hover,
|
|
135
|
+
.libpetri-viewer .diagram-btn:hover {
|
|
136
|
+
background: #e8e8e8;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.petrinet-diagram details {
|
|
140
|
+
padding: 0 16px 16px;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.petrinet-diagram summary {
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
font-weight: 500;
|
|
146
|
+
padding: 8px 0;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* Fullscreen mode */
|
|
150
|
+
.diagram-fullscreen {
|
|
151
|
+
position: fixed !important;
|
|
152
|
+
top: 0 !important;
|
|
153
|
+
left: 0 !important;
|
|
154
|
+
width: 100vw !important;
|
|
155
|
+
height: 100vh !important;
|
|
156
|
+
max-height: 100vh !important;
|
|
157
|
+
background: white !important;
|
|
158
|
+
z-index: 9999 !important;
|
|
159
|
+
margin: 0 !important;
|
|
160
|
+
border-radius: 0 !important;
|
|
161
|
+
overflow: hidden !important;
|
|
162
|
+
display: flex !important;
|
|
163
|
+
flex-direction: column !important;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.diagram-fullscreen .diagram-container {
|
|
167
|
+
flex: 1 !important;
|
|
168
|
+
min-height: 0 !important;
|
|
169
|
+
height: auto !important;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.diagram-fullscreen .diagram-controls {
|
|
173
|
+
position: fixed;
|
|
174
|
+
top: 16px;
|
|
175
|
+
right: 16px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.diagram-fullscreen details {
|
|
179
|
+
display: none;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.diagram-fullscreen svg {
|
|
183
|
+
max-width: none;
|
|
184
|
+
max-height: none;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/* 2.0 host is the mount target itself (no .diagram-container wrapper),
|
|
188
|
+
so size the direct child SVG to fill the viewport in fullscreen. */
|
|
189
|
+
.libpetri-viewer.diagram-fullscreen > svg {
|
|
190
|
+
flex: 1;
|
|
191
|
+
width: 100%;
|
|
192
|
+
height: 100%;
|
|
193
|
+
min-height: 0;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* Zoom indicator */
|
|
197
|
+
.zoom-indicator {
|
|
198
|
+
position: absolute;
|
|
199
|
+
bottom: 8px;
|
|
200
|
+
left: 8px;
|
|
201
|
+
background: rgba(0,0,0,0.6);
|
|
202
|
+
color: white;
|
|
203
|
+
padding: 4px 8px;
|
|
204
|
+
border-radius: 4px;
|
|
205
|
+
font-size: 12px;
|
|
206
|
+
opacity: 0;
|
|
207
|
+
transition: opacity 0.3s;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.zoom-indicator.visible {
|
|
211
|
+
opacity: 1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* ============================================================
|
|
215
|
+
* Subnet header (badges row above the diagram)
|
|
216
|
+
* ============================================================ */
|
|
217
|
+
.petrinet-diagram .subnet-header {
|
|
218
|
+
padding: 12px 16px;
|
|
219
|
+
border-bottom: 1px solid #e5e7eb;
|
|
220
|
+
background: #f8fafc;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.subnet-header-row {
|
|
224
|
+
display: flex;
|
|
225
|
+
align-items: center;
|
|
226
|
+
gap: 8px;
|
|
227
|
+
font-size: 14px;
|
|
228
|
+
flex-wrap: wrap;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.subnet-header-label {
|
|
232
|
+
font-weight: 600;
|
|
233
|
+
color: var(--lpv-muted);
|
|
234
|
+
text-transform: uppercase;
|
|
235
|
+
font-size: 11px;
|
|
236
|
+
letter-spacing: 0.05em;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
.subnet-header-name {
|
|
240
|
+
font-weight: 600;
|
|
241
|
+
color: var(--lpv-text);
|
|
242
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.subnet-header-of {
|
|
246
|
+
color: var(--lpv-muted);
|
|
247
|
+
font-size: 12px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.subnet-header-param,
|
|
251
|
+
.subnet-header-params {
|
|
252
|
+
color: #6d28d9;
|
|
253
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
254
|
+
font-size: 12px;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.subnet-header-params-null {
|
|
258
|
+
color: #9ca3af;
|
|
259
|
+
font-style: italic;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.subnet-header-badge {
|
|
263
|
+
padding: 2px 8px;
|
|
264
|
+
border-radius: 4px;
|
|
265
|
+
background: #fef3c7;
|
|
266
|
+
color: #92400e;
|
|
267
|
+
font-size: 11px;
|
|
268
|
+
font-weight: 500;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.subnet-header-badge-interface {
|
|
272
|
+
background: #e0e7ff;
|
|
273
|
+
color: #3730a3;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.subnet-header-badges {
|
|
277
|
+
display: flex;
|
|
278
|
+
gap: 6px;
|
|
279
|
+
margin-top: 8px;
|
|
280
|
+
flex-wrap: wrap;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.interface-port-badge,
|
|
284
|
+
.interface-channel-badge {
|
|
285
|
+
display: inline-flex;
|
|
286
|
+
align-items: center;
|
|
287
|
+
gap: 4px;
|
|
288
|
+
padding: 3px 8px;
|
|
289
|
+
border-radius: 12px;
|
|
290
|
+
font-size: 11px;
|
|
291
|
+
border: 1px solid;
|
|
292
|
+
background: white;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.interface-port-badge .badge-direction,
|
|
296
|
+
.interface-channel-badge .badge-direction {
|
|
297
|
+
font-weight: 600;
|
|
298
|
+
text-transform: uppercase;
|
|
299
|
+
font-size: 10px;
|
|
300
|
+
letter-spacing: 0.04em;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.interface-port-badge .badge-name,
|
|
304
|
+
.interface-channel-badge .badge-name {
|
|
305
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
306
|
+
font-weight: 500;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
.interface-port-badge .badge-type {
|
|
310
|
+
color: var(--lpv-muted);
|
|
311
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
312
|
+
font-size: 10px;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.interface-port-badge-input { border-color: #1d4ed8; color: #1d4ed8; background: #e7f1ff; }
|
|
316
|
+
.interface-port-badge-output { border-color: #047857; color: #047857; background: #d1fae5; }
|
|
317
|
+
.interface-port-badge-inout { border-color: #b45309; color: #b45309; background: #fef3c7; }
|
|
318
|
+
.interface-channel-badge { border-color: #6d28d9; color: #6d28d9; background: #f3e8ff; }
|
|
319
|
+
|
|
320
|
+
/* ============================================================
|
|
321
|
+
* Cluster legend sidebar
|
|
322
|
+
* ============================================================ */
|
|
323
|
+
.petrinet-diagram .diagram-legend,
|
|
324
|
+
.libpetri-viewer .diagram-legend {
|
|
325
|
+
position: absolute;
|
|
326
|
+
top: 8px;
|
|
327
|
+
left: 8px;
|
|
328
|
+
background: var(--lpv-legend-bg);
|
|
329
|
+
border: 1px solid #e5e7eb;
|
|
330
|
+
border-radius: 6px;
|
|
331
|
+
padding: 8px;
|
|
332
|
+
font-size: 12px;
|
|
333
|
+
z-index: 9;
|
|
334
|
+
max-height: calc(100% - 16px);
|
|
335
|
+
overflow-y: auto;
|
|
336
|
+
min-width: 160px;
|
|
337
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
.diagram-legend .legend-title {
|
|
341
|
+
font-weight: 600;
|
|
342
|
+
color: var(--lpv-muted);
|
|
343
|
+
text-transform: uppercase;
|
|
344
|
+
font-size: 10px;
|
|
345
|
+
letter-spacing: 0.05em;
|
|
346
|
+
margin-bottom: 6px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.diagram-legend .legend-row {
|
|
350
|
+
display: flex;
|
|
351
|
+
align-items: center;
|
|
352
|
+
gap: 6px;
|
|
353
|
+
padding: 4px 6px;
|
|
354
|
+
border-radius: 4px;
|
|
355
|
+
cursor: pointer;
|
|
356
|
+
transition: background 0.15s;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
.diagram-legend .legend-row:hover {
|
|
360
|
+
background: #f3f4f6;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
.diagram-legend .legend-ribbon {
|
|
364
|
+
width: 4px;
|
|
365
|
+
height: 16px;
|
|
366
|
+
border-radius: 2px;
|
|
367
|
+
flex: 0 0 auto;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.diagram-legend .legend-label {
|
|
371
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
372
|
+
flex: 1 1 auto;
|
|
373
|
+
color: var(--lpv-text);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
.diagram-legend .legend-count {
|
|
377
|
+
color: #9ca3af;
|
|
378
|
+
font-size: 10px;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/* ============================================================
|
|
382
|
+
* Filter chips
|
|
383
|
+
* ============================================================ */
|
|
384
|
+
.petrinet-diagram .diagram-filter-strip,
|
|
385
|
+
.libpetri-viewer .diagram-filter-strip {
|
|
386
|
+
position: absolute;
|
|
387
|
+
bottom: 8px;
|
|
388
|
+
left: 8px;
|
|
389
|
+
right: 8px;
|
|
390
|
+
display: flex;
|
|
391
|
+
flex-wrap: wrap;
|
|
392
|
+
align-items: center;
|
|
393
|
+
gap: 6px;
|
|
394
|
+
padding: 8px;
|
|
395
|
+
background: var(--lpv-legend-bg);
|
|
396
|
+
border: 1px solid #e5e7eb;
|
|
397
|
+
border-radius: 6px;
|
|
398
|
+
z-index: 8;
|
|
399
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.filter-strip-label {
|
|
403
|
+
font-size: 11px;
|
|
404
|
+
font-weight: 600;
|
|
405
|
+
color: var(--lpv-muted);
|
|
406
|
+
text-transform: uppercase;
|
|
407
|
+
letter-spacing: 0.04em;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.filter-chip {
|
|
411
|
+
display: inline-flex;
|
|
412
|
+
align-items: center;
|
|
413
|
+
gap: 4px;
|
|
414
|
+
padding: 3px 8px;
|
|
415
|
+
border-radius: 14px;
|
|
416
|
+
font-size: 11px;
|
|
417
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
418
|
+
background: var(--lpv-chip-bg);
|
|
419
|
+
border: 1px solid #d1d5db;
|
|
420
|
+
cursor: pointer;
|
|
421
|
+
transition: background 0.15s, transform 0.15s;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.filter-chip:hover {
|
|
425
|
+
background: #f3f4f6;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
.filter-chip-active {
|
|
429
|
+
background: var(--lpv-chip-active-bg);
|
|
430
|
+
color: white;
|
|
431
|
+
border-color: var(--lpv-chip-active-bg);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.filter-chip-active .chip-dot {
|
|
435
|
+
outline: 2px solid rgba(255, 255, 255, 0.4);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.filter-chip .chip-dot {
|
|
439
|
+
width: 8px;
|
|
440
|
+
height: 8px;
|
|
441
|
+
border-radius: 50%;
|
|
442
|
+
display: inline-block;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/* ============================================================
|
|
446
|
+
* Cluster visual baseline — make clusters visually obvious
|
|
447
|
+
*
|
|
448
|
+
* These rules apply globally to any rendered SVG that lives inside
|
|
449
|
+
* a `.petrinet-diagram` OR a `.libpetri-viewer` host. Tinted fill,
|
|
450
|
+
* thicker stroke, more prominent dash. Stroke colour is injected
|
|
451
|
+
* inline by the cluster-overlay (deterministic HSL per prefix).
|
|
452
|
+
* ============================================================ */
|
|
453
|
+
g.cluster > polygon,
|
|
454
|
+
g.cluster > path {
|
|
455
|
+
fill: var(--lpv-cluster-bg);
|
|
456
|
+
stroke-width: var(--lpv-cluster-stroke-width);
|
|
457
|
+
stroke-dasharray: var(--lpv-cluster-stroke-dash);
|
|
458
|
+
transition: fill 0.2s ease, stroke 0.2s ease;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/* ============================================================
|
|
462
|
+
* Cluster collapse / expand state
|
|
463
|
+
* ============================================================ */
|
|
464
|
+
.cluster-collapsed > polygon,
|
|
465
|
+
.cluster-collapsed > path {
|
|
466
|
+
fill: var(--lpv-cluster-bg-collapsed) !important;
|
|
467
|
+
stroke-dasharray: 3 3;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
.cluster-collapsed-badge {
|
|
471
|
+
font-style: italic;
|
|
472
|
+
pointer-events: none;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/* Sibling nodes/edges hidden while their cluster is collapsed. The cluster
|
|
476
|
+
* <g> retains its layout box (border, label, badge); the interior elements
|
|
477
|
+
* disappear so the box reads as a single tile. */
|
|
478
|
+
g.node.petri-collapsed-inside,
|
|
479
|
+
g.edge.petri-collapsed-inside {
|
|
480
|
+
display: none;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/* ============================================================
|
|
484
|
+
* Filter dim state (CSS-driven, applied per node by JS)
|
|
485
|
+
* ============================================================ */
|
|
486
|
+
g.node.petri-dimmed,
|
|
487
|
+
g.edge.petri-dimmed {
|
|
488
|
+
opacity: var(--lpv-dim-opacity);
|
|
489
|
+
transition: opacity 0.2s ease;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
g.node:not(.petri-dimmed),
|
|
493
|
+
g.edge:not(.petri-dimmed) {
|
|
494
|
+
transition: opacity 0.2s ease;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/* When the filter is active, give the SVG a hairline ring so the user
|
|
498
|
+
has a global signal that they're seeing a filtered view, not a partial
|
|
499
|
+
net. Matches the debug-ui's previous treatment. */
|
|
500
|
+
svg.has-active-filter {
|
|
501
|
+
outline: 1px solid var(--lpv-active-filter-outline);
|
|
502
|
+
outline-offset: -1px;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/* ============================================================
|
|
506
|
+
* C0 layout — left sidebar chrome
|
|
507
|
+
*
|
|
508
|
+
* The chip sidebar replaces the legacy `.diagram-legend` (top-right)
|
|
509
|
+
* and `.diagram-filter-strip` (bottom). Built by
|
|
510
|
+
* `viewer/chrome/sidebar.ts` when `mount({ chrome: true, layout: 'elk' })`.
|
|
511
|
+
* Lives inside the viewer container so it travels into fullscreen.
|
|
512
|
+
* ============================================================ */
|
|
513
|
+
:root,
|
|
514
|
+
.petrinet-diagram,
|
|
515
|
+
.libpetri-viewer {
|
|
516
|
+
--lpv-sidebar-bg: #fafafa;
|
|
517
|
+
--lpv-sidebar-border: #e2e2e2;
|
|
518
|
+
--lpv-sidebar-text: #333333;
|
|
519
|
+
--lpv-sidebar-muted: #6b7280;
|
|
520
|
+
--lpv-sidebar-chip-bg: #ffffff;
|
|
521
|
+
--lpv-sidebar-chip-hover-bg: #f4f4ff;
|
|
522
|
+
--lpv-sidebar-chip-off-opacity: 0.45;
|
|
523
|
+
--lpv-shared-glyph-color: #6b46c1;
|
|
524
|
+
--lpv-replica-fill: #f3f4f6;
|
|
525
|
+
--lpv-replica-stroke: #6b46c1;
|
|
526
|
+
--lpv-highlight-stroke: #d97706;
|
|
527
|
+
--lpv-highlight-glow: rgba(217, 119, 6, 0.55);
|
|
528
|
+
--lpv-neighbor-stroke: #2563eb;
|
|
529
|
+
--lpv-faded-node-opacity: 0.14;
|
|
530
|
+
--lpv-faded-edge-opacity: 0.08;
|
|
531
|
+
--lpv-faded-cluster-opacity: 0.25;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
.libpetri-viewer .libpetri-sidebar {
|
|
535
|
+
position: absolute;
|
|
536
|
+
top: 8px;
|
|
537
|
+
left: 8px;
|
|
538
|
+
width: 220px;
|
|
539
|
+
max-height: calc(100% - 16px);
|
|
540
|
+
overflow-y: auto;
|
|
541
|
+
background: var(--lpv-sidebar-bg);
|
|
542
|
+
border: 1px solid var(--lpv-sidebar-border);
|
|
543
|
+
border-radius: 6px;
|
|
544
|
+
padding: 10px;
|
|
545
|
+
font-size: 12px;
|
|
546
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
547
|
+
color: var(--lpv-sidebar-text);
|
|
548
|
+
z-index: 9;
|
|
549
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.libpetri-sidebar .libpetri-sidebar-title {
|
|
553
|
+
margin: 0 0 8px 0;
|
|
554
|
+
font-weight: 700;
|
|
555
|
+
font-size: 13px;
|
|
556
|
+
color: var(--lpv-sidebar-text);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.libpetri-sidebar .libpetri-sidebar-actions {
|
|
560
|
+
display: flex;
|
|
561
|
+
gap: 4px;
|
|
562
|
+
margin-bottom: 8px;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.libpetri-sidebar .libpetri-sidebar-actions button {
|
|
566
|
+
flex: 1 1 auto;
|
|
567
|
+
padding: 3px 8px;
|
|
568
|
+
font-size: 11px;
|
|
569
|
+
font-family: inherit;
|
|
570
|
+
border: 1px solid #ccc;
|
|
571
|
+
background: #fff;
|
|
572
|
+
border-radius: 4px;
|
|
573
|
+
cursor: pointer;
|
|
574
|
+
color: var(--lpv-sidebar-text);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.libpetri-sidebar .libpetri-sidebar-actions button:hover {
|
|
578
|
+
background: #f0f0f0;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.libpetri-sidebar .libpetri-sidebar-checkbox {
|
|
582
|
+
display: flex;
|
|
583
|
+
align-items: center;
|
|
584
|
+
gap: 6px;
|
|
585
|
+
margin: 6px 0;
|
|
586
|
+
font-size: 11.5px;
|
|
587
|
+
color: var(--lpv-sidebar-text);
|
|
588
|
+
cursor: pointer;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
.libpetri-sidebar .libpetri-sidebar-hint {
|
|
592
|
+
font-size: 10.5px;
|
|
593
|
+
color: var(--lpv-sidebar-muted);
|
|
594
|
+
line-height: 1.4;
|
|
595
|
+
margin: 6px 0 10px 0;
|
|
596
|
+
border-top: 1px solid var(--lpv-sidebar-border);
|
|
597
|
+
padding-top: 8px;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
.libpetri-sidebar .libpetri-sidebar-chips {
|
|
601
|
+
display: flex;
|
|
602
|
+
flex-direction: column;
|
|
603
|
+
gap: 4px;
|
|
604
|
+
margin: 0;
|
|
605
|
+
padding: 0;
|
|
606
|
+
list-style: none;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.libpetri-sidebar .libpetri-sidebar-chip {
|
|
610
|
+
display: flex;
|
|
611
|
+
align-items: center;
|
|
612
|
+
gap: 6px;
|
|
613
|
+
padding: 4px 8px;
|
|
614
|
+
border-radius: 4px;
|
|
615
|
+
cursor: pointer;
|
|
616
|
+
user-select: none;
|
|
617
|
+
border-left: 4px solid var(--lpv-shared-glyph-color);
|
|
618
|
+
background: var(--lpv-sidebar-chip-bg);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.libpetri-sidebar .libpetri-sidebar-chip:hover {
|
|
622
|
+
background: var(--lpv-sidebar-chip-hover-bg);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
.libpetri-sidebar .libpetri-sidebar-chip.is-off {
|
|
626
|
+
opacity: var(--lpv-sidebar-chip-off-opacity);
|
|
627
|
+
border-left-color: #ccc;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.libpetri-sidebar .libpetri-sidebar-chip-dot {
|
|
631
|
+
flex: 0 0 10px;
|
|
632
|
+
width: 10px;
|
|
633
|
+
height: 10px;
|
|
634
|
+
border-radius: 50%;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.libpetri-sidebar .libpetri-sidebar-chip-name {
|
|
638
|
+
flex: 1 1 auto;
|
|
639
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
640
|
+
font-weight: 500;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.libpetri-sidebar .libpetri-sidebar-chip-count {
|
|
644
|
+
color: var(--lpv-sidebar-muted);
|
|
645
|
+
font-size: 10.5px;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/* ============================================================
|
|
649
|
+
* Replica visuals
|
|
650
|
+
*
|
|
651
|
+
* Replicas of shared places get a dashed light-gray ring and a small
|
|
652
|
+
* ⇄ glyph in the upper-right corner. The glyph is injected by
|
|
653
|
+
* `replica-tagging.ts` as a sibling `<text>` inside `<g class="node">`.
|
|
654
|
+
* ============================================================ */
|
|
655
|
+
g.node.petri-replica > ellipse,
|
|
656
|
+
g.node.petri-replica > circle {
|
|
657
|
+
fill: var(--lpv-replica-fill) !important;
|
|
658
|
+
stroke: var(--lpv-replica-stroke) !important;
|
|
659
|
+
stroke-width: 1.2 !important;
|
|
660
|
+
stroke-dasharray: 3 2 !important;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
g.node.petri-replica > text.libpetri-replica-glyph {
|
|
664
|
+
fill: var(--lpv-shared-glyph-color);
|
|
665
|
+
font-weight: 700;
|
|
666
|
+
pointer-events: none;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/* ============================================================
|
|
670
|
+
* Highlight + visibility states
|
|
671
|
+
*
|
|
672
|
+
* `applyHighlight` (highlight.ts) sets `.is-highlight` on focus +
|
|
673
|
+
* every copy, `.is-neighbor` on real-place neighbors, `.is-faded`
|
|
674
|
+
* on the rest. `applyVisibility` (visibility.ts) sets `.is-hidden`
|
|
675
|
+
* on clusters / nodes / edges that the current selection excludes.
|
|
676
|
+
* Constants match v3 colors exactly.
|
|
677
|
+
* ============================================================ */
|
|
678
|
+
.libpetri-viewer svg g.node,
|
|
679
|
+
.libpetri-viewer svg g.edge {
|
|
680
|
+
transition: opacity 0.15s ease-out;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
.libpetri-viewer svg g.cluster.is-hidden,
|
|
684
|
+
.libpetri-viewer svg g.edge.is-hidden,
|
|
685
|
+
.libpetri-viewer svg g.node.is-hidden {
|
|
686
|
+
display: none !important;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
.libpetri-viewer svg g.node.is-faded {
|
|
690
|
+
opacity: var(--lpv-faded-node-opacity) !important;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
.libpetri-viewer svg g.edge.is-faded {
|
|
694
|
+
opacity: var(--lpv-faded-edge-opacity) !important;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/* When a cluster is faded, fade only its own border + label, NOT its
|
|
698
|
+
* descendant nodes/edges — intra-cluster highlighted edges must still pop. */
|
|
699
|
+
.libpetri-viewer svg g.cluster.is-faded > polygon,
|
|
700
|
+
.libpetri-viewer svg g.cluster.is-faded > path,
|
|
701
|
+
.libpetri-viewer svg g.cluster.is-faded > text {
|
|
702
|
+
opacity: var(--lpv-faded-cluster-opacity);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
.libpetri-viewer svg g.node.is-highlight > ellipse,
|
|
706
|
+
.libpetri-viewer svg g.node.is-highlight > circle,
|
|
707
|
+
.libpetri-viewer svg g.node.is-highlight > polygon,
|
|
708
|
+
.libpetri-viewer svg g.node.is-highlight > rect {
|
|
709
|
+
stroke: var(--lpv-highlight-stroke) !important;
|
|
710
|
+
stroke-width: 3 !important;
|
|
711
|
+
filter: drop-shadow(0 0 6px var(--lpv-highlight-glow));
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
.libpetri-viewer svg g.node.is-neighbor > ellipse,
|
|
715
|
+
.libpetri-viewer svg g.node.is-neighbor > circle,
|
|
716
|
+
.libpetri-viewer svg g.node.is-neighbor > polygon,
|
|
717
|
+
.libpetri-viewer svg g.node.is-neighbor > rect {
|
|
718
|
+
stroke: var(--lpv-neighbor-stroke) !important;
|
|
719
|
+
stroke-width: 2.5 !important;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
.libpetri-viewer svg g.edge.is-highlight {
|
|
723
|
+
opacity: 1 !important;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.libpetri-viewer svg g.edge.is-highlight path {
|
|
727
|
+
stroke-width: 2.4 !important;
|
|
728
|
+
opacity: 1 !important;
|
|
729
|
+
filter: drop-shadow(0 0 4px rgba(217, 119, 6, 0.35));
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/* ============================================================
|
|
733
|
+
* Dark theme overrides for the sidebar
|
|
734
|
+
*
|
|
735
|
+
* Triggered by `data-lpv-theme="dark"` on any ancestor (typically
|
|
736
|
+
* the viewer container itself). Debug-ui sets this so the sidebar
|
|
737
|
+
* matches the surrounding dark chrome.
|
|
738
|
+
* ============================================================ */
|
|
739
|
+
[data-lpv-theme="dark"] {
|
|
740
|
+
--lpv-sidebar-bg: #1f2937;
|
|
741
|
+
--lpv-sidebar-border: #374151;
|
|
742
|
+
--lpv-sidebar-text: #f3f4f6;
|
|
743
|
+
--lpv-sidebar-muted: #9ca3af;
|
|
744
|
+
--lpv-sidebar-chip-bg: #111827;
|
|
745
|
+
--lpv-sidebar-chip-hover-bg: #312e81;
|
|
746
|
+
--lpv-replica-fill: #312e81;
|
|
747
|
+
--lpv-replica-stroke: #a78bfa;
|
|
748
|
+
--lpv-shared-glyph-color: #a78bfa;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
[data-lpv-theme="dark"] .libpetri-sidebar .libpetri-sidebar-actions button {
|
|
752
|
+
background: #111827;
|
|
753
|
+
color: var(--lpv-sidebar-text);
|
|
754
|
+
border-color: #374151;
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
[data-lpv-theme="dark"] .libpetri-sidebar .libpetri-sidebar-actions button:hover {
|
|
758
|
+
background: #374151;
|
|
759
|
+
}
|