@expertrees/core 0.1.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/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +428 -0
- package/dist/index.js +882 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const B=require("graphology"),P=require("d3-force");class S{constructor(t){this._listeners=new Map,this._id=t.id,this._label=t.label,this._meta=t.meta,this._graph=new B({multi:!1,allowSelfLoops:!1});for(const e of t.nodes)this._graph.addNode(e.id,{...e});for(const e of t.edges)e.directed?this._graph.addDirectedEdge(e.source,e.target,{...e}):this._graph.addUndirectedEdge(e.source,e.target,{...e})}get id(){return this._id}get label(){return this._label}get meta(){return this._meta}getNode(t){if(this._graph.hasNode(t))return this._graph.getNodeAttributes(t)}getEdge(t){if(this._graph.hasEdge(t))return this._graph.getEdgeAttributes(t)}getNodes(){return this._graph.nodes().map(t=>this._graph.getNodeAttributes(t))}getEdges(){return this._graph.edges().map(t=>this._graph.getEdgeAttributes(t))}getChildren(t){const e=this.getNode(t);return e!=null&&e.childIds?e.childIds.flatMap(s=>{const i=this.getNode(s);return i?[i]:[]}):[]}getNodesAtDepth(t){return this.getNodes().filter(e=>e.depth===t)}getNodesUpToDepth(t){return this.getNodes().filter(e=>e.depth<=t)}setNodeState(t,e){this._graph.hasNode(t)&&(this._graph.mergeNodeAttributes(t,{state:e}),this._emit("nodeStateChanged",{nodeId:t,state:e}),this._emit("changed",void 0))}addNode(t){this._graph.addNode(t.id,{...t}),this._emit("nodeAdded",{node:t}),this._emit("changed",void 0)}removeNode(t){this._graph.hasNode(t)&&(this._graph.dropNode(t),this._emit("nodeRemoved",{nodeId:t}),this._emit("changed",void 0))}addEdge(t){t.directed?this._graph.addDirectedEdge(t.source,t.target,{...t}):this._graph.addUndirectedEdge(t.source,t.target,{...t}),this._emit("edgeAdded",{edge:t}),this._emit("changed",void 0)}removeEdge(t){this._graph.hasEdge(t)&&(this._graph.dropEdge(t),this._emit("edgeRemoved",{edgeId:t}),this._emit("changed",void 0))}addEvidence(t,e){if(!this._graph.hasNode(t))return;const i=this.getNode(t).evidence??[];this._graph.mergeNodeAttributes(t,{evidence:[...i,e]}),this._emit("evidenceAdded",{nodeId:t,evidence:e}),this._emit("changed",void 0)}removeEvidence(t,e){if(!this._graph.hasNode(t))return;const i=(this.getNode(t).evidence??[]).filter(o=>o.id!==e);this._graph.mergeNodeAttributes(t,{evidence:i}),this._emit("evidenceRemoved",{nodeId:t,evidenceId:e}),this._emit("changed",void 0)}toJSON(){const t={id:this._id,label:this._label,nodes:this.getNodes(),edges:this.getEdges()};return this._meta!==void 0&&(t.meta=this._meta),t}static fromJSON(t){return new S(t)}on(t,e){this._listeners.has(t)||this._listeners.set(t,new Set);const s=this._listeners.get(t);return s.add(e),()=>s.delete(e)}_emit(t,e){const s=this._listeners.get(t);if(s)for(const i of s)e===void 0?i():i(e)}}class x{constructor(t={nodeId:null,label:"Root"}){this._listeners=new Set,this._stack=[t]}get current(){return this._stack[this._stack.length-1]}get stack(){return this._stack}get canGoBack(){return this._stack.length>1}push(t){this._stack.push(t),this._notify()}pop(){if(!this.canGoBack)return;const t=this._stack.pop();return this._notify(),t}reset(){this._stack=[this._stack[0]],this._notify()}onChange(t){return this._listeners.add(t),()=>this._listeners.delete(t)}_notify(){for(const t of this._listeners)t(this._stack)}}const R=[{depth:0,minZoom:0,maxZoom:.3},{depth:1,minZoom:.3,maxZoom:.6},{depth:2,minZoom:.6,maxZoom:1.2},{depth:3,minZoom:1.2,maxZoom:2.5},{depth:4,minZoom:2.5,maxZoom:1/0}];class A{constructor(t){this._zoom=1,this._listeners=new Set,this._thresholds=t??R}get zoom(){return this._zoom}get thresholds(){return this._thresholds}setZoom(t){this._zoom=Math.max(.01,t),this._notify()}onChange(t){return this._listeners.add(t),()=>this._listeners.delete(t)}_notify(){for(const t of this._listeners)t(this._zoom)}}class T{constructor(){this._simulation=null}run(t,e,s){this.stop();const{width:i,height:o,chargeStrength:a=-200,collisionPadding:r=16,alphaDecay:n=.02}=s,l=t.map(h=>{var c,g;return{id:h.id,depth:h.depth,x:((c=h.position)==null?void 0:c.x)??(Math.random()-.5)*100,y:((g=h.position)==null?void 0:g.y)??(Math.random()-.5)*100}}),d=new Map(l.map(h=>[h.id,h])),v=[];for(const h of t)if(h.parentId){const c=d.get(h.parentId),g=d.get(h.id);c&&g&&v.push({id:`${h.parentId}->${h.id}`,source:c,target:g})}for(const h of e){const c=d.get(h.source),g=d.get(h.target);c&&g&&v.push({id:h.id,source:c,target:g})}const m=h=>{var c;(c=s.onTick)==null||c.call(s,h)};this._simulation=P.forceSimulation(l).alphaDecay(n).force("link",P.forceLink(v).id(h=>h.id).distance(h=>{const c=h.source,g=h.target;return 80+Math.abs(c.depth-g.depth)*20}).strength(.4)).force("charge",P.forceManyBody().strength(a)).force("center",P.forceCenter(0,0)).force("collide",P.forceCollide().radius(r)).on("tick",()=>{const h=this._collectPositions(l);m(h)}).on("end",()=>{var c;const h=this._collectPositions(l);(c=s.onEnd)==null||c.call(s,h)})}stop(){var t;(t=this._simulation)==null||t.stop(),this._simulation=null}reheat(){var t;(t=this._simulation)==null||t.alpha(.3).restart()}_collectPositions(t){const e=new Map;for(const s of t)e.set(s.id,{x:s.x,y:s.y});return e}}const b={background:"#050a1a",node:{color:"#4a9eff",glowColor:"#4a9eff",glowRadius:12,size:8,shape:"circle",opacity:1,labelColor:"#c8d8f0",labelSize:12,labelFont:"system-ui, sans-serif"},edge:{color:"#1e3a5f",width:1.5,opacity:.6,animated:!1},states:{default:{},active:{color:"#7fcdff",glowColor:"#7fcdff",glowRadius:20},locked:{color:"#2a3a4a",glowColor:"#2a3a4a",glowRadius:4,opacity:.5,labelColor:"#4a5a6a"},unlocked:{color:"#50fa7b",glowColor:"#50fa7b",glowRadius:16},highlighted:{color:"#ffb86c",glowColor:"#ffb86c",glowRadius:24}}};function I(p){var t,e,s,i,o;return p?{background:p.background??b.background,node:{...b.node,...p.node},edge:{...b.edge,...p.edge},states:{default:{...b.states.default,...(t=p.states)==null?void 0:t.default},active:{...b.states.active,...(e=p.states)==null?void 0:e.active},locked:{...b.states.locked,...(s=p.states)==null?void 0:s.locked},unlocked:{...b.states.unlocked,...(i=p.states)==null?void 0:i.unlocked},highlighted:{...b.states.highlighted,...(o=p.states)==null?void 0:o.highlighted}}}:b}const C=40;function L(p){return Array.from({length:p},()=>({x:Math.random(),y:Math.random(),r:.4+Math.random()*1.2,phase:Math.random()*Math.PI*2,speed:.4+Math.random()*1.2}))}function N(p){let t=0;for(let e=0;e<p.length;e++)t=t*31+p.charCodeAt(e)>>>0;return t%1e3/1e3*Math.PI*2}class z{constructor(t,e){this._burst=null,this._burstDuration=450,this._implode=null,this._implodeDuration=420,this._canvas=t;const s=t.getContext("2d");if(!s)throw new Error("Could not get 2D context from canvas");this._ctx=s,this._theme=I(e),this._dpr=window.devicePixelRatio??1,this._bgStars=L(150),this._resize()}get canvas(){return this._canvas}get isBurstActive(){return this._burst!==null}resize(){this._resize()}resolveNodeColor(t){return this._resolveStyle(t).color}updateTheme(t){this._theme=I(t)}triggerBurst(t,e){this._burst={startTime:performance.now(),center:t,color:e}}triggerImplode(t){this._implode={startTime:performance.now(),center:t}}render(t,e,s){var g;const i=this._ctx,o=performance.now()/1e3,{pan:a,zoom:r,contextFadeAlpha:n,positions:l,internalStates:d}=s,v=this._canvas.width/this._dpr,m=this._canvas.height/this._dpr;i.clearRect(0,0,this._canvas.width,this._canvas.height),i.fillStyle=this._theme.background,i.fillRect(0,0,this._canvas.width,this._canvas.height),i.save(),i.scale(this._dpr,this._dpr),this._renderBgStars(i,v,m,o),i.save(),i.translate(a.x,a.y),i.scale(r,r);for(const _ of e){const f=l.get(_.source),u=l.get(_.target);!f||!u||(this._drawEdge(i,f,u,_.style),(((g=_.style)==null?void 0:g.animated)??this._theme.edge.animated)&&this._renderEdgeParticles(i,f,u,_,o,r))}for(const _ of t){if(!_.parentId)continue;const f=l.get(_.parentId),u=l.get(_.id);!f||!u||(i.save(),i.globalAlpha=n*.35,i.strokeStyle=this._theme.edge.color,i.lineWidth=this._theme.edge.width/r,i.setLineDash([4,8]),i.beginPath(),i.moveTo(f.x,f.y),i.lineTo(u.x,u.y),i.stroke(),i.restore())}const h=t.filter(_=>_.childIds&&_.childIds.length>0),c=t.filter(_=>!_.childIds||_.childIds.length===0);for(const _ of[...h,...c]){const f=l.get(_.id);if(!f)continue;const u=d.get(_.id)??"idle",y=!!(_.childIds&&_.childIds.length>0);this._drawNode(i,_,f,u,n,r,y,o)}i.restore(),i.restore(),this._renderBurst(),this._renderImplode()}_renderBgStars(t,e,s,i){for(const o of this._bgStars){const a=.5+.5*Math.sin(i*o.speed+o.phase),r=.08+.25*a,n=o.r*(.7+.3*a);t.beginPath(),t.arc(o.x*e,o.y*s,n,0,Math.PI*2),t.fillStyle=`rgba(255,255,255,${r.toFixed(3)})`,t.fill()}}_drawNode(t,e,s,i,o,a,r,n){r?this._drawBubble(t,e,s,i,o,a,n):this._drawStar(t,e,s,i,o,a,n)}_drawBubble(t,e,s,i,o,a,r){const n=this._resolveStyle(e),l=N(e.id),v=1+(i==="selected"?.08:.04)*Math.sin(r*.6+l),m=C*v*(i==="hovered"?1.1:1),h=i==="selected";t.save(),t.globalAlpha=n.opacity*o;const c=h?m*2.8:m*2.2,g=h?n.glowColor+"55":n.glowColor+"30",_=t.createRadialGradient(s.x,s.y,m*.5,s.x,s.y,c);_.addColorStop(0,g),_.addColorStop(1,n.glowColor+"00"),t.fillStyle=_,t.beginPath(),t.arc(s.x,s.y,c,0,Math.PI*2),t.fill();const f=t.createRadialGradient(s.x,s.y-m*.3,m*.1,s.x,s.y,m);if(f.addColorStop(0,n.color+(h?"70":"50")),f.addColorStop(1,n.color+(h?"28":"18")),t.fillStyle=f,t.beginPath(),t.arc(s.x,s.y,m,0,Math.PI*2),t.fill(),t.strokeStyle=h?n.glowColor:n.color,t.lineWidth=(h?2.5:1.5)/a,t.globalAlpha=n.opacity*o*(h?1:.7),t.beginPath(),t.arc(s.x,s.y,m,0,Math.PI*2),t.stroke(),h){const u=r*.7+l;t.strokeStyle=n.glowColor,t.lineWidth=1.5/a,t.globalAlpha=n.opacity*o*.85,t.setLineDash([10/a,7/a]),t.lineDashOffset=-u*18,t.beginPath(),t.arc(s.x,s.y,m*1.3,0,Math.PI*2),t.stroke(),t.setLineDash([])}if(e.childIds&&e.childIds.length>0){const u=Math.min(e.childIds.length,8),y=m*.55,w=Math.max(1.5,m*.06),D=r*.15+l;t.globalAlpha=n.opacity*o*.6,t.fillStyle=n.color+"90";for(let M=0;M<u;M++){const E=D+M/u*Math.PI*2;t.beginPath(),t.arc(s.x+y*Math.cos(E),s.y+y*Math.sin(E),w,0,Math.PI*2),t.fill()}}t.globalAlpha=n.opacity*o,t.fillStyle=n.labelColor,t.font=`bold ${n.labelSize*1.2/a}px ${n.labelFont}`,t.textAlign="center",t.textBaseline="middle",t.fillText(e.label,s.x,s.y),t.restore()}_drawStar(t,e,s,i,o,a,r){const n=this._resolveStyle(e),l=N(e.id),d=i==="selected",m=1+(d?.4:.22)*Math.sin(r*1.7+l)+.08*Math.sin(r*3.1+l*1.3),h=n.glowRadius*m,c=n.size*(i==="hovered"?1.4:1);if(t.save(),t.globalAlpha=n.opacity*o,d){for(let _=0;_<2;_++){const f=_*.9,u=(r+f)%1.8/1.8,y=(c+h*.6)*(1+u*2.2),w=(1-u)*.6;t.strokeStyle=n.glowColor,t.lineWidth=1.5/a*(1-u*.5),t.globalAlpha=w*n.opacity*o,t.beginPath(),t.arc(s.x,s.y,y,0,Math.PI*2),t.stroke()}t.globalAlpha=n.opacity*o}if(h>0){const g=t.createRadialGradient(s.x,s.y,0,s.x,s.y,h+c);g.addColorStop(0,n.glowColor+(d?"ff":"cc")),g.addColorStop(.4,n.glowColor+(d?"88":"55")),g.addColorStop(1,n.glowColor+"00"),t.fillStyle=g,t.beginPath(),t.arc(s.x,s.y,h+c,0,Math.PI*2),t.fill()}t.fillStyle=d?n.glowColor:n.color,t.beginPath(),this._drawShape(t,s,c,n.shape),t.fill(),c*a>4&&(t.fillStyle=n.labelColor,t.font=`${n.labelSize/a}px ${n.labelFont}`,t.textAlign="center",t.textBaseline="top",t.fillText(e.label,s.x,s.y+c+4/a)),t.restore()}_renderEdgeParticles(t,e,s,i,o,a){var c;const r=s.x-e.x,n=s.y-e.y;if(Math.sqrt(r*r+n*n)<1)return;const d=3,v=.2,m=2.5/a,h=((c=i.style)==null?void 0:c.color)??this._theme.edge.color;for(let g=0;g<d;g++){const _=(o*v+g/d)%1,f=e.x+r*_,u=e.y+n*_,y=Math.sin(_*Math.PI);t.save(),t.globalAlpha=y*.85;const w=t.createRadialGradient(f,u,0,f,u,m*2.5);w.addColorStop(0,h+"ff"),w.addColorStop(.4,h+"99"),w.addColorStop(1,h+"00"),t.fillStyle=w,t.beginPath(),t.arc(f,u,m*2.5,0,Math.PI*2),t.fill(),t.restore()}}_renderBurst(){if(!this._burst)return;const t=this._ctx,s=(performance.now()-this._burst.startTime)/this._burstDuration;if(s>=1){this._burst=null;return}const i=1-Math.pow(1-s,3),o=Math.hypot(this._canvas.width,this._canvas.height),a=i*o,r=(1-i)*.35;t.save(),t.globalAlpha=r,t.fillStyle=this._burst.color,t.beginPath(),t.arc(this._burst.center.x*this._dpr,this._burst.center.y*this._dpr,a,0,Math.PI*2),t.fill(),t.restore()}_renderImplode(){if(!this._implode)return;const t=this._ctx,s=(performance.now()-this._implode.startTime)/this._implodeDuration;if(s>=1){this._implode=null;return}const i=s*s*s,o=Math.hypot(this._canvas.width,this._canvas.height),a=(1-i)*o,r=(1-s)*.55,n=(1-i)*40*this._dpr,l=this._implode.center.x*this._dpr,d=this._implode.center.y*this._dpr;t.save(),t.globalAlpha=r,t.strokeStyle="#9b5de5",t.lineWidth=n,t.shadowColor="#9b5de5",t.shadowBlur=30*this._dpr,t.beginPath(),t.arc(l,d,Math.max(0,a),0,Math.PI*2),t.stroke(),t.restore()}_resolveStyle(t){const e=this._theme.node,s=t.state?this._theme.states[t.state]:{},i=t.style??{};return{...e,...s,...i}}_drawShape(t,e,s,i){switch(i){case"circle":t.arc(e.x,e.y,s,0,Math.PI*2);break;case"hexagon":this._polygon(t,e,s,6,Math.PI/6);break;case"diamond":this._polygon(t,e,s,4,0);break;case"star":this._star(t,e,s);break}}_polygon(t,e,s,i,o){t.moveTo(e.x+s*Math.cos(o),e.y+s*Math.sin(o));for(let a=1;a<i;a++){const r=o+a*2*Math.PI/i;t.lineTo(e.x+s*Math.cos(r),e.y+s*Math.sin(r))}t.closePath()}_star(t,e,s){const i=s*.4,o=5;for(let a=0;a<o*2;a++){const r=a*Math.PI/o-Math.PI/2,n=a%2===0?s:i,l=e.x+n*Math.cos(r),d=e.y+n*Math.sin(r);a===0?t.moveTo(l,d):t.lineTo(l,d)}t.closePath()}_drawEdge(t,e,s,i){const o={...this._theme.edge,...i};t.save(),t.strokeStyle=o.color,t.lineWidth=o.width,t.globalAlpha=o.opacity,o.dashPattern&&t.setLineDash(o.dashPattern),t.beginPath(),t.moveTo(e.x,e.y),t.lineTo(s.x,s.y),t.stroke(),t.restore()}_resize(){const t=this._canvas.getBoundingClientRect(),e=Math.floor(t.width*this._dpr),s=Math.floor(t.height*this._dpr);(this._canvas.width!==e||this._canvas.height!==s)&&(this._canvas.width=e,this._canvas.height=s)}dispose(){}}class Z{constructor(t){this._isPanning=!1,this._lastPan={x:0,y:0},this._listeners=new Set,this._nodes=[],this._positions=new Map,this._nodeSize=8,this._targetZoom=1,this._zoomAnchor=null,this._zoomLerpAlpha=.14,this._timedZoom=null,this._activePointers=new Map,this._lastPinchDist=null,this._pointerDownPos={x:0,y:0},this._canvas=t,this._dpr=window.devicePixelRatio??1,this._state={pan:{x:t.width/(2*this._dpr),y:t.height/(2*this._dpr)},zoom:1,hoveredNodeId:null,selectedNodeId:null},this._onPointerDown=this._handlePointerDown.bind(this),this._onPointerMove=this._handlePointerMove.bind(this),this._onPointerUp=this._handlePointerUp.bind(this),this._onPointerCancel=this._handlePointerCancel.bind(this),this._onWheel=this._handleWheel.bind(this),t.addEventListener("pointerdown",this._onPointerDown),t.addEventListener("pointermove",this._onPointerMove),t.addEventListener("pointerup",this._onPointerUp),t.addEventListener("pointercancel",this._onPointerCancel),t.addEventListener("wheel",this._onWheel,{passive:!1})}get state(){return this._state}get targetZoom(){return this._targetZoom}get zoomAnchor(){return this._zoomAnchor}setTargetZoom(t,e){this._timedZoom=null,this._targetZoom=t,e!==void 0&&(this._zoomAnchor=e)}startTimedZoom(t,e,s,i="in"){this._timedZoom={startZoom:this._state.zoom,target:t,anchor:s??null,startTime:performance.now(),duration:e,easing:i},this._targetZoom=t,this._zoomAnchor=s??null}resetToCenter(t,e,s){this._timedZoom=null,this._targetZoom=t,this._state.zoom=t,this._state.pan={x:e,y:s},this._zoomAnchor=null}updateNodes(t,e,s){this._nodes=t,this._positions=e,this._nodeSize=s}onChange(t){return this._listeners.add(t),()=>this._listeners.delete(t)}tick(){const t=this._state.zoom;if(this._timedZoom){const{startZoom:e,target:s,anchor:i,startTime:o,duration:a,easing:r}=this._timedZoom,n=Math.min(1,(performance.now()-o)/a),l=r==="out"?1-Math.pow(1-n,3):n*n*n;this._state.zoom=e+(s-e)*l,n>=1?(this._timedZoom=null,this._zoomAnchor=null):this._zoomAnchor=i}else{const e=this._targetZoom-this._state.zoom;if(Math.abs(e)<1e-4)return this._zoomAnchor=null,!1;this._state.zoom+=e*this._zoomLerpAlpha}if(this._zoomAnchor){const{x:e,y:s}=this._zoomAnchor;this._state.pan={x:e-(e-this._state.pan.x)*(this._state.zoom/t),y:s-(s-this._state.pan.y)*(this._state.zoom/t)}}return!0}dispose(){this._canvas.removeEventListener("pointerdown",this._onPointerDown),this._canvas.removeEventListener("pointermove",this._onPointerMove),this._canvas.removeEventListener("pointerup",this._onPointerUp),this._canvas.removeEventListener("pointercancel",this._onPointerCancel),this._canvas.removeEventListener("wheel",this._onWheel)}canvasToWorld(t,e){return{x:(t-this._state.pan.x)/this._state.zoom,y:(e-this._state.pan.y)/this._state.zoom}}_getCanvasPos(t){const e=this._canvas.getBoundingClientRect();return{x:t.clientX-e.left,y:t.clientY-e.top}}_hitTest(t){let e=null,s=1/0;for(const i of this._nodes){const o=this._positions.get(i.id);if(!o)continue;const r=!!(i.childIds&&i.childIds.length>0)?C*1.3:this._nodeSize*4,n=t.x-o.x,l=t.y-o.y,d=Math.sqrt(n*n+l*l);d<r&&d<s&&(s=d,e=i.id)}return e}_emit(t){for(const e of this._listeners)e(t)}_handlePointerDown(t){this._canvas.setPointerCapture(t.pointerId);const e=this._getCanvasPos(t);this._activePointers.set(t.pointerId,e),this._activePointers.size===1?(this._isPanning=!0,this._lastPan=e,this._pointerDownPos=e):(this._isPanning=!1,this._lastPinchDist=null)}_handlePointerMove(t){const e=this._getCanvasPos(t);if(this._activePointers.set(t.pointerId,e),this._activePointers.size>=2){const s=[...this._activePointers.values()],i=Math.hypot(s[1].x-s[0].x,s[1].y-s[0].y);if(this._lastPinchDist!==null&&i>0){const o=i/this._lastPinchDist,a=(s[0].x+s[1].x)/2,r=(s[0].y+s[1].y)/2;this._targetZoom=Math.max(.05,Math.min(20,this._targetZoom*o)),this._zoomAnchor={x:a,y:r},this._emit({type:"zoom:change",zoom:this._targetZoom})}this._lastPinchDist=i;return}if(this._isPanning){const s=e.x-this._lastPan.x,i=e.y-this._lastPan.y;this._lastPan=e,this._state.pan={x:this._state.pan.x+s,y:this._state.pan.y+i},this._emit({type:"pan:change",pan:this._state.pan});return}if(t.pointerType!=="touch"){const s=this.canvasToWorld(e.x,e.y),i=this._hitTest(s);i!==this._state.hoveredNodeId&&(this._state.hoveredNodeId&&this._emit({type:"node:blur",nodeId:this._state.hoveredNodeId}),this._state.hoveredNodeId=i,i&&this._emit({type:"node:hover",nodeId:i}))}}_handlePointerUp(t){const e=this._getCanvasPos(t),s=this._activePointers.size>=2,i=this._pointerDownPos;if(this._activePointers.delete(t.pointerId),this._lastPinchDist=null,s){this._activePointers.size===1&&(this._isPanning=!0,this._lastPan=[...this._activePointers.values()][0]);return}this._isPanning=!1;const o=Math.abs(e.x-i.x),a=Math.abs(e.y-i.y);if(o<4&&a<4){const r=this.canvasToWorld(e.x,e.y),n=this._hitTest(r);n?(this._state.selectedNodeId=n===this._state.selectedNodeId?null:n,this._emit({type:"node:click",nodeId:n})):(this._state.selectedNodeId=null,this._emit({type:"canvas:click"}))}}_handlePointerCancel(t){this._activePointers.delete(t.pointerId),this._lastPinchDist=null,this._activePointers.size===0&&(this._isPanning=!1)}_handleWheel(t){t.preventDefault();const e=this._getCanvasPos(t),s=t.deltaY<0?1.1:1/1.1;this._targetZoom=Math.max(.05,Math.min(20,this._targetZoom*s)),this._zoomAnchor=e,this._emit({type:"zoom:change",zoom:this._targetZoom})}}const F=2.5,O=.35,k=700;class W{constructor(t){var r,n;this._positions=new Map,this._internalStates=new Map,this._rafId=null,this._pendingEnter=null,this._pendingExit=null,this._unsubscribers=[],this._visibleNodes=[],this._visibleEdges=[],this._contextFadeAlpha=1,this._contextFadeStart=0,this._contextFadeDuration=400,this._navCooldownUntil=0;const{canvas:e,data:s,theme:i,lod:o,on:a={}}=t;if(this._eventHandlers=a,this._model=new S(s),this._nav=new x({nodeId:null,label:s.label}),this._lod=new A(o),this._layout=new T,this._renderer=new z(e,i??s.theme),this._interaction=new Z(e),t.initialContextNodeId){const l=this._model.getNode(t.initialContextNodeId);l&&this._isBubble(l)&&this._nav.push({nodeId:l.id,label:l.label})}this._rebuildVisibleCache(),this._wireEvents(),this._runLayout(),this._startRenderLoop(),this._resizeObserver=new ResizeObserver(()=>{this._renderer.resize(),this._runLayout()}),this._resizeObserver.observe(e),(n=(r=this._eventHandlers)["graph:ready"])==null||n.call(r,s)}setNodeState(t,e){this._model.setNodeState(t,e)}addEvidence(t,e){this._model.addEvidence(t,e)}removeEvidence(t,e){this._model.removeEvidence(t,e)}updateTheme(t){this._renderer.updateTheme(t)}zoomIn(){this._interaction.setTargetZoom(Math.min(20,this._interaction.targetZoom*1.3))}zoomOut(){this._interaction.setTargetZoom(Math.max(.05,this._interaction.targetZoom/1.3))}goBack(){this._nav.canGoBack&&this._exitWithAnimation()}jumpToNavDepth(t){var a,r;if(this._nav.stack.length<=t)return;this._pendingEnter!==null&&(clearTimeout(this._pendingEnter),this._pendingEnter=null),this._pendingExit!==null&&(clearTimeout(this._pendingExit),this._pendingExit=null);let e;for(;this._nav.stack.length>t&&this._nav.canGoBack;)e=this._nav.pop();if(!e)return;const i=this._renderer.canvas.getBoundingClientRect(),o={x:i.width/2,y:i.height/2};this._navCooldownUntil=Date.now()+k,this._rebuildVisibleCache(),this._renderer.triggerImplode(o),this._interaction.resetToCenter(1,o.x,o.y),this._lod.setZoom(1);for(const n of this._visibleNodes)this._positions.set(n.id,{x:(Math.random()-.5)*80,y:(Math.random()-.5)*80});this._contextFadeAlpha=0,this._contextFadeStart=performance.now(),this._runLayout(),(r=(a=this._eventHandlers)["context:exit"])==null||r.call(a,e,this._nav.stack)}_exitWithAnimation(){if(!this._nav.canGoBack)return;this._pendingEnter!==null&&(clearTimeout(this._pendingEnter),this._pendingEnter=null);const e=this._renderer.canvas.getBoundingClientRect(),s={x:e.width/2,y:e.height/2},i=480;this._interaction.startTimedZoom(.45,i,void 0,"out"),this._pendingExit=setTimeout(()=>{this._pendingExit=null,this._renderer.triggerImplode(s),this._exitContext()},i)}enterContext(t){const e=this._model.getNode(t);if(!e||!this._isBubble(e))return;this._pendingEnter!==null&&(clearTimeout(this._pendingEnter),this._pendingEnter=null);const i=this._renderer.canvas.getBoundingClientRect(),o={x:i.width/2,y:i.height/2},a=this._positions.get(t),r=a?(()=>{const{zoom:l,pan:d}=this._interaction.state;return{x:a.x*l+d.x,y:a.y*l+d.y}})():void 0,n=480;this._interaction.startTimedZoom(2.5,n,r),this._pendingEnter=setTimeout(()=>{this._pendingEnter=null,this._enterContext(e,o)},n)}getGraph(){return this._model.toJSON()}getNavigationStack(){return this._nav.stack}dispose(){this._rafId!==null&&cancelAnimationFrame(this._rafId),this._pendingEnter!==null&&clearTimeout(this._pendingEnter),this._pendingExit!==null&&clearTimeout(this._pendingExit),this._resizeObserver.disconnect(),this._layout.stop(),this._renderer.dispose(),this._interaction.dispose();for(const t of this._unsubscribers)t()}_rebuildVisibleCache(){const t=this._nav.current.nodeId;this._visibleNodes=this._model.getNodes().filter(s=>t===null?!s.parentId:s.parentId===t);const e=new Set(this._visibleNodes.map(s=>s.id));this._visibleEdges=this._model.getEdges().filter(s=>e.has(s.source)&&e.has(s.target))}_isBubble(t){return!!(t.childIds&&t.childIds.length>0)}_enterContext(t,e){var o,a;this._nav.push({nodeId:t.id,label:t.label}),this._navCooldownUntil=Date.now()+k,this._rebuildVisibleCache(),this._renderer.triggerBurst(e,this._renderer.resolveNodeColor(t));const i=this._renderer.canvas.getBoundingClientRect();this._interaction.resetToCenter(1,i.width/2,i.height/2),this._lod.setZoom(1);for(const r of this._visibleNodes)this._positions.set(r.id,{x:(Math.random()-.5)*80,y:(Math.random()-.5)*80});this._contextFadeAlpha=0,this._contextFadeStart=performance.now(),this._runLayout(),(a=(o=this._eventHandlers)["context:enter"])==null||a.call(o,t,this._nav.stack)}_exitContext(){var i,o;const t=this._nav.pop();if(!t)return;this._navCooldownUntil=Date.now()+k,this._rebuildVisibleCache();const s=this._renderer.canvas.getBoundingClientRect();this._interaction.resetToCenter(1,s.width/2,s.height/2),this._lod.setZoom(1);for(const a of this._visibleNodes)this._positions.set(a.id,{x:(Math.random()-.5)*80,y:(Math.random()-.5)*80});this._contextFadeAlpha=0,this._contextFadeStart=performance.now(),this._runLayout(),(o=(i=this._eventHandlers)["context:exit"])==null||o.call(i,t,this._nav.stack)}_checkNavigation(){if(Date.now()<this._navCooldownUntil)return;const t=this._interaction.targetZoom,e=this._interaction.zoomAnchor;if(t>F&&e){const{zoom:s,pan:i}=this._interaction.state,o=this._findBubbleAtCanvas(e,i,s);if(o){this._enterContext(o,e);return}}t<O&&this._nav.canGoBack&&this._exitContext()}_findBubbleAtCanvas(t,e,s){const i=(t.x-e.x)/s,o=(t.y-e.y)/s;for(const a of this._visibleNodes){if(!this._isBubble(a))continue;const r=this._positions.get(a.id);if(!r)continue;const n=i-r.x,l=o-r.y;if(Math.sqrt(n*n+l*l)<=C*1.3)return a}return null}_wireEvents(){this._unsubscribers.push(this._model.on("changed",()=>{this._rebuildVisibleCache()})),this._unsubscribers.push(this._interaction.onChange(t=>{var e,s,i,o,a,r,n,l;switch(t.type){case"zoom:change":case"pan:change":break;case"node:hover":{this._internalStates.set(t.nodeId,"hovered");const d=this._model.getNode(t.nodeId);d&&((s=(e=this._eventHandlers)["node:hover"])==null||s.call(e,d));break}case"node:blur":{this._internalStates.get(t.nodeId)==="hovered"&&this._internalStates.set(t.nodeId,"idle");const v=this._model.getNode(t.nodeId);v&&((o=(i=this._eventHandlers)["node:blur"])==null||o.call(i,v));break}case"canvas:click":{for(const[d,v]of this._internalStates)v==="selected"&&this._internalStates.set(d,"idle");(r=(a=this._eventHandlers)["canvas:click"])==null||r.call(a);break}case"node:click":{const d=this._model.getNode(t.nodeId);if(!d)break;if(this._internalStates.get(t.nodeId)==="selected")this._internalStates.set(t.nodeId,"idle");else{for(const[h,c]of this._internalStates)c==="selected"&&this._internalStates.set(h,"idle");this._internalStates.set(t.nodeId,"selected")}(l=(n=this._eventHandlers)["node:click"])==null||l.call(n,d);break}}}))}_runLayout(){const t=this._renderer.canvas.getBoundingClientRect();this._layout.run(this._visibleNodes,this._visibleEdges,{width:t.width||800,height:t.height||600,onTick:e=>{for(const[s,i]of e)this._positions.set(s,i);this._interaction.updateNodes(this._visibleNodes,this._positions,8)}})}_startRenderLoop(){const t=()=>{var s,i;if(this._interaction.tick()){const o=this._interaction.state.zoom;this._lod.setZoom(o),(i=(s=this._eventHandlers)["zoom:change"])==null||i.call(s,o)}if(this._contextFadeAlpha<1){const o=performance.now()-this._contextFadeStart;this._contextFadeAlpha=Math.min(1,o/this._contextFadeDuration)}this._checkNavigation(),this._draw(),this._rafId=requestAnimationFrame(t)};this._rafId=requestAnimationFrame(t)}_draw(){const{pan:t,zoom:e}=this._interaction.state;this._renderer.render(this._visibleNodes,this._visibleEdges,{positions:this._positions,internalStates:this._internalStates,contextFadeAlpha:this._contextFadeAlpha,pan:t,zoom:e})}}exports.BUBBLE_WORLD_RADIUS=C;exports.CanvasRenderer=z;exports.ForceLayout=T;exports.InteractionController=Z;exports.LodController=A;exports.NavigationController=x;exports.SkillGraphModel=S;exports.SkillTreeEngine=W;exports.defaultTheme=b;exports.mergeTheme=I;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/graph/SkillGraphModel.ts","../src/graph/NavigationController.ts","../src/lod/LodController.ts","../src/layout/ForceLayout.ts","../src/theme/default.ts","../src/theme/merge.ts","../src/renderer/CanvasRenderer.ts","../src/renderer/InteractionController.ts","../src/SkillTreeEngine.ts"],"sourcesContent":["import Graph from 'graphology'\nimport type { SkillGraph, SkillNode, SkillEdge, NodeState, Evidence } from '../types/index.js'\n\ntype GraphEvents = {\n nodeStateChanged: { nodeId: string; state: NodeState }\n nodeAdded: { node: SkillNode }\n nodeRemoved: { nodeId: string }\n edgeAdded: { edge: SkillEdge }\n edgeRemoved: { edgeId: string }\n evidenceAdded: { nodeId: string; evidence: Evidence }\n evidenceRemoved: { nodeId: string; evidenceId: string }\n changed: void\n}\n\ntype EventListener<T> = T extends void ? () => void : (payload: T) => void\n\nexport class SkillGraphModel {\n private _graph: Graph\n private _meta: SkillGraph['meta']\n private _label: string\n private _id: string\n private _listeners: Map<keyof GraphEvents, Set<EventListener<unknown>>> = new Map()\n\n constructor(data: SkillGraph) {\n this._id = data.id\n this._label = data.label\n this._meta = data.meta\n\n this._graph = new Graph({ multi: false, allowSelfLoops: false })\n\n for (const node of data.nodes) {\n this._graph.addNode(node.id, { ...node })\n }\n\n for (const edge of data.edges) {\n if (edge.directed) {\n this._graph.addDirectedEdge(edge.source, edge.target, { ...edge })\n } else {\n this._graph.addUndirectedEdge(edge.source, edge.target, { ...edge })\n }\n }\n }\n\n // ─── Accessors ──────────────────────────────────────────────────────────────\n\n get id() { return this._id }\n get label() { return this._label }\n get meta() { return this._meta }\n\n getNode(id: string): SkillNode | undefined {\n if (!this._graph.hasNode(id)) return undefined\n return this._graph.getNodeAttributes(id) as SkillNode\n }\n\n getEdge(id: string): SkillEdge | undefined {\n if (!this._graph.hasEdge(id)) return undefined\n return this._graph.getEdgeAttributes(id) as SkillEdge\n }\n\n getNodes(): SkillNode[] {\n return this._graph.nodes().map(id => this._graph.getNodeAttributes(id) as SkillNode)\n }\n\n getEdges(): SkillEdge[] {\n return this._graph.edges().map(id => this._graph.getEdgeAttributes(id) as SkillEdge)\n }\n\n getChildren(nodeId: string): SkillNode[] {\n const node = this.getNode(nodeId)\n if (!node?.childIds) return []\n return node.childIds.flatMap(id => {\n const child = this.getNode(id)\n return child ? [child] : []\n })\n }\n\n getNodesAtDepth(depth: number): SkillNode[] {\n return this.getNodes().filter(n => n.depth === depth)\n }\n\n getNodesUpToDepth(depth: number): SkillNode[] {\n return this.getNodes().filter(n => n.depth <= depth)\n }\n\n // ─── Mutations ──────────────────────────────────────────────────────────────\n\n setNodeState(nodeId: string, state: NodeState): void {\n if (!this._graph.hasNode(nodeId)) return\n this._graph.mergeNodeAttributes(nodeId, { state })\n this._emit('nodeStateChanged', { nodeId, state })\n this._emit('changed', undefined)\n }\n\n addNode(node: SkillNode): void {\n this._graph.addNode(node.id, { ...node })\n this._emit('nodeAdded', { node })\n this._emit('changed', undefined)\n }\n\n removeNode(nodeId: string): void {\n if (!this._graph.hasNode(nodeId)) return\n this._graph.dropNode(nodeId)\n this._emit('nodeRemoved', { nodeId })\n this._emit('changed', undefined)\n }\n\n addEdge(edge: SkillEdge): void {\n if (edge.directed) {\n this._graph.addDirectedEdge(edge.source, edge.target, { ...edge })\n } else {\n this._graph.addUndirectedEdge(edge.source, edge.target, { ...edge })\n }\n this._emit('edgeAdded', { edge })\n this._emit('changed', undefined)\n }\n\n removeEdge(edgeId: string): void {\n if (!this._graph.hasEdge(edgeId)) return\n this._graph.dropEdge(edgeId)\n this._emit('edgeRemoved', { edgeId })\n this._emit('changed', undefined)\n }\n\n addEvidence(nodeId: string, evidence: Evidence): void {\n if (!this._graph.hasNode(nodeId)) return\n const node = this.getNode(nodeId)!\n const existing = node.evidence ?? []\n this._graph.mergeNodeAttributes(nodeId, { evidence: [...existing, evidence] })\n this._emit('evidenceAdded', { nodeId, evidence })\n this._emit('changed', undefined)\n }\n\n removeEvidence(nodeId: string, evidenceId: string): void {\n if (!this._graph.hasNode(nodeId)) return\n const node = this.getNode(nodeId)!\n const evidence = (node.evidence ?? []).filter(e => e.id !== evidenceId)\n this._graph.mergeNodeAttributes(nodeId, { evidence })\n this._emit('evidenceRemoved', { nodeId, evidenceId })\n this._emit('changed', undefined)\n }\n\n // ─── Serialization ──────────────────────────────────────────────────────────\n\n toJSON(): SkillGraph {\n const result: SkillGraph = {\n id: this._id,\n label: this._label,\n nodes: this.getNodes(),\n edges: this.getEdges(),\n }\n if (this._meta !== undefined) result.meta = this._meta\n return result\n }\n\n static fromJSON(data: SkillGraph): SkillGraphModel {\n return new SkillGraphModel(data)\n }\n\n // ─── Events ─────────────────────────────────────────────────────────────────\n\n on<K extends keyof GraphEvents>(event: K, listener: EventListener<GraphEvents[K]>): () => void {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set())\n }\n const set = this._listeners.get(event)!\n set.add(listener as EventListener<unknown>)\n return () => set.delete(listener as EventListener<unknown>)\n }\n\n private _emit<K extends keyof GraphEvents>(event: K, payload: GraphEvents[K]): void {\n const listeners = this._listeners.get(event)\n if (!listeners) return\n for (const listener of listeners) {\n if (payload === undefined) {\n (listener as () => void)()\n } else {\n (listener as (p: unknown) => void)(payload)\n }\n }\n }\n}\n","import type { NavigationFrame } from '../types/index.js'\n\nexport class NavigationController {\n private _stack: NavigationFrame[]\n private _listeners: Set<(stack: readonly NavigationFrame[]) => void> = new Set()\n\n constructor(root: NavigationFrame = { nodeId: null, label: 'Root' }) {\n this._stack = [root]\n }\n\n get current(): NavigationFrame {\n return this._stack[this._stack.length - 1]!\n }\n\n get stack(): readonly NavigationFrame[] {\n return this._stack\n }\n\n get canGoBack(): boolean {\n return this._stack.length > 1\n }\n\n push(frame: NavigationFrame): void {\n this._stack.push(frame)\n this._notify()\n }\n\n pop(): NavigationFrame | undefined {\n if (!this.canGoBack) return undefined\n const popped = this._stack.pop()\n this._notify()\n return popped\n }\n\n reset(): void {\n this._stack = [this._stack[0]!]\n this._notify()\n }\n\n onChange(listener: (stack: readonly NavigationFrame[]) => void): () => void {\n this._listeners.add(listener)\n return () => this._listeners.delete(listener)\n }\n\n private _notify(): void {\n for (const l of this._listeners) l(this._stack)\n }\n}\n","import type { LodThreshold } from '../types/index.js'\n\nconst DEFAULT_THRESHOLDS: LodThreshold[] = [\n { depth: 0, minZoom: 0, maxZoom: 0.3 },\n { depth: 1, minZoom: 0.3, maxZoom: 0.6 },\n { depth: 2, minZoom: 0.6, maxZoom: 1.2 },\n { depth: 3, minZoom: 1.2, maxZoom: 2.5 },\n { depth: 4, minZoom: 2.5, maxZoom: Infinity },\n]\n\nexport class LodController {\n private _thresholds: LodThreshold[]\n private _zoom: number = 1\n private _listeners: Set<(zoom: number) => void> = new Set()\n\n constructor(thresholds?: LodThreshold[]) {\n this._thresholds = thresholds ?? DEFAULT_THRESHOLDS\n }\n\n get zoom() { return this._zoom }\n\n get thresholds(): readonly LodThreshold[] { return this._thresholds }\n\n setZoom(zoom: number): void {\n this._zoom = Math.max(0.01, zoom)\n this._notify()\n }\n\n onChange(listener: (zoom: number) => void): () => void {\n this._listeners.add(listener)\n return () => this._listeners.delete(listener)\n }\n\n private _notify(): void {\n for (const listener of this._listeners) listener(this._zoom)\n }\n}\n","import {\n forceSimulation,\n forceLink,\n forceManyBody,\n forceCenter,\n forceCollide,\n type Simulation,\n type SimulationNodeDatum,\n type SimulationLinkDatum,\n} from 'd3-force'\nimport type { SkillNode, SkillEdge, Position } from '../types/index.js'\n\nexport interface LayoutNode extends SimulationNodeDatum {\n id: string\n depth: number\n x: number\n y: number\n}\n\nexport interface LayoutLink extends SimulationLinkDatum<LayoutNode> {\n id: string\n}\n\nexport interface ForceLayoutOptions {\n width: number\n height: number\n /** Charge strength — negative = repulsion */\n chargeStrength?: number\n /** Collision radius multiplier relative to node size */\n collisionPadding?: number\n alphaDecay?: number\n onTick?: (positions: Map<string, Position>) => void\n onEnd?: (positions: Map<string, Position>) => void\n}\n\nexport class ForceLayout {\n private _simulation: Simulation<LayoutNode, LayoutLink> | null = null\n\n run(nodes: SkillNode[], edges: SkillEdge[], options: ForceLayoutOptions): void {\n this.stop()\n\n const { width, height, chargeStrength = -200, collisionPadding = 16, alphaDecay = 0.02 } = options\n\n const layoutNodes: LayoutNode[] = nodes.map(n => ({\n id: n.id,\n depth: n.depth,\n // Seed from manual position if provided, else random near world origin.\n // World (0,0) maps to canvas center given the initial pan of (width/2, height/2).\n x: n.position?.x ?? (Math.random() - 0.5) * 100,\n y: n.position?.y ?? (Math.random() - 0.5) * 100,\n }))\n\n const nodeIndex = new Map(layoutNodes.map(n => [n.id, n]))\n\n const layoutLinks: LayoutLink[] = []\n\n // Parent-child edges for layout purposes\n for (const node of nodes) {\n if (node.parentId) {\n const source = nodeIndex.get(node.parentId)\n const target = nodeIndex.get(node.id)\n if (source && target) {\n layoutLinks.push({ id: `${node.parentId}->${node.id}`, source, target })\n }\n }\n }\n\n // Explicit cross-depth edges\n for (const edge of edges) {\n const source = nodeIndex.get(edge.source)\n const target = nodeIndex.get(edge.target)\n if (source && target) {\n layoutLinks.push({ id: edge.id, source, target })\n }\n }\n\n const emit = (positions: Map<string, Position>) => {\n options.onTick?.(positions)\n }\n\n this._simulation = forceSimulation<LayoutNode, LayoutLink>(layoutNodes)\n .alphaDecay(alphaDecay)\n .force('link', forceLink<LayoutNode, LayoutLink>(layoutLinks)\n .id(d => d.id)\n .distance(d => {\n const s = d.source as LayoutNode\n const t = d.target as LayoutNode\n // Deeper nodes cluster closer\n return 80 + (Math.abs(s.depth - t.depth) * 20)\n })\n .strength(0.4)\n )\n .force('charge', forceManyBody().strength(chargeStrength))\n .force('center', forceCenter(0, 0))\n .force('collide', forceCollide<LayoutNode>().radius(collisionPadding))\n .on('tick', () => {\n const positions = this._collectPositions(layoutNodes)\n emit(positions)\n })\n .on('end', () => {\n const positions = this._collectPositions(layoutNodes)\n options.onEnd?.(positions)\n })\n }\n\n stop(): void {\n this._simulation?.stop()\n this._simulation = null\n }\n\n reheat(): void {\n this._simulation?.alpha(0.3).restart()\n }\n\n private _collectPositions(nodes: LayoutNode[]): Map<string, Position> {\n const positions = new Map<string, Position>()\n for (const n of nodes) {\n positions.set(n.id, { x: n.x, y: n.y })\n }\n return positions\n }\n}\n","import type { Theme } from '../types/index.js'\n\nexport const defaultTheme: Theme = {\n background: '#050a1a',\n node: {\n color: '#4a9eff',\n glowColor: '#4a9eff',\n glowRadius: 12,\n size: 8,\n shape: 'circle',\n opacity: 1,\n labelColor: '#c8d8f0',\n labelSize: 12,\n labelFont: 'system-ui, sans-serif',\n },\n edge: {\n color: '#1e3a5f',\n width: 1.5,\n opacity: 0.6,\n animated: false,\n },\n states: {\n default: {},\n active: {\n color: '#7fcdff',\n glowColor: '#7fcdff',\n glowRadius: 20,\n },\n locked: {\n color: '#2a3a4a',\n glowColor: '#2a3a4a',\n glowRadius: 4,\n opacity: 0.5,\n labelColor: '#4a5a6a',\n },\n unlocked: {\n color: '#50fa7b',\n glowColor: '#50fa7b',\n glowRadius: 16,\n },\n highlighted: {\n color: '#ffb86c',\n glowColor: '#ffb86c',\n glowRadius: 24,\n },\n },\n}\n","import type { Theme, ThemeInput } from '../types/index.js'\nimport { defaultTheme } from './default.js'\n\nexport function mergeTheme(overrides?: ThemeInput): Theme {\n if (!overrides) return defaultTheme\n\n return {\n background: overrides.background ?? defaultTheme.background,\n node: { ...defaultTheme.node, ...overrides.node },\n edge: { ...defaultTheme.edge, ...overrides.edge },\n states: {\n default: { ...defaultTheme.states.default, ...overrides.states?.['default'] },\n active: { ...defaultTheme.states.active, ...overrides.states?.active },\n locked: { ...defaultTheme.states.locked, ...overrides.states?.locked },\n unlocked: { ...defaultTheme.states.unlocked, ...overrides.states?.unlocked },\n highlighted: { ...defaultTheme.states.highlighted, ...overrides.states?.highlighted },\n },\n }\n}\n","import type {\n SkillNode,\n SkillEdge,\n NodeStyle,\n EdgeStyle,\n Theme,\n ThemeInput,\n Position,\n InternalNodeState,\n} from '../types/index.js'\nimport { mergeTheme } from '../theme/merge.js'\n\nexport interface RenderState {\n positions: Map<string, Position>\n internalStates: Map<string, InternalNodeState>\n contextFadeAlpha: number\n pan: Position\n zoom: number\n}\n\nexport const BUBBLE_WORLD_RADIUS = 40\n\n// ─── Background star field ────────────────────────────────────────────────────\n\ninterface BgStar {\n x: number // 0–1 normalized CSS position\n y: number\n r: number // base radius (CSS px)\n phase: number\n speed: number // twinkle speed (radians/sec)\n}\n\nfunction generateBgStars(count: number): BgStar[] {\n return Array.from({ length: count }, () => ({\n x: Math.random(),\n y: Math.random(),\n r: 0.4 + Math.random() * 1.2,\n phase: Math.random() * Math.PI * 2,\n speed: 0.4 + Math.random() * 1.2,\n }))\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\n/** Deterministic phase from a node id — avoids all nodes twinkling in sync. */\nfunction hashPhase(id: string): number {\n let h = 0\n for (let i = 0; i < id.length; i++) h = (h * 31 + id.charCodeAt(i)) >>> 0\n return ((h % 1000) / 1000) * Math.PI * 2\n}\n\nexport class CanvasRenderer {\n private _canvas: HTMLCanvasElement\n private _ctx: CanvasRenderingContext2D\n private _theme: Theme\n private _dpr: number\n private _bgStars: BgStar[]\n\n // Burst animation state (entry)\n private _burst: { startTime: number; center: Position; color: string } | null = null\n private readonly _burstDuration = 450\n\n // Implode animation state (exit)\n private _implode: { startTime: number; center: Position } | null = null\n private readonly _implodeDuration = 420\n\n constructor(canvas: HTMLCanvasElement, theme?: ThemeInput) {\n this._canvas = canvas\n const ctx = canvas.getContext('2d')\n if (!ctx) throw new Error('Could not get 2D context from canvas')\n this._ctx = ctx\n this._theme = mergeTheme(theme)\n this._dpr = window.devicePixelRatio ?? 1\n this._bgStars = generateBgStars(150)\n this._resize()\n }\n\n get canvas(): HTMLCanvasElement { return this._canvas }\n\n get isBurstActive(): boolean { return this._burst !== null }\n\n /** Sync canvas physical pixel size to its CSS size. Call when the container resizes. */\n resize(): void { this._resize() }\n\n /** Returns the resolved fill color for a node (used for burst color matching). */\n resolveNodeColor(node: SkillNode): string {\n return this._resolveStyle(node).color\n }\n\n updateTheme(theme: ThemeInput): void {\n this._theme = mergeTheme(theme)\n }\n\n triggerBurst(centerCanvas: Position, color: string): void {\n this._burst = { startTime: performance.now(), center: centerCanvas, color }\n }\n\n triggerImplode(centerCanvas: Position): void {\n this._implode = { startTime: performance.now(), center: centerCanvas }\n }\n\n render(nodes: SkillNode[], edges: SkillEdge[], state: RenderState): void {\n const ctx = this._ctx\n const t = performance.now() / 1000 // seconds — drives all animations\n const { pan, zoom, contextFadeAlpha, positions, internalStates } = state\n\n const cssW = this._canvas.width / this._dpr\n const cssH = this._canvas.height / this._dpr\n\n // ── Background ───────────────────────────────────────────────────────────\n ctx.clearRect(0, 0, this._canvas.width, this._canvas.height)\n ctx.fillStyle = this._theme.background\n ctx.fillRect(0, 0, this._canvas.width, this._canvas.height)\n\n // ── DPR scale (all CSS-pixel drawing lives inside here) ──────────────────\n ctx.save()\n ctx.scale(this._dpr, this._dpr)\n\n // Background star field — screen space, doesn't pan/zoom\n this._renderBgStars(ctx, cssW, cssH, t)\n\n // ── World transform ───────────────────────────────────────────────────────\n ctx.save()\n ctx.translate(pan.x, pan.y)\n ctx.scale(zoom, zoom)\n\n // Edges (solid) + animated particles\n for (const edge of edges) {\n const sourcePos = positions.get(edge.source)\n const targetPos = positions.get(edge.target)\n if (!sourcePos || !targetPos) continue\n this._drawEdge(ctx, sourcePos, targetPos, edge.style)\n if (edge.style?.animated ?? this._theme.edge.animated) {\n this._renderEdgeParticles(ctx, sourcePos, targetPos, edge, t, zoom)\n }\n }\n\n // Parent-child connectors\n for (const node of nodes) {\n if (!node.parentId) continue\n const parentPos = positions.get(node.parentId)\n const childPos = positions.get(node.id)\n if (!parentPos || !childPos) continue\n ctx.save()\n ctx.globalAlpha = contextFadeAlpha * 0.35\n ctx.strokeStyle = this._theme.edge.color\n ctx.lineWidth = this._theme.edge.width / zoom\n ctx.setLineDash([4, 8])\n ctx.beginPath()\n ctx.moveTo(parentPos.x, parentPos.y)\n ctx.lineTo(childPos.x, childPos.y)\n ctx.stroke()\n ctx.restore()\n }\n\n // Nodes — bubbles first so stars sit on top\n const bubbles = nodes.filter(n => n.childIds && n.childIds.length > 0)\n const stars = nodes.filter(n => !n.childIds || n.childIds.length === 0)\n\n for (const node of [...bubbles, ...stars]) {\n const pos = positions.get(node.id)\n if (!pos) continue\n const internal = internalStates.get(node.id) ?? 'idle'\n const isBubble = !!(node.childIds && node.childIds.length > 0)\n this._drawNode(ctx, node, pos, internal, contextFadeAlpha, zoom, isBubble, t)\n }\n\n ctx.restore() // undo world transform\n ctx.restore() // undo DPR scale\n\n // Overlays — physical pixels, screen space\n this._renderBurst()\n this._renderImplode()\n }\n\n // ─── Background stars ─────────────────────────────────────────────────────\n\n private _renderBgStars(ctx: CanvasRenderingContext2D, w: number, h: number, t: number): void {\n for (const star of this._bgStars) {\n const brightness = 0.5 + 0.5 * Math.sin(t * star.speed + star.phase)\n const alpha = 0.08 + 0.25 * brightness\n const r = star.r * (0.7 + 0.3 * brightness)\n ctx.beginPath()\n ctx.arc(star.x * w, star.y * h, r, 0, Math.PI * 2)\n ctx.fillStyle = `rgba(255,255,255,${alpha.toFixed(3)})`\n ctx.fill()\n }\n }\n\n // ─── Node rendering ───────────────────────────────────────────────────────\n\n private _drawNode(\n ctx: CanvasRenderingContext2D,\n node: SkillNode,\n pos: Position,\n internal: InternalNodeState,\n alpha: number,\n zoom: number,\n isBubble: boolean,\n t: number,\n ): void {\n if (isBubble) {\n this._drawBubble(ctx, node, pos, internal, alpha, zoom, t)\n } else {\n this._drawStar(ctx, node, pos, internal, alpha, zoom, t)\n }\n }\n\n private _drawBubble(\n ctx: CanvasRenderingContext2D,\n node: SkillNode,\n pos: Position,\n internal: InternalNodeState,\n alpha: number,\n zoom: number,\n t: number,\n ): void {\n const style = this._resolveStyle(node)\n const phase = hashPhase(node.id)\n // Bubbles breathe very slowly; selected breathes a bit more\n const breatheAmp = internal === 'selected' ? 0.08 : 0.04\n const breathe = 1 + breatheAmp * Math.sin(t * 0.6 + phase)\n const r = BUBBLE_WORLD_RADIUS * breathe * (internal === 'hovered' ? 1.1 : 1)\n const isSelected = internal === 'selected'\n\n ctx.save()\n ctx.globalAlpha = style.opacity * alpha\n\n // Outer halo — brighter when selected\n const haloOuter = isSelected ? r * 2.8 : r * 2.2\n const haloStop0 = isSelected ? style.glowColor + '55' : style.glowColor + '30'\n const halo = ctx.createRadialGradient(pos.x, pos.y, r * 0.5, pos.x, pos.y, haloOuter)\n halo.addColorStop(0, haloStop0)\n halo.addColorStop(1, style.glowColor + '00')\n ctx.fillStyle = halo\n ctx.beginPath()\n ctx.arc(pos.x, pos.y, haloOuter, 0, Math.PI * 2)\n ctx.fill()\n\n // Inner fill\n const fill = ctx.createRadialGradient(pos.x, pos.y - r * 0.3, r * 0.1, pos.x, pos.y, r)\n fill.addColorStop(0, style.color + (isSelected ? '70' : '50'))\n fill.addColorStop(1, style.color + (isSelected ? '28' : '18'))\n ctx.fillStyle = fill\n ctx.beginPath()\n ctx.arc(pos.x, pos.y, r, 0, Math.PI * 2)\n ctx.fill()\n\n // Border ring\n ctx.strokeStyle = isSelected ? style.glowColor : style.color\n ctx.lineWidth = (isSelected ? 2.5 : 1.5) / zoom\n ctx.globalAlpha = style.opacity * alpha * (isSelected ? 1 : 0.7)\n ctx.beginPath()\n ctx.arc(pos.x, pos.y, r, 0, Math.PI * 2)\n ctx.stroke()\n\n // Selected: spinning dashed outer ring\n if (isSelected) {\n const spinAngle = t * 0.7 + phase\n ctx.strokeStyle = style.glowColor\n ctx.lineWidth = 1.5 / zoom\n ctx.globalAlpha = style.opacity * alpha * 0.85\n ctx.setLineDash([10 / zoom, 7 / zoom])\n ctx.lineDashOffset = -spinAngle * 18\n ctx.beginPath()\n ctx.arc(pos.x, pos.y, r * 1.3, 0, Math.PI * 2)\n ctx.stroke()\n ctx.setLineDash([])\n }\n\n // Child-count dots orbiting inside\n if (node.childIds && node.childIds.length > 0) {\n const count = Math.min(node.childIds.length, 8)\n const dotOrbit = r * 0.55\n const dotSize = Math.max(1.5, r * 0.06)\n const orbit = t * 0.15 + phase // slow rotation\n ctx.globalAlpha = style.opacity * alpha * 0.6\n ctx.fillStyle = style.color + '90'\n for (let i = 0; i < count; i++) {\n const angle = orbit + (i / count) * Math.PI * 2\n ctx.beginPath()\n ctx.arc(\n pos.x + dotOrbit * Math.cos(angle),\n pos.y + dotOrbit * Math.sin(angle),\n dotSize, 0, Math.PI * 2,\n )\n ctx.fill()\n }\n }\n\n // Label\n ctx.globalAlpha = style.opacity * alpha\n ctx.fillStyle = style.labelColor\n ctx.font = `bold ${(style.labelSize * 1.2) / zoom}px ${style.labelFont}`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'middle'\n ctx.fillText(node.label, pos.x, pos.y)\n\n ctx.restore()\n }\n\n private _drawStar(\n ctx: CanvasRenderingContext2D,\n node: SkillNode,\n pos: Position,\n internal: InternalNodeState,\n alpha: number,\n zoom: number,\n t: number,\n ): void {\n const style = this._resolveStyle(node)\n const phase = hashPhase(node.id)\n // Two independent sine waves for a natural twinkle\n const isSelected = internal === 'selected'\n // Selected nodes twinkle more dramatically\n const twinkleAmp = isSelected ? 0.4 : 0.22\n const twinkle = 1 + twinkleAmp * Math.sin(t * 1.7 + phase)\n + 0.08 * Math.sin(t * 3.1 + phase * 1.3)\n const glowPulse = style.glowRadius * twinkle\n const size = style.size * (internal === 'hovered' ? 1.4 : 1)\n\n ctx.save()\n ctx.globalAlpha = style.opacity * alpha\n\n // Selected: pulsing rings that radiate outward and fade\n if (isSelected) {\n const ringPeriod = 1.8\n for (let i = 0; i < 2; i++) {\n const offset = i * (ringPeriod / 2)\n const pulse = ((t + offset) % ringPeriod) / ringPeriod // 0→1\n const pulseR = (size + glowPulse * 0.6) * (1 + pulse * 2.2)\n const ringAlpha = (1 - pulse) * 0.6\n ctx.strokeStyle = style.glowColor\n ctx.lineWidth = (1.5 / zoom) * (1 - pulse * 0.5)\n ctx.globalAlpha = ringAlpha * style.opacity * alpha\n ctx.beginPath()\n ctx.arc(pos.x, pos.y, pulseR, 0, Math.PI * 2)\n ctx.stroke()\n }\n ctx.globalAlpha = style.opacity * alpha\n }\n\n // Glow\n if (glowPulse > 0) {\n const grad = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, glowPulse + size)\n grad.addColorStop(0, style.glowColor + (isSelected ? 'ff' : 'cc'))\n grad.addColorStop(0.4, style.glowColor + (isSelected ? '88' : '55'))\n grad.addColorStop(1, style.glowColor + '00')\n ctx.fillStyle = grad\n ctx.beginPath()\n ctx.arc(pos.x, pos.y, glowPulse + size, 0, Math.PI * 2)\n ctx.fill()\n }\n\n // Body\n ctx.fillStyle = isSelected ? style.glowColor : style.color\n ctx.beginPath()\n this._drawShape(ctx, pos, size, style.shape)\n ctx.fill()\n\n // Label\n if (size * zoom > 4) {\n ctx.fillStyle = style.labelColor\n ctx.font = `${style.labelSize / zoom}px ${style.labelFont}`\n ctx.textAlign = 'center'\n ctx.textBaseline = 'top'\n ctx.fillText(node.label, pos.x, pos.y + size + 4 / zoom)\n }\n\n ctx.restore()\n }\n\n // ─── Edge particles ───────────────────────────────────────────────────────\n\n private _renderEdgeParticles(\n ctx: CanvasRenderingContext2D,\n from: Position,\n to: Position,\n edge: SkillEdge,\n t: number,\n zoom: number,\n ): void {\n const dx = to.x - from.x\n const dy = to.y - from.y\n const len = Math.sqrt(dx * dx + dy * dy)\n if (len < 1) return\n\n const particleCount = 3\n const speed = 0.2 // edge traversals per second\n const particleR = 2.5 / zoom\n const color = edge.style?.color ?? this._theme.edge.color\n\n for (let i = 0; i < particleCount; i++) {\n const progress = ((t * speed + i / particleCount) % 1)\n const x = from.x + dx * progress\n const y = from.y + dy * progress\n const fade = Math.sin(progress * Math.PI) // fade at both ends\n\n ctx.save()\n ctx.globalAlpha = fade * 0.85\n\n const grad = ctx.createRadialGradient(x, y, 0, x, y, particleR * 2.5)\n grad.addColorStop(0, color + 'ff')\n grad.addColorStop(0.4, color + '99')\n grad.addColorStop(1, color + '00')\n ctx.fillStyle = grad\n ctx.beginPath()\n ctx.arc(x, y, particleR * 2.5, 0, Math.PI * 2)\n ctx.fill()\n\n ctx.restore()\n }\n }\n\n // ─── Burst animation ──────────────────────────────────────────────────────\n\n private _renderBurst(): void {\n if (!this._burst) return\n const ctx = this._ctx\n const elapsed = performance.now() - this._burst.startTime\n const t = elapsed / this._burstDuration\n if (t >= 1) { this._burst = null; return }\n\n const eased = 1 - Math.pow(1 - t, 3)\n const maxR = Math.hypot(this._canvas.width, this._canvas.height)\n const r = eased * maxR\n const alpha = (1 - eased) * 0.35\n\n ctx.save()\n ctx.globalAlpha = alpha\n ctx.fillStyle = this._burst.color\n ctx.beginPath()\n ctx.arc(\n this._burst.center.x * this._dpr,\n this._burst.center.y * this._dpr,\n r, 0, Math.PI * 2,\n )\n ctx.fill()\n ctx.restore()\n }\n\n // ─── Implode animation ────────────────────────────────────────────────────\n\n private _renderImplode(): void {\n if (!this._implode) return\n const ctx = this._ctx\n const elapsed = performance.now() - this._implode.startTime\n const t = elapsed / this._implodeDuration\n if (t >= 1) { this._implode = null; return }\n\n // Cubic ease-in: ring starts at full radius and slowly at first,\n // then accelerates sharply as it collapses to center\n const eased = t * t * t\n const maxR = Math.hypot(this._canvas.width, this._canvas.height)\n const r = (1 - eased) * maxR\n const alpha = (1 - t) * 0.55\n const lineWidth = (1 - eased) * 40 * this._dpr\n\n const cx = this._implode.center.x * this._dpr\n const cy = this._implode.center.y * this._dpr\n\n ctx.save()\n ctx.globalAlpha = alpha\n ctx.strokeStyle = '#9b5de5'\n ctx.lineWidth = lineWidth\n ctx.shadowColor = '#9b5de5'\n ctx.shadowBlur = 30 * this._dpr\n ctx.beginPath()\n ctx.arc(cx, cy, Math.max(0, r), 0, Math.PI * 2)\n ctx.stroke()\n ctx.restore()\n }\n\n // ─── Shapes ───────────────────────────────────────────────────────────────\n\n private _resolveStyle(node: SkillNode): NodeStyle {\n const base = this._theme.node\n const stateOverride = node.state ? this._theme.states[node.state] : {}\n const nodeOverride = node.style ?? {}\n return { ...base, ...stateOverride, ...nodeOverride }\n }\n\n private _drawShape(\n ctx: CanvasRenderingContext2D,\n pos: Position,\n size: number,\n shape: NodeStyle['shape'],\n ): void {\n switch (shape) {\n case 'circle':\n ctx.arc(pos.x, pos.y, size, 0, Math.PI * 2)\n break\n case 'hexagon':\n this._polygon(ctx, pos, size, 6, Math.PI / 6)\n break\n case 'diamond':\n this._polygon(ctx, pos, size, 4, 0)\n break\n case 'star':\n this._star(ctx, pos, size)\n break\n }\n }\n\n private _polygon(ctx: CanvasRenderingContext2D, pos: Position, r: number, sides: number, offset: number): void {\n ctx.moveTo(pos.x + r * Math.cos(offset), pos.y + r * Math.sin(offset))\n for (let i = 1; i < sides; i++) {\n const angle = offset + (i * 2 * Math.PI) / sides\n ctx.lineTo(pos.x + r * Math.cos(angle), pos.y + r * Math.sin(angle))\n }\n ctx.closePath()\n }\n\n private _star(ctx: CanvasRenderingContext2D, pos: Position, r: number): void {\n const inner = r * 0.4\n const points = 5\n for (let i = 0; i < points * 2; i++) {\n const angle = (i * Math.PI) / points - Math.PI / 2\n const radius = i % 2 === 0 ? r : inner\n const x = pos.x + radius * Math.cos(angle)\n const y = pos.y + radius * Math.sin(angle)\n i === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y)\n }\n ctx.closePath()\n }\n\n private _drawEdge(\n ctx: CanvasRenderingContext2D,\n from: Position,\n to: Position,\n styleOverride?: Partial<EdgeStyle>,\n ): void {\n const style = { ...this._theme.edge, ...styleOverride }\n ctx.save()\n ctx.strokeStyle = style.color\n ctx.lineWidth = style.width\n ctx.globalAlpha = style.opacity\n if (style.dashPattern) ctx.setLineDash(style.dashPattern)\n ctx.beginPath()\n ctx.moveTo(from.x, from.y)\n ctx.lineTo(to.x, to.y)\n ctx.stroke()\n ctx.restore()\n }\n\n private _resize(): void {\n const rect = this._canvas.getBoundingClientRect()\n const w = Math.floor(rect.width * this._dpr)\n const h = Math.floor(rect.height * this._dpr)\n if (this._canvas.width !== w || this._canvas.height !== h) {\n this._canvas.width = w\n this._canvas.height = h\n }\n }\n\n dispose(): void { /* nothing to clean up */ }\n}\n","import type { SkillNode, Position } from '../types/index.js'\nimport { BUBBLE_WORLD_RADIUS } from './CanvasRenderer.js'\n\nexport interface InteractionState {\n pan: Position\n zoom: number\n hoveredNodeId: string | null\n selectedNodeId: string | null\n}\n\nexport type InteractionEvent =\n | { type: 'node:click'; nodeId: string }\n | { type: 'node:hover'; nodeId: string }\n | { type: 'node:blur'; nodeId: string }\n | { type: 'canvas:click' }\n | { type: 'zoom:change'; zoom: number }\n | { type: 'pan:change'; pan: Position }\n\nexport class InteractionController {\n private _canvas: HTMLCanvasElement\n private _state: InteractionState\n private _isPanning = false\n private _lastPan: Position = { x: 0, y: 0 }\n private _listeners: Set<(event: InteractionEvent) => void> = new Set()\n private _nodes: SkillNode[] = []\n private _positions: Map<string, Position> = new Map()\n private _nodeSize = 8\n private _dpr: number\n\n // Smooth zoom (lerp)\n private _targetZoom: number = 1\n private _zoomAnchor: Position | null = null\n private readonly _zoomLerpAlpha = 0.14\n\n // Timed zoom — takes priority over lerp when active\n private _timedZoom: {\n startZoom: number\n target: number\n anchor: Position | null\n startTime: number\n duration: number\n easing: 'in' | 'out'\n } | null = null\n\n // Multi-touch tracking\n private _activePointers: Map<number, Position> = new Map()\n private _lastPinchDist: number | null = null\n // Separate from _lastPan (which is updated on every move) — used for drag vs click detection\n private _pointerDownPos: Position = { x: 0, y: 0 }\n\n private _onPointerDown: (e: PointerEvent) => void\n private _onPointerMove: (e: PointerEvent) => void\n private _onPointerUp: (e: PointerEvent) => void\n private _onPointerCancel: (e: PointerEvent) => void\n private _onWheel: (e: WheelEvent) => void\n\n constructor(canvas: HTMLCanvasElement) {\n this._canvas = canvas\n this._dpr = window.devicePixelRatio ?? 1\n this._state = {\n pan: { x: canvas.width / (2 * this._dpr), y: canvas.height / (2 * this._dpr) },\n zoom: 1,\n hoveredNodeId: null,\n selectedNodeId: null,\n }\n\n this._onPointerDown = this._handlePointerDown.bind(this)\n this._onPointerMove = this._handlePointerMove.bind(this)\n this._onPointerUp = this._handlePointerUp.bind(this)\n this._onPointerCancel = this._handlePointerCancel.bind(this)\n this._onWheel = this._handleWheel.bind(this)\n\n canvas.addEventListener('pointerdown', this._onPointerDown)\n canvas.addEventListener('pointermove', this._onPointerMove)\n canvas.addEventListener('pointerup', this._onPointerUp)\n canvas.addEventListener('pointercancel', this._onPointerCancel)\n canvas.addEventListener('wheel', this._onWheel, { passive: false })\n }\n\n get state(): Readonly<InteractionState> { return this._state }\n get targetZoom(): number { return this._targetZoom }\n get zoomAnchor(): Position | null { return this._zoomAnchor }\n\n setTargetZoom(value: number, anchor?: Position): void {\n this._timedZoom = null\n this._targetZoom = value\n if (anchor !== undefined) this._zoomAnchor = anchor\n }\n\n /**\n * Animate zoom to `target` over `duration` ms.\n * `easing: 'in'` — cubic ease-in (slow start, accelerates toward target).\n * `easing: 'out'` — cubic ease-out (fast start, decelerates toward target).\n */\n startTimedZoom(target: number, duration: number, anchor?: Position, easing: 'in' | 'out' = 'in'): void {\n this._timedZoom = {\n startZoom: this._state.zoom,\n target,\n anchor: anchor ?? null,\n startTime: performance.now(),\n duration,\n easing,\n }\n this._targetZoom = target\n this._zoomAnchor = anchor ?? null\n }\n\n /** Instantly snap zoom and pan to a new center — used on navigation transitions. */\n resetToCenter(zoom: number, cx: number, cy: number): void {\n this._timedZoom = null\n this._targetZoom = zoom\n this._state.zoom = zoom\n this._state.pan = { x: cx, y: cy }\n this._zoomAnchor = null\n }\n\n updateNodes(nodes: SkillNode[], positions: Map<string, Position>, nodeSize: number): void {\n this._nodes = nodes\n this._positions = positions\n this._nodeSize = nodeSize\n }\n\n onChange(listener: (event: InteractionEvent) => void): () => void {\n this._listeners.add(listener)\n return () => this._listeners.delete(listener)\n }\n\n /**\n * Advance zoom animation one frame. Called every RAF frame by the engine.\n * Returns true while the animation is still in progress.\n */\n tick(): boolean {\n const prevZoom = this._state.zoom\n\n if (this._timedZoom) {\n const { startZoom, target, anchor, startTime, duration, easing } = this._timedZoom\n const t = Math.min(1, (performance.now() - startTime) / duration)\n // ease-in: t³ — slow start, accelerates toward target\n // ease-out: 1-(1-t)³ — fast start, decelerates toward target\n const eased = easing === 'out' ? 1 - Math.pow(1 - t, 3) : t * t * t\n this._state.zoom = startZoom + (target - startZoom) * eased\n\n if (t >= 1) {\n this._timedZoom = null\n this._zoomAnchor = null\n } else {\n this._zoomAnchor = anchor\n }\n } else {\n // Fallback: standard lerp toward target\n const dz = this._targetZoom - this._state.zoom\n if (Math.abs(dz) < 0.0001) {\n this._zoomAnchor = null\n return false\n }\n this._state.zoom += dz * this._zoomLerpAlpha\n }\n\n // Keep the zoom anchor point fixed in world space\n if (this._zoomAnchor) {\n const { x: ax, y: ay } = this._zoomAnchor\n this._state.pan = {\n x: ax - (ax - this._state.pan.x) * (this._state.zoom / prevZoom),\n y: ay - (ay - this._state.pan.y) * (this._state.zoom / prevZoom),\n }\n }\n\n return true\n }\n\n dispose(): void {\n this._canvas.removeEventListener('pointerdown', this._onPointerDown)\n this._canvas.removeEventListener('pointermove', this._onPointerMove)\n this._canvas.removeEventListener('pointerup', this._onPointerUp)\n this._canvas.removeEventListener('pointercancel', this._onPointerCancel)\n this._canvas.removeEventListener('wheel', this._onWheel)\n }\n\n // ─── Canvas → world coordinate transform ────────────────────────────────────\n\n canvasToWorld(canvasX: number, canvasY: number): Position {\n return {\n x: (canvasX - this._state.pan.x) / this._state.zoom,\n y: (canvasY - this._state.pan.y) / this._state.zoom,\n }\n }\n\n private _getCanvasPos(e: MouseEvent): Position {\n const rect = this._canvas.getBoundingClientRect()\n return { x: e.clientX - rect.left, y: e.clientY - rect.top }\n }\n\n private _hitTest(worldPos: Position): string | null {\n let closest: string | null = null\n let closestDist = Infinity\n\n for (const node of this._nodes) {\n const pos = this._positions.get(node.id)\n if (!pos) continue\n const isBubble = !!(node.childIds && node.childIds.length > 0)\n // Bubbles: match visual radius. Stars: generous touch target covering label.\n const hitRadius = isBubble ? BUBBLE_WORLD_RADIUS * 1.3 : this._nodeSize * 4\n const dx = worldPos.x - pos.x\n const dy = worldPos.y - pos.y\n const dist = Math.sqrt(dx * dx + dy * dy)\n if (dist < hitRadius && dist < closestDist) {\n closestDist = dist\n closest = node.id\n }\n }\n return closest\n }\n\n private _emit(event: InteractionEvent): void {\n for (const listener of this._listeners) listener(event)\n }\n\n // ─── Pointer handlers ────────────────────────────────────────────────────────\n\n private _handlePointerDown(e: PointerEvent): void {\n this._canvas.setPointerCapture(e.pointerId)\n const pos = this._getCanvasPos(e)\n this._activePointers.set(e.pointerId, pos)\n\n if (this._activePointers.size === 1) {\n // Single pointer — prepare for pan or click\n this._isPanning = true\n this._lastPan = pos\n this._pointerDownPos = pos // fixed reference for drag vs click detection\n } else {\n // Second finger arrived — switch to pinch mode\n this._isPanning = false\n this._lastPinchDist = null\n }\n }\n\n private _handlePointerMove(e: PointerEvent): void {\n const canvasPos = this._getCanvasPos(e)\n this._activePointers.set(e.pointerId, canvasPos)\n\n // ── Pinch zoom (two fingers) ──────────────────────────────────────────────\n if (this._activePointers.size >= 2) {\n const pts = [...this._activePointers.values()]\n const dist = Math.hypot(pts[1]!.x - pts[0]!.x, pts[1]!.y - pts[0]!.y)\n\n if (this._lastPinchDist !== null && dist > 0) {\n const scale = dist / this._lastPinchDist\n const midX = (pts[0]!.x + pts[1]!.x) / 2\n const midY = (pts[0]!.y + pts[1]!.y) / 2\n this._targetZoom = Math.max(0.05, Math.min(20, this._targetZoom * scale))\n this._zoomAnchor = { x: midX, y: midY }\n this._emit({ type: 'zoom:change', zoom: this._targetZoom })\n }\n this._lastPinchDist = dist\n return\n }\n\n // ── Single-pointer pan ───────────────────────────────────────────────────\n if (this._isPanning) {\n const dx = canvasPos.x - this._lastPan.x\n const dy = canvasPos.y - this._lastPan.y\n this._lastPan = canvasPos\n this._state.pan = { x: this._state.pan.x + dx, y: this._state.pan.y + dy }\n this._emit({ type: 'pan:change', pan: this._state.pan })\n return\n }\n\n // ── Hover detection (mouse / stylus, not bare touch) ─────────────────────\n if (e.pointerType !== 'touch') {\n const worldPos = this.canvasToWorld(canvasPos.x, canvasPos.y)\n const hit = this._hitTest(worldPos)\n if (hit !== this._state.hoveredNodeId) {\n if (this._state.hoveredNodeId) {\n this._emit({ type: 'node:blur', nodeId: this._state.hoveredNodeId })\n }\n this._state.hoveredNodeId = hit\n if (hit) this._emit({ type: 'node:hover', nodeId: hit })\n }\n }\n }\n\n private _handlePointerUp(e: PointerEvent): void {\n const canvasPos = this._getCanvasPos(e)\n const wasMultiTouch = this._activePointers.size >= 2\n const dragOrigin = this._pointerDownPos // original down position, not the moving _lastPan\n this._activePointers.delete(e.pointerId)\n this._lastPinchDist = null\n\n if (wasMultiTouch) {\n // Came back to single touch — resume pan from current remaining finger\n if (this._activePointers.size === 1) {\n this._isPanning = true\n this._lastPan = [...this._activePointers.values()][0]!\n }\n return\n }\n\n this._isPanning = false\n\n // Fire click if pointer didn't travel (not a drag)\n const dx = Math.abs(canvasPos.x - dragOrigin.x)\n const dy = Math.abs(canvasPos.y - dragOrigin.y)\n if (dx < 4 && dy < 4) {\n const worldPos = this.canvasToWorld(canvasPos.x, canvasPos.y)\n const hit = this._hitTest(worldPos)\n if (hit) {\n this._state.selectedNodeId = hit === this._state.selectedNodeId ? null : hit\n this._emit({ type: 'node:click', nodeId: hit })\n } else {\n this._state.selectedNodeId = null\n this._emit({ type: 'canvas:click' })\n }\n }\n }\n\n private _handlePointerCancel(e: PointerEvent): void {\n this._activePointers.delete(e.pointerId)\n this._lastPinchDist = null\n if (this._activePointers.size === 0) {\n this._isPanning = false\n }\n }\n\n // ── Wheel (mouse / trackpad) ─────────────────────────────────────────────────\n\n private _handleWheel(e: WheelEvent): void {\n e.preventDefault()\n const canvasPos = this._getCanvasPos(e)\n const factor = e.deltaY < 0 ? 1.1 : 1 / 1.1\n this._targetZoom = Math.max(0.05, Math.min(20, this._targetZoom * factor))\n this._zoomAnchor = canvasPos\n this._emit({ type: 'zoom:change', zoom: this._targetZoom })\n }\n}\n","import type {\n SkillGraph,\n SkillNode,\n SkillEdge,\n NodeState,\n Evidence,\n ThemeInput,\n LodThreshold,\n SkillTreeEvents,\n InternalNodeState,\n Position,\n NavigationFrame,\n} from './types/index.js'\nimport { SkillGraphModel } from './graph/SkillGraphModel.js'\nimport { NavigationController } from './graph/NavigationController.js'\nimport { LodController } from './lod/LodController.js'\nimport { ForceLayout } from './layout/ForceLayout.js'\nimport { CanvasRenderer, BUBBLE_WORLD_RADIUS } from './renderer/CanvasRenderer.js'\nimport { InteractionController } from './renderer/InteractionController.js'\n\nconst ENTER_ZOOM_THRESHOLD = 2.5 // zoom target must exceed this to enter a bubble\nconst EXIT_ZOOM_THRESHOLD = 0.35 // zoom target must drop below this to exit context\nconst NAV_COOLDOWN_MS = 700 // prevent rapid enter/exit toggling\n\nexport interface SkillTreeEngineOptions {\n canvas: HTMLCanvasElement\n data: SkillGraph\n theme?: ThemeInput\n lod?: LodThreshold[]\n on?: Partial<SkillTreeEvents>\n /** Silently start inside this bubble node's context — no burst or fade animation */\n initialContextNodeId?: string\n}\n\nexport class SkillTreeEngine {\n private _model: SkillGraphModel\n private _nav: NavigationController\n private _lod: LodController\n private _layout: ForceLayout\n private _renderer: CanvasRenderer\n private _interaction: InteractionController\n private _positions: Map<string, Position> = new Map()\n private _internalStates: Map<string, InternalNodeState> = new Map()\n private _eventHandlers: Partial<SkillTreeEvents>\n private _rafId: number | null = null\n private _pendingEnter: ReturnType<typeof setTimeout> | null = null\n private _pendingExit: ReturnType<typeof setTimeout> | null = null\n private _unsubscribers: Array<() => void> = []\n private _resizeObserver: ResizeObserver\n\n // Cached visible node/edge lists — rebuilt on navigation and model changes\n private _visibleNodes: SkillNode[] = []\n private _visibleEdges: SkillEdge[] = []\n\n // Context fade-in\n private _contextFadeAlpha = 1.0\n private _contextFadeStart = 0\n private readonly _contextFadeDuration = 400\n\n // Navigation cooldown\n private _navCooldownUntil = 0\n\n constructor(options: SkillTreeEngineOptions) {\n const { canvas, data, theme, lod, on = {} } = options\n\n this._eventHandlers = on\n this._model = new SkillGraphModel(data)\n this._nav = new NavigationController({ nodeId: null, label: data.label })\n this._lod = new LodController(lod)\n this._layout = new ForceLayout()\n this._renderer = new CanvasRenderer(canvas, theme ?? data.theme)\n this._interaction = new InteractionController(canvas)\n\n if (options.initialContextNodeId) {\n const initNode = this._model.getNode(options.initialContextNodeId)\n if (initNode && this._isBubble(initNode)) {\n this._nav.push({ nodeId: initNode.id, label: initNode.label })\n }\n }\n\n this._rebuildVisibleCache()\n this._wireEvents()\n this._runLayout()\n this._startRenderLoop()\n\n this._resizeObserver = new ResizeObserver(() => {\n this._renderer.resize()\n this._runLayout()\n })\n this._resizeObserver.observe(canvas)\n\n this._eventHandlers['graph:ready']?.(data)\n }\n\n // ─── Public API ─────────────────────────────────────────────────────────────\n\n setNodeState(nodeId: string, state: NodeState): void {\n this._model.setNodeState(nodeId, state)\n }\n\n addEvidence(nodeId: string, evidence: Evidence): void {\n this._model.addEvidence(nodeId, evidence)\n }\n\n removeEvidence(nodeId: string, evidenceId: string): void {\n this._model.removeEvidence(nodeId, evidenceId)\n }\n\n updateTheme(theme: ThemeInput): void {\n this._renderer.updateTheme(theme)\n }\n\n zoomIn(): void {\n this._interaction.setTargetZoom(Math.min(20, this._interaction.targetZoom * 1.3))\n }\n\n zoomOut(): void {\n this._interaction.setTargetZoom(Math.max(0.05, this._interaction.targetZoom / 1.3))\n }\n\n goBack(): void {\n if (this._nav.canGoBack) this._exitWithAnimation()\n }\n\n /**\n * Atomically jump to a specific nav stack depth, firing a single animation.\n * `targetLength` is the desired `stack.length` after the jump.\n * Silently pops intermediate frames; plays one implode + fade transition.\n */\n jumpToNavDepth(targetLength: number): void {\n if (this._nav.stack.length <= targetLength) return\n\n // Cancel any in-flight transitions\n if (this._pendingEnter !== null) { clearTimeout(this._pendingEnter); this._pendingEnter = null }\n if (this._pendingExit !== null) { clearTimeout(this._pendingExit); this._pendingExit = null }\n\n // Silently pop all excess frames; capture the last one for the event\n let lastPopped: NavigationFrame | undefined\n while (this._nav.stack.length > targetLength && this._nav.canGoBack) {\n lastPopped = this._nav.pop()\n }\n if (!lastPopped) return\n\n const canvas = this._renderer.canvas\n const rect = canvas.getBoundingClientRect()\n const center = { x: rect.width / 2, y: rect.height / 2 }\n\n this._navCooldownUntil = Date.now() + NAV_COOLDOWN_MS\n this._rebuildVisibleCache()\n this._renderer.triggerImplode(center)\n this._interaction.resetToCenter(1.0, center.x, center.y)\n this._lod.setZoom(1.0)\n\n for (const n of this._visibleNodes) {\n this._positions.set(n.id, { x: (Math.random() - 0.5) * 80, y: (Math.random() - 0.5) * 80 })\n }\n\n this._contextFadeAlpha = 0\n this._contextFadeStart = performance.now()\n this._runLayout()\n this._eventHandlers['context:exit']?.(lastPopped, this._nav.stack)\n }\n\n private _exitWithAnimation(): void {\n if (!this._nav.canGoBack) return\n\n // Cancel any pending enter transition\n if (this._pendingEnter !== null) {\n clearTimeout(this._pendingEnter)\n this._pendingEnter = null\n }\n\n const canvas = this._renderer.canvas\n const rect = canvas.getBoundingClientRect()\n const center = { x: rect.width / 2, y: rect.height / 2 }\n const duration = 480\n\n // Ease-out: camera pulls back fast, then settles\n this._interaction.startTimedZoom(0.45, duration, undefined, 'out')\n\n this._pendingExit = setTimeout(() => {\n this._pendingExit = null\n this._renderer.triggerImplode(center)\n this._exitContext()\n }, duration)\n }\n\n enterContext(nodeId: string): void {\n const node = this._model.getNode(nodeId)\n if (!node || !this._isBubble(node)) return\n\n // Cancel any previous pending transition\n if (this._pendingEnter !== null) {\n clearTimeout(this._pendingEnter)\n this._pendingEnter = null\n }\n\n const canvas = this._renderer.canvas\n const rect = canvas.getBoundingClientRect()\n const burstPos = { x: rect.width / 2, y: rect.height / 2 }\n\n // Cubic ease-in zoom toward the node, then transition exactly when it peaks\n const nodePos = this._positions.get(nodeId)\n const anchor = nodePos ? (() => {\n const { zoom, pan } = this._interaction.state\n return { x: nodePos.x * zoom + pan.x, y: nodePos.y * zoom + pan.y }\n })() : undefined\n\n const duration = 480\n this._interaction.startTimedZoom(2.5, duration, anchor)\n\n this._pendingEnter = setTimeout(() => {\n this._pendingEnter = null\n this._enterContext(node, burstPos)\n }, duration)\n }\n\n getGraph(): SkillGraph { return this._model.toJSON() }\n\n getNavigationStack(): readonly NavigationFrame[] { return this._nav.stack }\n\n dispose(): void {\n if (this._rafId !== null) cancelAnimationFrame(this._rafId)\n if (this._pendingEnter !== null) clearTimeout(this._pendingEnter)\n if (this._pendingExit !== null) clearTimeout(this._pendingExit)\n this._resizeObserver.disconnect()\n this._layout.stop()\n this._renderer.dispose()\n this._interaction.dispose()\n for (const unsub of this._unsubscribers) unsub()\n }\n\n // ─── Visible node cache ──────────────────────────────────────────────────────\n\n private _rebuildVisibleCache(): void {\n const currentId = this._nav.current.nodeId\n this._visibleNodes = this._model.getNodes().filter(n =>\n currentId === null ? !n.parentId : n.parentId === currentId\n )\n const ids = new Set(this._visibleNodes.map(n => n.id))\n this._visibleEdges = this._model.getEdges().filter(\n e => ids.has(e.source) && ids.has(e.target)\n )\n }\n\n // ─── Navigation ─────────────────────────────────────────────────────────────\n\n private _isBubble(node: SkillNode): boolean {\n return !!(node.childIds && node.childIds.length > 0)\n }\n\n private _enterContext(node: SkillNode, burstCanvasPos: Position): void {\n this._nav.push({ nodeId: node.id, label: node.label })\n this._navCooldownUntil = Date.now() + NAV_COOLDOWN_MS\n this._rebuildVisibleCache()\n\n this._renderer.triggerBurst(burstCanvasPos, this._renderer.resolveNodeColor(node))\n\n // Reset zoom/pan to center\n const canvas = this._renderer.canvas\n const rect = canvas.getBoundingClientRect()\n this._interaction.resetToCenter(1.0, rect.width / 2, rect.height / 2)\n this._lod.setZoom(1.0)\n\n // Seed visible nodes near world origin\n for (const n of this._visibleNodes) {\n this._positions.set(n.id, {\n x: (Math.random() - 0.5) * 80,\n y: (Math.random() - 0.5) * 80,\n })\n }\n\n this._contextFadeAlpha = 0\n this._contextFadeStart = performance.now()\n\n this._runLayout()\n this._eventHandlers['context:enter']?.(node, this._nav.stack)\n }\n\n private _exitContext(): void {\n const exited = this._nav.pop()\n if (!exited) return\n this._navCooldownUntil = Date.now() + NAV_COOLDOWN_MS\n this._rebuildVisibleCache()\n\n const canvas = this._renderer.canvas\n const rect = canvas.getBoundingClientRect()\n this._interaction.resetToCenter(1.0, rect.width / 2, rect.height / 2)\n this._lod.setZoom(1.0)\n\n for (const n of this._visibleNodes) {\n this._positions.set(n.id, {\n x: (Math.random() - 0.5) * 80,\n y: (Math.random() - 0.5) * 80,\n })\n }\n\n this._contextFadeAlpha = 0\n this._contextFadeStart = performance.now()\n\n this._runLayout()\n this._eventHandlers['context:exit']?.(exited, this._nav.stack)\n }\n\n private _checkNavigation(): void {\n if (Date.now() < this._navCooldownUntil) return\n\n const targetZoom = this._interaction.targetZoom\n const anchor = this._interaction.zoomAnchor\n\n if (targetZoom > ENTER_ZOOM_THRESHOLD && anchor) {\n const { zoom, pan } = this._interaction.state\n const bubble = this._findBubbleAtCanvas(anchor, pan, zoom)\n if (bubble) {\n this._enterContext(bubble, anchor)\n return\n }\n }\n\n if (targetZoom < EXIT_ZOOM_THRESHOLD && this._nav.canGoBack) {\n this._exitContext()\n }\n }\n\n private _findBubbleAtCanvas(canvasPos: Position, pan: Position, zoom: number): SkillNode | null {\n const worldX = (canvasPos.x - pan.x) / zoom\n const worldY = (canvasPos.y - pan.y) / zoom\n\n for (const node of this._visibleNodes) {\n if (!this._isBubble(node)) continue\n const pos = this._positions.get(node.id)\n if (!pos) continue\n const dx = worldX - pos.x\n const dy = worldY - pos.y\n if (Math.sqrt(dx * dx + dy * dy) <= BUBBLE_WORLD_RADIUS * 1.3) {\n return node\n }\n }\n return null\n }\n\n // ─── Events ─────────────────────────────────────────────────────────────────\n\n private _wireEvents(): void {\n this._unsubscribers.push(\n this._model.on('changed', () => {\n this._rebuildVisibleCache()\n })\n )\n\n this._unsubscribers.push(\n this._interaction.onChange(event => {\n switch (event.type) {\n case 'zoom:change':\n case 'pan:change':\n break\n case 'node:hover': {\n this._internalStates.set(event.nodeId, 'hovered')\n const node = this._model.getNode(event.nodeId)\n if (node) this._eventHandlers['node:hover']?.(node)\n break\n }\n case 'node:blur': {\n const prev = this._internalStates.get(event.nodeId)\n if (prev === 'hovered') this._internalStates.set(event.nodeId, 'idle')\n const node = this._model.getNode(event.nodeId)\n if (node) this._eventHandlers['node:blur']?.(node)\n break\n }\n case 'canvas:click': {\n // Clear any selected node\n for (const [id, s] of this._internalStates) {\n if (s === 'selected') this._internalStates.set(id, 'idle')\n }\n this._eventHandlers['canvas:click']?.()\n break\n }\n\n case 'node:click': {\n const node = this._model.getNode(event.nodeId)\n if (!node) break\n\n // Toggle selection internal state for all nodes\n const prev = this._internalStates.get(event.nodeId)\n const isSelected = prev === 'selected'\n if (isSelected) {\n this._internalStates.set(event.nodeId, 'idle')\n } else {\n for (const [id, s] of this._internalStates) {\n if (s === 'selected') this._internalStates.set(id, 'idle')\n }\n this._internalStates.set(event.nodeId, 'selected')\n }\n\n this._eventHandlers['node:click']?.(node)\n break\n }\n }\n })\n )\n }\n\n // ─── Layout ─────────────────────────────────────────────────────────────────\n\n private _runLayout(): void {\n const rect = this._renderer.canvas.getBoundingClientRect()\n\n this._layout.run(this._visibleNodes, this._visibleEdges, {\n width: rect.width || 800,\n height: rect.height || 600,\n onTick: positions => {\n for (const [id, pos] of positions) this._positions.set(id, pos)\n this._interaction.updateNodes(this._visibleNodes, this._positions, 8)\n },\n })\n }\n\n // ─── Render loop ────────────────────────────────────────────────────────────\n\n private _startRenderLoop(): void {\n const loop = () => {\n const zooming = this._interaction.tick()\n if (zooming) {\n const z = this._interaction.state.zoom\n this._lod.setZoom(z)\n this._eventHandlers['zoom:change']?.(z)\n }\n\n if (this._contextFadeAlpha < 1) {\n const elapsed = performance.now() - this._contextFadeStart\n this._contextFadeAlpha = Math.min(1, elapsed / this._contextFadeDuration)\n }\n\n this._checkNavigation()\n this._draw()\n\n this._rafId = requestAnimationFrame(loop)\n }\n this._rafId = requestAnimationFrame(loop)\n }\n\n private _draw(): void {\n const { pan, zoom } = this._interaction.state\n\n this._renderer.render(this._visibleNodes, this._visibleEdges, {\n positions: this._positions,\n internalStates: this._internalStates,\n contextFadeAlpha: this._contextFadeAlpha,\n pan,\n zoom,\n })\n }\n}\n"],"names":["SkillGraphModel","data","Graph","node","edge","id","nodeId","child","depth","n","state","edgeId","evidence","existing","evidenceId","e","result","event","listener","set","payload","listeners","NavigationController","root","frame","popped","l","DEFAULT_THRESHOLDS","LodController","thresholds","zoom","ForceLayout","nodes","edges","options","width","height","chargeStrength","collisionPadding","alphaDecay","layoutNodes","_a","_b","nodeIndex","layoutLinks","source","target","emit","positions","forceSimulation","forceLink","d","s","t","forceManyBody","forceCenter","forceCollide","defaultTheme","mergeTheme","overrides","_c","_d","_e","BUBBLE_WORLD_RADIUS","generateBgStars","count","hashPhase","h","i","CanvasRenderer","canvas","theme","ctx","centerCanvas","color","pan","contextFadeAlpha","internalStates","cssW","cssH","sourcePos","targetPos","parentPos","childPos","bubbles","stars","pos","internal","isBubble","w","star","brightness","alpha","r","style","phase","breathe","isSelected","haloOuter","haloStop0","halo","fill","spinAngle","dotOrbit","dotSize","orbit","angle","twinkle","glowPulse","size","offset","pulse","pulseR","ringAlpha","grad","from","to","dx","dy","particleCount","speed","particleR","progress","x","y","fade","eased","maxR","lineWidth","cx","cy","base","stateOverride","nodeOverride","shape","sides","inner","points","radius","styleOverride","rect","InteractionController","value","anchor","duration","easing","nodeSize","prevZoom","startZoom","startTime","dz","ax","ay","canvasX","canvasY","worldPos","closest","closestDist","hitRadius","dist","canvasPos","pts","scale","midX","midY","hit","wasMultiTouch","dragOrigin","factor","ENTER_ZOOM_THRESHOLD","EXIT_ZOOM_THRESHOLD","NAV_COOLDOWN_MS","SkillTreeEngine","lod","on","initNode","targetLength","lastPopped","center","burstPos","nodePos","unsub","currentId","ids","burstCanvasPos","exited","targetZoom","bubble","worldX","worldY","_f","_h","_g","loop","z","elapsed"],"mappings":"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"}
|