@expertrees/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +163 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +249 -178
- package/dist/index.js.map +1 -1
- package/package.json +17 -3
package/README.md
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# @expertrees/core
|
|
2
|
+
|
|
3
|
+
Framework-agnostic engine for building and visualizing hierarchical knowledge graphs — navigable like a star map.
|
|
4
|
+
|
|
5
|
+
Nodes with children render as glowing **bubbles**. Leaf nodes render as **twinkling stars**. Click into a bubble to explore its sub-graph. Evidence (links, images, text, files) can be attached to any node.
|
|
6
|
+
|
|
7
|
+
Built on Canvas 2D with force-directed layout, continuous animations, and touch/pinch-to-zoom support.
|
|
8
|
+
|
|
9
|
+
> For framework-specific packages see [@expertrees/vue](https://www.npmjs.com/package/@expertrees/vue), [@expertrees/react](https://www.npmjs.com/package/@expertrees/react), [@expertrees/angular](https://www.npmjs.com/package/@expertrees/angular).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm add @expertrees/core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Basic usage
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import { SkillTreeEngine } from '@expertrees/core'
|
|
21
|
+
|
|
22
|
+
const engine = new SkillTreeEngine({
|
|
23
|
+
canvas: document.querySelector('canvas')!,
|
|
24
|
+
data: {
|
|
25
|
+
id: 'my-skills',
|
|
26
|
+
label: 'My Skills',
|
|
27
|
+
nodes: [
|
|
28
|
+
{ id: 'eng', label: 'Engineering', depth: 0, childIds: ['fe', 'be'] },
|
|
29
|
+
{ id: 'fe', label: 'Frontend', depth: 1, parentId: 'eng', childIds: ['vue', 'react'] },
|
|
30
|
+
{ id: 'be', label: 'Backend', depth: 1, parentId: 'eng' },
|
|
31
|
+
{ id: 'vue', label: 'Vue', depth: 2, parentId: 'fe' },
|
|
32
|
+
{ id: 'react', label: 'React', depth: 2, parentId: 'fe' },
|
|
33
|
+
],
|
|
34
|
+
edges: [],
|
|
35
|
+
},
|
|
36
|
+
on: {
|
|
37
|
+
'node:click': node => console.log('clicked', node.label),
|
|
38
|
+
'context:enter': (node, stack) => console.log('entered', node.label),
|
|
39
|
+
'zoom:change': zoom => console.log('zoom', zoom),
|
|
40
|
+
},
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// Always call dispose when unmounting — cancels the animation loop
|
|
44
|
+
engine.dispose()
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### Constructor options
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
new SkillTreeEngine({
|
|
53
|
+
canvas: HTMLCanvasElement // required
|
|
54
|
+
data: SkillGraph // required
|
|
55
|
+
theme?: ThemeInput // optional visual overrides
|
|
56
|
+
lod?: LodThreshold[] // optional level-of-detail config
|
|
57
|
+
initialContextNodeId?: string // start inside a specific bubble
|
|
58
|
+
on?: Partial<SkillTreeEvents> // event handlers
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Methods
|
|
63
|
+
|
|
64
|
+
| Method | Signature | Description |
|
|
65
|
+
|--------|-----------|-------------|
|
|
66
|
+
| `setNodeState` | `(id, NodeState) => void` | Update a node's semantic state |
|
|
67
|
+
| `addEvidence` | `(id, Evidence) => void` | Attach evidence to a node |
|
|
68
|
+
| `removeEvidence` | `(id, evidenceId) => void` | Remove evidence from a node |
|
|
69
|
+
| `updateTheme` | `(ThemeInput) => void` | Hot-swap the visual theme |
|
|
70
|
+
| `zoomIn` | `() => void` | Programmatic zoom in |
|
|
71
|
+
| `zoomOut` | `() => void` | Programmatic zoom out |
|
|
72
|
+
| `goBack` | `() => void` | Exit to parent context |
|
|
73
|
+
| `enterContext` | `(nodeId) => void` | Programmatically enter a bubble |
|
|
74
|
+
| `jumpToNavDepth` | `(targetLength) => void` | Jump to a stack depth in one animation |
|
|
75
|
+
| `getGraph` | `() => SkillGraph` | Serialize current graph state |
|
|
76
|
+
| `getNavigationStack` | `() => readonly NavigationFrame[]` | Current navigation stack |
|
|
77
|
+
| `dispose` | `() => void` | Cancel animation loop and clean up |
|
|
78
|
+
|
|
79
|
+
### Events
|
|
80
|
+
|
|
81
|
+
| Event | Payload | When |
|
|
82
|
+
|-------|---------|------|
|
|
83
|
+
| `node:click` | `SkillNode` | User clicks a node |
|
|
84
|
+
| `node:hover` | `SkillNode` | Mouse enters a node |
|
|
85
|
+
| `node:blur` | `SkillNode` | Mouse leaves a node |
|
|
86
|
+
| `canvas:click` | — | Click on empty canvas |
|
|
87
|
+
| `zoom:change` | `number` | Every zoom frame |
|
|
88
|
+
| `context:enter` | `(SkillNode, NavigationFrame[])` | Entering a bubble |
|
|
89
|
+
| `context:exit` | `(NavigationFrame, NavigationFrame[])` | Exiting a bubble |
|
|
90
|
+
| `graph:ready` | `SkillGraph` | Engine mounted and ready |
|
|
91
|
+
|
|
92
|
+
## Data model
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
interface SkillGraph {
|
|
96
|
+
id: string
|
|
97
|
+
label: string
|
|
98
|
+
nodes: SkillNode[]
|
|
99
|
+
edges: SkillEdge[]
|
|
100
|
+
theme?: ThemeInput
|
|
101
|
+
meta?: Record<string, unknown>
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
interface SkillNode {
|
|
105
|
+
id: string
|
|
106
|
+
label: string
|
|
107
|
+
description?: string
|
|
108
|
+
depth: number // 0 = root; higher = more specific
|
|
109
|
+
parentId?: string // required for children inside a bubble
|
|
110
|
+
childIds?: string[] // present on bubble nodes; omit for leaf/star nodes
|
|
111
|
+
position?: { x: number, y: number }
|
|
112
|
+
state?: NodeState // 'default' | 'active' | 'locked' | 'unlocked' | 'highlighted'
|
|
113
|
+
evidence?: Evidence[]
|
|
114
|
+
style?: Partial<NodeStyle>
|
|
115
|
+
meta?: Record<string, unknown>
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface Evidence {
|
|
119
|
+
id: string
|
|
120
|
+
type: 'link' | 'text' | 'image' | 'video' | 'file'
|
|
121
|
+
label: string
|
|
122
|
+
description?: string
|
|
123
|
+
date?: string // ISO 8601
|
|
124
|
+
tags?: string[]
|
|
125
|
+
thumbnail?: string
|
|
126
|
+
meta?: Record<string, unknown>
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Theming
|
|
131
|
+
|
|
132
|
+
All fields are optional and merge over built-in defaults.
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
const theme: ThemeInput = {
|
|
136
|
+
background: '#050a1a',
|
|
137
|
+
node: {
|
|
138
|
+
color: '#4a9eff',
|
|
139
|
+
glowColor: '#4a9eff',
|
|
140
|
+
glowRadius: 12,
|
|
141
|
+
size: 8,
|
|
142
|
+
shape: 'circle', // 'circle' | 'star' | 'hexagon' | 'diamond'
|
|
143
|
+
},
|
|
144
|
+
edge: {
|
|
145
|
+
color: '#4a9eff',
|
|
146
|
+
width: 1,
|
|
147
|
+
opacity: 0.4,
|
|
148
|
+
animated: true,
|
|
149
|
+
},
|
|
150
|
+
states: {
|
|
151
|
+
active: { color: '#50fa7b', glowColor: '#50fa7b' },
|
|
152
|
+
unlocked: { color: '#50fa7b', glowColor: '#50fa7b' },
|
|
153
|
+
locked: { color: '#2a3a4a', opacity: 0.4 },
|
|
154
|
+
highlighted: { color: '#ffb86c', glowColor: '#ffb86c' },
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
engine.updateTheme({ node: { color: '#ff79c6' } })
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
MIT — [github.com/0xMack/expertrees](https://github.com/0xMack/expertrees)
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +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;
|
|
1
|
+
"use strict";var R=Object.defineProperty;var L=(u,t,e)=>t in u?R(u,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):u[t]=e;var r=(u,t,e)=>L(u,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const O=require("graphology"),S=require("d3-force");class C{constructor(t){r(this,"_graph");r(this,"_meta");r(this,"_label");r(this,"_id");r(this,"_listeners",new Map);this._id=t.id,this._label=t.label,this._meta=t.meta,this._graph=new O({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 C(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 A{constructor(t={nodeId:null,label:"Root"}){r(this,"_stack");r(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 F=[{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 T{constructor(t){r(this,"_thresholds");r(this,"_zoom",1);r(this,"_listeners",new Set);this._thresholds=t??F}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 z{constructor(){r(this,"_simulation",null)}run(t,e,s){this.stop();const{width:i,height:o,chargeStrength:a=-200,collisionPadding:h=16,alphaDecay:n=.02}=s,d=t.map(l=>{var _,p;return{id:l.id,depth:l.depth,x:((_=l.position)==null?void 0:_.x)??(Math.random()-.5)*100,y:((p=l.position)==null?void 0:p.y)??(Math.random()-.5)*100}}),c=new Map(d.map(l=>[l.id,l])),b=[];for(const l of t)if(l.parentId){const _=c.get(l.parentId),p=c.get(l.id);_&&p&&b.push({id:`${l.parentId}->${l.id}`,source:_,target:p})}for(const l of e){const _=c.get(l.source),p=c.get(l.target);_&&p&&b.push({id:l.id,source:_,target:p})}const f=l=>{var _;(_=s.onTick)==null||_.call(s,l)};this._simulation=S.forceSimulation(d).alphaDecay(n).force("link",S.forceLink(b).id(l=>l.id).distance(l=>{const _=l.source,p=l.target;return 80+Math.abs(_.depth-p.depth)*20}).strength(.4)).force("charge",S.forceManyBody().strength(a)).force("center",S.forceCenter(0,0)).force("collide",S.forceCollide().radius(h)).on("tick",()=>{const l=this._collectPositions(d);f(l)}).on("end",()=>{var _;const l=this._collectPositions(d);(_=s.onEnd)==null||_.call(s,l)})}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 y={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 x(u){var t,e,s,i,o;return u?{background:u.background??y.background,node:{...y.node,...u.node},edge:{...y.edge,...u.edge},states:{default:{...y.states.default,...(t=u.states)==null?void 0:t.default},active:{...y.states.active,...(e=u.states)==null?void 0:e.active},locked:{...y.states.locked,...(s=u.states)==null?void 0:s.locked},unlocked:{...y.states.unlocked,...(i=u.states)==null?void 0:i.unlocked},highlighted:{...y.states.highlighted,...(o=u.states)==null?void 0:o.highlighted}}}:y}const M=40;function W(u){return Array.from({length:u},()=>({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(u){let t=0;for(let e=0;e<u.length;e++)t=t*31+u.charCodeAt(e)>>>0;return t%1e3/1e3*Math.PI*2}class D{constructor(t,e){r(this,"_canvas");r(this,"_ctx");r(this,"_theme");r(this,"_dpr");r(this,"_bgStars");r(this,"_burst",null);r(this,"_burstDuration",450);r(this,"_implode",null);r(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=x(e),this._dpr=window.devicePixelRatio??1,this._bgStars=W(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=x(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 p;const i=this._ctx,o=performance.now()/1e3,{pan:a,zoom:h,contextFadeAlpha:n,positions:d,internalStates:c}=s,b=this._canvas.width/this._dpr,f=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,b,f,o),i.save(),i.translate(a.x,a.y),i.scale(h,h);for(const g of e){const v=d.get(g.source),m=d.get(g.target);!v||!m||(this._drawEdge(i,v,m,g.style),(((p=g.style)==null?void 0:p.animated)??this._theme.edge.animated)&&this._renderEdgeParticles(i,v,m,g,o,h))}for(const g of t){if(!g.parentId)continue;const v=d.get(g.parentId),m=d.get(g.id);!v||!m||(i.save(),i.globalAlpha=n*.35,i.strokeStyle=this._theme.edge.color,i.lineWidth=this._theme.edge.width/h,i.setLineDash([4,8]),i.beginPath(),i.moveTo(v.x,v.y),i.lineTo(m.x,m.y),i.stroke(),i.restore())}const l=t.filter(g=>g.childIds&&g.childIds.length>0),_=t.filter(g=>!g.childIds||g.childIds.length===0);for(const g of[...l,..._]){const v=d.get(g.id);if(!v)continue;const m=c.get(g.id)??"idle",w=!!(g.childIds&&g.childIds.length>0);this._drawNode(i,g,v,m,n,h,w,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),h=.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,${h.toFixed(3)})`,t.fill()}}_drawNode(t,e,s,i,o,a,h,n){h?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,h){const n=this._resolveStyle(e),d=N(e.id),b=1+(i==="selected"?.08:.04)*Math.sin(h*.6+d),f=M*b*(i==="hovered"?1.1:1),l=i==="selected";t.save(),t.globalAlpha=n.opacity*o;const _=l?f*2.8:f*2.2,p=l?n.glowColor+"55":n.glowColor+"30",g=t.createRadialGradient(s.x,s.y,f*.5,s.x,s.y,_);g.addColorStop(0,p),g.addColorStop(1,n.glowColor+"00"),t.fillStyle=g,t.beginPath(),t.arc(s.x,s.y,_,0,Math.PI*2),t.fill();const v=t.createRadialGradient(s.x,s.y-f*.3,f*.1,s.x,s.y,f);if(v.addColorStop(0,n.color+(l?"70":"50")),v.addColorStop(1,n.color+(l?"28":"18")),t.fillStyle=v,t.beginPath(),t.arc(s.x,s.y,f,0,Math.PI*2),t.fill(),t.strokeStyle=l?n.glowColor:n.color,t.lineWidth=(l?2.5:1.5)/a,t.globalAlpha=n.opacity*o*(l?1:.7),t.beginPath(),t.arc(s.x,s.y,f,0,Math.PI*2),t.stroke(),l){const m=h*.7+d;t.strokeStyle=n.glowColor,t.lineWidth=1.5/a,t.globalAlpha=n.opacity*o*.85,t.setLineDash([10/a,7/a]),t.lineDashOffset=-m*18,t.beginPath(),t.arc(s.x,s.y,f*1.3,0,Math.PI*2),t.stroke(),t.setLineDash([])}if(e.childIds&&e.childIds.length>0){const m=Math.min(e.childIds.length,8),w=f*.55,P=Math.max(1.5,f*.06),B=h*.15+d;t.globalAlpha=n.opacity*o*.6,t.fillStyle=n.color+"90";for(let k=0;k<m;k++){const E=B+k/m*Math.PI*2;t.beginPath(),t.arc(s.x+w*Math.cos(E),s.y+w*Math.sin(E),P,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,h){const n=this._resolveStyle(e),d=N(e.id),c=i==="selected",f=1+(c?.4:.22)*Math.sin(h*1.7+d)+.08*Math.sin(h*3.1+d*1.3),l=n.glowRadius*f,_=n.size*(i==="hovered"?1.4:1);if(t.save(),t.globalAlpha=n.opacity*o,c){for(let g=0;g<2;g++){const v=g*.9,m=(h+v)%1.8/1.8,w=(_+l*.6)*(1+m*2.2),P=(1-m)*.6;t.strokeStyle=n.glowColor,t.lineWidth=1.5/a*(1-m*.5),t.globalAlpha=P*n.opacity*o,t.beginPath(),t.arc(s.x,s.y,w,0,Math.PI*2),t.stroke()}t.globalAlpha=n.opacity*o}if(l>0){const p=t.createRadialGradient(s.x,s.y,0,s.x,s.y,l+_);p.addColorStop(0,n.glowColor+(c?"ff":"cc")),p.addColorStop(.4,n.glowColor+(c?"88":"55")),p.addColorStop(1,n.glowColor+"00"),t.fillStyle=p,t.beginPath(),t.arc(s.x,s.y,l+_,0,Math.PI*2),t.fill()}t.fillStyle=c?n.glowColor:n.color,t.beginPath(),this._drawShape(t,s,_,n.shape),t.fill(),_*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+_+4/a)),t.restore()}_renderEdgeParticles(t,e,s,i,o,a){var _;const h=s.x-e.x,n=s.y-e.y;if(Math.sqrt(h*h+n*n)<1)return;const c=3,b=.2,f=2.5/a,l=((_=i.style)==null?void 0:_.color)??this._theme.edge.color;for(let p=0;p<c;p++){const g=(o*b+p/c)%1,v=e.x+h*g,m=e.y+n*g,w=Math.sin(g*Math.PI);t.save(),t.globalAlpha=w*.85;const P=t.createRadialGradient(v,m,0,v,m,f*2.5);P.addColorStop(0,l+"ff"),P.addColorStop(.4,l+"99"),P.addColorStop(1,l+"00"),t.fillStyle=P,t.beginPath(),t.arc(v,m,f*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,h=(1-i)*.35;t.save(),t.globalAlpha=h,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,h=(1-s)*.55,n=(1-i)*40*this._dpr,d=this._implode.center.x*this._dpr,c=this._implode.center.y*this._dpr;t.save(),t.globalAlpha=h,t.strokeStyle="#9b5de5",t.lineWidth=n,t.shadowColor="#9b5de5",t.shadowBlur=30*this._dpr,t.beginPath(),t.arc(d,c,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 h=o+a*2*Math.PI/i;t.lineTo(e.x+s*Math.cos(h),e.y+s*Math.sin(h))}t.closePath()}_star(t,e,s){const i=s*.4,o=5;for(let a=0;a<o*2;a++){const h=a*Math.PI/o-Math.PI/2,n=a%2===0?s:i,d=e.x+n*Math.cos(h),c=e.y+n*Math.sin(h);a===0?t.moveTo(d,c):t.lineTo(d,c)}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){r(this,"_canvas");r(this,"_state");r(this,"_isPanning",!1);r(this,"_lastPan",{x:0,y:0});r(this,"_listeners",new Set);r(this,"_nodes",[]);r(this,"_positions",new Map);r(this,"_nodeSize",8);r(this,"_dpr");r(this,"_targetZoom",1);r(this,"_zoomAnchor",null);r(this,"_zoomLerpAlpha",.14);r(this,"_timedZoom",null);r(this,"_activePointers",new Map);r(this,"_lastPinchDist",null);r(this,"_pointerDownPos",{x:0,y:0});r(this,"_onPointerDown");r(this,"_onPointerMove");r(this,"_onPointerUp");r(this,"_onPointerCancel");r(this,"_onWheel");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:h}=this._timedZoom,n=Math.min(1,(performance.now()-o)/a),d=h==="out"?1-Math.pow(1-n,3):n*n*n;this._state.zoom=e+(s-e)*d,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 h=!!(i.childIds&&i.childIds.length>0)?M*1.3:this._nodeSize*4,n=t.x-o.x,d=t.y-o.y,c=Math.sqrt(n*n+d*d);c<h&&c<s&&(s=c,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,h=(s[0].y+s[1].y)/2;this._targetZoom=Math.max(.05,Math.min(20,this._targetZoom*o)),this._zoomAnchor={x:a,y:h},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 h=this.canvasToWorld(e.x,e.y),n=this._hitTest(h);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 U=2.5,H=.35,I=700;class G{constructor(t){r(this,"_model");r(this,"_nav");r(this,"_lod");r(this,"_layout");r(this,"_renderer");r(this,"_interaction");r(this,"_positions",new Map);r(this,"_internalStates",new Map);r(this,"_eventHandlers");r(this,"_rafId",null);r(this,"_pendingEnter",null);r(this,"_pendingExit",null);r(this,"_unsubscribers",[]);r(this,"_resizeObserver");r(this,"_visibleNodes",[]);r(this,"_visibleEdges",[]);r(this,"_contextFadeAlpha",1);r(this,"_contextFadeStart",0);r(this,"_contextFadeDuration",400);r(this,"_navCooldownUntil",0);var h,n;const{canvas:e,data:s,theme:i,lod:o,on:a={}}=t;if(this._eventHandlers=a,this._model=new C(s),this._nav=new A({nodeId:null,label:s.label}),this._lod=new T(o),this._layout=new z,this._renderer=new D(e,i??s.theme),this._interaction=new Z(e),t.initialContextNodeId){const d=this._model.getNode(t.initialContextNodeId);d&&this._isBubble(d)&&this._nav.push({nodeId:d.id,label:d.label})}this._rebuildVisibleCache(),this._wireEvents(),this._runLayout(),this._startRenderLoop(),this._resizeObserver=new ResizeObserver(()=>{this._renderer.resize(),this._runLayout()}),this._resizeObserver.observe(e),(n=(h=this._eventHandlers)["graph:ready"])==null||n.call(h,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,h;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()+I,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(),(h=(a=this._eventHandlers)["context:exit"])==null||h.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),h=a?(()=>{const{zoom:d,pan:c}=this._interaction.state;return{x:a.x*d+c.x,y:a.y*d+c.y}})():void 0,n=480;this._interaction.startTimedZoom(2.5,n,h),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()+I,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 h of this._visibleNodes)this._positions.set(h.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()+I,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>U&&e){const{zoom:s,pan:i}=this._interaction.state,o=this._findBubbleAtCanvas(e,i,s);if(o){this._enterContext(o,e);return}}t<H&&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 h=this._positions.get(a.id);if(!h)continue;const n=i-h.x,d=o-h.y;if(Math.sqrt(n*n+d*d)<=M*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,h,n,d;switch(t.type){case"zoom:change":case"pan:change":break;case"node:hover":{this._internalStates.set(t.nodeId,"hovered");const c=this._model.getNode(t.nodeId);c&&((s=(e=this._eventHandlers)["node:hover"])==null||s.call(e,c));break}case"node:blur":{this._internalStates.get(t.nodeId)==="hovered"&&this._internalStates.set(t.nodeId,"idle");const b=this._model.getNode(t.nodeId);b&&((o=(i=this._eventHandlers)["node:blur"])==null||o.call(i,b));break}case"canvas:click":{for(const[c,b]of this._internalStates)b==="selected"&&this._internalStates.set(c,"idle");(h=(a=this._eventHandlers)["canvas:click"])==null||h.call(a);break}case"node:click":{const c=this._model.getNode(t.nodeId);if(!c)break;if(this._internalStates.get(t.nodeId)==="selected")this._internalStates.set(t.nodeId,"idle");else{for(const[l,_]of this._internalStates)_==="selected"&&this._internalStates.set(l,"idle");this._internalStates.set(t.nodeId,"selected")}(d=(n=this._eventHandlers)["node:click"])==null||d.call(n,c);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=M;exports.CanvasRenderer=D;exports.ForceLayout=z;exports.InteractionController=Z;exports.LodController=T;exports.NavigationController=A;exports.SkillGraphModel=C;exports.SkillTreeEngine=G;exports.defaultTheme=y;exports.mergeTheme=x;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|