vizcraft 1.16.0 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # vizcraft
2
2
 
3
+ ## 1.17.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`aeb6058`](https://github.com/ChipiKaf/vizcraft/commit/aeb60582bc56ffa24ef26902f8f8d119cc2f0e92) Thanks [@ChipiKaf](https://github.com/ChipiKaf)! - Add `createStepController` and `createStepControllerFromSpec` for zero-infrastructure step-through walkthroughs.
8
+
9
+ Each step mounts its own scene, runs its own signal animations via the internal animator, and fires `onReady` when animations complete — without a Redux store or external rAF loop.
10
+
11
+ New exports:
12
+ - `createStepController(opts)` — imperative API; accepts `StepDef[]` with per-step `builder` factories and `autoSignals`
13
+ - `createStepControllerFromSpec(spec, container, opts?)` — declarative API; converts `VizSpec.steps` (`VizStepSpec[]`) into step definitions, applies `highlight` dimming (non-highlighted nodes at 30% opacity), merges step overlays onto the base scene, and forces `loop: false` on all step signals
14
+ - `StepDef`, `StepControllerOptions`, `StepController` types
15
+
16
+ `VizSpec.steps` and `VizStepSpec` (previously stubs) are now fully wired.
17
+
3
18
  ## 1.16.0
4
19
 
5
20
  ### Minor Changes
package/README.md CHANGED
@@ -18,7 +18,7 @@ VizCraft is designed to make creating beautiful, animated node-link diagrams and
18
18
  - **Fluent Builder API**: Define your visualization scene using a readable, chainable API.
19
19
  - **Grid System**: Built-in 2D grid system for easy, structured layout of nodes.
20
20
  - **Auto-Layout Algorithms**: Built-in circular and grid layouts, custom sync/async algorithm support (e.g. ELK), and a `getNodeBoundingBox` utility for robust layout integration.
21
- - **Two Animation Systems**: Lightweight registry/CSS animations (e.g. edge `flow`) and data-only timeline animations (`AnimationSpec`).
21
+ - **Three Animation Systems + Step Controller**: Lightweight registry/CSS animations, data-only timeline animations (`AnimationSpec`), self-animating signals (`autoSignal`), and `createStepController` for zero-infrastructure step-through walkthroughs.
22
22
  - **Framework Agnostic**: The core logic is pure TypeScript and can be used with any framework or Vanilla JS.
23
23
  - **Custom Overlays**: Create complex, custom UI elements that float on top of your visualization.
24
24
  - **Dangling Edges**: Create edges with free endpoints for drag-to-connect interactions.
@@ -59,6 +59,29 @@ const container = document.getElementById('viz-basic');
59
59
  if (container) builder.mount(container);
60
60
  ```
61
61
 
62
+ ### Declarative spec API (`fromSpec`)
63
+
64
+ If you prefer to describe a scene as a plain JSON object — or you're generating scenes programmatically / from an LLM — use `fromSpec`. It returns an ordinary `VizBuilder` you can chain or mount normally:
65
+
66
+ ```typescript
67
+ import { fromSpec } from 'vizcraft';
68
+
69
+ const builder = fromSpec({
70
+ view: { width: 900, height: 360 },
71
+ nodes: [
72
+ { id: 'client', label: 'Client', x: 80, y: 180, fill: '#0e7490' },
73
+ { id: 'lb', label: 'Load Balancer', x: 420, y: 180, fill: '#7c3aed' },
74
+ { id: 's1', label: 'Server 1', shape: 'cylinder', x: 760, y: 100 },
75
+ ],
76
+ edges: [
77
+ { from: 'client', to: 'lb' },
78
+ { from: 'lb', to: 's1' },
79
+ ],
80
+ });
81
+
82
+ builder.mount(document.getElementById('canvas')!);
83
+ ```
84
+
62
85
  More walkthroughs and examples: [docs here](https://vizcraft-docs.vercel.app/docs/examples).
63
86
 
64
87
  ## 📚 Documentation (Topics)
@@ -827,9 +850,102 @@ builder.overlay(
827
850
  );
828
851
  ```
829
852
 
