visset 0.1.0__py3-none-any.whl

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/__init__.py ADDED
@@ -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)
visset/__main__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .cli import main
2
+
3
+ raise SystemExit(main())
@@ -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);})();
visset/_version.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
visset/cli.py ADDED
@@ -0,0 +1,165 @@
1
+ """`visset` command line. Imports are lazy so `visset --help` starts instantly."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import json
7
+ import sys
8
+ from pathlib import Path
9
+ from typing import Any
10
+
11
+
12
+ def _add_trace_args(p: argparse.ArgumentParser) -> None:
13
+ p.add_argument("file", help="Python file to trace")
14
+ p.add_argument(
15
+ "--fn",
16
+ metavar="NAME",
17
+ help="trace one call of this function (or Class.method); shows only its source",
18
+ )
19
+ p.add_argument(
20
+ "--lines",
21
+ metavar="A-B",
22
+ help="show only these lines, e.g. 4-9 (part of a function); the program still runs",
23
+ )
24
+ p.add_argument(
25
+ "--given",
26
+ default="",
27
+ metavar="INPUTS",
28
+ help='inputs for a snippet, e.g. "arr=[1, 3, 5], target=3"',
29
+ )
30
+ p.add_argument(
31
+ "--args",
32
+ default="",
33
+ metavar="ARGS",
34
+ help='arguments for --fn as Python literals, e.g. "[1, 3, 5], 3"',
35
+ )
36
+ p.add_argument(
37
+ "--stdin", default=None, metavar="TEXT", help="text the program reads via input()"
38
+ )
39
+ p.add_argument("--max-steps", type=int, default=10_000, metavar="N")
40
+ p.add_argument("--timeout", type=float, default=5.0, metavar="SECONDS")
41
+ p.add_argument(
42
+ "--no-sandbox",
43
+ action="store_true",
44
+ help="run in this process: faster start, no time/memory protection",
45
+ )
46
+
47
+
48
+ def _parser() -> argparse.ArgumentParser:
49
+ from . import __version__
50
+
51
+ parser = argparse.ArgumentParser(
52
+ prog="visset",
53
+ description="Step-by-step visual explanations of code.",
54
+ )
55
+ parser.add_argument("--version", action="version", version=f"visset {__version__}")
56
+ sub = parser.add_subparsers(dest="command", required=True, metavar="COMMAND")
57
+
58
+ run = sub.add_parser("run", help="trace a file and open the walkthrough (or export with -o)")
59
+ _add_trace_args(run)
60
+ run.add_argument(
61
+ "-o", "--output", metavar="FILE.html", help="write HTML here instead of opening"
62
+ )
63
+ run.add_argument("--open", action="store_true", help="open in the browser even with -o")
64
+
65
+ trace = sub.add_parser("trace", help="trace a file and write the raw JSON trace")
66
+ _add_trace_args(trace)
67
+ trace.add_argument("-o", "--output", metavar="FILE.json", help="default: stdout")
68
+
69
+ render = sub.add_parser("render", help="render a JSON trace to HTML")
70
+ render.add_argument("trace", help="trace .json file")
71
+ render.add_argument("-o", "--output", metavar="FILE.html")
72
+ render.add_argument("--open", action="store_true")
73
+ return parser
74
+
75
+
76
+ def _trace(ns: argparse.Namespace) -> tuple[dict[str, Any], int]:
77
+ from .tracer import Limits, TraceRequest, execute, run_sandboxed
78
+
79
+ path = Path(ns.file)
80
+ try:
81
+ source = path.read_text(encoding="utf-8")
82
+ except OSError as e:
83
+ raise SystemExit(f"visset: cannot read {path}: {e.strerror}") from e
84
+ from .tracer.window import WindowError, parse_lines
85
+
86
+ try:
87
+ lines = parse_lines(ns.lines) if ns.lines else None
88
+ except WindowError as e:
89
+ raise SystemExit(f"visset: {e}") from e
90
+ req = TraceRequest(
91
+ source=source,
92
+ path=str(path.resolve()),
93
+ function=ns.fn,
94
+ args=ns.args,
95
+ given=ns.given,
96
+ lines=lines,
97
+ stdin=ns.stdin or "",
98
+ limits=Limits(max_steps=ns.max_steps, timeout_ms=int(ns.timeout * 1000)),
99
+ )
100
+ return execute(req) if ns.no_sandbox else run_sandboxed(req)
101
+
102
+
103
+ def _report(trace: dict[str, Any]) -> None:
104
+ meta = trace["meta"]
105
+ n = len(trace["steps"])
106
+ err = sys.stderr
107
+ first = trace["sources"][0].get("firstLine")
108
+ if n == 0 and first:
109
+ err.write("note: no code ran inside the selected lines.\n")
110
+ if meta.get("error"):
111
+ e = meta["error"]
112
+ where = f" on line {e['line']}" if e.get("line") else ""
113
+ err.write(f"note: the program raised {e['type']}{where}; the walkthrough ends there.\n")
114
+ if meta.get("truncated"):
115
+ t = meta["truncated"]
116
+ why = {
117
+ "maxSteps": f"after {t['limit']:,} steps",
118
+ "timeout": f"after {t['limit'] / 1000:g}s",
119
+ "maxStack": f"at {t['limit']} nested calls",
120
+ }.get(t["reason"], t["reason"])
121
+ err.write(f"note: tracing stopped {why}; the walkthrough shows what ran until then.\n")
122
+ err.write(f"{n} step{'s' if n != 1 else ''} recorded.\n")
123
+
124
+
125
+ def main(argv: list[str] | None = None) -> int:
126
+ ns = _parser().parse_args(argv)
127
+ from .tracer import TraceError
128
+
129
+ try:
130
+ if ns.command == "render":
131
+ trace = json.loads(Path(ns.trace).read_text(encoding="utf-8"))
132
+ return _emit_html(trace, ns.output, ns.open)
133
+ trace, _status = _trace(ns)
134
+ except TraceError as e:
135
+ sys.stderr.write(f"visset: {e}\n")
136
+ return 1
137
+ _report(trace)
138
+ if ns.command == "trace":
139
+ text = json.dumps(trace, ensure_ascii=False, indent=None, separators=(",", ":"))
140
+ if ns.output:
141
+ Path(ns.output).write_text(text, encoding="utf-8")
142
+ else:
143
+ sys.stdout.write(text + "\n")
144
+ return 0
145
+ return _emit_html(trace, ns.output, ns.open)
146
+
147
+
148
+ def _emit_html(trace: dict[str, Any], output: str | None, force_open: bool) -> int:
149
+ from .export.html import PlayerMissingError, open_in_browser, render_html
150
+
151
+ try:
152
+ if output and not force_open:
153
+ Path(output).write_text(render_html(trace), encoding="utf-8")
154
+ sys.stderr.write(f"wrote {output}\n")
155
+ else:
156
+ path = open_in_browser(trace, output)
157
+ sys.stderr.write(f"opened {path}\n")
158
+ except PlayerMissingError as e:
159
+ sys.stderr.write(f"visset: {e}\n")
160
+ return 1
161
+ return 0
162
+
163
+
164
+ if __name__ == "__main__":
165
+ raise SystemExit(main())
@@ -0,0 +1 @@
1
+ """Exporters: turn a trace into something you can open or share."""