jssm 5.155.1 → 5.156.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 +7 -7
- package/custom-elements.json +39 -0
- package/dist/cdn/instance.js +1 -1
- package/dist/cdn/viz.js +135 -1
- package/dist/cli/fsl-export-system-prompt.cjs +1 -1
- package/dist/cli/fsl-render.cjs +1 -1
- package/dist/cli/fsl.cjs +1 -1
- package/dist/cli/lib.cjs +1 -1
- package/dist/cli/lib.mjs +1 -1
- package/dist/deno/README.md +7 -7
- package/dist/deno/jssm.js +1 -1
- package/dist/jssm.es5.cjs +1 -1
- package/dist/jssm.es5.iife.js +1 -1
- package/dist/jssm.es6.mjs +1 -1
- package/dist/jssm_viz.cjs +1 -1
- package/dist/jssm_viz.iife.cjs +1 -1
- package/dist/jssm_viz.mjs +1 -1
- package/dist/wc/viz.js +134 -0
- package/package.json +1 -1
package/dist/wc/viz.js
CHANGED
|
@@ -351,6 +351,125 @@ class FslViz extends LitElement {
|
|
|
351
351
|
render() {
|
|
352
352
|
return html `<div class="container">${unsafeHTML(this._svg)}</div>`;
|
|
353
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Clears any active programmatic highlights from the rendered SVG,
|
|
356
|
+
* restoring every node and edge to its default Graphviz presentation.
|
|
357
|
+
*
|
|
358
|
+
* Removes only the inline `fill` / `stroke` / `opacity` overrides that
|
|
359
|
+
* {@link highlightTrace} installs; the SVG's own presentation attributes
|
|
360
|
+
* are untouched. Safe to call when nothing is highlighted, before the
|
|
361
|
+
* first render, or while detached — it no-ops if there is no rendered
|
|
362
|
+
* SVG to clear.
|
|
363
|
+
*
|
|
364
|
+
* ```typescript
|
|
365
|
+
* const viz = document.querySelector('fsl-viz');
|
|
366
|
+
* viz.highlightTrace(['a', 'b']);
|
|
367
|
+
* viz.clearHighlights(); // back to the default rendering
|
|
368
|
+
* ```
|
|
369
|
+
*
|
|
370
|
+
* @see highlightTrace
|
|
371
|
+
*/
|
|
372
|
+
clearHighlights() {
|
|
373
|
+
if (!this.shadowRoot) {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const container = this.shadowRoot.querySelector('.container');
|
|
377
|
+
if (!container) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
// Remove the inline styles that override the SVG presentation attributes.
|
|
381
|
+
const elements = container.querySelectorAll('.node, .edge, .node *, .edge *');
|
|
382
|
+
elements.forEach(el => {
|
|
383
|
+
el.style.removeProperty('fill');
|
|
384
|
+
el.style.removeProperty('stroke');
|
|
385
|
+
el.style.removeProperty('opacity');
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Programmatically highlights one execution trace (a path of state names)
|
|
390
|
+
* through the rendered graph, optionally fading everything off the path.
|
|
391
|
+
*
|
|
392
|
+
* Matches nodes by their Graphviz `<title>` (the state name) and edges by
|
|
393
|
+
* the `from->to` title Graphviz emits, applying inline style overrides so
|
|
394
|
+
* the highlight composes over — and is reversible against — the default
|
|
395
|
+
* rendering (see {@link clearHighlights}, which this calls first). No-ops
|
|
396
|
+
* when detached, before the first render, or given an empty trace.
|
|
397
|
+
*
|
|
398
|
+
* ```typescript
|
|
399
|
+
* // Highlight a -> b -> c in green, without dimming the rest:
|
|
400
|
+
* viz.highlightTrace(['a', 'b', 'c'], { color: '#2e7d32', fadeOthers: false });
|
|
401
|
+
* ```
|
|
402
|
+
*
|
|
403
|
+
* @param trace Ordered state names describing the path (e.g.
|
|
404
|
+
* `['A', 'B', 'C']`). Consecutive pairs select the edges
|
|
405
|
+
* `A->B` and `B->C`. An empty array is a no-op.
|
|
406
|
+
* @param options Highlight styling; see {@link HighlightOptions}. Defaults
|
|
407
|
+
* to crimson with off-trace fading enabled.
|
|
408
|
+
*
|
|
409
|
+
* @see clearHighlights
|
|
410
|
+
* @see HighlightOptions
|
|
411
|
+
*/
|
|
412
|
+
highlightTrace(trace, options = {}) {
|
|
413
|
+
if (!this.shadowRoot) {
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
const container = this.shadowRoot.querySelector('.container');
|
|
417
|
+
if (!container || trace.length === 0) {
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
this.clearHighlights();
|
|
421
|
+
const color = options.color || '#b71c1c'; // default: a distinct crimson
|
|
422
|
+
const fadeOthers = options.fadeOthers !== false; // default: true
|
|
423
|
+
const targetNodes = new Set();
|
|
424
|
+
const targetEdges = new Set();
|
|
425
|
+
for (let i = 0; i < trace.length; i++) {
|
|
426
|
+
targetNodes.add(trace[i]);
|
|
427
|
+
if (i < trace.length - 1) {
|
|
428
|
+
targetEdges.add(`${trace[i]}->${trace[i + 1]}`);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const allNodes = container.querySelectorAll('.node');
|
|
432
|
+
const allEdges = container.querySelectorAll('.edge');
|
|
433
|
+
// Graphviz escapes '->' inside <title> as '->'; DOM textContent
|
|
434
|
+
// usually decodes it, but normalize defensively (and drop quotes).
|
|
435
|
+
const unescapeTitle = (title) => title.replace(/-/g, '-').replace(/>/g, '>').replace(/"/g, '');
|
|
436
|
+
allNodes.forEach(node => {
|
|
437
|
+
const titleEl = node.querySelector('title');
|
|
438
|
+
const title = titleEl ? unescapeTitle(titleEl.textContent || '') : '';
|
|
439
|
+
if (targetNodes.has(title)) {
|
|
440
|
+
node.querySelectorAll('polygon, ellipse, path').forEach(shape => {
|
|
441
|
+
shape.style.stroke = color;
|
|
442
|
+
shape.style.strokeWidth = '2px';
|
|
443
|
+
});
|
|
444
|
+
node.querySelectorAll('text').forEach(text => {
|
|
445
|
+
text.style.fill = color;
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
else if (fadeOthers) {
|
|
449
|
+
node.style.opacity = '0.2';
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
allEdges.forEach(edge => {
|
|
453
|
+
const titleEl = edge.querySelector('title');
|
|
454
|
+
const title = titleEl ? unescapeTitle(titleEl.textContent || '') : '';
|
|
455
|
+
if (targetEdges.has(title)) {
|
|
456
|
+
// Recolour the edge line and its arrowhead. The edge's action label
|
|
457
|
+
// is intentionally left alone: reorder_svg_layers() hoists every edge
|
|
458
|
+
// <text> out of its group onto a top paint layer (so labels can't be
|
|
459
|
+
// overdrawn), where it carries no <title> to correlate back to this
|
|
460
|
+
// edge — so a state-name trace cannot reliably re-style it.
|
|
461
|
+
edge.querySelectorAll('path, polygon').forEach(shape => {
|
|
462
|
+
shape.style.stroke = color;
|
|
463
|
+
if (shape.tagName.toLowerCase() === 'polygon') {
|
|
464
|
+
shape.style.fill = color; // arrowheads
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
else if (fadeOthers) {
|
|
469
|
+
edge.style.opacity = '0.2';
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
}
|
|
354
473
|
}
|
|
355
474
|
FslViz.styles = css `
|
|
356
475
|
:host {
|
|
@@ -371,6 +490,21 @@ FslViz.styles = css `
|
|
|
371
490
|
width: 90%;
|
|
372
491
|
height: 90%;
|
|
373
492
|
}
|
|
493
|
+
|
|
494
|
+
/* Smoothly animate the inline style overrides applied by highlightTrace()
|
|
495
|
+
so a trace fades in/out rather than snapping. */
|
|
496
|
+
.container svg g.node path,
|
|
497
|
+
.container svg g.node polygon,
|
|
498
|
+
.container svg g.node ellipse,
|
|
499
|
+
.container svg g.edge path,
|
|
500
|
+
.container svg g.edge polygon {
|
|
501
|
+
transition: fill 0.3s ease, stroke 0.3s ease, opacity 0.3s ease;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.container svg g.node text,
|
|
505
|
+
.container svg g.edge text {
|
|
506
|
+
transition: fill 0.3s ease, opacity 0.3s ease;
|
|
507
|
+
}
|
|
374
508
|
`;
|
|
375
509
|
__decorate([
|
|
376
510
|
property({ type: String })
|