jssm 5.155.0 → 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 +53 -1
- package/dist/cdn/instance.js +65 -7
- 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/instance.js +64 -6
- package/dist/wc/viz.js +134 -0
- package/dist/wc/widgets.js +20 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,10 +18,10 @@ Please edit the file it's derived from, instead: `./src/md/readme_base.md`
|
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
* Generated for version 5.
|
|
21
|
+
* Generated for version 5.156.0 at 6/28/2026, 9:25:43 PM
|
|
22
22
|
|
|
23
23
|
-->
|
|
24
|
-
# jssm 5.
|
|
24
|
+
# jssm 5.156.0
|
|
25
25
|
|
|
26
26
|
[**Try the live editor**](https://stonecypher.github.io/jssm-viz-demo/graph_explorer.html) ·
|
|
27
27
|
[Documentation](https://stonecypher.github.io/jssm/docs/) ·
|
|
@@ -312,7 +312,7 @@ That decision shows up everywhere downstream:
|
|
|
312
312
|
or run `npm run benny` against your own machine.
|
|
313
313
|
|
|
314
314
|
- **More thoroughly tested than any other JavaScript state-machine
|
|
315
|
-
library.** 7,
|
|
315
|
+
library.** 7,822 tests at 100.0% line coverage
|
|
316
316
|
([report](https://coveralls.io/github/StoneCypher/jssm)), plus
|
|
317
317
|
fuzz testing via `fast-check`, with parser test data across ten natural
|
|
318
318
|
languages and Emoji.
|
|
@@ -445,11 +445,11 @@ If your contribution is missing here, please open an issue.
|
|
|
445
445
|
|
|
446
446
|
<br/>
|
|
447
447
|
|
|
448
|
-
***7,
|
|
448
|
+
***7,822 tests***, run 82,864 times.
|
|
449
449
|
|
|
450
|
-
- 7,
|
|
451
|
-
- 758 fuzz tests with 54.
|
|
452
|
-
- 9,
|
|
450
|
+
- 7,064 specs with 100.0% coverage
|
|
451
|
+
- 758 fuzz tests with 54.1% coverage
|
|
452
|
+
- 9,761 TypeScript lines - 0.8 tests per line, 8.5 generated tests per line
|
|
453
453
|
|
|
454
454
|
[](https://github.com/StoneCypher/jssm/actions)
|
|
455
455
|
[](https://www.npmjs.com/package/jssm)
|
package/custom-elements.json
CHANGED
|
@@ -3107,6 +3107,11 @@
|
|
|
3107
3107
|
],
|
|
3108
3108
|
"description": "DEFLATE `bytes` (raw, headerless) via the platform `CompressionStream`."
|
|
3109
3109
|
},
|
|
3110
|
+
{
|
|
3111
|
+
"kind": "variable",
|
|
3112
|
+
"name": "MAX_PERMALINK_INFLATE_BYTES",
|
|
3113
|
+
"description": "Hard ceiling on the inflated size of a permalink, in bytes. A permalink rides\r\nin a URL an attacker can hand a victim, and inflate_raw runs on it\r\nautomatically on page load, so an uncapped inflate is a decompression-bomb\r\nvector (a tiny `#m=…` could expand to hundreds of MB and OOM the tab). This is\r\ngenerous for real FSL (text — even a vast machine is well under a megabyte)."
|
|
3114
|
+
},
|
|
3110
3115
|
{
|
|
3111
3116
|
"kind": "function",
|
|
3112
3117
|
"name": "inflate_raw",
|
|
@@ -3123,7 +3128,7 @@
|
|
|
3123
3128
|
}
|
|
3124
3129
|
}
|
|
3125
3130
|
],
|
|
3126
|
-
"description": "Inverse of deflate_raw."
|
|
3131
|
+
"description": "Inverse of deflate_raw, reading the stream in chunks and aborting once\r\nthe inflated output would exceed MAX_PERMALINK_INFLATE_BYTES (a\r\ndecompression-bomb guard — see that constant)."
|
|
3127
3132
|
},
|
|
3128
3133
|
{
|
|
3129
3134
|
"kind": "function",
|
|
@@ -3331,6 +3336,14 @@
|
|
|
3331
3336
|
"module": "src/ts/wc/fsl_permalink.ts"
|
|
3332
3337
|
}
|
|
3333
3338
|
},
|
|
3339
|
+
{
|
|
3340
|
+
"kind": "js",
|
|
3341
|
+
"name": "MAX_PERMALINK_INFLATE_BYTES",
|
|
3342
|
+
"declaration": {
|
|
3343
|
+
"name": "MAX_PERMALINK_INFLATE_BYTES",
|
|
3344
|
+
"module": "src/ts/wc/fsl_permalink.ts"
|
|
3345
|
+
}
|
|
3346
|
+
},
|
|
3334
3347
|
{
|
|
3335
3348
|
"kind": "js",
|
|
3336
3349
|
"name": "inflate_raw",
|
|
@@ -4530,6 +4543,45 @@
|
|
|
4530
4543
|
}
|
|
4531
4544
|
},
|
|
4532
4545
|
"description": "Render the current `fsl` source to an SVG string via the headless\r\n`fsl_to_svg_string` pipeline. Updates `_svg` on success; emits a\r\n`viz-error` `CustomEvent` on failure. Guards against stale results\r\nwhen `fsl` changes mid-flight."
|
|
4546
|
+
},
|
|
4547
|
+
{
|
|
4548
|
+
"kind": "method",
|
|
4549
|
+
"name": "clearHighlights",
|
|
4550
|
+
"privacy": "public",
|
|
4551
|
+
"return": {
|
|
4552
|
+
"type": {
|
|
4553
|
+
"text": "void"
|
|
4554
|
+
}
|
|
4555
|
+
},
|
|
4556
|
+
"description": "Clears any active programmatic highlights from the rendered SVG,\r\nrestoring every node and edge to its default Graphviz presentation.\r\n\r\nRemoves only the inline `fill` / `stroke` / `opacity` overrides that\r\nhighlightTrace installs; the SVG's own presentation attributes\r\nare untouched. Safe to call when nothing is highlighted, before the\r\nfirst render, or while detached — it no-ops if there is no rendered\r\nSVG to clear.\r\n\r\n```typescript\r\nconst viz = document.querySelector('fsl-viz');\r\nviz.highlightTrace(['a', 'b']);\r\nviz.clearHighlights(); // back to the default rendering\r\n```"
|
|
4557
|
+
},
|
|
4558
|
+
{
|
|
4559
|
+
"kind": "method",
|
|
4560
|
+
"name": "highlightTrace",
|
|
4561
|
+
"privacy": "public",
|
|
4562
|
+
"return": {
|
|
4563
|
+
"type": {
|
|
4564
|
+
"text": "void"
|
|
4565
|
+
}
|
|
4566
|
+
},
|
|
4567
|
+
"parameters": [
|
|
4568
|
+
{
|
|
4569
|
+
"name": "trace",
|
|
4570
|
+
"type": {
|
|
4571
|
+
"text": "string[]"
|
|
4572
|
+
},
|
|
4573
|
+
"description": "Ordered state names describing the path (e.g.\r\n`['A', 'B', 'C']`). Consecutive pairs select the edges\r\n`A->B` and `B->C`. An empty array is a no-op."
|
|
4574
|
+
},
|
|
4575
|
+
{
|
|
4576
|
+
"name": "options",
|
|
4577
|
+
"default": "{}",
|
|
4578
|
+
"type": {
|
|
4579
|
+
"text": "HighlightOptions"
|
|
4580
|
+
},
|
|
4581
|
+
"description": "Highlight styling; see {@link HighlightOptions}. Defaults\r\n * to crimson with off-trace fading enabled.\r\n *\r\n * "
|
|
4582
|
+
}
|
|
4583
|
+
],
|
|
4584
|
+
"description": "Programmatically highlights one execution trace (a path of state names)\r\nthrough the rendered graph, optionally fading everything off the path.\r\n\r\nMatches nodes by their Graphviz `<title>` (the state name) and edges by\r\nthe `from->to` title Graphviz emits, applying inline style overrides so\r\nthe highlight composes over — and is reversible against — the default\r\nrendering (see clearHighlights, which this calls first). No-ops\r\nwhen detached, before the first render, or given an empty trace.\r\n\r\n```typescript\r\n// Highlight a -> b -> c in green, without dimming the rest:\r\nviz.highlightTrace(['a', 'b', 'c'], { color: '#2e7d32', fadeOthers: false });\r\n```"
|
|
4533
4585
|
}
|
|
4534
4586
|
],
|
|
4535
4587
|
"events": [
|
package/dist/cdn/instance.js
CHANGED
|
@@ -23563,7 +23563,7 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
23563
23563
|
* Useful for runtime diagnostics and for embedding in serialized machine
|
|
23564
23564
|
* snapshots so that deserializers can detect version-skew.
|
|
23565
23565
|
*/
|
|
23566
|
-
const version = "5.
|
|
23566
|
+
const version = "5.156.0";
|
|
23567
23567
|
|
|
23568
23568
|
// whargarbl lots of these return arrays could/should be sets
|
|
23569
23569
|
const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
|
|
@@ -29436,7 +29436,19 @@ async function deflate_raw(bytes) {
|
|
|
29436
29436
|
return new Uint8Array(await new Response(stream.readable).arrayBuffer());
|
|
29437
29437
|
}
|
|
29438
29438
|
/**
|
|
29439
|
-
*
|
|
29439
|
+
* Hard ceiling on the inflated size of a permalink, in bytes. A permalink rides
|
|
29440
|
+
* in a URL an attacker can hand a victim, and {@link inflate_raw} runs on it
|
|
29441
|
+
* automatically on page load, so an uncapped inflate is a decompression-bomb
|
|
29442
|
+
* vector (a tiny `#m=…` could expand to hundreds of MB and OOM the tab). This is
|
|
29443
|
+
* generous for real FSL (text — even a vast machine is well under a megabyte).
|
|
29444
|
+
*/
|
|
29445
|
+
const MAX_PERMALINK_INFLATE_BYTES = 5 * 1024 * 1024;
|
|
29446
|
+
/**
|
|
29447
|
+
* Inverse of {@link deflate_raw}, reading the stream in chunks and aborting once
|
|
29448
|
+
* the inflated output would exceed {@link MAX_PERMALINK_INFLATE_BYTES} (a
|
|
29449
|
+
* decompression-bomb guard — see that constant).
|
|
29450
|
+
*
|
|
29451
|
+
* @throws RangeError when the inflated output exceeds the cap.
|
|
29440
29452
|
*
|
|
29441
29453
|
* @example
|
|
29442
29454
|
* new TextDecoder().decode(await inflate_raw(await deflate_raw(new TextEncoder().encode("hi")))); // "hi"
|
|
@@ -29446,7 +29458,30 @@ async function inflate_raw(bytes) {
|
|
|
29446
29458
|
const writer = stream.writable.getWriter();
|
|
29447
29459
|
void writer.write(bytes);
|
|
29448
29460
|
void writer.close();
|
|
29449
|
-
|
|
29461
|
+
// Read incrementally; stopping past the cap leaves the stream half-drained, so
|
|
29462
|
+
// backpressure halts further inflation and the abandoned stream is GC'd. We do
|
|
29463
|
+
// not cancel (which would abort the writable and leak an unhandled rejection).
|
|
29464
|
+
const reader = stream.readable.getReader();
|
|
29465
|
+
const chunks = [];
|
|
29466
|
+
let total = 0;
|
|
29467
|
+
for (;;) {
|
|
29468
|
+
const { done, value } = await reader.read();
|
|
29469
|
+
if (done) {
|
|
29470
|
+
break;
|
|
29471
|
+
}
|
|
29472
|
+
total += value.length;
|
|
29473
|
+
if (total > MAX_PERMALINK_INFLATE_BYTES) {
|
|
29474
|
+
throw new RangeError(`permalink inflate exceeded ${MAX_PERMALINK_INFLATE_BYTES} bytes`);
|
|
29475
|
+
}
|
|
29476
|
+
chunks.push(value);
|
|
29477
|
+
}
|
|
29478
|
+
const out = new Uint8Array(total);
|
|
29479
|
+
let offset = 0;
|
|
29480
|
+
for (const chunk of chunks) {
|
|
29481
|
+
out.set(chunk, offset);
|
|
29482
|
+
offset += chunk.length;
|
|
29483
|
+
}
|
|
29484
|
+
return out;
|
|
29450
29485
|
}
|
|
29451
29486
|
/**
|
|
29452
29487
|
* Encode FSL to a `<scheme><payload>` segment value (the part after `key=`).
|
|
@@ -29475,14 +29510,28 @@ async function decode_machine(segment) {
|
|
|
29475
29510
|
const plain = scheme === '1' ? await inflate_raw(bytes) : bytes;
|
|
29476
29511
|
return new TextDecoder().decode(plain);
|
|
29477
29512
|
}
|
|
29478
|
-
/**
|
|
29513
|
+
/** `decodeURIComponent` that returns its input untouched on a malformed escape,
|
|
29514
|
+
* so a hand-mangled fragment never throws out of {@link read_fragment_param}. */
|
|
29515
|
+
function safe_decode(text) {
|
|
29516
|
+
try {
|
|
29517
|
+
return decodeURIComponent(text);
|
|
29518
|
+
}
|
|
29519
|
+
catch (_a) {
|
|
29520
|
+
return text;
|
|
29521
|
+
}
|
|
29522
|
+
}
|
|
29523
|
+
/**
|
|
29524
|
+
* Split a fragment (leading `#` optional) into `[key, value]` pairs, dropping
|
|
29525
|
+
* empties. Keys are percent-decoded (they are percent-encoded on write by
|
|
29526
|
+
* {@link set_fragment_param}); values are the URL-safe base64 payload as-is.
|
|
29527
|
+
*/
|
|
29479
29528
|
function fragment_pairs(hash) {
|
|
29480
29529
|
const body = hash.startsWith('#') ? hash.slice(1) : hash;
|
|
29481
29530
|
return body.split('&').filter(Boolean).map(seg => {
|
|
29482
29531
|
const eq = seg.indexOf('=');
|
|
29483
29532
|
return eq === -1
|
|
29484
|
-
? [seg, '']
|
|
29485
|
-
: [seg.slice(0, eq), seg.slice(eq + 1)];
|
|
29533
|
+
? [safe_decode(seg), '']
|
|
29534
|
+
: [safe_decode(seg.slice(0, eq)), seg.slice(eq + 1)];
|
|
29486
29535
|
});
|
|
29487
29536
|
}
|
|
29488
29537
|
/**
|
|
@@ -29513,7 +29562,9 @@ function set_fragment_param(hash, key, value) {
|
|
|
29513
29562
|
else {
|
|
29514
29563
|
pairs[at] = [key, value];
|
|
29515
29564
|
}
|
|
29516
|
-
|
|
29565
|
+
// Percent-encode the key so an `id`/`uhash` containing `=`, `&`, or `#` cannot
|
|
29566
|
+
// break segmentation or collide with a sibling. Values are URL-safe base64.
|
|
29567
|
+
return pairs.map(([k, v]) => `${encodeURIComponent(k)}=${v}`).join('&');
|
|
29517
29568
|
}
|
|
29518
29569
|
/**
|
|
29519
29570
|
* The fragment key an element owns: its `uhash` attribute if set, else its
|
|
@@ -29581,6 +29632,13 @@ class FslPermalinkSync {
|
|
|
29581
29632
|
}
|
|
29582
29633
|
try {
|
|
29583
29634
|
const fsl = await decode_machine(segment);
|
|
29635
|
+
// The decode is async; if the host was disconnected while it ran, drop the
|
|
29636
|
+
// result rather than mutating a detached element (and triggering a stray
|
|
29637
|
+
// rebuild on a later reconnect). A reconnect runs hostConnected → _restore
|
|
29638
|
+
// afresh.
|
|
29639
|
+
if (!this.host.isConnected) {
|
|
29640
|
+
return;
|
|
29641
|
+
}
|
|
29584
29642
|
this._last = segment;
|
|
29585
29643
|
this.host.fsl = fsl;
|
|
29586
29644
|
}
|
package/dist/cdn/viz.js
CHANGED
|
@@ -23588,7 +23588,7 @@ var constants = /*#__PURE__*/Object.freeze({
|
|
|
23588
23588
|
* Useful for runtime diagnostics and for embedding in serialized machine
|
|
23589
23589
|
* snapshots so that deserializers can detect version-skew.
|
|
23590
23590
|
*/
|
|
23591
|
-
const version = "5.
|
|
23591
|
+
const version = "5.156.0";
|
|
23592
23592
|
|
|
23593
23593
|
// whargarbl lots of these return arrays could/should be sets
|
|
23594
23594
|
const { state_name_chars, state_name_first_chars, action_label_chars } = constants;
|
|
@@ -30372,6 +30372,125 @@ class FslViz extends i$1 {
|
|
|
30372
30372
|
render() {
|
|
30373
30373
|
return b `<div class="container">${o(this._svg)}</div>`;
|
|
30374
30374
|
}
|
|
30375
|
+
/**
|
|
30376
|
+
* Clears any active programmatic highlights from the rendered SVG,
|
|
30377
|
+
* restoring every node and edge to its default Graphviz presentation.
|
|
30378
|
+
*
|
|
30379
|
+
* Removes only the inline `fill` / `stroke` / `opacity` overrides that
|
|
30380
|
+
* {@link highlightTrace} installs; the SVG's own presentation attributes
|
|
30381
|
+
* are untouched. Safe to call when nothing is highlighted, before the
|
|
30382
|
+
* first render, or while detached — it no-ops if there is no rendered
|
|
30383
|
+
* SVG to clear.
|
|
30384
|
+
*
|
|
30385
|
+
* ```typescript
|
|
30386
|
+
* const viz = document.querySelector('fsl-viz');
|
|
30387
|
+
* viz.highlightTrace(['a', 'b']);
|
|
30388
|
+
* viz.clearHighlights(); // back to the default rendering
|
|
30389
|
+
* ```
|
|
30390
|
+
*
|
|
30391
|
+
* @see highlightTrace
|
|
30392
|
+
*/
|
|
30393
|
+
clearHighlights() {
|
|
30394
|
+
if (!this.shadowRoot) {
|
|
30395
|
+
return;
|
|
30396
|
+
}
|
|
30397
|
+
const container = this.shadowRoot.querySelector('.container');
|
|
30398
|
+
if (!container) {
|
|
30399
|
+
return;
|
|
30400
|
+
}
|
|
30401
|
+
// Remove the inline styles that override the SVG presentation attributes.
|
|
30402
|
+
const elements = container.querySelectorAll('.node, .edge, .node *, .edge *');
|
|
30403
|
+
elements.forEach(el => {
|
|
30404
|
+
el.style.removeProperty('fill');
|
|
30405
|
+
el.style.removeProperty('stroke');
|
|
30406
|
+
el.style.removeProperty('opacity');
|
|
30407
|
+
});
|
|
30408
|
+
}
|
|
30409
|
+
/**
|
|
30410
|
+
* Programmatically highlights one execution trace (a path of state names)
|
|
30411
|
+
* through the rendered graph, optionally fading everything off the path.
|
|
30412
|
+
*
|
|
30413
|
+
* Matches nodes by their Graphviz `<title>` (the state name) and edges by
|
|
30414
|
+
* the `from->to` title Graphviz emits, applying inline style overrides so
|
|
30415
|
+
* the highlight composes over — and is reversible against — the default
|
|
30416
|
+
* rendering (see {@link clearHighlights}, which this calls first). No-ops
|
|
30417
|
+
* when detached, before the first render, or given an empty trace.
|
|
30418
|
+
*
|
|
30419
|
+
* ```typescript
|
|
30420
|
+
* // Highlight a -> b -> c in green, without dimming the rest:
|
|
30421
|
+
* viz.highlightTrace(['a', 'b', 'c'], { color: '#2e7d32', fadeOthers: false });
|
|
30422
|
+
* ```
|
|
30423
|
+
*
|
|
30424
|
+
* @param trace Ordered state names describing the path (e.g.
|
|
30425
|
+
* `['A', 'B', 'C']`). Consecutive pairs select the edges
|
|
30426
|
+
* `A->B` and `B->C`. An empty array is a no-op.
|
|
30427
|
+
* @param options Highlight styling; see {@link HighlightOptions}. Defaults
|
|
30428
|
+
* to crimson with off-trace fading enabled.
|
|
30429
|
+
*
|
|
30430
|
+
* @see clearHighlights
|
|
30431
|
+
* @see HighlightOptions
|
|
30432
|
+
*/
|
|
30433
|
+
highlightTrace(trace, options = {}) {
|
|
30434
|
+
if (!this.shadowRoot) {
|
|
30435
|
+
return;
|
|
30436
|
+
}
|
|
30437
|
+
const container = this.shadowRoot.querySelector('.container');
|
|
30438
|
+
if (!container || trace.length === 0) {
|
|
30439
|
+
return;
|
|
30440
|
+
}
|
|
30441
|
+
this.clearHighlights();
|
|
30442
|
+
const color = options.color || '#b71c1c'; // default: a distinct crimson
|
|
30443
|
+
const fadeOthers = options.fadeOthers !== false; // default: true
|
|
30444
|
+
const targetNodes = new Set();
|
|
30445
|
+
const targetEdges = new Set();
|
|
30446
|
+
for (let i = 0; i < trace.length; i++) {
|
|
30447
|
+
targetNodes.add(trace[i]);
|
|
30448
|
+
if (i < trace.length - 1) {
|
|
30449
|
+
targetEdges.add(`${trace[i]}->${trace[i + 1]}`);
|
|
30450
|
+
}
|
|
30451
|
+
}
|
|
30452
|
+
const allNodes = container.querySelectorAll('.node');
|
|
30453
|
+
const allEdges = container.querySelectorAll('.edge');
|
|
30454
|
+
// Graphviz escapes '->' inside <title> as '->'; DOM textContent
|
|
30455
|
+
// usually decodes it, but normalize defensively (and drop quotes).
|
|
30456
|
+
const unescapeTitle = (title) => title.replace(/-/g, '-').replace(/>/g, '>').replace(/"/g, '');
|
|
30457
|
+
allNodes.forEach(node => {
|
|
30458
|
+
const titleEl = node.querySelector('title');
|
|
30459
|
+
const title = titleEl ? unescapeTitle(titleEl.textContent || '') : '';
|
|
30460
|
+
if (targetNodes.has(title)) {
|
|
30461
|
+
node.querySelectorAll('polygon, ellipse, path').forEach(shape => {
|
|
30462
|
+
shape.style.stroke = color;
|
|
30463
|
+
shape.style.strokeWidth = '2px';
|
|
30464
|
+
});
|
|
30465
|
+
node.querySelectorAll('text').forEach(text => {
|
|
30466
|
+
text.style.fill = color;
|
|
30467
|
+
});
|
|
30468
|
+
}
|
|
30469
|
+
else if (fadeOthers) {
|
|
30470
|
+
node.style.opacity = '0.2';
|
|
30471
|
+
}
|
|
30472
|
+
});
|
|
30473
|
+
allEdges.forEach(edge => {
|
|
30474
|
+
const titleEl = edge.querySelector('title');
|
|
30475
|
+
const title = titleEl ? unescapeTitle(titleEl.textContent || '') : '';
|
|
30476
|
+
if (targetEdges.has(title)) {
|
|
30477
|
+
// Recolour the edge line and its arrowhead. The edge's action label
|
|
30478
|
+
// is intentionally left alone: reorder_svg_layers() hoists every edge
|
|
30479
|
+
// <text> out of its group onto a top paint layer (so labels can't be
|
|
30480
|
+
// overdrawn), where it carries no <title> to correlate back to this
|
|
30481
|
+
// edge — so a state-name trace cannot reliably re-style it.
|
|
30482
|
+
edge.querySelectorAll('path, polygon').forEach(shape => {
|
|
30483
|
+
shape.style.stroke = color;
|
|
30484
|
+
if (shape.tagName.toLowerCase() === 'polygon') {
|
|
30485
|
+
shape.style.fill = color; // arrowheads
|
|
30486
|
+
}
|
|
30487
|
+
});
|
|
30488
|
+
}
|
|
30489
|
+
else if (fadeOthers) {
|
|
30490
|
+
edge.style.opacity = '0.2';
|
|
30491
|
+
}
|
|
30492
|
+
});
|
|
30493
|
+
}
|
|
30375
30494
|
}
|
|
30376
30495
|
FslViz.styles = i$4 `
|
|
30377
30496
|
:host {
|
|
@@ -30392,6 +30511,21 @@ FslViz.styles = i$4 `
|
|
|
30392
30511
|
width: 90%;
|
|
30393
30512
|
height: 90%;
|
|
30394
30513
|
}
|
|
30514
|
+
|
|
30515
|
+
/* Smoothly animate the inline style overrides applied by highlightTrace()
|
|
30516
|
+
so a trace fades in/out rather than snapping. */
|
|
30517
|
+
.container svg g.node path,
|
|
30518
|
+
.container svg g.node polygon,
|
|
30519
|
+
.container svg g.node ellipse,
|
|
30520
|
+
.container svg g.edge path,
|
|
30521
|
+
.container svg g.edge polygon {
|
|
30522
|
+
transition: fill 0.3s ease, stroke 0.3s ease, opacity 0.3s ease;
|
|
30523
|
+
}
|
|
30524
|
+
|
|
30525
|
+
.container svg g.node text,
|
|
30526
|
+
.container svg g.edge text {
|
|
30527
|
+
transition: fill 0.3s ease, opacity 0.3s ease;
|
|
30528
|
+
}
|
|
30395
30529
|
`;
|
|
30396
30530
|
__decorate([
|
|
30397
30531
|
n({ type: String })
|