visset 0.1.0__tar.gz
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.
- visset-0.1.0/.gitignore +23 -0
- visset-0.1.0/LICENSE +21 -0
- visset-0.1.0/PKG-INFO +50 -0
- visset-0.1.0/README.md +26 -0
- visset-0.1.0/pyproject.toml +75 -0
- visset-0.1.0/src/visset/__init__.py +118 -0
- visset-0.1.0/src/visset/__main__.py +3 -0
- visset-0.1.0/src/visset/_assets/player.js +204 -0
- visset-0.1.0/src/visset/_version.py +1 -0
- visset-0.1.0/src/visset/cli.py +165 -0
- visset-0.1.0/src/visset/export/__init__.py +1 -0
- visset-0.1.0/src/visset/export/html.py +90 -0
- visset-0.1.0/src/visset/py.typed +0 -0
- visset-0.1.0/src/visset/tracer/__init__.py +6 -0
- visset-0.1.0/src/visset/tracer/captions.py +210 -0
- visset-0.1.0/src/visset/tracer/engine.py +546 -0
- visset-0.1.0/src/visset/tracer/live.py +74 -0
- visset-0.1.0/src/visset/tracer/positions.py +394 -0
- visset-0.1.0/src/visset/tracer/request.py +88 -0
- visset-0.1.0/src/visset/tracer/run.py +401 -0
- visset-0.1.0/src/visset/tracer/snapshot.py +258 -0
- visset-0.1.0/src/visset/tracer/tokens.py +87 -0
- visset-0.1.0/src/visset/tracer/window.py +86 -0
- visset-0.1.0/tests/conftest.py +45 -0
- visset-0.1.0/tests/test_cli.py +81 -0
- visset-0.1.0/tests/test_modular.py +148 -0
- visset-0.1.0/tests/test_positions.py +58 -0
- visset-0.1.0/tests/test_sandbox.py +58 -0
- visset-0.1.0/tests/test_schema.py +71 -0
- visset-0.1.0/tests/test_tracer.py +163 -0
- visset-0.1.0/tests/update_goldens.py +19 -0
- visset-0.1.0/uv.lock +574 -0
visset-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# JS
|
|
2
|
+
node_modules/
|
|
3
|
+
packages/*/dist/
|
|
4
|
+
|
|
5
|
+
# Python
|
|
6
|
+
__pycache__/
|
|
7
|
+
*.py[cod]
|
|
8
|
+
.venv/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
build/
|
|
11
|
+
dist/
|
|
12
|
+
.pytest_cache/
|
|
13
|
+
.mypy_cache/
|
|
14
|
+
.ruff_cache/
|
|
15
|
+
|
|
16
|
+
# Built player bundle, shipped inside the wheel (run `pnpm build`)
|
|
17
|
+
python/src/visset/_assets/player.js
|
|
18
|
+
|
|
19
|
+
# Editors / OS
|
|
20
|
+
.DS_Store
|
|
21
|
+
.idea/
|
|
22
|
+
.vscode/
|
|
23
|
+
*.swp
|
visset-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 visset contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
visset-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: visset
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Step-by-step visual explanations of code: hand-drawn annotations, synced diagrams.
|
|
5
|
+
Project-URL: Homepage, https://github.com/nur0078/visset
|
|
6
|
+
Project-URL: Issues, https://github.com/nur0078/visset/issues
|
|
7
|
+
Author: visset contributors
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: debugger,education,teaching,tracing,visualization
|
|
11
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Education
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Education
|
|
19
|
+
Classifier: Topic :: Software Development :: Debuggers
|
|
20
|
+
Requires-Python: >=3.12
|
|
21
|
+
Provides-Extra: fast
|
|
22
|
+
Requires-Dist: orjson>=3.10; extra == 'fast'
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# visset
|
|
26
|
+
|
|
27
|
+
Step-by-step visual explanations of code. visset runs a Python program, records every step with
|
|
28
|
+
the exact expression involved, and plays it back as a guided walkthrough: the code with a
|
|
29
|
+
hand-drawn mark on what just ran, the call stack and variables beside it, and a plain-English
|
|
30
|
+
caption.
|
|
31
|
+
|
|
32
|
+
visset is made for a function, a snippet, or part of a function, not a whole codebase.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import visset
|
|
36
|
+
|
|
37
|
+
visset.show(binary_search, [1, 3, 5, 7], 5) # one call, like plt.show()
|
|
38
|
+
visset.show("total = 0\nfor x in xs:\n total += x", given={"xs": [4, 5]})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
visset run bsearch.py --fn binary_search --args "[1, 3, 5, 7], 5"
|
|
43
|
+
visset run bsearch.py --lines 4-9 -o part.html # just part of it, as one HTML file
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
- Python 3.12+ (uses `sys.monitoring`), no runtime dependencies.
|
|
47
|
+
- Programs run in a child process with time, memory and step limits. This protects against
|
|
48
|
+
accidents, not malicious code.
|
|
49
|
+
|
|
50
|
+
Documentation, the trace schema and the roadmap: https://github.com/nur0078/visset
|
visset-0.1.0/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# visset
|
|
2
|
+
|
|
3
|
+
Step-by-step visual explanations of code. visset runs a Python program, records every step with
|
|
4
|
+
the exact expression involved, and plays it back as a guided walkthrough: the code with a
|
|
5
|
+
hand-drawn mark on what just ran, the call stack and variables beside it, and a plain-English
|
|
6
|
+
caption.
|
|
7
|
+
|
|
8
|
+
visset is made for a function, a snippet, or part of a function, not a whole codebase.
|
|
9
|
+
|
|
10
|
+
```python
|
|
11
|
+
import visset
|
|
12
|
+
|
|
13
|
+
visset.show(binary_search, [1, 3, 5, 7], 5) # one call, like plt.show()
|
|
14
|
+
visset.show("total = 0\nfor x in xs:\n total += x", given={"xs": [4, 5]})
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
visset run bsearch.py --fn binary_search --args "[1, 3, 5, 7], 5"
|
|
19
|
+
visset run bsearch.py --lines 4-9 -o part.html # just part of it, as one HTML file
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- Python 3.12+ (uses `sys.monitoring`), no runtime dependencies.
|
|
23
|
+
- Programs run in a child process with time, memory and step limits. This protects against
|
|
24
|
+
accidents, not malicious code.
|
|
25
|
+
|
|
26
|
+
Documentation, the trace schema and the roadmap: https://github.com/nur0078/visset
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "visset"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Step-by-step visual explanations of code: hand-drawn annotations, synced diagrams."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"] # copy of ../LICENSE (sdists cannot include files outside this folder)
|
|
13
|
+
authors = [{ name = "visset contributors" }]
|
|
14
|
+
keywords = ["visualization", "education", "tracing", "debugger", "teaching"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 2 - Pre-Alpha",
|
|
17
|
+
"Intended Audience :: Education",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Topic :: Education",
|
|
24
|
+
"Topic :: Software Development :: Debuggers",
|
|
25
|
+
]
|
|
26
|
+
# No runtime dependencies: tracing and HTML export use only the standard library.
|
|
27
|
+
dependencies = []
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
fast = ["orjson>=3.10"]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/nur0078/visset"
|
|
34
|
+
Issues = "https://github.com/nur0078/visset/issues"
|
|
35
|
+
|
|
36
|
+
[project.scripts]
|
|
37
|
+
visset = "visset.cli:main"
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = ["pytest>=8.3", "jsonschema>=4.23", "ruff>=0.8", "mypy>=1.13"]
|
|
41
|
+
|
|
42
|
+
[tool.hatch.version]
|
|
43
|
+
path = "src/visset/_version.py"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/visset"]
|
|
47
|
+
# Built from packages/player by `pnpm build`; not committed, but shipped in the wheel.
|
|
48
|
+
artifacts = ["src/visset/_assets/player.js"]
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.sdist]
|
|
51
|
+
artifacts = ["src/visset/_assets/player.js"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff]
|
|
54
|
+
line-length = 100
|
|
55
|
+
target-version = "py312"
|
|
56
|
+
src = ["src", "tests"]
|
|
57
|
+
extend-exclude = ["README.md"]
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF", "C4"]
|
|
61
|
+
ignore = ["RUF001", "RUF002", "RUF003", "SIM105"]
|
|
62
|
+
|
|
63
|
+
[tool.mypy]
|
|
64
|
+
python_version = "3.12"
|
|
65
|
+
strict = true
|
|
66
|
+
files = ["src"]
|
|
67
|
+
warn_unreachable = true
|
|
68
|
+
|
|
69
|
+
[[tool.mypy.overrides]]
|
|
70
|
+
module = ["orjson"]
|
|
71
|
+
ignore_missing_imports = true
|
|
72
|
+
|
|
73
|
+
[tool.pytest.ini_options]
|
|
74
|
+
testpaths = ["tests"]
|
|
75
|
+
addopts = "-ra"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""visset: step-by-step visual explanations of code.
|
|
2
|
+
|
|
3
|
+
import visset
|
|
4
|
+
visset.show(binary_search, [1, 3, 5, 7], 5) # a live call, like plt.show()
|
|
5
|
+
visset.show("total = 0\\nfor x in xs:\\n total += x", given={"xs": [4, 5]})
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
12
|
+
|
|
13
|
+
from ._version import __version__
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from .tracer.request import Limits
|
|
17
|
+
|
|
18
|
+
__all__ = ["__version__", "show", "to_html", "trace", "trace_call", "trace_source"]
|
|
19
|
+
|
|
20
|
+
Trace = dict[str, Any]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def trace(target: str | Callable[..., Any], /, *args: Any, **kwargs: Any) -> Trace:
|
|
24
|
+
"""Trace a function call or a piece of source code.
|
|
25
|
+
|
|
26
|
+
- `trace(fn, *args, **kwargs)` calls `fn` and records only that call.
|
|
27
|
+
(Use `trace_call` for options such as `lines=` or `limits=`.)
|
|
28
|
+
- `trace("source", **options)` runs source text; see `trace_source` for the options.
|
|
29
|
+
"""
|
|
30
|
+
if callable(target):
|
|
31
|
+
return trace_call(target, args, kwargs)
|
|
32
|
+
if args:
|
|
33
|
+
raise TypeError("trace(source, ...) takes options as keywords; see trace_source()")
|
|
34
|
+
return trace_source(target, **kwargs)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def trace_call(
|
|
38
|
+
fn: Callable[..., Any],
|
|
39
|
+
args: tuple[Any, ...] = (),
|
|
40
|
+
kwargs: dict[str, Any] | None = None,
|
|
41
|
+
*,
|
|
42
|
+
lines: tuple[int, int] | None = None,
|
|
43
|
+
limits: Limits | None = None,
|
|
44
|
+
title: str | None = None,
|
|
45
|
+
) -> Trace:
|
|
46
|
+
"""Trace one call of `fn` in this process. Shows only `fn`'s source (or `lines` of its file).
|
|
47
|
+
|
|
48
|
+
Arguments can be any objects. Runs without the sandbox, so use it on code you trust to
|
|
49
|
+
finish; the step and recursion limits still apply.
|
|
50
|
+
"""
|
|
51
|
+
from .tracer.live import trace_call as _trace_call
|
|
52
|
+
|
|
53
|
+
return _trace_call(fn, args, kwargs, lines=lines, limits=limits, title=title)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def trace_source(
|
|
57
|
+
source: str,
|
|
58
|
+
*,
|
|
59
|
+
path: str = "snippet.py",
|
|
60
|
+
function: str | None = None,
|
|
61
|
+
args: str | tuple[Any, ...] = "",
|
|
62
|
+
given: str | dict[str, Any] | None = None,
|
|
63
|
+
lines: tuple[int, int] | None = None,
|
|
64
|
+
stdin: str = "",
|
|
65
|
+
limits: Limits | None = None,
|
|
66
|
+
sandbox: bool = True,
|
|
67
|
+
title: str | None = None,
|
|
68
|
+
) -> Trace:
|
|
69
|
+
"""Run source text and record it.
|
|
70
|
+
|
|
71
|
+
- `given`: inputs for a snippet, e.g. `{"arr": [1, 3, 5], "target": 3}` (literals).
|
|
72
|
+
- `function` + `args`: run the module untraced, then trace only that call.
|
|
73
|
+
- `lines`: show only these lines (1-based, inclusive), e.g. part of a function.
|
|
74
|
+
- `sandbox=False`: run in this process (faster start, no time/memory protection).
|
|
75
|
+
"""
|
|
76
|
+
import textwrap
|
|
77
|
+
|
|
78
|
+
from .tracer import Limits, TraceRequest, execute, run_sandboxed
|
|
79
|
+
|
|
80
|
+
if isinstance(args, tuple):
|
|
81
|
+
args = ", ".join(repr(a) for a in args)
|
|
82
|
+
if isinstance(given, dict):
|
|
83
|
+
given = ", ".join(f"{k}={v!r}" for k, v in given.items())
|
|
84
|
+
req = TraceRequest(
|
|
85
|
+
source=textwrap.dedent(source), # a pasted fragment of a function is indented
|
|
86
|
+
path=path,
|
|
87
|
+
function=function,
|
|
88
|
+
args=args,
|
|
89
|
+
given=given or "",
|
|
90
|
+
lines=lines,
|
|
91
|
+
stdin=stdin,
|
|
92
|
+
limits=limits or Limits(),
|
|
93
|
+
title=title,
|
|
94
|
+
)
|
|
95
|
+
result, _status = run_sandboxed(req) if sandbox else execute(req)
|
|
96
|
+
return result
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def to_html(trace: Trace, path: str | None = None) -> str:
|
|
100
|
+
"""Render a trace as one self-contained HTML file. Returns the HTML; writes it if `path`."""
|
|
101
|
+
from .export.html import render_html
|
|
102
|
+
|
|
103
|
+
html = render_html(trace)
|
|
104
|
+
if path is not None:
|
|
105
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
106
|
+
f.write(html)
|
|
107
|
+
return html
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def show(target: Trace | str | Callable[..., Any], /, *args: Any, **kwargs: Any) -> str:
|
|
111
|
+
"""Open a walkthrough in the browser, like `plt.show()`. Returns the HTML file's path.
|
|
112
|
+
|
|
113
|
+
Accepts a trace, or the same arguments as `trace()`: `show(fn, *args)` / `show("code")`.
|
|
114
|
+
"""
|
|
115
|
+
from .export.html import open_in_browser
|
|
116
|
+
|
|
117
|
+
data = target if isinstance(target, dict) else trace(target, *args, **kwargs)
|
|
118
|
+
return open_in_browser(data)
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";(()=>{var Ze=Object.defineProperty;var Qe=(a,s,t)=>s in a?Ze(a,s,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[s]=t;var k=(a,s,t)=>Qe(a,typeof s!="symbol"?s+"":s,t);function ae(a,s,t){if(a&&a.length){let[e,n]=s,r=Math.PI/180*t,i=Math.cos(r),o=Math.sin(r);for(let l of a){let[c,h]=l;l[0]=(c-e)*i-(h-n)*o+e,l[1]=(c-e)*o+(h-n)*i+n}}}function Ue(a,s){return a[0]===s[0]&&a[1]===s[1]}function Je(a,s,t,e=1){let n=t,r=Math.max(s,.1),i=a[0]&&a[0][0]&&typeof a[0][0]=="number"?[a]:a,o=[0,0];if(n)for(let c of i)ae(c,o,n);let l=(function(c,h,m){let d=[];for(let b of c){let x=[...b];Ue(x[0],x[x.length-1])||x.push([x[0][0],x[0][1]]),x.length>2&&d.push(x)}let u=[];h=Math.max(h,.1);let f=[];for(let b of d)for(let x=0;x<b.length-1;x++){let w=b[x],S=b[x+1];if(w[1]!==S[1]){let M=Math.min(w[1],S[1]);f.push({ymin:M,ymax:Math.max(w[1],S[1]),x:M===w[1]?w[0]:S[0],islope:(S[0]-w[0])/(S[1]-w[1])})}}if(f.sort(((b,x)=>b.ymin<x.ymin?-1:b.ymin>x.ymin?1:b.x<x.x?-1:b.x>x.x?1:b.ymax===x.ymax?0:(b.ymax-x.ymax)/Math.abs(b.ymax-x.ymax))),!f.length)return u;let p=[],g=f[0].ymin,v=0;for(;p.length||f.length;){if(f.length){let b=-1;for(let x=0;x<f.length&&!(f[x].ymin>g);x++)b=x;f.splice(0,b+1).forEach((x=>{p.push({s:g,edge:x})}))}if(p=p.filter((b=>!(b.edge.ymax<=g))),p.sort(((b,x)=>b.edge.x===x.edge.x?0:(b.edge.x-x.edge.x)/Math.abs(b.edge.x-x.edge.x))),(m!==1||v%h==0)&&p.length>1)for(let b=0;b<p.length;b+=2){let x=b+1;if(x>=p.length)break;let w=p[b].edge,S=p[x].edge;u.push([[Math.round(w.x),g],[Math.round(S.x),g]])}g+=m,p.forEach((b=>{b.edge.x=b.edge.x+m*b.edge.islope})),v++}return u})(i,r,e);if(n){for(let c of i)ae(c,o,-n);(function(c,h,m){let d=[];c.forEach((u=>d.push(...u))),ae(d,h,m)})(l,o,-n)}return l}function q(a,s){var t;let e=s.hachureAngle+90,n=s.hachureGap;n<0&&(n=4*s.strokeWidth),n=Math.round(Math.max(n,.1));let r=1;return s.roughness>=1&&(((t=s.randomizer)===null||t===void 0?void 0:t.next())||Math.random())>.7&&(r=n),Je(a,n,e,r||1)}var F=class{constructor(s){this.helper=s}fillPolygons(s,t){return this._fillPolygons(s,t)}_fillPolygons(s,t){let e=q(s,t);return{type:"fillSketch",ops:this.renderLines(e,t)}}renderLines(s,t){let e=[];for(let n of s)e.push(...this.helper.doubleLineOps(n[0][0],n[0][1],n[1][0],n[1][1],t));return e}};function ne(a){let s=a[0],t=a[1];return Math.sqrt(Math.pow(s[0]-t[0],2)+Math.pow(s[1]-t[1],2))}var pe=class extends F{fillPolygons(s,t){let e=t.hachureGap;e<0&&(e=4*t.strokeWidth),e=Math.max(e,.1);let n=q(s,Object.assign({},t,{hachureGap:e})),r=Math.PI/180*t.hachureAngle,i=[],o=.5*e*Math.cos(r),l=.5*e*Math.sin(r);for(let[c,h]of n)ne([c,h])&&i.push([[c[0]-o,c[1]+l],[...h]],[[c[0]+o,c[1]-l],[...h]]);return{type:"fillSketch",ops:this.renderLines(i,t)}}},ue=class extends F{fillPolygons(s,t){let e=this._fillPolygons(s,t),n=Object.assign({},t,{hachureAngle:t.hachureAngle+90}),r=this._fillPolygons(s,n);return e.ops=e.ops.concat(r.ops),e}},de=class{constructor(s){this.helper=s}fillPolygons(s,t){let e=q(s,t=Object.assign({},t,{hachureAngle:0}));return this.dotsOnLines(e,t)}dotsOnLines(s,t){let e=[],n=t.hachureGap;n<0&&(n=4*t.strokeWidth),n=Math.max(n,.1);let r=t.fillWeight;r<0&&(r=t.strokeWidth/2);let i=n/4;for(let o of s){let l=ne(o),c=l/n,h=Math.ceil(c)-1,m=l-h*n,d=(o[0][0]+o[1][0])/2-n/4,u=Math.min(o[0][1],o[1][1]);for(let f=0;f<h;f++){let p=u+m+f*n,g=d-i+2*Math.random()*i,v=p-i+2*Math.random()*i,b=this.helper.ellipse(g,v,r,r,t);e.push(...b.ops)}}return{type:"fillSketch",ops:e}}},fe=class{constructor(s){this.helper=s}fillPolygons(s,t){let e=q(s,t);return{type:"fillSketch",ops:this.dashedLine(e,t)}}dashedLine(s,t){let e=t.dashOffset<0?t.hachureGap<0?4*t.strokeWidth:t.hachureGap:t.dashOffset,n=t.dashGap<0?t.hachureGap<0?4*t.strokeWidth:t.hachureGap:t.dashGap,r=[];return s.forEach((i=>{let o=ne(i),l=Math.floor(o/(e+n)),c=(o+n-l*(e+n))/2,h=i[0],m=i[1];h[0]>m[0]&&(h=i[1],m=i[0]);let d=Math.atan((m[1]-h[1])/(m[0]-h[0]));for(let u=0;u<l;u++){let f=u*(e+n),p=f+e,g=[h[0]+f*Math.cos(d)+c*Math.cos(d),h[1]+f*Math.sin(d)+c*Math.sin(d)],v=[h[0]+p*Math.cos(d)+c*Math.cos(d),h[1]+p*Math.sin(d)+c*Math.sin(d)];r.push(...this.helper.doubleLineOps(g[0],g[1],v[0],v[1],t))}})),r}},me=class{constructor(s){this.helper=s}fillPolygons(s,t){let e=t.hachureGap<0?4*t.strokeWidth:t.hachureGap,n=t.zigzagOffset<0?e:t.zigzagOffset,r=q(s,t=Object.assign({},t,{hachureGap:e+n}));return{type:"fillSketch",ops:this.zigzagLines(r,n,t)}}zigzagLines(s,t,e){let n=[];return s.forEach((r=>{let i=ne(r),o=Math.round(i/(2*t)),l=r[0],c=r[1];l[0]>c[0]&&(l=r[1],c=r[0]);let h=Math.atan((c[1]-l[1])/(c[0]-l[0]));for(let m=0;m<o;m++){let d=2*m*t,u=2*(m+1)*t,f=Math.sqrt(2*Math.pow(t,2)),p=[l[0]+d*Math.cos(h),l[1]+d*Math.sin(h)],g=[l[0]+u*Math.cos(h),l[1]+u*Math.sin(h)],v=[p[0]+f*Math.cos(h+Math.PI/4),p[1]+f*Math.sin(h+Math.PI/4)];n.push(...this.helper.doubleLineOps(p[0],p[1],v[0],v[1],e),...this.helper.doubleLineOps(v[0],v[1],g[0],g[1],e))}})),n}},T={},ge=class{constructor(s){this.seed=s}next(){return this.seed?(2**31-1&(this.seed=Math.imul(48271,this.seed)))/2**31:Math.random()}},Xe=0,ie=1,Ee=2,Q={A:7,a:7,C:6,c:6,H:1,h:1,L:2,l:2,M:2,m:2,Q:4,q:4,S:4,s:4,T:2,t:2,V:1,v:1,Z:0,z:0};function le(a,s){return a.type===s}function Me(a){let s=[],t=(function(i){let o=new Array;for(;i!=="";)if(i.match(/^([ \t\r\n,]+)/))i=i.substr(RegExp.$1.length);else if(i.match(/^([aAcChHlLmMqQsStTvVzZ])/))o[o.length]={type:Xe,text:RegExp.$1},i=i.substr(RegExp.$1.length);else{if(!i.match(/^(([-+]?[0-9]+(\.[0-9]*)?|[-+]?\.[0-9]+)([eE][-+]?[0-9]+)?)/))return[];o[o.length]={type:ie,text:`${parseFloat(RegExp.$1)}`},i=i.substr(RegExp.$1.length)}return o[o.length]={type:Ee,text:""},o})(a),e="BOD",n=0,r=t[n];for(;!le(r,Ee);){let i=0,o=[];if(e==="BOD"){if(r.text!=="M"&&r.text!=="m")return Me("M0,0"+a);n++,i=Q[r.text],e=r.text}else le(r,ie)?i=Q[e]:(n++,i=Q[r.text],e=r.text);if(!(n+i<t.length))throw new Error("Path data ended short");for(let l=n;l<n+i;l++){let c=t[l];if(!le(c,ie))throw new Error("Param not a number: "+e+","+c.text);o[o.length]=+c.text}if(typeof Q[e]!="number")throw new Error("Bad segment: "+e);{let l={key:e,data:o};s.push(l),n+=i,r=t[n],e==="M"&&(e="L"),e==="m"&&(e="l")}}return s}function He(a){let s=0,t=0,e=0,n=0,r=[];for(let{key:i,data:o}of a)switch(i){case"M":r.push({key:"M",data:[...o]}),[s,t]=o,[e,n]=o;break;case"m":s+=o[0],t+=o[1],r.push({key:"M",data:[s,t]}),e=s,n=t;break;case"L":r.push({key:"L",data:[...o]}),[s,t]=o;break;case"l":s+=o[0],t+=o[1],r.push({key:"L",data:[s,t]});break;case"C":r.push({key:"C",data:[...o]}),s=o[4],t=o[5];break;case"c":{let l=o.map(((c,h)=>h%2?c+t:c+s));r.push({key:"C",data:l}),s=l[4],t=l[5];break}case"Q":r.push({key:"Q",data:[...o]}),s=o[2],t=o[3];break;case"q":{let l=o.map(((c,h)=>h%2?c+t:c+s));r.push({key:"Q",data:l}),s=l[2],t=l[3];break}case"A":r.push({key:"A",data:[...o]}),s=o[5],t=o[6];break;case"a":s+=o[5],t+=o[6],r.push({key:"A",data:[o[0],o[1],o[2],o[3],o[4],s,t]});break;case"H":r.push({key:"H",data:[...o]}),s=o[0];break;case"h":s+=o[0],r.push({key:"H",data:[s]});break;case"V":r.push({key:"V",data:[...o]}),t=o[0];break;case"v":t+=o[0],r.push({key:"V",data:[t]});break;case"S":r.push({key:"S",data:[...o]}),s=o[2],t=o[3];break;case"s":{let l=o.map(((c,h)=>h%2?c+t:c+s));r.push({key:"S",data:l}),s=l[2],t=l[3];break}case"T":r.push({key:"T",data:[...o]}),s=o[0],t=o[1];break;case"t":s+=o[0],t+=o[1],r.push({key:"T",data:[s,t]});break;case"Z":case"z":r.push({key:"Z",data:[]}),s=e,t=n}return r}function Ne(a){let s=[],t="",e=0,n=0,r=0,i=0,o=0,l=0;for(let{key:c,data:h}of a){switch(c){case"M":s.push({key:"M",data:[...h]}),[e,n]=h,[r,i]=h;break;case"C":s.push({key:"C",data:[...h]}),e=h[4],n=h[5],o=h[2],l=h[3];break;case"L":s.push({key:"L",data:[...h]}),[e,n]=h;break;case"H":e=h[0],s.push({key:"L",data:[e,n]});break;case"V":n=h[0],s.push({key:"L",data:[e,n]});break;case"S":{let m=0,d=0;t==="C"||t==="S"?(m=e+(e-o),d=n+(n-l)):(m=e,d=n),s.push({key:"C",data:[m,d,...h]}),o=h[0],l=h[1],e=h[2],n=h[3];break}case"T":{let[m,d]=h,u=0,f=0;t==="Q"||t==="T"?(u=e+(e-o),f=n+(n-l)):(u=e,f=n);let p=e+2*(u-e)/3,g=n+2*(f-n)/3,v=m+2*(u-m)/3,b=d+2*(f-d)/3;s.push({key:"C",data:[p,g,v,b,m,d]}),o=u,l=f,e=m,n=d;break}case"Q":{let[m,d,u,f]=h,p=e+2*(m-e)/3,g=n+2*(d-n)/3,v=u+2*(m-u)/3,b=f+2*(d-f)/3;s.push({key:"C",data:[p,g,v,b,u,f]}),o=m,l=d,e=u,n=f;break}case"A":{let m=Math.abs(h[0]),d=Math.abs(h[1]),u=h[2],f=h[3],p=h[4],g=h[5],v=h[6];m===0||d===0?(s.push({key:"C",data:[e,n,g,v,g,v]}),e=g,n=v):(e!==g||n!==v)&&(De(e,n,g,v,m,d,u,f,p).forEach((function(b){s.push({key:"C",data:b})})),e=g,n=v);break}case"Z":s.push({key:"Z",data:[]}),e=r,n=i}t=c}return s}function _(a,s,t){return[a*Math.cos(t)-s*Math.sin(t),a*Math.sin(t)+s*Math.cos(t)]}function De(a,s,t,e,n,r,i,o,l,c){let h=(m=i,Math.PI*m/180);var m;let d=[],u=0,f=0,p=0,g=0;if(c)[u,f,p,g]=c;else{[a,s]=_(a,s,-h),[t,e]=_(t,e,-h);let $=(a-t)/2,E=(s-e)/2,A=$*$/(n*n)+E*E/(r*r);A>1&&(A=Math.sqrt(A),n*=A,r*=A);let z=n*n,I=r*r,Ge=z*I-z*E*E-I*$*$,Ke=z*E*E+I*$*$,Se=(o===l?-1:1)*Math.sqrt(Math.abs(Ge/Ke));p=Se*n*E/r+(a+t)/2,g=Se*-r*$/n+(s+e)/2,u=Math.asin(parseFloat(((s-g)/r).toFixed(9))),f=Math.asin(parseFloat(((e-g)/r).toFixed(9))),a<p&&(u=Math.PI-u),t<p&&(f=Math.PI-f),u<0&&(u=2*Math.PI+u),f<0&&(f=2*Math.PI+f),l&&u>f&&(u-=2*Math.PI),!l&&f>u&&(f-=2*Math.PI)}let v=f-u;if(Math.abs(v)>120*Math.PI/180){let $=f,E=t,A=e;f=l&&f>u?u+120*Math.PI/180*1:u+120*Math.PI/180*-1,d=De(t=p+n*Math.cos(f),e=g+r*Math.sin(f),E,A,n,r,i,0,l,[f,$,p,g])}v=f-u;let b=Math.cos(u),x=Math.sin(u),w=Math.cos(f),S=Math.sin(f),M=Math.tan(v/4),P=4/3*n*M,O=4/3*r*M,Z=[a,s],C=[a+P*x,s-O*b],N=[t+P*S,e-O*w],we=[t,e];if(C[0]=2*Z[0]-C[0],C[1]=2*Z[1]-C[1],c)return[C,N,we].concat(d);{d=[C,N,we].concat(d);let $=[];for(let E=0;E<d.length;E+=3){let A=_(d[E][0],d[E][1],h),z=_(d[E+1][0],d[E+1][1],h),I=_(d[E+2][0],d[E+2][1],h);$.push([A[0],A[1],z[0],z[1],I[0],I[1]])}return $}}var Ye={randOffset:function(a,s){return y(a,s)},randOffsetWithRange:function(a,s,t){return ee(a,s,t)},ellipse:function(a,s,t,e,n){let r=ze(t,e,n);return be(a,s,n,r).opset},doubleLineOps:function(a,s,t,e,n){return H(a,s,t,e,n,!0)}};function Ve(a,s,t,e,n){return{type:"path",ops:H(a,s,t,e,n)}}function X(a,s,t){let e=(a||[]).length;if(e>2){let n=[];for(let r=0;r<e-1;r++)n.push(...H(a[r][0],a[r][1],a[r+1][0],a[r+1][1],t));return s&&n.push(...H(a[e-1][0],a[e-1][1],a[0][0],a[0][1],t)),{type:"path",ops:n}}return e===2?Ve(a[0][0],a[0][1],a[1][0],a[1][1],t):{type:"path",ops:[]}}function et(a,s,t,e,n){return(function(r,i){return X(r,!0,i)})([[a,s],[a+t,s],[a+t,s+e],[a,s+e]],n)}function Te(a,s){if(a.length){let t=typeof a[0][0]=="number"?[a]:a,e=U(t[0],1*(1+.2*s.roughness),s),n=s.disableMultiStroke?[]:U(t[0],1.5*(1+.22*s.roughness),$e(s));for(let r=1;r<t.length;r++){let i=t[r];if(i.length){let o=U(i,1*(1+.2*s.roughness),s),l=s.disableMultiStroke?[]:U(i,1.5*(1+.22*s.roughness),$e(s));for(let c of o)c.op!=="move"&&e.push(c);for(let c of l)c.op!=="move"&&n.push(c)}}return{type:"path",ops:e.concat(n)}}return{type:"path",ops:[]}}function ze(a,s,t){let e=Math.sqrt(2*Math.PI*Math.sqrt((Math.pow(a/2,2)+Math.pow(s/2,2))/2)),n=Math.ceil(Math.max(t.curveStepCount,t.curveStepCount/Math.sqrt(200)*e)),r=2*Math.PI/n,i=Math.abs(a/2),o=Math.abs(s/2),l=1-t.curveFitting;return i+=y(i*l,t),o+=y(o*l,t),{increment:r,rx:i,ry:o}}function be(a,s,t,e){let[n,r]=Ce(e.increment,a,s,e.rx,e.ry,1,e.increment*ee(.1,ee(.4,1,t),t),t),i=te(n,null,t);if(!t.disableMultiStroke&&t.roughness!==0){let[o]=Ce(e.increment,a,s,e.rx,e.ry,1.5,0,t),l=te(o,null,t);i=i.concat(l)}return{estimatedPoints:r,opset:{type:"path",ops:i}}}function Le(a,s,t,e,n,r,i,o,l){let c=a,h=s,m=Math.abs(t/2),d=Math.abs(e/2);m+=y(.01*m,l),d+=y(.01*d,l);let u=n,f=r;for(;u<0;)u+=2*Math.PI,f+=2*Math.PI;f-u>2*Math.PI&&(u=0,f=2*Math.PI);let p=2*Math.PI/l.curveStepCount,g=Math.min(p/2,(f-u)/2),v=Oe(g,c,h,m,d,u,f,1,l);if(!l.disableMultiStroke){let b=Oe(g,c,h,m,d,u,f,1.5,l);v.push(...b)}return i&&(o?v.push(...H(c,h,c+m*Math.cos(u),h+d*Math.sin(u),l),...H(c,h,c+m*Math.cos(f),h+d*Math.sin(f),l)):v.push({op:"lineTo",data:[c,h]},{op:"lineTo",data:[c+m*Math.cos(u),h+d*Math.sin(u)]})),{type:"path",ops:v}}function Pe(a,s){let t=Ne(He(Me(a))),e=[],n=[0,0],r=[0,0];for(let{key:i,data:o}of t)switch(i){case"M":r=[o[0],o[1]],n=[o[0],o[1]];break;case"L":e.push(...H(r[0],r[1],o[0],o[1],s)),r=[o[0],o[1]];break;case"C":{let[l,c,h,m,d,u]=o;e.push(...tt(l,c,h,m,d,u,r,s)),r=[d,u];break}case"Z":e.push(...H(r[0],r[1],n[0],n[1],s)),r=[n[0],n[1]]}return{type:"path",ops:e}}function ce(a,s){let t=[];for(let e of a)if(e.length){let n=s.maxRandomnessOffset||0,r=e.length;if(r>2){t.push({op:"move",data:[e[0][0]+y(n,s),e[0][1]+y(n,s)]});for(let i=1;i<r;i++)t.push({op:"lineTo",data:[e[i][0]+y(n,s),e[i][1]+y(n,s)]})}}return{type:"fillPath",ops:t}}function R(a,s){return(function(t,e){let n=t.fillStyle||"hachure";if(!T[n])switch(n){case"zigzag":T[n]||(T[n]=new pe(e));break;case"cross-hatch":T[n]||(T[n]=new ue(e));break;case"dots":T[n]||(T[n]=new de(e));break;case"dashed":T[n]||(T[n]=new fe(e));break;case"zigzag-line":T[n]||(T[n]=new me(e));break;default:n="hachure",T[n]||(T[n]=new F(e))}return T[n]})(s,Ye).fillPolygons(a,s)}function $e(a){let s=Object.assign({},a);return s.randomizer=void 0,a.seed&&(s.seed=a.seed+1),s}function Ie(a){return a.randomizer||(a.randomizer=new ge(a.seed||0)),a.randomizer.next()}function ee(a,s,t,e=1){return t.roughness*e*(Ie(t)*(s-a)+a)}function y(a,s,t=1){return ee(-a,a,s,t)}function H(a,s,t,e,n,r=!1){let i=r?n.disableMultiStrokeFill:n.disableMultiStroke,o=ve(a,s,t,e,n,!0,!1);if(i)return o;let l=ve(a,s,t,e,n,!0,!0);return o.concat(l)}function ve(a,s,t,e,n,r,i){let o=Math.pow(a-t,2)+Math.pow(s-e,2),l=Math.sqrt(o),c=1;c=l<200?1:l>500?.4:-.0016668*l+1.233334;let h=n.maxRandomnessOffset||0;h*h*100>o&&(h=l/10);let m=h/2,d=.2+.2*Ie(n),u=n.bowing*n.maxRandomnessOffset*(e-s)/200,f=n.bowing*n.maxRandomnessOffset*(a-t)/200;u=y(u,n,c),f=y(f,n,c);let p=[],g=()=>y(m,n,c),v=()=>y(h,n,c),b=n.preserveVertices;return r&&(i?p.push({op:"move",data:[a+(b?0:g()),s+(b?0:g())]}):p.push({op:"move",data:[a+(b?0:y(h,n,c)),s+(b?0:y(h,n,c))]})),i?p.push({op:"bcurveTo",data:[u+a+(t-a)*d+g(),f+s+(e-s)*d+g(),u+a+2*(t-a)*d+g(),f+s+2*(e-s)*d+g(),t+(b?0:g()),e+(b?0:g())]}):p.push({op:"bcurveTo",data:[u+a+(t-a)*d+v(),f+s+(e-s)*d+v(),u+a+2*(t-a)*d+v(),f+s+2*(e-s)*d+v(),t+(b?0:v()),e+(b?0:v())]}),p}function U(a,s,t){if(!a.length)return[];let e=[];e.push([a[0][0]+y(s,t),a[0][1]+y(s,t)]),e.push([a[0][0]+y(s,t),a[0][1]+y(s,t)]);for(let n=1;n<a.length;n++)e.push([a[n][0]+y(s,t),a[n][1]+y(s,t)]),n===a.length-1&&e.push([a[n][0]+y(s,t),a[n][1]+y(s,t)]);return te(e,null,t)}function te(a,s,t){let e=a.length,n=[];if(e>3){let r=[],i=1-t.curveTightness;n.push({op:"move",data:[a[1][0],a[1][1]]});for(let o=1;o+2<e;o++){let l=a[o];r[0]=[l[0],l[1]],r[1]=[l[0]+(i*a[o+1][0]-i*a[o-1][0])/6,l[1]+(i*a[o+1][1]-i*a[o-1][1])/6],r[2]=[a[o+1][0]+(i*a[o][0]-i*a[o+2][0])/6,a[o+1][1]+(i*a[o][1]-i*a[o+2][1])/6],r[3]=[a[o+1][0],a[o+1][1]],n.push({op:"bcurveTo",data:[r[1][0],r[1][1],r[2][0],r[2][1],r[3][0],r[3][1]]})}if(s&&s.length===2){let o=t.maxRandomnessOffset;n.push({op:"lineTo",data:[s[0]+y(o,t),s[1]+y(o,t)]})}}else e===3?(n.push({op:"move",data:[a[1][0],a[1][1]]}),n.push({op:"bcurveTo",data:[a[1][0],a[1][1],a[2][0],a[2][1],a[2][0],a[2][1]]})):e===2&&n.push(...ve(a[0][0],a[0][1],a[1][0],a[1][1],t,!0,!0));return n}function Ce(a,s,t,e,n,r,i,o){let l=[],c=[];if(o.roughness===0){a/=4,c.push([s+e*Math.cos(-a),t+n*Math.sin(-a)]);for(let h=0;h<=2*Math.PI;h+=a){let m=[s+e*Math.cos(h),t+n*Math.sin(h)];l.push(m),c.push(m)}c.push([s+e*Math.cos(0),t+n*Math.sin(0)]),c.push([s+e*Math.cos(a),t+n*Math.sin(a)])}else{let h=y(.5,o)-Math.PI/2;c.push([y(r,o)+s+.9*e*Math.cos(h-a),y(r,o)+t+.9*n*Math.sin(h-a)]);let m=2*Math.PI+h-.01;for(let d=h;d<m;d+=a){let u=[y(r,o)+s+e*Math.cos(d),y(r,o)+t+n*Math.sin(d)];l.push(u),c.push(u)}c.push([y(r,o)+s+e*Math.cos(h+2*Math.PI+.5*i),y(r,o)+t+n*Math.sin(h+2*Math.PI+.5*i)]),c.push([y(r,o)+s+.98*e*Math.cos(h+i),y(r,o)+t+.98*n*Math.sin(h+i)]),c.push([y(r,o)+s+.9*e*Math.cos(h+.5*i),y(r,o)+t+.9*n*Math.sin(h+.5*i)])}return[c,l]}function Oe(a,s,t,e,n,r,i,o,l){let c=r+y(.1,l),h=[];h.push([y(o,l)+s+.9*e*Math.cos(c-a),y(o,l)+t+.9*n*Math.sin(c-a)]);for(let m=c;m<=i;m+=a)h.push([y(o,l)+s+e*Math.cos(m),y(o,l)+t+n*Math.sin(m)]);return h.push([s+e*Math.cos(i),t+n*Math.sin(i)]),h.push([s+e*Math.cos(i),t+n*Math.sin(i)]),te(h,null,l)}function tt(a,s,t,e,n,r,i,o){let l=[],c=[o.maxRandomnessOffset||1,(o.maxRandomnessOffset||1)+.3],h=[0,0],m=o.disableMultiStroke?1:2,d=o.preserveVertices;for(let u=0;u<m;u++)u===0?l.push({op:"move",data:[i[0],i[1]]}):l.push({op:"move",data:[i[0]+(d?0:y(c[0],o)),i[1]+(d?0:y(c[0],o))]}),h=d?[n,r]:[n+y(c[u],o),r+y(c[u],o)],l.push({op:"bcurveTo",data:[a+y(c[u],o),s+y(c[u],o),t+y(c[u],o),e+y(c[u],o),h[0],h[1]]});return l}function B(a){return[...a]}function Ae(a,s=0){let t=a.length;if(t<3)throw new Error("A curve must have at least three points.");let e=[];if(t===3)e.push(B(a[0]),B(a[1]),B(a[2]),B(a[2]));else{let n=[];n.push(a[0],a[0]);for(let o=1;o<a.length;o++)n.push(a[o]),o===a.length-1&&n.push(a[o]);let r=[],i=1-s;e.push(B(n[0]));for(let o=1;o+2<n.length;o++){let l=n[o];r[0]=[l[0],l[1]],r[1]=[l[0]+(i*n[o+1][0]-i*n[o-1][0])/6,l[1]+(i*n[o+1][1]-i*n[o-1][1])/6],r[2]=[n[o+1][0]+(i*n[o][0]-i*n[o+2][0])/6,n[o+1][1]+(i*n[o][1]-i*n[o+2][1])/6],r[3]=[n[o+1][0],n[o+1][1]],e.push(r[1],r[2],r[3])}}return e}function Y(a,s){return Math.pow(a[0]-s[0],2)+Math.pow(a[1]-s[1],2)}function st(a,s,t){let e=Y(s,t);if(e===0)return Y(a,s);let n=((a[0]-s[0])*(t[0]-s[0])+(a[1]-s[1])*(t[1]-s[1]))/e;return n=Math.max(0,Math.min(1,n)),Y(a,D(s,t,n))}function D(a,s,t){return[a[0]+(s[0]-a[0])*t,a[1]+(s[1]-a[1])*t]}function xe(a,s,t,e){let n=e||[];if((function(o,l){let c=o[l+0],h=o[l+1],m=o[l+2],d=o[l+3],u=3*h[0]-2*c[0]-d[0];u*=u;let f=3*h[1]-2*c[1]-d[1];f*=f;let p=3*m[0]-2*d[0]-c[0];p*=p;let g=3*m[1]-2*d[1]-c[1];return g*=g,u<p&&(u=p),f<g&&(f=g),u+f})(a,s)<t){let o=a[s+0];n.length?(r=n[n.length-1],i=o,Math.sqrt(Y(r,i))>1&&n.push(o)):n.push(o),n.push(a[s+3])}else{let l=a[s+0],c=a[s+1],h=a[s+2],m=a[s+3],d=D(l,c,.5),u=D(c,h,.5),f=D(h,m,.5),p=D(d,u,.5),g=D(u,f,.5),v=D(p,g,.5);xe([l,d,p,v],0,t,n),xe([v,g,f,m],0,t,n)}var r,i;return n}function nt(a,s){return se(a,0,a.length,s)}function se(a,s,t,e,n){let r=n||[],i=a[s],o=a[t-1],l=0,c=1;for(let h=s+1;h<t-1;++h){let m=st(a[h],i,o);m>l&&(l=m,c=h)}return Math.sqrt(l)>e?(se(a,s,c+1,e,r),se(a,c,t,e,r)):(r.length||r.push(i),r.push(o)),r}function he(a,s=.15,t){let e=[],n=(a.length-1)/3;for(let r=0;r<n;r++)xe(a,3*r,s,e);return t&&t>0?se(e,0,e.length,t):e}var L="none",W=class{constructor(s){this.defaultOptions={maxRandomnessOffset:2,roughness:1,bowing:1,stroke:"#000",strokeWidth:1,curveTightness:0,curveFitting:.95,curveStepCount:9,fillStyle:"hachure",fillWeight:-1,hachureAngle:-41,hachureGap:-1,dashOffset:-1,dashGap:-1,zigzagOffset:-1,seed:0,disableMultiStroke:!1,disableMultiStrokeFill:!1,preserveVertices:!1,fillShapeRoughnessGain:.8},this.config=s||{},this.config.options&&(this.defaultOptions=this._o(this.config.options))}static newSeed(){return Math.floor(Math.random()*2**31)}_o(s){return s?Object.assign({},this.defaultOptions,s):this.defaultOptions}_d(s,t,e){return{shape:s,sets:t||[],options:e||this.defaultOptions}}line(s,t,e,n,r){let i=this._o(r);return this._d("line",[Ve(s,t,e,n,i)],i)}rectangle(s,t,e,n,r){let i=this._o(r),o=[],l=et(s,t,e,n,i);if(i.fill){let c=[[s,t],[s+e,t],[s+e,t+n],[s,t+n]];i.fillStyle==="solid"?o.push(ce([c],i)):o.push(R([c],i))}return i.stroke!==L&&o.push(l),this._d("rectangle",o,i)}ellipse(s,t,e,n,r){let i=this._o(r),o=[],l=ze(e,n,i),c=be(s,t,i,l);if(i.fill)if(i.fillStyle==="solid"){let h=be(s,t,i,l).opset;h.type="fillPath",o.push(h)}else o.push(R([c.estimatedPoints],i));return i.stroke!==L&&o.push(c.opset),this._d("ellipse",o,i)}circle(s,t,e,n){let r=this.ellipse(s,t,e,e,n);return r.shape="circle",r}linearPath(s,t){let e=this._o(t);return this._d("linearPath",[X(s,!1,e)],e)}arc(s,t,e,n,r,i,o=!1,l){let c=this._o(l),h=[],m=Le(s,t,e,n,r,i,o,!0,c);if(o&&c.fill)if(c.fillStyle==="solid"){let d=Object.assign({},c);d.disableMultiStroke=!0;let u=Le(s,t,e,n,r,i,!0,!1,d);u.type="fillPath",h.push(u)}else h.push((function(d,u,f,p,g,v,b){let x=d,w=u,S=Math.abs(f/2),M=Math.abs(p/2);S+=y(.01*S,b),M+=y(.01*M,b);let P=g,O=v;for(;P<0;)P+=2*Math.PI,O+=2*Math.PI;O-P>2*Math.PI&&(P=0,O=2*Math.PI);let Z=(O-P)/b.curveStepCount,C=[];for(let N=P;N<=O;N+=Z)C.push([x+S*Math.cos(N),w+M*Math.sin(N)]);return C.push([x+S*Math.cos(O),w+M*Math.sin(O)]),C.push([x,w]),R([C],b)})(s,t,e,n,r,i,c));return c.stroke!==L&&h.push(m),this._d("arc",h,c)}curve(s,t){let e=this._o(t),n=[],r=Te(s,e);if(e.fill&&e.fill!==L)if(e.fillStyle==="solid"){let i=Te(s,Object.assign(Object.assign({},e),{disableMultiStroke:!0,roughness:e.roughness?e.roughness+e.fillShapeRoughnessGain:0}));n.push({type:"fillPath",ops:this._mergedShape(i.ops)})}else{let i=[],o=s;if(o.length){let l=typeof o[0][0]=="number"?[o]:o;for(let c of l)c.length<3?i.push(...c):c.length===3?i.push(...he(Ae([c[0],c[0],c[1],c[2]]),10,(1+e.roughness)/2)):i.push(...he(Ae(c),10,(1+e.roughness)/2))}i.length&&n.push(R([i],e))}return e.stroke!==L&&n.push(r),this._d("curve",n,e)}polygon(s,t){let e=this._o(t),n=[],r=X(s,!0,e);return e.fill&&(e.fillStyle==="solid"?n.push(ce([s],e)):n.push(R([s],e))),e.stroke!==L&&n.push(r),this._d("polygon",n,e)}path(s,t){let e=this._o(t),n=[];if(!s)return this._d("path",n,e);s=(s||"").replace(/\n/g," ").replace(/(-\s)/g,"-").replace("/(ss)/g"," ");let r=e.fill&&e.fill!=="transparent"&&e.fill!==L,i=e.stroke!==L,o=!!(e.simplification&&e.simplification<1),l=(function(h,m,d){let u=Ne(He(Me(h))),f=[],p=[],g=[0,0],v=[],b=()=>{v.length>=4&&p.push(...he(v,m)),v=[]},x=()=>{b(),p.length&&(f.push(p),p=[])};for(let{key:S,data:M}of u)switch(S){case"M":x(),g=[M[0],M[1]],p.push(g);break;case"L":b(),p.push([M[0],M[1]]);break;case"C":if(!v.length){let P=p.length?p[p.length-1]:g;v.push([P[0],P[1]])}v.push([M[0],M[1]]),v.push([M[2],M[3]]),v.push([M[4],M[5]]);break;case"Z":b(),p.push([g[0],g[1]])}if(x(),!d)return f;let w=[];for(let S of f){let M=nt(S,d);M.length&&w.push(M)}return w})(s,1,o?4-4*(e.simplification||1):(1+e.roughness)/2),c=Pe(s,e);if(r)if(e.fillStyle==="solid")if(l.length===1){let h=Pe(s,Object.assign(Object.assign({},e),{disableMultiStroke:!0,roughness:e.roughness?e.roughness+e.fillShapeRoughnessGain:0}));n.push({type:"fillPath",ops:this._mergedShape(h.ops)})}else n.push(ce(l,e));else n.push(R(l,e));return i&&(o?l.forEach((h=>{n.push(X(h,!1,e))})):n.push(c)),this._d("path",n,e)}opsToPath(s,t){let e="";for(let n of s.ops){let r=typeof t=="number"&&t>=0?n.data.map((i=>+i.toFixed(t))):n.data;switch(n.op){case"move":e+=`M${r[0]} ${r[1]} `;break;case"bcurveTo":e+=`C${r[0]} ${r[1]}, ${r[2]} ${r[3]}, ${r[4]} ${r[5]} `;break;case"lineTo":e+=`L${r[0]} ${r[1]} `}}return e.trim()}toPaths(s){let t=s.sets||[],e=s.options||this.defaultOptions,n=[];for(let r of t){let i=null;switch(r.type){case"path":i={d:this.opsToPath(r),stroke:e.stroke,strokeWidth:e.strokeWidth,fill:L};break;case"fillPath":i={d:this.opsToPath(r),stroke:L,strokeWidth:0,fill:e.fill||L};break;case"fillSketch":i=this.fillSketch(r,e)}i&&n.push(i)}return n}fillSketch(s,t){let e=t.fillWeight;return e<0&&(e=t.strokeWidth/2),{d:this.opsToPath(s),stroke:t.fill||L,strokeWidth:e,fill:L}}_mergedShape(s){return s.filter(((t,e)=>e===0||t.op!=="move"))}},ye=class{constructor(s,t){this.canvas=s,this.ctx=this.canvas.getContext("2d"),this.gen=new W(t)}draw(s){let t=s.sets||[],e=s.options||this.getDefaultOptions(),n=this.ctx,r=s.options.fixedDecimalPlaceDigits;for(let i of t)switch(i.type){case"path":n.save(),n.strokeStyle=e.stroke==="none"?"transparent":e.stroke,n.lineWidth=e.strokeWidth,e.strokeLineDash&&n.setLineDash(e.strokeLineDash),e.strokeLineDashOffset&&(n.lineDashOffset=e.strokeLineDashOffset),this._drawToContext(n,i,r),n.restore();break;case"fillPath":{n.save(),n.fillStyle=e.fill||"";let o=s.shape==="curve"||s.shape==="polygon"||s.shape==="path"?"evenodd":"nonzero";this._drawToContext(n,i,r,o),n.restore();break}case"fillSketch":this.fillSketch(n,i,e)}}fillSketch(s,t,e){let n=e.fillWeight;n<0&&(n=e.strokeWidth/2),s.save(),e.fillLineDash&&s.setLineDash(e.fillLineDash),e.fillLineDashOffset&&(s.lineDashOffset=e.fillLineDashOffset),s.strokeStyle=e.fill||"",s.lineWidth=n,this._drawToContext(s,t,e.fixedDecimalPlaceDigits),s.restore()}_drawToContext(s,t,e,n="nonzero"){s.beginPath();for(let r of t.ops){let i=typeof e=="number"&&e>=0?r.data.map((o=>+o.toFixed(e))):r.data;switch(r.op){case"move":s.moveTo(i[0],i[1]);break;case"bcurveTo":s.bezierCurveTo(i[0],i[1],i[2],i[3],i[4],i[5]);break;case"lineTo":s.lineTo(i[0],i[1])}}t.type==="fillPath"?s.fill(n):s.stroke()}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}line(s,t,e,n,r){let i=this.gen.line(s,t,e,n,r);return this.draw(i),i}rectangle(s,t,e,n,r){let i=this.gen.rectangle(s,t,e,n,r);return this.draw(i),i}ellipse(s,t,e,n,r){let i=this.gen.ellipse(s,t,e,n,r);return this.draw(i),i}circle(s,t,e,n){let r=this.gen.circle(s,t,e,n);return this.draw(r),r}linearPath(s,t){let e=this.gen.linearPath(s,t);return this.draw(e),e}polygon(s,t){let e=this.gen.polygon(s,t);return this.draw(e),e}arc(s,t,e,n,r,i,o=!1,l){let c=this.gen.arc(s,t,e,n,r,i,o,l);return this.draw(c),c}curve(s,t){let e=this.gen.curve(s,t);return this.draw(e),e}path(s,t){let e=this.gen.path(s,t);return this.draw(e),e}},J="http://www.w3.org/2000/svg",ke=class{constructor(s,t){this.svg=s,this.gen=new W(t)}draw(s){let t=s.sets||[],e=s.options||this.getDefaultOptions(),n=this.svg.ownerDocument||window.document,r=n.createElementNS(J,"g"),i=s.options.fixedDecimalPlaceDigits;for(let o of t){let l=null;switch(o.type){case"path":l=n.createElementNS(J,"path"),l.setAttribute("d",this.opsToPath(o,i)),l.setAttribute("stroke",e.stroke),l.setAttribute("stroke-width",e.strokeWidth+""),l.setAttribute("fill","none"),e.strokeLineDash&&l.setAttribute("stroke-dasharray",e.strokeLineDash.join(" ").trim()),e.strokeLineDashOffset&&l.setAttribute("stroke-dashoffset",`${e.strokeLineDashOffset}`);break;case"fillPath":l=n.createElementNS(J,"path"),l.setAttribute("d",this.opsToPath(o,i)),l.setAttribute("stroke","none"),l.setAttribute("stroke-width","0"),l.setAttribute("fill",e.fill||""),s.shape!=="curve"&&s.shape!=="polygon"||l.setAttribute("fill-rule","evenodd");break;case"fillSketch":l=this.fillSketch(n,o,e)}l&&r.appendChild(l)}return r}fillSketch(s,t,e){let n=e.fillWeight;n<0&&(n=e.strokeWidth/2);let r=s.createElementNS(J,"path");return r.setAttribute("d",this.opsToPath(t,e.fixedDecimalPlaceDigits)),r.setAttribute("stroke",e.fill||""),r.setAttribute("stroke-width",n+""),r.setAttribute("fill","none"),e.fillLineDash&&r.setAttribute("stroke-dasharray",e.fillLineDash.join(" ").trim()),e.fillLineDashOffset&&r.setAttribute("stroke-dashoffset",`${e.fillLineDashOffset}`),r}get generator(){return this.gen}getDefaultOptions(){return this.gen.defaultOptions}opsToPath(s,t){return this.gen.opsToPath(s,t)}line(s,t,e,n,r){let i=this.gen.line(s,t,e,n,r);return this.draw(i)}rectangle(s,t,e,n,r){let i=this.gen.rectangle(s,t,e,n,r);return this.draw(i)}ellipse(s,t,e,n,r){let i=this.gen.ellipse(s,t,e,n,r);return this.draw(i)}circle(s,t,e,n){let r=this.gen.circle(s,t,e,n);return this.draw(r)}linearPath(s,t){let e=this.gen.linearPath(s,t);return this.draw(e)}polygon(s,t){let e=this.gen.polygon(s,t);return this.draw(e)}arc(s,t,e,n,r,i,o=!1,l){let c=this.gen.arc(s,t,e,n,r,i,o,l);return this.draw(c)}curve(s,t){let e=this.gen.curve(s,t);return this.draw(e)}path(s,t){let e=this.gen.path(s,t);return this.draw(e)}},Re={canvas:(a,s)=>new ye(a,s),svg:(a,s)=>new ke(a,s),generator:a=>new W(a),newSeed:()=>W.newSeed()};var _e="http://www.w3.org/2000/svg";function Be(a,s,t,e){if(!t.length)return;let n=Re.svg(a),r={seed:e.seed,stroke:"currentColor",strokeWidth:1.7,roughness:1.1,bowing:1.2},i=document.createElementNS(_e,"g");i.setAttribute("class",`mark mark-${s} tone-${e.tone}`);let o=t[0],l=t[t.length-1],c=Math.min(...t.map(p=>p.x)),h=Math.max(...t.map(p=>p.x+p.w)),m=o.y,d=l.y+l.h;switch(t.length>1&&s==="circle"?"bracket":s){case"circle":{let p=Math.min(14,6+o.w*.08);i.append(n.ellipse(o.x+o.w/2,o.y+o.h/2,o.w+p*2,o.h+12,{...r,roughness:.9,curveStepCount:14}));break}case"underline":for(let p of t){let g=p.y+p.h-1;i.append(n.line(p.x-1,g,p.x+p.w+1,g+.5,{...r,roughness:.8}))}break;case"strike":for(let p of t){let g=p.y+p.h/2;i.append(n.line(p.x-2,g,p.x+p.w+2,g,r))}break;case"box":i.append(n.rectangle(c-5,m-2,h-c+10,d-m+4,r));break;case"bracket":{let p=Math.max(2,e.gutter);if(i.append(n.linearPath([[p+7,m+2],[p,m+2],[p,d-2],[p+7,d-2]],{...r,roughness:.7})),s==="circle"||s==="bracket")for(let g of t)i.prepend(We(g));break}case"highlight":for(let p of t)i.append(We(p));break;case"arrow":{let p=o.y+o.h/2,g=o.x-6,v=Math.max(2,g-34);i.append(n.line(v,p,g,p,r)),i.append(n.linearPath([[g-7,p-5],[g,p],[g-7,p+5]],r));break}}a.append(i),e.animate&&rt(i)}function We(a){let s=document.createElementNS(_e,"rect");return s.setAttribute("x",String(a.x-3)),s.setAttribute("y",String(a.y+1)),s.setAttribute("width",String(a.w+6)),s.setAttribute("height",String(a.h-2)),s.setAttribute("rx","4"),s.setAttribute("class","mark-fill"),s}function rt(a){let s=Array.from(a.querySelectorAll("path")),t=320/Math.max(1,Math.min(s.length,2));s.forEach((e,n)=>{let r=e.getTotalLength?.()??0;r&&(e.style.strokeDasharray=`${r} ${r}`,e.style.strokeDashoffset=String(r),e.animate([{strokeDashoffset:r},{strokeDashoffset:0}],{duration:t,delay:40+n*t*.55,easing:"cubic-bezier(.3,.7,.4,1)",fill:"forwards"}))});for(let e of a.querySelectorAll(".mark-fill"))e.animate([{opacity:0},{opacity:1}],{duration:220,fill:"both"})}var ot="http://www.w3.org/2000/svg",re=class{constructor(s){k(this,"el");k(this,"content");k(this,"svg");k(this,"rows",[]);k(this,"segments",[]);k(this,"lines");k(this,"marks",[]);k(this,"seed",1);k(this,"activeRows",[]);k(this,"scrollTarget");k(this,"scrollTimer");this.lines=s.text.replace(/\n$/,"").split(`
|
|
2
|
+
`),this.el=document.createElement("div"),this.el.className="code-scroll",this.el.tabIndex=0,this.el.setAttribute("role","region"),this.el.setAttribute("aria-label",`Source code${s.path?`: ${s.path}`:""}`),this.content=document.createElement("div"),this.content.className="code",this.svg=document.createElementNS(ot,"svg"),this.svg.setAttribute("class","marks"),this.svg.setAttribute("aria-hidden","true"),this.renderLines(s),this.content.append(this.svg),this.el.append(this.content),new ResizeObserver(()=>this.redraw(!1)).observe(this.content)}renderLines(s){let t=new Map;for(let[n,r,i,o]of s.tokens??[]){let l=t.get(n)??[];l.push([r,i,o]),t.set(n,l)}let e=document.createDocumentFragment();this.lines.forEach((n,r)=>{let i=r+1,o=document.createElement("div");o.className="line";let l=document.createElement("span");l.className="ln",l.textContent=String(i+(s.firstLine??1)-1);let c=document.createElement("span");c.className="src";let h=Array.from(n),m=(t.get(i)??[]).sort((p,g)=>p[0]-g[0]),d=[],u=0,f=(p,g,v)=>{if(g<=p)return;let b=h.slice(p,g).join(""),x=document.createTextNode(b);if(v){let w=document.createElement("span");w.className=`tok-${v}`,w.append(x),c.append(w)}else c.append(x);d.push({node:x,cpStart:p,text:b})};for(let[p,g,v]of m)p<u||(f(u,p),f(p,Math.min(g,h.length),v),u=Math.min(g,h.length));f(u,h.length),h.length||c.append(document.createTextNode("\u200B")),o.append(l,c),e.append(o),this.rows.push(o),this.segments.push(d)}),this.content.append(e)}setFocus(s,t,e,n=[]){this.marks=s,this.seed=t;for(let r of this.activeRows)r.classList.remove("active","caller");this.activeRows=[];for(let r of n)this.flag(r,"caller");for(let r of s)for(let i=r.start[0];i<=r.end[0];i++)this.flag(i,"active");this.redraw(e),this.reveal()}flag(s,t){let e=this.rows[s-1];e&&(e.classList.add(t),this.activeRows.push(e))}redraw(s){if(this.svg.replaceChildren(),!this.content.isConnected)return;let t=(this.rows[0]?.querySelector(".src")?.offsetLeft??40)-12;this.marks.forEach((e,n)=>{Be(this.svg,e.mark,this.boxes(e),{seed:this.seed*31+n+1,animate:s,tone:e.tone??"accent",gutter:t})})}boxes(s){let t=this.content.getBoundingClientRect(),e=[];for(let n=s.start[0];n<=s.end[0];n++){let r=this.lines[n-1]??"",i=Array.from(r).length,o=i-Array.from(r.trimStart()).length,l=n===s.start[0]?s.start[1]:o,c=n===s.end[0]?s.end[1]:i,h=this.measure(n,l,Math.max(l,c));h&&e.push({x:h.left-t.left,y:h.top-t.top,w:h.width,h:h.height})}return e}measure(s,t,e){let n=this.segments[s-1],r=this.rows[s-1];if(!n||!r)return null;let i=r.querySelector(".src");if(!n.length||t===e){let p=i.getBoundingClientRect();return new DOMRect(p.left,p.top,Math.max(8,p.width),p.height)}let o=document.createRange(),l=p=>{for(let v of n){let b=Array.from(v.text).length;if(p<=v.cpStart+b)return[v.node,Array.from(v.text).slice(0,p-v.cpStart).join("").length]}let g=n[n.length-1];return[g.node,g.text.length]},[c,h]=l(t),[m,d]=l(e);o.setStart(c,h),o.setEnd(m,d);let u=o.getBoundingClientRect(),f=r.getBoundingClientRect();return new DOMRect(u.left,f.top,u.width,f.height)}reveal(){let s=this.marks[0];if(!s)return;let t=this.rows[s.start[0]-1],e=this.rows[s.end[0]-1]??t;if(!t||!e)return;let n=t.offsetTop,r=e.offsetTop+e.offsetHeight,i=this.el.clientHeight,o=this.scrollTarget??this.el.scrollTop,l=24;if(n>=o+l&&r<=o+i-l)return;let c=this.el.scrollHeight-i,h=Math.max(0,Math.min(c,n-(i-(r-n))/2)),m=matchMedia("(prefers-reduced-motion: reduce)").matches;this.scrollTarget=h,this.el.scrollTo({top:h,behavior:m?"auto":"smooth"}),clearTimeout(this.scrollTimer),this.scrollTimer=window.setTimeout(()=>{this.scrollTarget=void 0},600)}};var at="visset.trace/0.",j=class{constructor(s){k(this,"trace");k(this,"length");k(this,"outEnds",[]);k(this,"allOut");k(this,"versionMaps",new Map);k(this,"changeCache",new Map);if(!s||typeof s.schema!="string"||!s.schema.startsWith(at))throw new Error(`visset: unsupported trace (schema ${String(s?.schema)})`);this.trace=s,this.length=s.steps.length;let t="";for(let e of s.steps)t+=e.stdout??"",this.outEnds.push(t.length);this.allOut=t}step(s){let t=this.trace.steps[s];if(!t)throw new RangeError(`step ${s} out of range`);return t}output(s){let t=this.outEnds[s]??0,e=s>0?this.outEnds[s-1]??0:0;return{text:this.allOut.slice(0,t),freshFrom:e}}resolve(s,t){if(!("ref"in t))return;let e=this.versions(s).get(t.ref);return e?this.trace.objects[e]:void 0}versions(s){let t=this.versionMaps.get(s);if(!t){t=new Map;for(let e of this.step(s).heap)t.set(e.slice(0,e.lastIndexOf("@")),e);this.versionMaps.set(s,t)}return t}format(s,t,e=80){let n=this.fmt(s,t,0,new Set);return n.length>e?`${n.slice(0,e-1)}\u2026`:n}fmt(s,t,e,n){if("p"in t)return t.p;let r=this.resolve(s,t);if(!r)return"\u2026";if(n.has(t.ref))return r.shape==="sequence"?"[\u2026]":"{\u2026}";if(e>=3&&["sequence","mapping","set","record"].includes(r.shape))return"\u2026";n.add(t.ref);let o=h=>this.fmt(s,h,e+1,n),l=r.more?`, \u2026 ${r.more} more`:"",c;switch(r.shape){case"sequence":{let h=(r.items??[]).map(o).join(", ")+l;c=r.type==="tuple"?`(${h}${r.items?.length===1?",":""})`:`[${h}]`;break}case"set":c=(r.items?.length??0)===0?`${r.type}()`:`{${(r.items??[]).map(o).join(", ")}${l}}`;break;case"mapping":c=`{${(r.entries??[]).map(([h,m])=>`${o(h)}: ${o(m)}`).join(", ")}${l}}`;break;case"record":c=`${r.type}(${(r.fields??[]).map(([h,m])=>`${h}=${o(m)}`).join(", ")})`;break;case"function":c=`\u0192 ${r.repr??r.type}`;break;case"class":c=`class ${r.repr??r.type}`;break;default:c=r.repr??r.type}return n.delete(t.ref),c}changes(s){let t=this.changeCache.get(s);if(t)return t;let e=this.step(s),n;return e.changes?n=new Set(e.changes):s===0?n=new Set(e.stack.flatMap(r=>r.locals.map(([i])=>`${r.id}.${i}`))):n=this.diff(s-1,s),this.changeCache.set(s,n),n}diff(s,t){let e=new Set,n=new Map(this.step(s).stack.map(r=>[r.id,r]));for(let r of this.step(t).stack){let i=n.get(r.id);i||e.add(r.id);let o=new Map(i?.locals??[]);for(let[l,c]of r.locals){let h=o.get(l);(!h||!it(h,c)||this.signature(s,c)!==this.signature(t,c))&&e.add(`${r.id}.${l}`)}(r.returnValue||r.yieldValue)&&e.add(`${r.id}:return`)}return e}signature(s,t){if(!("ref"in t))return"";let e=this.versions(s),n=[],r=[t.ref],i=new Set;for(;r.length&&i.size<200;){let o=r.pop();if(i.has(o))continue;i.add(o);let l=e.get(o);n.push(l??o);let c=l?this.trace.objects[l]:void 0;if(c)for(let h of lt(c))r.push(h)}return n.sort().join("|")}};function it(a,s){return"ref"in a||"ref"in s?"ref"in a&&"ref"in s&&a.ref===s.ref:a.p===s.p&&a.t===s.t}function lt(a){let s=[],t=e=>{"ref"in e&&s.push(e.ref)};for(let e of a.items??[])t(e);for(let[e,n]of a.entries??[])t(e),t(n);for(let[,e]of a.fields??[])t(e);return s}function Fe(a){let s=[],t=/`([^`]+)`/g,e=0;for(let n=t.exec(a);n;n=t.exec(a))n.index>e&&s.push({code:!1,text:a.slice(e,n.index)}),s.push({code:!0,text:n[1]??""}),e=n.index+n[0].length;return e<a.length&&s.push({code:!1,text:a.slice(e)}),s}var oe=class{constructor(){k(this,"el");k(this,"framesEl");k(this,"outputEl");k(this,"frameEls",new Map);this.el=document.createElement("div"),this.el.className="state";let s=document.createElement("div");s.className="pane-head",s.textContent="Frames",this.framesEl=document.createElement("div"),this.framesEl.className="frames",this.framesEl.setAttribute("role","list"),this.framesEl.setAttribute("aria-label","Call stack");let t=document.createElement("div");t.className="console";let e=document.createElement("div");e.className="pane-head",e.textContent="Output",this.outputEl=document.createElement("pre"),this.outputEl.setAttribute("aria-label","Program output"),t.append(e,this.outputEl),this.el.append(s,this.framesEl,t)}render(s,t,e){let n=s.step(t),r=s.changes(t),i=new Set;n.stack.forEach((l,c)=>{i.add(l.id);let h=this.frameEl(l,r.has(l.id)&&e);h.classList.toggle("top",c===n.stack.length-1),this.fillFrame(h,l,s,t,r,e),this.framesEl.children[c]!==h&&this.framesEl.insertBefore(h,this.framesEl.children[c]??null)});for(let[l,c]of this.frameEls)i.has(l)||(c.remove(),this.frameEls.delete(l));n.stack.length?this.framesEl.querySelector(":scope > .empty")?.remove():(this.framesEl.innerHTML='<p class="empty">No frames: the program has not started.</p>',this.frameEls.clear());let o=this.framesEl.lastElementChild;o&&o.offsetTop+o.offsetHeight>this.framesEl.scrollTop+this.framesEl.clientHeight&&(this.framesEl.scrollTop=o.offsetTop+o.offsetHeight-this.framesEl.clientHeight),this.renderOutput(s,t)}frameEl(s,t){let e=this.frameEls.get(s.id);return e||(e=document.createElement("section"),e.className="frame",e.setAttribute("role","listitem"),e.innerHTML='<header class="frame-head"><span class="frame-name"></span><span class="frame-ret"></span></header><div class="vars"></div>',t&&e.classList.add("entering"),this.frameEls.set(s.id,e)),e}fillFrame(s,t,e,n,r,i){s.querySelector(".frame-name").textContent=t.name,s.setAttribute("aria-label",`Frame ${t.name}`);let o=s.querySelector(".frame-ret"),l=t.returnValue??t.yieldValue;o.textContent=l?`${t.returnValue?"returns":"yields"} ${e.format(n,l,40)}`:"",o.classList.toggle("changed",!!l);let c=s.querySelector(".vars");if(!t.locals.length){c.innerHTML='<span class="empty">no variables yet</span>';return}c.querySelector(":scope > .empty")?.remove();let h=new Map;for(let u of c.querySelectorAll(".var-name"))h.set(u.dataset.name,[u,u.nextElementSibling]);let m=new Set,d=c.firstElementChild;for(let[u,f]of t.locals){m.add(u);let p=h.get(u);if(!p){let b=document.createElement("span");b.className="var-name",b.dataset.name=u,b.textContent=u;let x=document.createElement("span");x.className="var-value",p=[b,x]}let[g,v]=p;d!==g&&c.insertBefore(g,d),c.insertBefore(v,g.nextSibling),d=v.nextElementSibling,this.setValue(v,f,e,n,r.has(`${t.id}.${u}`),i)}for(let[u,[f,p]]of h)m.has(u)||(f.remove(),p.remove())}setValue(s,t,e,n,r,i){s.textContent=e.format(n,t),s.dataset.type="p"in t?t.t:e.resolve(n,t)?.shape??"ref",s.title="p"in t?t.t:e.resolve(n,t)?.type??"",s.classList.remove("changed","pulse"),r&&(s.classList.add("changed"),i&&(s.offsetWidth,s.classList.add("pulse")))}renderOutput(s,t){let{text:e,freshFrom:n}=s.output(t);if(this.outputEl.replaceChildren(),!e){let r=document.createElement("span");r.className="empty",r.textContent="Nothing printed yet.",this.outputEl.append(r);return}if(this.outputEl.append(document.createTextNode(e.slice(0,n))),n<e.length){let r=document.createElement("mark");r.className="fresh",r.textContent=e.slice(n),this.outputEl.append(r)}this.outputEl.scrollTop=this.outputEl.scrollHeight}};var je=`
|
|
3
|
+
:host {
|
|
4
|
+
--font-ui: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Inter", "Segoe UI", Roboto, system-ui, sans-serif;
|
|
5
|
+
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, Consolas, monospace;
|
|
6
|
+
--accent: #5e5ce6;
|
|
7
|
+
--accent-soft: rgba(94, 92, 230, 0.11);
|
|
8
|
+
--accent-strong: rgba(94, 92, 230, 0.28);
|
|
9
|
+
--on-accent: #fff;
|
|
10
|
+
--error: #d70015;
|
|
11
|
+
--error-soft: rgba(215, 0, 21, 0.09);
|
|
12
|
+
--surface: #ffffff;
|
|
13
|
+
--panel: #fbfbfd;
|
|
14
|
+
--frame: #ffffff;
|
|
15
|
+
--text: #1d1d1f;
|
|
16
|
+
--muted: #6e6e73;
|
|
17
|
+
--faint: #a1a1a6;
|
|
18
|
+
--line: rgba(0, 0, 0, 0.08);
|
|
19
|
+
--active-line: rgba(94, 92, 230, 0.06);
|
|
20
|
+
--caller-line: rgba(0, 0, 0, 0.03);
|
|
21
|
+
--code-bg: rgba(0, 0, 0, 0.05);
|
|
22
|
+
--track: rgba(0, 0, 0, 0.1);
|
|
23
|
+
--shadow: 0 1px 2px rgba(0, 0, 0, 0.04), 0 12px 40px rgba(0, 0, 0, 0.07);
|
|
24
|
+
--syn-keyword: #9b2393;
|
|
25
|
+
--syn-string: #1d7a4f;
|
|
26
|
+
--syn-number: #8a5100;
|
|
27
|
+
--syn-comment: #6e6e73;
|
|
28
|
+
--syn-builtin: #2c5d8f;
|
|
29
|
+
--syn-def: #1d1d1f;
|
|
30
|
+
display: block;
|
|
31
|
+
height: 600px;
|
|
32
|
+
min-height: 420px;
|
|
33
|
+
container-type: inline-size;
|
|
34
|
+
color-scheme: light;
|
|
35
|
+
-webkit-font-smoothing: antialiased;
|
|
36
|
+
}
|
|
37
|
+
:host([hidden]) { display: none; }
|
|
38
|
+
${qe(':host([theme="dark"])')}
|
|
39
|
+
@media (prefers-color-scheme: dark) {
|
|
40
|
+
${qe(':host(:not([theme="light"]))')}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
* { box-sizing: border-box; }
|
|
44
|
+
.player {
|
|
45
|
+
height: 100%;
|
|
46
|
+
display: grid;
|
|
47
|
+
grid-template-rows: auto minmax(0, 1fr) auto auto;
|
|
48
|
+
background: var(--surface);
|
|
49
|
+
color: var(--text);
|
|
50
|
+
font: 14px/1.45 var(--font-ui);
|
|
51
|
+
border: 1px solid var(--line);
|
|
52
|
+
border-radius: 18px;
|
|
53
|
+
box-shadow: var(--shadow);
|
|
54
|
+
overflow: hidden;
|
|
55
|
+
}
|
|
56
|
+
.player:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
57
|
+
|
|
58
|
+
/* header */
|
|
59
|
+
.bar { display: flex; align-items: baseline; gap: 12px; padding: 16px 22px 12px; min-width: 0; }
|
|
60
|
+
.title { font-weight: 600; font-size: 15px; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
61
|
+
.meta { margin-left: auto; font-size: 12px; color: var(--muted); white-space: nowrap; }
|
|
62
|
+
|
|
63
|
+
/* panes */
|
|
64
|
+
.panes { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 12px; padding: 0 14px; min-height: 0; }
|
|
65
|
+
.pane { background: var(--panel); border: 1px solid var(--line); border-radius: 12px; min-height: 0; display: flex; flex-direction: column; overflow: hidden; }
|
|
66
|
+
.pane-head { flex: none; font-size: 11px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); padding: 11px 14px 5px; }
|
|
67
|
+
|
|
68
|
+
/* code */
|
|
69
|
+
.code-scroll { flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; }
|
|
70
|
+
.code-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; border-radius: 0 0 12px 12px; }
|
|
71
|
+
.code { position: relative; min-width: max-content; padding: 4px 0 28px; font: 13.5px/1.8 var(--font-mono); font-variant-ligatures: none; }
|
|
72
|
+
.line { display: flex; padding-right: 32px; transition: background-color 0.25s ease; }
|
|
73
|
+
.ln { flex: none; width: 3.4em; padding-right: 1.3em; text-align: right; color: var(--faint); user-select: none; }
|
|
74
|
+
.src { white-space: pre; }
|
|
75
|
+
.line.caller { background: var(--caller-line); }
|
|
76
|
+
.line.active { background: var(--active-line); }
|
|
77
|
+
.line.active .ln { color: var(--text); }
|
|
78
|
+
.marks { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; color: var(--accent); }
|
|
79
|
+
.marks .tone-error { color: var(--error); }
|
|
80
|
+
.mark-fill { fill: var(--accent-soft); stroke: none; }
|
|
81
|
+
.tone-error .mark-fill { fill: var(--error-soft); }
|
|
82
|
+
.tok-keyword { color: var(--syn-keyword); }
|
|
83
|
+
.tok-constant { color: var(--syn-keyword); }
|
|
84
|
+
.tok-string { color: var(--syn-string); }
|
|
85
|
+
.tok-number { color: var(--syn-number); }
|
|
86
|
+
.tok-comment { color: var(--syn-comment); font-style: italic; }
|
|
87
|
+
.tok-builtin { color: var(--syn-builtin); }
|
|
88
|
+
.tok-definition { color: var(--syn-def); font-weight: 600; }
|
|
89
|
+
|
|
90
|
+
/* state */
|
|
91
|
+
.state { display: flex; flex-direction: column; height: 100%; min-height: 0; }
|
|
92
|
+
.frames { position: relative; flex: 1 1 auto; min-height: 0; overflow: auto; overscroll-behavior: contain; display: flex; flex-direction: column; gap: 8px; padding: 2px 12px 12px; }
|
|
93
|
+
.frame { flex: none; background: var(--frame); border: 1px solid var(--line); border-radius: 10px; padding: 8px 12px 9px; transition: border-color 0.25s ease, box-shadow 0.25s ease; }
|
|
94
|
+
.frame.top { border-color: var(--accent-strong); box-shadow: inset 3px 0 0 var(--accent); }
|
|
95
|
+
.frame.entering { animation: vs-enter 0.32s cubic-bezier(.2,.8,.3,1); }
|
|
96
|
+
.frame-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; font-size: 12.5px; }
|
|
97
|
+
.frame-name { font-weight: 600; font-family: var(--font-mono); }
|
|
98
|
+
.frame-ret { margin-left: auto; font: 12px var(--font-mono); color: var(--accent); }
|
|
99
|
+
.vars { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: 16px; row-gap: 2px; font: 13px/1.6 var(--font-mono); }
|
|
100
|
+
.var-name { color: var(--muted); }
|
|
101
|
+
.var-value { min-width: 0; overflow-wrap: anywhere; border-radius: 5px; margin: 0 -5px; padding: 0 5px; transition: background-color 0.4s ease; }
|
|
102
|
+
.var-value[data-type="str"] { color: var(--syn-string); }
|
|
103
|
+
.var-value.changed { background: var(--accent-soft); }
|
|
104
|
+
.var-value.pulse { animation: vs-pulse 0.9s ease-out; }
|
|
105
|
+
.empty { color: var(--faint); font: 12.5px var(--font-ui); }
|
|
106
|
+
.console { flex: 0 0 auto; max-height: 36%; display: flex; flex-direction: column; border-top: 1px solid var(--line); }
|
|
107
|
+
.console pre { margin: 0; padding: 0 14px 12px; min-height: 2.4em; overflow: auto; font: 12.5px/1.6 var(--font-mono); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
108
|
+
.console mark.fresh { background: var(--accent-soft); color: inherit; border-radius: 3px; }
|
|
109
|
+
|
|
110
|
+
/* caption */
|
|
111
|
+
.caption-row { padding: 16px 22px 4px; min-height: 0; }
|
|
112
|
+
.caption { margin: 0; min-height: 2.9em; font-size: 15.5px; line-height: 1.45; letter-spacing: -0.005em; text-wrap: pretty; }
|
|
113
|
+
.caption.enter { animation: vs-fade 0.22s ease-out; }
|
|
114
|
+
.caption code { font: 0.88em var(--font-mono); background: var(--code-bg); padding: 1px 5px; border-radius: 5px; }
|
|
115
|
+
.caption.error code { color: var(--error); background: var(--error-soft); }
|
|
116
|
+
.notice { margin: 8px 0 0; padding: 7px 11px; font-size: 12.5px; color: var(--muted); background: var(--code-bg); border-radius: 8px; }
|
|
117
|
+
.notice.error { color: var(--error); background: var(--error-soft); }
|
|
118
|
+
|
|
119
|
+
/* controls */
|
|
120
|
+
.controls { display: flex; align-items: center; gap: 6px; padding: 8px 16px 16px; }
|
|
121
|
+
button, select { font: inherit; color: inherit; }
|
|
122
|
+
.btn { display: inline-grid; place-items: center; width: 34px; height: 34px; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--text); cursor: pointer; transition: background-color 0.15s ease, transform 0.1s ease; }
|
|
123
|
+
.btn:hover { background: var(--code-bg); }
|
|
124
|
+
.btn:active { transform: scale(0.94); }
|
|
125
|
+
.btn:disabled { color: var(--faint); cursor: default; background: transparent; transform: none; }
|
|
126
|
+
.btn svg { width: 16px; height: 16px; fill: currentColor; }
|
|
127
|
+
.btn.play { width: 40px; height: 40px; background: var(--accent); color: var(--on-accent); }
|
|
128
|
+
.btn.play:hover { filter: brightness(1.08); background: var(--accent); }
|
|
129
|
+
.btn:focus-visible, .speed:focus-visible, .slider:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
130
|
+
.slider { flex: 1; min-width: 60px; margin: 0 8px; height: 22px; background: transparent; appearance: none; -webkit-appearance: none; cursor: pointer; --pct: 0%; }
|
|
131
|
+
.slider::-webkit-slider-runnable-track { height: 4px; border-radius: 2px; background: linear-gradient(to right, var(--accent) var(--pct), var(--track) var(--pct)); }
|
|
132
|
+
.slider::-moz-range-track { height: 4px; border-radius: 2px; background: linear-gradient(to right, var(--accent) var(--pct), var(--track) var(--pct)); }
|
|
133
|
+
.slider::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; margin-top: -6px; border-radius: 50%; background: #fff; border: 0; box-shadow: 0 0 0 0.5px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.25); }
|
|
134
|
+
.slider::-moz-range-thumb { width: 16px; height: 16px; border-radius: 50%; background: #fff; border: 0; box-shadow: 0 0 0 0.5px rgba(0,0,0,0.12), 0 1px 4px rgba(0,0,0,0.25); }
|
|
135
|
+
.counter { min-width: 6.5em; text-align: right; font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }
|
|
136
|
+
.speed { height: 28px; padding: 0 6px; border: 1px solid var(--line); border-radius: 8px; background: var(--surface); font-size: 12.5px; color: var(--muted); cursor: pointer; }
|
|
137
|
+
|
|
138
|
+
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
|
|
139
|
+
|
|
140
|
+
@container (max-width: 720px) {
|
|
141
|
+
.panes { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1.25fr) minmax(0, 1fr); }
|
|
142
|
+
.bar { padding: 14px 16px 10px; }
|
|
143
|
+
.caption-row { padding: 12px 16px 2px; }
|
|
144
|
+
.caption { font-size: 15px; }
|
|
145
|
+
.controls { padding: 6px 10px 12px; }
|
|
146
|
+
.counter { min-width: 0; }
|
|
147
|
+
.speed { display: none; }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@keyframes vs-pulse { 0% { background: var(--accent-strong); } 100% { background: var(--accent-soft); } }
|
|
151
|
+
@keyframes vs-enter { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
|
|
152
|
+
@keyframes vs-fade { from { opacity: 0.2; transform: translateY(2px); } to { opacity: 1; transform: none; } }
|
|
153
|
+
|
|
154
|
+
@media (prefers-reduced-motion: reduce) {
|
|
155
|
+
*, *::before, *::after { animation: none !important; transition: none !important; }
|
|
156
|
+
}
|
|
157
|
+
`;function qe(a){return`${a} {
|
|
158
|
+
--accent: #8b88ff;
|
|
159
|
+
--accent-soft: rgba(139, 136, 255, 0.16);
|
|
160
|
+
--accent-strong: rgba(139, 136, 255, 0.36);
|
|
161
|
+
--on-accent: #0b0b14;
|
|
162
|
+
--error: #ff6961;
|
|
163
|
+
--error-soft: rgba(255, 105, 97, 0.14);
|
|
164
|
+
--surface: #161618;
|
|
165
|
+
--panel: #1c1c1e;
|
|
166
|
+
--frame: #232326;
|
|
167
|
+
--text: #f5f5f7;
|
|
168
|
+
--muted: #a1a1a6;
|
|
169
|
+
--faint: #6e6e73;
|
|
170
|
+
--line: rgba(255, 255, 255, 0.09);
|
|
171
|
+
--active-line: rgba(139, 136, 255, 0.1);
|
|
172
|
+
--caller-line: rgba(255, 255, 255, 0.035);
|
|
173
|
+
--code-bg: rgba(255, 255, 255, 0.08);
|
|
174
|
+
--track: rgba(255, 255, 255, 0.16);
|
|
175
|
+
--shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 40px rgba(0, 0, 0, 0.4);
|
|
176
|
+
--syn-keyword: #ff7ab2;
|
|
177
|
+
--syn-string: #7ed4a0;
|
|
178
|
+
--syn-number: #e5b567;
|
|
179
|
+
--syn-comment: #8e8e93;
|
|
180
|
+
--syn-builtin: #7cc0f5;
|
|
181
|
+
--syn-def: #f5f5f7;
|
|
182
|
+
color-scheme: dark;
|
|
183
|
+
}`}var V=[.5,1,1.5,2,3],ct=1300,G={prev:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M10.3 2.3a1 1 0 0 1 0 1.4L6 8l4.3 4.3a1 1 0 1 1-1.4 1.4l-5-5a1 1 0 0 1 0-1.4l5-5a1 1 0 0 1 1.4 0Z"/></svg>',next:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M5.7 2.3a1 1 0 0 0 0 1.4L10 8l-4.3 4.3a1 1 0 1 0 1.4 1.4l5-5a1 1 0 0 0 0-1.4l-5-5a1 1 0 0 0-1.4 0Z"/></svg>',play:'<svg viewBox="0 0 16 16" aria-hidden="true"><path d="M5 2.9v10.2a.9.9 0 0 0 1.4.76l7.6-5.1a.9.9 0 0 0 0-1.5L6.4 2.1A.9.9 0 0 0 5 2.9Z"/></svg>',pause:'<svg viewBox="0 0 16 16" aria-hidden="true"><rect x="3.5" y="2.5" width="3.2" height="11" rx="1"/><rect x="9.3" y="2.5" width="3.2" height="11" rx="1"/></svg>'},ht=`
|
|
184
|
+
<div class="player" part="player">
|
|
185
|
+
<header class="bar"><span class="title"></span><span class="meta"></span></header>
|
|
186
|
+
<div class="panes">
|
|
187
|
+
<section class="pane code-pane" aria-label="Code"><div class="pane-head file"></div></section>
|
|
188
|
+
<section class="pane state-pane" aria-label="Program state"></section>
|
|
189
|
+
</div>
|
|
190
|
+
<div class="caption-row">
|
|
191
|
+
<p class="caption" aria-live="polite"></p>
|
|
192
|
+
<p class="notice" hidden></p>
|
|
193
|
+
</div>
|
|
194
|
+
<nav class="controls" aria-label="Walkthrough controls">
|
|
195
|
+
<button class="btn prev" type="button" aria-label="Previous step (Left arrow)" title="Previous (\u2190)">${G.prev}</button>
|
|
196
|
+
<button class="btn play" type="button" aria-label="Play (Space)" title="Play (Space)">${G.play}</button>
|
|
197
|
+
<button class="btn next" type="button" aria-label="Next step (Right arrow)" title="Next (\u2192)">${G.next}</button>
|
|
198
|
+
<input class="slider" type="range" min="0" max="0" value="0" step="1" aria-label="Step">
|
|
199
|
+
<span class="counter" aria-hidden="true"></span>
|
|
200
|
+
<select class="speed" aria-label="Playback speed" title="Speed ([ and ])">
|
|
201
|
+
${V.map(a=>`<option value="${a}"${a===1?" selected":""}>${a}\xD7</option>`).join("")}
|
|
202
|
+
</select>
|
|
203
|
+
</nav>
|
|
204
|
+
</div>`,K=class extends HTMLElement{constructor(){super();k(this,"model",null);k(this,"idx",0);k(this,"timer");k(this,"speed",1);k(this,"codeView",null);k(this,"stateView",new oe);k(this,"root");k(this,"els");k(this,"onWindowKey",t=>this.handleKey(t));this.root=this.attachShadow({mode:"open"}),this.root.innerHTML=`<style>${je}</style>${ht}`;let t=e=>this.root.querySelector(e);this.els={title:t(".title"),meta:t(".meta"),file:t(".file"),codePane:t(".code-pane"),caption:t(".caption"),notice:t(".notice"),prev:t(".prev"),next:t(".next"),play:t(".play"),slider:t(".slider"),counter:t(".counter"),speed:t(".speed")},t(".state-pane").append(this.stateView.el),this.els.prev.addEventListener("click",()=>this.prev()),this.els.next.addEventListener("click",()=>this.next()),this.els.play.addEventListener("click",()=>this.toggle()),this.els.slider.addEventListener("input",()=>{this.pause(),this.goTo(Number(this.els.slider.value))}),this.els.speed.addEventListener("change",()=>this.setSpeed(Number(this.els.speed.value))),this.addEventListener("keydown",e=>{this.hasAttribute("global-keys")||this.handleKey(e)})}connectedCallback(){if(this.hasAttribute("tabindex")||(this.tabIndex=0),this.hasAttribute("global-keys")&&window.addEventListener("keydown",this.onWindowKey),!this.model){let t=this.querySelector('script[type="application/json"]');t?.textContent?this.trace=JSON.parse(t.textContent):this.getAttribute("src")&&this.load(this.getAttribute("src"))}}disconnectedCallback(){this.pause(),window.removeEventListener("keydown",this.onWindowKey)}attributeChangedCallback(t,e,n){e!==n&&(t==="src"&&n&&this.isConnected&&this.load(n),t==="step"&&n!==null&&this.goTo(Number(n)),t==="speed"&&n!==null&&this.setSpeed(Number(n)))}get trace(){return this.model?.trace??null}set trace(t){if(this.pause(),!!t){try{this.model=new j(t)}catch(e){this.showError(String(e instanceof Error?e.message:e));return}this.mount()}}get index(){return this.idx}get length(){return this.model?.length??0}goTo(t,e=!0){let n=this.model;if(!n?.length||Number.isNaN(t))return;let r=Math.max(0,Math.min(n.length-1,Math.trunc(t))),i=r!==this.idx;this.idx=r,this.render(e&&i&&!pt()),i&&this.dispatchEvent(new CustomEvent("stepchange",{detail:{index:r,step:n.step(r)}}))}next(){this.goTo(this.idx+1)}prev(){this.pause(),this.goTo(this.idx-1)}play(){!this.model||this.timer!==void 0||(this.idx>=this.model.length-1&&this.goTo(0),this.timer=window.setInterval(()=>{!this.model||this.idx>=this.model.length-1?this.pause():this.next()},ct/this.speed),this.syncPlayButton())}pause(){this.timer!==void 0&&window.clearInterval(this.timer),this.timer=void 0,this.syncPlayButton()}toggle(){this.timer===void 0?this.play():this.pause()}setSpeed(t){if(!Number.isFinite(t)||t<=0)return;this.speed=t;let e=V.includes(t)?String(t):"";e&&(this.els.speed.value=e),this.timer!==void 0&&(this.pause(),this.play())}async load(t){try{let e=await fetch(t);if(!e.ok)throw new Error(`HTTP ${e.status}`);this.trace=await e.json()}catch(e){this.showError(`Could not load ${t}: ${e instanceof Error?e.message:e}`)}}mount(){let t=this.model,{meta:e,sources:n}=t.trace,r=n[0];this.els.title.textContent=e.title??r.path??"Walkthrough";let i=e.language?e.language[0].toUpperCase()+e.language.slice(1):"";this.els.meta.textContent=[i,`${t.length.toLocaleString()} step${t.length===1?"":"s"}`].filter(Boolean).join(" \xB7 ");let o=this.els.title.textContent;this.els.file.textContent=r.path&&r.path!==o?r.path:"Code",this.codeView?.el.remove(),this.codeView=new re(r),this.els.codePane.append(this.codeView.el),this.els.slider.max=String(Math.max(0,t.length-1)),this.idx=Math.max(0,Math.min(t.length-1,Number(this.getAttribute("step")??0)||0));let l=Number(this.getAttribute("speed"));l>0&&this.setSpeed(l),requestAnimationFrame(()=>this.render(!1))}render(t){let e=this.model;if(!e?.length){this.els.caption.textContent="This trace has no steps.";return}let n=this.idx,r=e.step(n),i=r.stack.slice(0,-1).map(c=>c.line??0).filter(c=>c>0);this.codeView?.setFocus(r.focus,n+1,t,i),this.stateView.render(e,n,t);let o=this.els.caption;o.replaceChildren(...Fe(r.caption??"").map(c=>{if(!c.code)return document.createTextNode(c.text);let h=document.createElement("code");return h.textContent=c.text,h})),o.classList.toggle("error",r.kind==="exception"),t&&(o.classList.remove("enter"),o.offsetWidth,o.classList.add("enter")),this.renderNotice(n===e.length-1);let l=e.length;this.els.slider.value=String(n),this.els.slider.style.setProperty("--pct",l>1?`${n/(l-1)*100}%`:"100%"),this.els.slider.setAttribute("aria-valuetext",`Step ${n+1} of ${l}: ${r.caption??""}`),this.els.counter.textContent=`${(n+1).toLocaleString()} / ${l.toLocaleString()}`,this.els.prev.disabled=n===0,this.els.next.disabled=n===l-1}renderNotice(t){let{truncated:e,error:n}=this.model.trace.meta,r=this.els.notice,i="";if(t&&n){let o=n.line?` on line ${n.line}`:"";i=`The program stopped with ${n.type}${o}: ${n.message}`}else if(t&&e){let o=e.limit??0;i=e.reason==="maxSteps"?`Stopped after ${o.toLocaleString()} steps: the program may loop forever.`:e.reason==="timeout"?`Stopped after ${o/1e3}s: the program took too long.`:e.reason==="maxStack"?`Stopped at ${o} nested calls: the recursion may never end.`:"Stopped early."}r.textContent=i,r.hidden=!i,r.classList.toggle("error",!!(t&&n))}showError(t){this.els.caption.textContent=t,this.els.caption.classList.add("error")}syncPlayButton(){let t=this.timer!==void 0;this.els.play.innerHTML=t?G.pause:G.play,this.els.play.setAttribute("aria-label",t?"Pause (Space)":"Play (Space)"),this.els.play.title=t?"Pause (Space)":"Play (Space)"}handleKey(t){if(t.defaultPrevented||t.altKey||t.ctrlKey||t.metaKey)return;let e=t.composedPath()[0];if(e instanceof HTMLElement&&e.closest("input:not(.slider), select, textarea, [contenteditable]"))return;let n=this.model;if(!n)return;let i={ArrowRight:()=>{this.pause(),this.next()},ArrowLeft:()=>this.prev()," ":()=>this.toggle(),Home:()=>{this.pause(),this.goTo(0)},End:()=>{this.pause(),this.goTo(n.length-1)},"[":()=>this.setSpeed(V[Math.max(0,V.indexOf(this.speed)-1)]??1),"]":()=>this.setSpeed(V[Math.min(V.length-1,V.indexOf(this.speed)+1)]??1)}[t.key];i&&(t.preventDefault(),i())}};k(K,"observedAttributes",["src","step","speed"]);function pt(){return matchMedia("(prefers-reduced-motion: reduce)").matches}typeof customElements<"u"&&!customElements.get("visset-player")&&customElements.define("visset-player",K);})();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|