830
- ## 🤝 Contributing
853
+ ## Self-animating signals (`autoSignal`)
854
+
855
+ Declare a continuously-moving signal dot without writing any playback code.
856
+ `autoSignal()` drives its own rAF loop — it starts automatically on `mount()`.
857
+
858
+ ```typescript
859
+ const controller = viz()
860
+ .view(500, 200)
861
+ .node('a', { circle: { r: 20 }, at: { x: 80, y: 100 } })
862
+ .node('b', { circle: { r: 20 }, at: { x: 250, y: 100 } })
863
+ .node('c', { circle: { r: 20 }, at: { x: 420, y: 100 } })
864
+ .edge('a', 'b')
865
+ .arrow()
866
+ .edge('b', 'c')
867
+ .arrow()
868
+ .autoSignal({
869
+ id: 'flow',
870
+ chain: ['a', 'b', 'c'],
871
+ loop: true,
872
+ durationPerHop: 800,
873
+ })
874
+ .mount(document.getElementById('container')!);
875
+
876
+ // Control the animator at any time via the returned MountController
877
+ controller.pause();
878
+ controller.resume();
879
+ controller.setSpeed(2); // 2× speed
880
+ controller.stop(); // clears dots
881
+ controller.restart(); // restart from progress 0
882
+
883
+ const unsub = controller.onSignalComplete('flow', () => {
884
+ console.log('one lap done');
885
+ });
886
+ unsub(); // unsubscribe
887
+ ```
888
+
889
+ ### Patching signals manually
890
+
891
+ For full control, drive progress from your own loop:
892
+
893
+ ```typescript
894
+ const controller = viz()
895
+ .view(500, 200)
896
+ .node('a', { circle: { r: 20 }, at: { x: 80, y: 100 } })
897
+ .node('b', { circle: { r: 20 }, at: { x: 420, y: 100 } })
898
+ .edge('a', 'b')
899
+ .arrow()
900
+ .mount(container);
901
+
902
+ let progress = 0;
903
+ (function animate() {
904
+ progress = (progress + 0.004) % 1;
905
+ controller.patchSignals([{ key: 'sig', from: 'a', to: 'b', progress }]);
906
+ requestAnimationFrame(animate);
907
+ })();
908
+
909
+ // Remove all signal dots
910
+ controller.clearSignals();
911
+ ```
912
+
913
+ `mount()` always returns a `MountController`. Pan-zoom (when enabled) is at `controller.panZoom`.
914
+
915
+ ## Step-through walkthroughs (`createStepController`)
916
+
917
+ `createStepController` manages mounting, signal animation, and step lifecycle — no Redux, no external rAF loop.
918
+
919
+ ```ts
920
+ import { createStepController } from 'vizcraft';
831
921
 
832
- Contributions are welcome! This is a monorepo managed with Turbo.
922
+ const ctrl = createStepController({
923
+ container: document.getElementById('canvas')!,
924
+ steps: [
925
+ {
926
+ label: 'Client sends a request',
927
+ builder: () => viz().view(560, 180) /* ... */,
928
+ autoSignals: [{ id: 'req', chain: ['a', 'b'], durationPerHop: 900 }],
929
+ },
930
+ {
931
+ label: 'Cache miss — forwarding to DB',
932
+ builder: () => viz().view(560, 180) /* ... */,
933
+ autoSignals: [{ id: 'fwd', chain: ['b', 'c'], durationPerHop: 900 }],
934
+ },
935
+ ],
936
+ onStepChange: (i, step) => {
937
+ label.textContent = `Step ${i + 1}: ${step.label}`;
938
+ nextBtn.disabled = true;
939
+ },
940
+ onReady: () => (nextBtn.disabled = false),
941
+ });
942
+
943
+ nextBtn.addEventListener('click', () => ctrl.next());
944
+ ```
945
+
946
+ For declarative/AI-generated walkthroughs, use `createStepControllerFromSpec` with `VizSpec.steps`.
947
+
948
+ ## 🤝 Contributing
833
949
 
834
950
  1. Clone the repo
835
951
  2. Install dependencies: `pnpm install`
@@ -1 +1 @@
1
- export { az as AnimProperty, aF as AnimatableProps, aG as AnimationBuilder, aQ as AnimationHostAdapter, aC as AnimationSpec, ax as AnimationTarget, ay as CoreAnimProperty, aE as CoreAnimatableProps, aA as Ease, aI as ExtendAdapter, aJ as PlaybackController, aP as PropHandlers, aN as PropReader, aO as PropWriter, aR as RegistrableAdapter, aD as TweenOptions, aB as TweenSpec, aH as buildAnimationSpec, aL as createBuilderPlayback, aK as createScenePlayback, aM as playAnimationSpec } from './index-n9JLZMa1.mjs';
1
+ export { aE as AnimProperty, aK as AnimatableProps, aL as AnimationBuilder, aV as AnimationHostAdapter, aH as AnimationSpec, aC as AnimationTarget, aD as CoreAnimProperty, aJ as CoreAnimatableProps, aF as Ease, aN as ExtendAdapter, aO as PlaybackController, aU as PropHandlers, aS as PropReader, aT as PropWriter, aW as RegistrableAdapter, aI as TweenOptions, aG as TweenSpec, aM as buildAnimationSpec, aQ as createBuilderPlayback, aP as createScenePlayback, aR as playAnimationSpec } from './index-Bvue3FPE.mjs';
@@ -1 +1 @@
1
- export { az as AnimProperty, aF as AnimatableProps, aG as AnimationBuilder, aQ as AnimationHostAdapter, aC as AnimationSpec, ax as AnimationTarget, ay as CoreAnimProperty, aE as CoreAnimatableProps, aA as Ease, aI as ExtendAdapter, aJ as PlaybackController, aP as PropHandlers, aN as PropReader, aO as PropWriter, aR as RegistrableAdapter, aD as TweenOptions, aB as TweenSpec, aH as buildAnimationSpec, aL as createBuilderPlayback, aK as createScenePlayback, aM as playAnimationSpec } from './index-n9JLZMa1.js';
1
+ export { aE as AnimProperty, aK as AnimatableProps, aL as AnimationBuilder, aV as AnimationHostAdapter, aH as AnimationSpec, aC as AnimationTarget, aD as CoreAnimProperty, aJ as CoreAnimatableProps, aF as Ease, aN as ExtendAdapter, aO as PlaybackController, aU as PropHandlers, aS as PropReader, aT as PropWriter, aW as RegistrableAdapter, aI as TweenOptions, aG as TweenSpec, aM as buildAnimationSpec, aQ as createBuilderPlayback, aP as createScenePlayback, aR as playAnimationSpec } from './index-Bvue3FPE.js';
package/dist/animation.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var chunkC27QMQJJ_js=require('./chunk-C27QMQJJ.js');Object.defineProperty(exports,"AnimationBuilder",{enumerable:true,get:function(){return chunkC27QMQJJ_js.c}});Object.defineProperty(exports,"buildAnimationSpec",{enumerable:true,get:function(){return chunkC27QMQJJ_js.d}});Object.defineProperty(exports,"createBuilderPlayback",{enumerable:true,get:function(){return chunkC27QMQJJ_js.f}});Object.defineProperty(exports,"createScenePlayback",{enumerable:true,get:function(){return chunkC27QMQJJ_js.e}});Object.defineProperty(exports,"playAnimationSpec",{enumerable:true,get:function(){return chunkC27QMQJJ_js.g}});
1
+ 'use strict';var chunkM7FVMWEX_js=require('./chunk-M7FVMWEX.js');Object.defineProperty(exports,"AnimationBuilder",{enumerable:true,get:function(){return chunkM7FVMWEX_js.c}});Object.defineProperty(exports,"buildAnimationSpec",{enumerable:true,get:function(){return chunkM7FVMWEX_js.d}});Object.defineProperty(exports,"createBuilderPlayback",{enumerable:true,get:function(){return chunkM7FVMWEX_js.f}});Object.defineProperty(exports,"createScenePlayback",{enumerable:true,get:function(){return chunkM7FVMWEX_js.e}});Object.defineProperty(exports,"playAnimationSpec",{enumerable:true,get:function(){return chunkM7FVMWEX_js.g}});
@@ -1 +1 @@
1
- export{c as AnimationBuilder,d as buildAnimationSpec,f as createBuilderPlayback,e as createScenePlayback,g as playAnimationSpec}from'./chunk-LQJHDSRP.mjs';
1
+ export{c as AnimationBuilder,d as buildAnimationSpec,f as createBuilderPlayback,e as createScenePlayback,g as playAnimationSpec}from'./chunk-G4YFT72R.mjs';
@@ -1 +1 @@
1
- var E=Symbol("vizcraft.overlay.runtimeDirty");var k=Symbol("vizcraft.adapterExtensions");function V(r){let i=r[k];return Array.isArray(i)?i:[]}function T(r){return typeof r=="number"&&Number.isFinite(r)}function z(r,i){return `${r}:${i}`}var S=class{cursorMs=0;currentTarget=null;tweens=[];adapterExtensions=[];extendAdapter(i){return this.adapterExtensions.push(i),this}node(i){return this.currentTarget=z("node",i),this}overlay(i){return this.currentTarget=z("overlay",i),this}edge(i,g,b){let l=g===void 0?i:b??`${i}->${g}`;return this.currentTarget=z("edge",l),this}at(i){return this.cursorMs=Math.max(0,i),this}wait(i){return this.cursorMs=Math.max(0,this.cursorMs+Math.max(0,i)),this}to(i,g){if(!this.currentTarget)throw new Error("AnimationBuilder.to(): no target selected (call node(...), edge(...), or overlay(...))");let b=Math.max(0,g.duration),l=g.easing,u=g.from;for(let[m,y]of Object.entries(i)){if(!T(y))continue;let h={kind:"tween",target:this.currentTarget,property:m,to:y,duration:b,delay:this.cursorMs,easing:l},x=u?.[m];T(x)&&(h.from=x),this.tweens.push(h);}return this.cursorMs+=b,this}build(){let i={version:"viz-anim/1",tweens:[...this.tweens]};return this.adapterExtensions.length>0&&(i[k]=[...this.adapterExtensions]),i}};function D(r){let i=new S;return r(i),i.build()}function P(r,i=0,g=1){return Math.max(i,Math.min(g,r))}var L={linear:r=>r,easeIn:r=>r*r,easeOut:r=>1-(1-r)*(1-r),easeInOut:r=>r<.5?2*r*r:1-Math.pow(-2*r+2,2)/2};function R(r){let i=null,g=[],b=0,l=0,u=false,m=null,y=0,h=false;function x(o){let e=o.tweens.map((s,f)=>{let d=s.delay??0,w=d+s.duration;return Object.assign({},s,{start:d,end:w,_from:s.from??0,_i:f})}),t=new Map;for(let s of e){let f=`${String(s.target)}|${String(s.property)}`,d=t.get(f);d||(d={key:f,target:s.target,property:s.property,base:0,tweens:[]},t.set(f,d)),d.tweens.push(s);}let n=Array.from(t.values());for(let s of n)s.tweens.sort((f,d)=>f.start!==d.start?f.start-d.start:f._i-d._i);let c=e.reduce((s,f)=>Math.max(s,f.end),0);return {tracks:n,dur:c}}function A(){if(!(h||!i)){for(let o of g){let e=r.get(o.target,o.property);o.base=typeof e=="number"?e:0;let t=null;for(let n of o.tweens)n.from!==void 0?n._from=n.from:t&&t.end<=n.start?n._from=t.to:n._from=o.base,t=n;}h=true;}}function v(o){for(let e of g){let t=e.tweens,n=0,c=t.length-1,s=-1;for(;n<=c;){let d=n+c>>1;t[d].start<=o?(s=d,n=d+1):c=d-1;}let f;if(s===-1)f=e.base;else {let d=t[s],w=o-d.start;if(w<=0)f=d._from;else if(d.duration<=0)f=d.to;else if(w>=d.duration)f=d.to;else {let O=P(w/d.duration,0,1),M=L[d.easing??"linear"],I=M(O);f=d._from+(d.to-d._from)*I;}}r.set(e.target,e.property,f);}r.flush?.();}function a(o){if(!u)return;let e=o-y;if(y=o,l=Math.min(b,l+e),v(l),l>=b){u=false,m=null;return}m=globalThis.requestAnimationFrame(a);}let p={load(o){i=o;let e=x(o);return g=e.tracks,b=e.dur,l=0,h=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,A(),v(0),p},play(){i&&(A(),!u&&(u=true,y=performance.now(),m=globalThis.requestAnimationFrame(a)));},pause(){u&&(u=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null));},seek(o){i&&(A(),l=P(o,0,b),v(l));},stop(){i&&(m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,l=0,A(),v(0));},isPlaying(){return u},time(){return l},duration(){return b}};return p}function C(r){let{flush:i}=r,g={},b={},l={},u={};function m(a,p){g[a]=p;}function y(a,p,o){o.get&&(l[a]=l[a]??{},l[a][p]=o.get),o.set&&(u[a]=u[a]??{},u[a][p]=o.set);}function h(a,p){let o=g[a];if(o){if(p&&p!==o)throw new Error(`Animation adapter: kind "${a}" already registered with a different resolver`);if(b[a])return b[a];let t={prop(n,c){return y(a,n,c),t}};return b[a]=t,t}if(!p)throw new Error(`Animation adapter: kind "${a}" is not registered yet`);m(a,p);let e={prop(t,n){return y(a,t,n),e}};return b[a]=e,e}function x(a){let p=String(a).indexOf(":");if(p===-1)return;let o=String(a).slice(0,p),e=String(a).slice(p+1),t=g[o];if(!t)return;let n=t(e);if(n!==void 0)return {kind:o,el:n}}function A(a,p){let o=x(a);if(!o)return;let{kind:e,el:t}=o,c=(l[e]??{})[String(p)];if(c)return c(t)}function v(a,p,o){let e=x(a);if(!e)return;let{kind:t,el:n}=e,s=(u[t]??{})[String(p)];s&&s(n,o);}return {get:A,set:v,flush:i,kind:h,register:y}}function N(r,i){let g=new Map(r.nodes.map(e=>[e.id,e])),b=new Map(r.edges.map(e=>[e.id,e])),l=r.overlays??[],u=new Map;for(let e of l){let t=e.key??e.id;u.set(t,e);}let m=C({flush:i}),y=m.kind("node",e=>g.get(e)),h=m.kind("edge",e=>b.get(e)),x=m.kind("overlay",e=>u.get(e));y.prop("x",{get:e=>{let t=e;return t.runtime?.x??t.pos.x},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.x=t;}}).prop("y",{get:e=>{let t=e;return t.runtime?.y??t.pos.y},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.y=t;}}).prop("opacity",{get:e=>{let t=e;return t.runtime?.opacity??t.style?.opacity??1},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("scale",{get:e=>e.runtime?.scale??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.scale=t;}}).prop("rotation",{get:e=>e.runtime?.rotation,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.rotation=t;}}),h.prop("opacity",{get:e=>e.runtime?.opacity??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("strokeDashoffset",{get:e=>e.runtime?.strokeDashoffset,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.strokeDashoffset=t;}});let A=(e,t)=>{let s=e.params?.[t];return typeof s=="number"&&Number.isFinite(s)?s:void 0},v=(e,t,n)=>{let c=e,s=c.params,f=s&&typeof s=="object"&&!Array.isArray(s)?s:{};f[t]=n,c.params=f,c[E]=true;},a=e=>{let t=String(e);if(!t.startsWith("overlay:"))return;let n=t.slice(8);return u.get(n)};x.prop("progress",{get:e=>A(e,"progress"),set:(e,t)=>v(e,"progress",t)});let p=m.get,o=m.set;return {...m,get(e,t){let n=p(e,t);if(n!==void 0)return n;let c=a(e);if(c)return A(c,String(t))},set(e,t,n){o(e,t,n);let c=a(e);c&&v(c,String(t),n);}}}function W(r){let i=N(r.scene,r.requestRender);return r.extendAdapter?.(i),R(i)}function H(r){let i=r.builder.build();return W({scene:i,requestRender:()=>r.builder.patchRuntime(r.container),extendAdapter:r.extendAdapter})}function Q(r){let i=V(r.spec),g=i.length>0||r.extendAdapter?l=>{for(let u of i)u(l);r.extendAdapter?.(l);}:void 0,b=H({builder:r.builder,container:r.container,extendAdapter:g});return b.load(r.spec),r.autoPlay!==false&&b.play(),b}export{E as a,V as b,S as c,D as d,W as e,H as f,Q as g};
1
+ var E=Symbol("vizcraft.overlay.runtimeDirty");var k=Symbol("vizcraft.adapterExtensions");function V(r){let i=r[k];return Array.isArray(i)?i:[]}function T(r){return typeof r=="number"&&Number.isFinite(r)}function S(r,i){return `${r}:${i}`}var z=class{cursorMs=0;currentTarget=null;tweens=[];adapterExtensions=[];extendAdapter(i){return this.adapterExtensions.push(i),this}node(i){return this.currentTarget=S("node",i),this}overlay(i){return this.currentTarget=S("overlay",i),this}edge(i,g,b){let l=g===void 0?i:b??`${i}->${g}`;return this.currentTarget=S("edge",l),this}at(i){return this.cursorMs=Math.max(0,i),this}wait(i){return this.cursorMs=Math.max(0,this.cursorMs+Math.max(0,i)),this}to(i,g){if(!this.currentTarget)throw new Error("AnimationBuilder.to(): no target selected (call node(...), edge(...), or overlay(...))");let b=Math.max(0,g.duration),l=g.easing,u=g.from;for(let[m,y]of Object.entries(i)){if(!T(y))continue;let h={kind:"tween",target:this.currentTarget,property:m,to:y,duration:b,delay:this.cursorMs,easing:l},x=u?.[m];T(x)&&(h.from=x),this.tweens.push(h);}return this.cursorMs+=b,this}build(){let i={version:"viz-anim/1",tweens:[...this.tweens]};return this.adapterExtensions.length>0&&(i[k]=[...this.adapterExtensions]),i}};function D(r){let i=new z;return r(i),i.build()}function P(r,i=0,g=1){return Math.max(i,Math.min(g,r))}var L={linear:r=>r,easeIn:r=>r*r,easeOut:r=>1-(1-r)*(1-r),easeInOut:r=>r<.5?2*r*r:1-Math.pow(-2*r+2,2)/2};function R(r){let i=null,g=[],b=0,l=0,u=false,m=null,y=0,h=false;function x(o){let e=o.tweens.map((s,f)=>{let d=s.delay??0,w=d+s.duration;return Object.assign({},s,{start:d,end:w,_from:s.from??0,_i:f})}),t=new Map;for(let s of e){let f=`${String(s.target)}|${String(s.property)}`,d=t.get(f);d||(d={key:f,target:s.target,property:s.property,base:0,tweens:[]},t.set(f,d)),d.tweens.push(s);}let n=Array.from(t.values());for(let s of n)s.tweens.sort((f,d)=>f.start!==d.start?f.start-d.start:f._i-d._i);let c=e.reduce((s,f)=>Math.max(s,f.end),0);return {tracks:n,dur:c}}function A(){if(!(h||!i)){for(let o of g){let e=r.get(o.target,o.property);o.base=typeof e=="number"?e:0;let t=null;for(let n of o.tweens)n.from!==void 0?n._from=n.from:t&&t.end<=n.start?n._from=t.to:n._from=o.base,t=n;}h=true;}}function v(o){for(let e of g){let t=e.tweens,n=0,c=t.length-1,s=-1;for(;n<=c;){let d=n+c>>1;t[d].start<=o?(s=d,n=d+1):c=d-1;}let f;if(s===-1)f=e.base;else {let d=t[s],w=o-d.start;if(w<=0)f=d._from;else if(d.duration<=0)f=d.to;else if(w>=d.duration)f=d.to;else {let O=P(w/d.duration,0,1),M=L[d.easing??"linear"],I=M(O);f=d._from+(d.to-d._from)*I;}}r.set(e.target,e.property,f);}r.flush?.();}function a(o){if(!u)return;let e=o-y;if(y=o,l=Math.min(b,l+e),v(l),l>=b){u=false,m=null;return}m=globalThis.requestAnimationFrame(a);}let p={load(o){i=o;let e=x(o);return g=e.tracks,b=e.dur,l=0,h=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,A(),v(0),p},play(){i&&(A(),!u&&(u=true,y=performance.now(),m=globalThis.requestAnimationFrame(a)));},pause(){u&&(u=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null));},seek(o){i&&(A(),l=P(o,0,b),v(l));},stop(){i&&(m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,l=0,A(),v(0));},isPlaying(){return u},time(){return l},duration(){return b}};return p}function C(r){let{flush:i}=r,g={},b={},l={},u={};function m(a,p){g[a]=p;}function y(a,p,o){o.get&&(l[a]=l[a]??{},l[a][p]=o.get),o.set&&(u[a]=u[a]??{},u[a][p]=o.set);}function h(a,p){let o=g[a];if(o){if(p&&p!==o)throw new Error(`Animation adapter: kind "${a}" already registered with a different resolver`);if(b[a])return b[a];let t={prop(n,c){return y(a,n,c),t}};return b[a]=t,t}if(!p)throw new Error(`Animation adapter: kind "${a}" is not registered yet`);m(a,p);let e={prop(t,n){return y(a,t,n),e}};return b[a]=e,e}function x(a){let p=String(a).indexOf(":");if(p===-1)return;let o=String(a).slice(0,p),e=String(a).slice(p+1),t=g[o];if(!t)return;let n=t(e);if(n!==void 0)return {kind:o,el:n}}function A(a,p){let o=x(a);if(!o)return;let{kind:e,el:t}=o,c=(l[e]??{})[String(p)];if(c)return c(t)}function v(a,p,o){let e=x(a);if(!e)return;let{kind:t,el:n}=e,s=(u[t]??{})[String(p)];s&&s(n,o);}return {get:A,set:v,flush:i,kind:h,register:y}}function N(r,i){let g=new Map(r.nodes.map(e=>[e.id,e])),b=new Map(r.edges.map(e=>[e.id,e])),l=r.overlays??[],u=new Map;for(let e of l){let t=e.key??e.id;u.set(t,e);}let m=C({flush:i}),y=m.kind("node",e=>g.get(e)),h=m.kind("edge",e=>b.get(e)),x=m.kind("overlay",e=>u.get(e));y.prop("x",{get:e=>{let t=e;return t.runtime?.x??t.pos.x},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.x=t;}}).prop("y",{get:e=>{let t=e;return t.runtime?.y??t.pos.y},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.y=t;}}).prop("opacity",{get:e=>{let t=e;return t.runtime?.opacity??t.style?.opacity??1},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("scale",{get:e=>e.runtime?.scale??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.scale=t;}}).prop("rotation",{get:e=>e.runtime?.rotation,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.rotation=t;}}),h.prop("opacity",{get:e=>e.runtime?.opacity??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("strokeDashoffset",{get:e=>e.runtime?.strokeDashoffset,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.strokeDashoffset=t;}});let A=(e,t)=>{let s=e.params?.[t];return typeof s=="number"&&Number.isFinite(s)?s:void 0},v=(e,t,n)=>{let c=e,s=c.params,f=s&&typeof s=="object"&&!Array.isArray(s)?s:{};f[t]=n,c.params=f,c[E]=true;},a=e=>{let t=String(e);if(!t.startsWith("overlay:"))return;let n=t.slice(8);return u.get(n)};x.prop("progress",{get:e=>A(e,"progress"),set:(e,t)=>v(e,"progress",t)});let p=m.get,o=m.set;return {...m,get(e,t){let n=p(e,t);if(n!==void 0)return n;let c=a(e);if(c)return A(c,String(t))},set(e,t,n){o(e,t,n);let c=a(e);c&&v(c,String(t),n);}}}function W(r){let i=N(r.scene,r.requestRender);return r.extendAdapter?.(i),R(i)}function H(r){let i=r.builder.build();return W({scene:i,requestRender:()=>r.builder.patchRuntime(r.container),extendAdapter:r.extendAdapter})}function Q(r){let i=V(r.spec),g=i.length>0||r.extendAdapter?l=>{for(let u of i)u(l);r.extendAdapter?.(l);}:void 0,b=H({builder:r.builder,container:r.container,extendAdapter:g});return b.load(r.spec),r.autoPlay!==false&&b.play(),b}export{E as a,V as b,z as c,D as d,W as e,H as f,Q as g};
@@ -1 +1 @@
1
- 'use strict';var E=Symbol("vizcraft.overlay.runtimeDirty");var k=Symbol("vizcraft.adapterExtensions");function V(r){let i=r[k];return Array.isArray(i)?i:[]}function T(r){return typeof r=="number"&&Number.isFinite(r)}function z(r,i){return `${r}:${i}`}var S=class{cursorMs=0;currentTarget=null;tweens=[];adapterExtensions=[];extendAdapter(i){return this.adapterExtensions.push(i),this}node(i){return this.currentTarget=z("node",i),this}overlay(i){return this.currentTarget=z("overlay",i),this}edge(i,g,b){let l=g===void 0?i:b??`${i}->${g}`;return this.currentTarget=z("edge",l),this}at(i){return this.cursorMs=Math.max(0,i),this}wait(i){return this.cursorMs=Math.max(0,this.cursorMs+Math.max(0,i)),this}to(i,g){if(!this.currentTarget)throw new Error("AnimationBuilder.to(): no target selected (call node(...), edge(...), or overlay(...))");let b=Math.max(0,g.duration),l=g.easing,u=g.from;for(let[m,y]of Object.entries(i)){if(!T(y))continue;let h={kind:"tween",target:this.currentTarget,property:m,to:y,duration:b,delay:this.cursorMs,easing:l},x=u?.[m];T(x)&&(h.from=x),this.tweens.push(h);}return this.cursorMs+=b,this}build(){let i={version:"viz-anim/1",tweens:[...this.tweens]};return this.adapterExtensions.length>0&&(i[k]=[...this.adapterExtensions]),i}};function D(r){let i=new S;return r(i),i.build()}function P(r,i=0,g=1){return Math.max(i,Math.min(g,r))}var L={linear:r=>r,easeIn:r=>r*r,easeOut:r=>1-(1-r)*(1-r),easeInOut:r=>r<.5?2*r*r:1-Math.pow(-2*r+2,2)/2};function R(r){let i=null,g=[],b=0,l=0,u=false,m=null,y=0,h=false;function x(o){let e=o.tweens.map((s,f)=>{let d=s.delay??0,w=d+s.duration;return Object.assign({},s,{start:d,end:w,_from:s.from??0,_i:f})}),t=new Map;for(let s of e){let f=`${String(s.target)}|${String(s.property)}`,d=t.get(f);d||(d={key:f,target:s.target,property:s.property,base:0,tweens:[]},t.set(f,d)),d.tweens.push(s);}let n=Array.from(t.values());for(let s of n)s.tweens.sort((f,d)=>f.start!==d.start?f.start-d.start:f._i-d._i);let c=e.reduce((s,f)=>Math.max(s,f.end),0);return {tracks:n,dur:c}}function A(){if(!(h||!i)){for(let o of g){let e=r.get(o.target,o.property);o.base=typeof e=="number"?e:0;let t=null;for(let n of o.tweens)n.from!==void 0?n._from=n.from:t&&t.end<=n.start?n._from=t.to:n._from=o.base,t=n;}h=true;}}function v(o){for(let e of g){let t=e.tweens,n=0,c=t.length-1,s=-1;for(;n<=c;){let d=n+c>>1;t[d].start<=o?(s=d,n=d+1):c=d-1;}let f;if(s===-1)f=e.base;else {let d=t[s],w=o-d.start;if(w<=0)f=d._from;else if(d.duration<=0)f=d.to;else if(w>=d.duration)f=d.to;else {let O=P(w/d.duration,0,1),M=L[d.easing??"linear"],I=M(O);f=d._from+(d.to-d._from)*I;}}r.set(e.target,e.property,f);}r.flush?.();}function a(o){if(!u)return;let e=o-y;if(y=o,l=Math.min(b,l+e),v(l),l>=b){u=false,m=null;return}m=globalThis.requestAnimationFrame(a);}let p={load(o){i=o;let e=x(o);return g=e.tracks,b=e.dur,l=0,h=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,A(),v(0),p},play(){i&&(A(),!u&&(u=true,y=performance.now(),m=globalThis.requestAnimationFrame(a)));},pause(){u&&(u=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null));},seek(o){i&&(A(),l=P(o,0,b),v(l));},stop(){i&&(m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,l=0,A(),v(0));},isPlaying(){return u},time(){return l},duration(){return b}};return p}function C(r){let{flush:i}=r,g={},b={},l={},u={};function m(a,p){g[a]=p;}function y(a,p,o){o.get&&(l[a]=l[a]??{},l[a][p]=o.get),o.set&&(u[a]=u[a]??{},u[a][p]=o.set);}function h(a,p){let o=g[a];if(o){if(p&&p!==o)throw new Error(`Animation adapter: kind "${a}" already registered with a different resolver`);if(b[a])return b[a];let t={prop(n,c){return y(a,n,c),t}};return b[a]=t,t}if(!p)throw new Error(`Animation adapter: kind "${a}" is not registered yet`);m(a,p);let e={prop(t,n){return y(a,t,n),e}};return b[a]=e,e}function x(a){let p=String(a).indexOf(":");if(p===-1)return;let o=String(a).slice(0,p),e=String(a).slice(p+1),t=g[o];if(!t)return;let n=t(e);if(n!==void 0)return {kind:o,el:n}}function A(a,p){let o=x(a);if(!o)return;let{kind:e,el:t}=o,c=(l[e]??{})[String(p)];if(c)return c(t)}function v(a,p,o){let e=x(a);if(!e)return;let{kind:t,el:n}=e,s=(u[t]??{})[String(p)];s&&s(n,o);}return {get:A,set:v,flush:i,kind:h,register:y}}function N(r,i){let g=new Map(r.nodes.map(e=>[e.id,e])),b=new Map(r.edges.map(e=>[e.id,e])),l=r.overlays??[],u=new Map;for(let e of l){let t=e.key??e.id;u.set(t,e);}let m=C({flush:i}),y=m.kind("node",e=>g.get(e)),h=m.kind("edge",e=>b.get(e)),x=m.kind("overlay",e=>u.get(e));y.prop("x",{get:e=>{let t=e;return t.runtime?.x??t.pos.x},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.x=t;}}).prop("y",{get:e=>{let t=e;return t.runtime?.y??t.pos.y},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.y=t;}}).prop("opacity",{get:e=>{let t=e;return t.runtime?.opacity??t.style?.opacity??1},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("scale",{get:e=>e.runtime?.scale??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.scale=t;}}).prop("rotation",{get:e=>e.runtime?.rotation,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.rotation=t;}}),h.prop("opacity",{get:e=>e.runtime?.opacity??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("strokeDashoffset",{get:e=>e.runtime?.strokeDashoffset,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.strokeDashoffset=t;}});let A=(e,t)=>{let s=e.params?.[t];return typeof s=="number"&&Number.isFinite(s)?s:void 0},v=(e,t,n)=>{let c=e,s=c.params,f=s&&typeof s=="object"&&!Array.isArray(s)?s:{};f[t]=n,c.params=f,c[E]=true;},a=e=>{let t=String(e);if(!t.startsWith("overlay:"))return;let n=t.slice(8);return u.get(n)};x.prop("progress",{get:e=>A(e,"progress"),set:(e,t)=>v(e,"progress",t)});let p=m.get,o=m.set;return {...m,get(e,t){let n=p(e,t);if(n!==void 0)return n;let c=a(e);if(c)return A(c,String(t))},set(e,t,n){o(e,t,n);let c=a(e);c&&v(c,String(t),n);}}}function W(r){let i=N(r.scene,r.requestRender);return r.extendAdapter?.(i),R(i)}function H(r){let i=r.builder.build();return W({scene:i,requestRender:()=>r.builder.patchRuntime(r.container),extendAdapter:r.extendAdapter})}function Q(r){let i=V(r.spec),g=i.length>0||r.extendAdapter?l=>{for(let u of i)u(l);r.extendAdapter?.(l);}:void 0,b=H({builder:r.builder,container:r.container,extendAdapter:g});return b.load(r.spec),r.autoPlay!==false&&b.play(),b}exports.a=E;exports.b=V;exports.c=S;exports.d=D;exports.e=W;exports.f=H;exports.g=Q;
1
+ 'use strict';var E=Symbol("vizcraft.overlay.runtimeDirty");var k=Symbol("vizcraft.adapterExtensions");function V(r){let i=r[k];return Array.isArray(i)?i:[]}function T(r){return typeof r=="number"&&Number.isFinite(r)}function S(r,i){return `${r}:${i}`}var z=class{cursorMs=0;currentTarget=null;tweens=[];adapterExtensions=[];extendAdapter(i){return this.adapterExtensions.push(i),this}node(i){return this.currentTarget=S("node",i),this}overlay(i){return this.currentTarget=S("overlay",i),this}edge(i,g,b){let l=g===void 0?i:b??`${i}->${g}`;return this.currentTarget=S("edge",l),this}at(i){return this.cursorMs=Math.max(0,i),this}wait(i){return this.cursorMs=Math.max(0,this.cursorMs+Math.max(0,i)),this}to(i,g){if(!this.currentTarget)throw new Error("AnimationBuilder.to(): no target selected (call node(...), edge(...), or overlay(...))");let b=Math.max(0,g.duration),l=g.easing,u=g.from;for(let[m,y]of Object.entries(i)){if(!T(y))continue;let h={kind:"tween",target:this.currentTarget,property:m,to:y,duration:b,delay:this.cursorMs,easing:l},x=u?.[m];T(x)&&(h.from=x),this.tweens.push(h);}return this.cursorMs+=b,this}build(){let i={version:"viz-anim/1",tweens:[...this.tweens]};return this.adapterExtensions.length>0&&(i[k]=[...this.adapterExtensions]),i}};function D(r){let i=new z;return r(i),i.build()}function P(r,i=0,g=1){return Math.max(i,Math.min(g,r))}var L={linear:r=>r,easeIn:r=>r*r,easeOut:r=>1-(1-r)*(1-r),easeInOut:r=>r<.5?2*r*r:1-Math.pow(-2*r+2,2)/2};function R(r){let i=null,g=[],b=0,l=0,u=false,m=null,y=0,h=false;function x(o){let e=o.tweens.map((s,f)=>{let d=s.delay??0,w=d+s.duration;return Object.assign({},s,{start:d,end:w,_from:s.from??0,_i:f})}),t=new Map;for(let s of e){let f=`${String(s.target)}|${String(s.property)}`,d=t.get(f);d||(d={key:f,target:s.target,property:s.property,base:0,tweens:[]},t.set(f,d)),d.tweens.push(s);}let n=Array.from(t.values());for(let s of n)s.tweens.sort((f,d)=>f.start!==d.start?f.start-d.start:f._i-d._i);let c=e.reduce((s,f)=>Math.max(s,f.end),0);return {tracks:n,dur:c}}function A(){if(!(h||!i)){for(let o of g){let e=r.get(o.target,o.property);o.base=typeof e=="number"?e:0;let t=null;for(let n of o.tweens)n.from!==void 0?n._from=n.from:t&&t.end<=n.start?n._from=t.to:n._from=o.base,t=n;}h=true;}}function v(o){for(let e of g){let t=e.tweens,n=0,c=t.length-1,s=-1;for(;n<=c;){let d=n+c>>1;t[d].start<=o?(s=d,n=d+1):c=d-1;}let f;if(s===-1)f=e.base;else {let d=t[s],w=o-d.start;if(w<=0)f=d._from;else if(d.duration<=0)f=d.to;else if(w>=d.duration)f=d.to;else {let O=P(w/d.duration,0,1),M=L[d.easing??"linear"],I=M(O);f=d._from+(d.to-d._from)*I;}}r.set(e.target,e.property,f);}r.flush?.();}function a(o){if(!u)return;let e=o-y;if(y=o,l=Math.min(b,l+e),v(l),l>=b){u=false,m=null;return}m=globalThis.requestAnimationFrame(a);}let p={load(o){i=o;let e=x(o);return g=e.tracks,b=e.dur,l=0,h=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,A(),v(0),p},play(){i&&(A(),!u&&(u=true,y=performance.now(),m=globalThis.requestAnimationFrame(a)));},pause(){u&&(u=false,m!=null&&(globalThis.cancelAnimationFrame(m),m=null));},seek(o){i&&(A(),l=P(o,0,b),v(l));},stop(){i&&(m!=null&&(globalThis.cancelAnimationFrame(m),m=null),u=false,l=0,A(),v(0));},isPlaying(){return u},time(){return l},duration(){return b}};return p}function C(r){let{flush:i}=r,g={},b={},l={},u={};function m(a,p){g[a]=p;}function y(a,p,o){o.get&&(l[a]=l[a]??{},l[a][p]=o.get),o.set&&(u[a]=u[a]??{},u[a][p]=o.set);}function h(a,p){let o=g[a];if(o){if(p&&p!==o)throw new Error(`Animation adapter: kind "${a}" already registered with a different resolver`);if(b[a])return b[a];let t={prop(n,c){return y(a,n,c),t}};return b[a]=t,t}if(!p)throw new Error(`Animation adapter: kind "${a}" is not registered yet`);m(a,p);let e={prop(t,n){return y(a,t,n),e}};return b[a]=e,e}function x(a){let p=String(a).indexOf(":");if(p===-1)return;let o=String(a).slice(0,p),e=String(a).slice(p+1),t=g[o];if(!t)return;let n=t(e);if(n!==void 0)return {kind:o,el:n}}function A(a,p){let o=x(a);if(!o)return;let{kind:e,el:t}=o,c=(l[e]??{})[String(p)];if(c)return c(t)}function v(a,p,o){let e=x(a);if(!e)return;let{kind:t,el:n}=e,s=(u[t]??{})[String(p)];s&&s(n,o);}return {get:A,set:v,flush:i,kind:h,register:y}}function N(r,i){let g=new Map(r.nodes.map(e=>[e.id,e])),b=new Map(r.edges.map(e=>[e.id,e])),l=r.overlays??[],u=new Map;for(let e of l){let t=e.key??e.id;u.set(t,e);}let m=C({flush:i}),y=m.kind("node",e=>g.get(e)),h=m.kind("edge",e=>b.get(e)),x=m.kind("overlay",e=>u.get(e));y.prop("x",{get:e=>{let t=e;return t.runtime?.x??t.pos.x},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.x=t;}}).prop("y",{get:e=>{let t=e;return t.runtime?.y??t.pos.y},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.y=t;}}).prop("opacity",{get:e=>{let t=e;return t.runtime?.opacity??t.style?.opacity??1},set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("scale",{get:e=>e.runtime?.scale??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.scale=t;}}).prop("rotation",{get:e=>e.runtime?.rotation,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.rotation=t;}}),h.prop("opacity",{get:e=>e.runtime?.opacity??1,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.opacity=t;}}).prop("strokeDashoffset",{get:e=>e.runtime?.strokeDashoffset,set:(e,t)=>{let n=e;n.runtime=n.runtime??{},n.runtime.strokeDashoffset=t;}});let A=(e,t)=>{let s=e.params?.[t];return typeof s=="number"&&Number.isFinite(s)?s:void 0},v=(e,t,n)=>{let c=e,s=c.params,f=s&&typeof s=="object"&&!Array.isArray(s)?s:{};f[t]=n,c.params=f,c[E]=true;},a=e=>{let t=String(e);if(!t.startsWith("overlay:"))return;let n=t.slice(8);return u.get(n)};x.prop("progress",{get:e=>A(e,"progress"),set:(e,t)=>v(e,"progress",t)});let p=m.get,o=m.set;return {...m,get(e,t){let n=p(e,t);if(n!==void 0)return n;let c=a(e);if(c)return A(c,String(t))},set(e,t,n){o(e,t,n);let c=a(e);c&&v(c,String(t),n);}}}function W(r){let i=N(r.scene,r.requestRender);return r.extendAdapter?.(i),R(i)}function H(r){let i=r.builder.build();return W({scene:i,requestRender:()=>r.builder.patchRuntime(r.container),extendAdapter:r.extendAdapter})}function Q(r){let i=V(r.spec),g=i.length>0||r.extendAdapter?l=>{for(let u of i)u(l);r.extendAdapter?.(l);}:void 0,b=H({builder:r.builder,container:r.container,extendAdapter:g});return b.load(r.spec),r.autoPlay!==false&&b.play(),b}exports.a=E;exports.b=V;exports.c=z;exports.d=D;exports.e=W;exports.f=H;exports.g=Q;
@@ -1,4 +1,4 @@
1
- import { b as VizScene, P as PanZoomOptions, c as PanZoomController, d as Vec2, V as VizNode } from './index-n9JLZMa1.js';
1
+ import { b as VizScene, P as PanZoomOptions, c as PanZoomController, d as Vec2, V as VizNode } from './index-Bvue3FPE.js';
2
2
 
3
3
  declare function setupPanZoom(svg: SVGSVGElement, viewport: SVGGElement, scene: VizScene, options?: PanZoomOptions): PanZoomController;
4
4
 
@@ -1,4 +1,4 @@
1
- import { b as VizScene, P as PanZoomOptions, c as PanZoomController, d as Vec2, V as VizNode } from './index-n9JLZMa1.mjs';
1
+ import { b as VizScene, P as PanZoomOptions, c as PanZoomController, d as Vec2, V as VizNode } from './index-Bvue3FPE.mjs';
2
2
 
3
3
  declare function setupPanZoom(svg: SVGSVGElement, viewport: SVGGElement, scene: VizScene, options?: PanZoomOptions): PanZoomController;
4
4