@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/dist/index.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
var A = Object.defineProperty;
|
|
2
|
+
var z = (u, t, e) => t in u ? A(u, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : u[t] = e;
|
|
3
|
+
var r = (u, t, e) => z(u, typeof t != "symbol" ? t + "" : t, e);
|
|
4
|
+
import T from "graphology";
|
|
5
|
+
import { forceSimulation as Z, forceLink as D, forceManyBody as B, forceCenter as R, forceCollide as L } from "d3-force";
|
|
6
|
+
class M {
|
|
4
7
|
constructor(t) {
|
|
5
|
-
|
|
8
|
+
r(this, "_graph");
|
|
9
|
+
r(this, "_meta");
|
|
10
|
+
r(this, "_label");
|
|
11
|
+
r(this, "_id");
|
|
12
|
+
r(this, "_listeners", /* @__PURE__ */ new Map());
|
|
13
|
+
this._id = t.id, this._label = t.label, this._meta = t.meta, this._graph = new T({ multi: !1, allowSelfLoops: !1 });
|
|
6
14
|
for (const e of t.nodes)
|
|
7
15
|
this._graph.addNode(e.id, { ...e });
|
|
8
16
|
for (const e of t.edges)
|
|
@@ -82,7 +90,7 @@ class S {
|
|
|
82
90
|
return this._meta !== void 0 && (t.meta = this._meta), t;
|
|
83
91
|
}
|
|
84
92
|
static fromJSON(t) {
|
|
85
|
-
return new
|
|
93
|
+
return new M(t);
|
|
86
94
|
}
|
|
87
95
|
// ─── Events ─────────────────────────────────────────────────────────────────
|
|
88
96
|
on(t, e) {
|
|
@@ -97,9 +105,11 @@ class S {
|
|
|
97
105
|
e === void 0 ? i() : i(e);
|
|
98
106
|
}
|
|
99
107
|
}
|
|
100
|
-
class
|
|
108
|
+
class O {
|
|
101
109
|
constructor(t = { nodeId: null, label: "Root" }) {
|
|
102
|
-
this
|
|
110
|
+
r(this, "_stack");
|
|
111
|
+
r(this, "_listeners", /* @__PURE__ */ new Set());
|
|
112
|
+
this._stack = [t];
|
|
103
113
|
}
|
|
104
114
|
get current() {
|
|
105
115
|
return this._stack[this._stack.length - 1];
|
|
@@ -128,16 +138,19 @@ class B {
|
|
|
128
138
|
for (const t of this._listeners) t(this._stack);
|
|
129
139
|
}
|
|
130
140
|
}
|
|
131
|
-
const
|
|
141
|
+
const F = [
|
|
132
142
|
{ depth: 0, minZoom: 0, maxZoom: 0.3 },
|
|
133
143
|
{ depth: 1, minZoom: 0.3, maxZoom: 0.6 },
|
|
134
144
|
{ depth: 2, minZoom: 0.6, maxZoom: 1.2 },
|
|
135
145
|
{ depth: 3, minZoom: 1.2, maxZoom: 2.5 },
|
|
136
146
|
{ depth: 4, minZoom: 2.5, maxZoom: 1 / 0 }
|
|
137
147
|
];
|
|
138
|
-
class
|
|
148
|
+
class W {
|
|
139
149
|
constructor(t) {
|
|
140
|
-
this
|
|
150
|
+
r(this, "_thresholds");
|
|
151
|
+
r(this, "_zoom", 1);
|
|
152
|
+
r(this, "_listeners", /* @__PURE__ */ new Set());
|
|
153
|
+
this._thresholds = t ?? F;
|
|
141
154
|
}
|
|
142
155
|
get zoom() {
|
|
143
156
|
return this._zoom;
|
|
@@ -155,49 +168,49 @@ class L {
|
|
|
155
168
|
for (const t of this._listeners) t(this._zoom);
|
|
156
169
|
}
|
|
157
170
|
}
|
|
158
|
-
class
|
|
171
|
+
class H {
|
|
159
172
|
constructor() {
|
|
160
|
-
this
|
|
173
|
+
r(this, "_simulation", null);
|
|
161
174
|
}
|
|
162
175
|
run(t, e, s) {
|
|
163
176
|
this.stop();
|
|
164
|
-
const { width: i, height: o, chargeStrength: a = -200, collisionPadding:
|
|
165
|
-
var
|
|
177
|
+
const { width: i, height: o, chargeStrength: a = -200, collisionPadding: h = 16, alphaDecay: n = 0.02 } = s, d = t.map((l) => {
|
|
178
|
+
var _, p;
|
|
166
179
|
return {
|
|
167
|
-
id:
|
|
168
|
-
depth:
|
|
180
|
+
id: l.id,
|
|
181
|
+
depth: l.depth,
|
|
169
182
|
// Seed from manual position if provided, else random near world origin.
|
|
170
183
|
// World (0,0) maps to canvas center given the initial pan of (width/2, height/2).
|
|
171
|
-
x: ((
|
|
172
|
-
y: ((
|
|
184
|
+
x: ((_ = l.position) == null ? void 0 : _.x) ?? (Math.random() - 0.5) * 100,
|
|
185
|
+
y: ((p = l.position) == null ? void 0 : p.y) ?? (Math.random() - 0.5) * 100
|
|
173
186
|
};
|
|
174
|
-
}),
|
|
175
|
-
for (const
|
|
176
|
-
if (
|
|
177
|
-
const
|
|
178
|
-
|
|
187
|
+
}), c = new Map(d.map((l) => [l.id, l])), b = [];
|
|
188
|
+
for (const l of t)
|
|
189
|
+
if (l.parentId) {
|
|
190
|
+
const _ = c.get(l.parentId), p = c.get(l.id);
|
|
191
|
+
_ && p && b.push({ id: `${l.parentId}->${l.id}`, source: _, target: p });
|
|
179
192
|
}
|
|
180
|
-
for (const
|
|
181
|
-
const
|
|
182
|
-
|
|
193
|
+
for (const l of e) {
|
|
194
|
+
const _ = c.get(l.source), p = c.get(l.target);
|
|
195
|
+
_ && p && b.push({ id: l.id, source: _, target: p });
|
|
183
196
|
}
|
|
184
|
-
const
|
|
185
|
-
var
|
|
186
|
-
(
|
|
197
|
+
const f = (l) => {
|
|
198
|
+
var _;
|
|
199
|
+
(_ = s.onTick) == null || _.call(s, l);
|
|
187
200
|
};
|
|
188
|
-
this._simulation =
|
|
201
|
+
this._simulation = Z(d).alphaDecay(n).force(
|
|
189
202
|
"link",
|
|
190
|
-
|
|
191
|
-
const
|
|
192
|
-
return 80 + Math.abs(
|
|
203
|
+
D(b).id((l) => l.id).distance((l) => {
|
|
204
|
+
const _ = l.source, p = l.target;
|
|
205
|
+
return 80 + Math.abs(_.depth - p.depth) * 20;
|
|
193
206
|
}).strength(0.4)
|
|
194
|
-
).force("charge",
|
|
195
|
-
const
|
|
196
|
-
|
|
207
|
+
).force("charge", B().strength(a)).force("center", R(0, 0)).force("collide", L().radius(h)).on("tick", () => {
|
|
208
|
+
const l = this._collectPositions(d);
|
|
209
|
+
f(l);
|
|
197
210
|
}).on("end", () => {
|
|
198
|
-
var
|
|
199
|
-
const
|
|
200
|
-
(
|
|
211
|
+
var _;
|
|
212
|
+
const l = this._collectPositions(d);
|
|
213
|
+
(_ = s.onEnd) == null || _.call(s, l);
|
|
201
214
|
});
|
|
202
215
|
}
|
|
203
216
|
stop() {
|
|
@@ -215,7 +228,7 @@ class F {
|
|
|
215
228
|
return e;
|
|
216
229
|
}
|
|
217
230
|
}
|
|
218
|
-
const
|
|
231
|
+
const y = {
|
|
219
232
|
background: "#050a1a",
|
|
220
233
|
node: {
|
|
221
234
|
color: "#4a9eff",
|
|
@@ -260,24 +273,24 @@ const b = {
|
|
|
260
273
|
}
|
|
261
274
|
}
|
|
262
275
|
};
|
|
263
|
-
function
|
|
276
|
+
function x(u) {
|
|
264
277
|
var t, e, s, i, o;
|
|
265
|
-
return
|
|
266
|
-
background:
|
|
267
|
-
node: { ...
|
|
268
|
-
edge: { ...
|
|
278
|
+
return u ? {
|
|
279
|
+
background: u.background ?? y.background,
|
|
280
|
+
node: { ...y.node, ...u.node },
|
|
281
|
+
edge: { ...y.edge, ...u.edge },
|
|
269
282
|
states: {
|
|
270
|
-
default: { ...
|
|
271
|
-
active: { ...
|
|
272
|
-
locked: { ...
|
|
273
|
-
unlocked: { ...
|
|
274
|
-
highlighted: { ...
|
|
283
|
+
default: { ...y.states.default, ...(t = u.states) == null ? void 0 : t.default },
|
|
284
|
+
active: { ...y.states.active, ...(e = u.states) == null ? void 0 : e.active },
|
|
285
|
+
locked: { ...y.states.locked, ...(s = u.states) == null ? void 0 : s.locked },
|
|
286
|
+
unlocked: { ...y.states.unlocked, ...(i = u.states) == null ? void 0 : i.unlocked },
|
|
287
|
+
highlighted: { ...y.states.highlighted, ...(o = u.states) == null ? void 0 : o.highlighted }
|
|
275
288
|
}
|
|
276
|
-
} :
|
|
289
|
+
} : y;
|
|
277
290
|
}
|
|
278
|
-
const
|
|
279
|
-
function
|
|
280
|
-
return Array.from({ length:
|
|
291
|
+
const k = 40;
|
|
292
|
+
function U(u) {
|
|
293
|
+
return Array.from({ length: u }, () => ({
|
|
281
294
|
x: Math.random(),
|
|
282
295
|
y: Math.random(),
|
|
283
296
|
r: 0.4 + Math.random() * 1.2,
|
|
@@ -285,17 +298,28 @@ function O(m) {
|
|
|
285
298
|
speed: 0.4 + Math.random() * 1.2
|
|
286
299
|
}));
|
|
287
300
|
}
|
|
288
|
-
function
|
|
301
|
+
function N(u) {
|
|
289
302
|
let t = 0;
|
|
290
|
-
for (let e = 0; e <
|
|
303
|
+
for (let e = 0; e < u.length; e++) t = t * 31 + u.charCodeAt(e) >>> 0;
|
|
291
304
|
return t % 1e3 / 1e3 * Math.PI * 2;
|
|
292
305
|
}
|
|
293
|
-
class
|
|
306
|
+
class G {
|
|
294
307
|
constructor(t, e) {
|
|
295
|
-
this
|
|
308
|
+
r(this, "_canvas");
|
|
309
|
+
r(this, "_ctx");
|
|
310
|
+
r(this, "_theme");
|
|
311
|
+
r(this, "_dpr");
|
|
312
|
+
r(this, "_bgStars");
|
|
313
|
+
// Burst animation state (entry)
|
|
314
|
+
r(this, "_burst", null);
|
|
315
|
+
r(this, "_burstDuration", 450);
|
|
316
|
+
// Implode animation state (exit)
|
|
317
|
+
r(this, "_implode", null);
|
|
318
|
+
r(this, "_implodeDuration", 420);
|
|
319
|
+
this._canvas = t;
|
|
296
320
|
const s = t.getContext("2d");
|
|
297
321
|
if (!s) throw new Error("Could not get 2D context from canvas");
|
|
298
|
-
this._ctx = s, this._theme =
|
|
322
|
+
this._ctx = s, this._theme = x(e), this._dpr = window.devicePixelRatio ?? 1, this._bgStars = U(150), this._resize();
|
|
299
323
|
}
|
|
300
324
|
get canvas() {
|
|
301
325
|
return this._canvas;
|
|
@@ -312,7 +336,7 @@ class W {
|
|
|
312
336
|
return this._resolveStyle(t).color;
|
|
313
337
|
}
|
|
314
338
|
updateTheme(t) {
|
|
315
|
-
this._theme =
|
|
339
|
+
this._theme = x(t);
|
|
316
340
|
}
|
|
317
341
|
triggerBurst(t, e) {
|
|
318
342
|
this._burst = { startTime: performance.now(), center: t, color: e };
|
|
@@ -321,57 +345,57 @@ class W {
|
|
|
321
345
|
this._implode = { startTime: performance.now(), center: t };
|
|
322
346
|
}
|
|
323
347
|
render(t, e, s) {
|
|
324
|
-
var
|
|
325
|
-
const i = this._ctx, o = performance.now() / 1e3, { pan: a, zoom:
|
|
326
|
-
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,
|
|
327
|
-
for (const
|
|
328
|
-
const
|
|
329
|
-
!
|
|
348
|
+
var p;
|
|
349
|
+
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;
|
|
350
|
+
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);
|
|
351
|
+
for (const g of e) {
|
|
352
|
+
const v = d.get(g.source), m = d.get(g.target);
|
|
353
|
+
!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));
|
|
330
354
|
}
|
|
331
|
-
for (const
|
|
332
|
-
if (!
|
|
333
|
-
const
|
|
334
|
-
!
|
|
355
|
+
for (const g of t) {
|
|
356
|
+
if (!g.parentId) continue;
|
|
357
|
+
const v = d.get(g.parentId), m = d.get(g.id);
|
|
358
|
+
!v || !m || (i.save(), i.globalAlpha = n * 0.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());
|
|
335
359
|
}
|
|
336
|
-
const
|
|
337
|
-
for (const
|
|
338
|
-
const
|
|
339
|
-
if (!
|
|
340
|
-
const
|
|
341
|
-
this._drawNode(i,
|
|
360
|
+
const l = t.filter((g) => g.childIds && g.childIds.length > 0), _ = t.filter((g) => !g.childIds || g.childIds.length === 0);
|
|
361
|
+
for (const g of [...l, ..._]) {
|
|
362
|
+
const v = d.get(g.id);
|
|
363
|
+
if (!v) continue;
|
|
364
|
+
const m = c.get(g.id) ?? "idle", w = !!(g.childIds && g.childIds.length > 0);
|
|
365
|
+
this._drawNode(i, g, v, m, n, h, w, o);
|
|
342
366
|
}
|
|
343
367
|
i.restore(), i.restore(), this._renderBurst(), this._renderImplode();
|
|
344
368
|
}
|
|
345
369
|
// ─── Background stars ─────────────────────────────────────────────────────
|
|
346
370
|
_renderBgStars(t, e, s, i) {
|
|
347
371
|
for (const o of this._bgStars) {
|
|
348
|
-
const a = 0.5 + 0.5 * Math.sin(i * o.speed + o.phase),
|
|
349
|
-
t.beginPath(), t.arc(o.x * e, o.y * s, n, 0, Math.PI * 2), t.fillStyle = `rgba(255,255,255,${
|
|
372
|
+
const a = 0.5 + 0.5 * Math.sin(i * o.speed + o.phase), h = 0.08 + 0.25 * a, n = o.r * (0.7 + 0.3 * a);
|
|
373
|
+
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();
|
|
350
374
|
}
|
|
351
375
|
}
|
|
352
376
|
// ─── Node rendering ───────────────────────────────────────────────────────
|
|
353
|
-
_drawNode(t, e, s, i, o, a,
|
|
354
|
-
|
|
377
|
+
_drawNode(t, e, s, i, o, a, h, n) {
|
|
378
|
+
h ? this._drawBubble(t, e, s, i, o, a, n) : this._drawStar(t, e, s, i, o, a, n);
|
|
355
379
|
}
|
|
356
|
-
_drawBubble(t, e, s, i, o, a,
|
|
357
|
-
const n = this._resolveStyle(e),
|
|
380
|
+
_drawBubble(t, e, s, i, o, a, h) {
|
|
381
|
+
const n = this._resolveStyle(e), d = N(e.id), b = 1 + (i === "selected" ? 0.08 : 0.04) * Math.sin(h * 0.6 + d), f = k * b * (i === "hovered" ? 1.1 : 1), l = i === "selected";
|
|
358
382
|
t.save(), t.globalAlpha = n.opacity * o;
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
const
|
|
362
|
-
if (
|
|
363
|
-
const
|
|
364
|
-
t.strokeStyle = n.glowColor, t.lineWidth = 1.5 / a, t.globalAlpha = n.opacity * o * 0.85, t.setLineDash([10 / a, 7 / a]), t.lineDashOffset = -
|
|
383
|
+
const _ = l ? f * 2.8 : f * 2.2, p = l ? n.glowColor + "55" : n.glowColor + "30", g = t.createRadialGradient(s.x, s.y, f * 0.5, s.x, s.y, _);
|
|
384
|
+
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();
|
|
385
|
+
const v = t.createRadialGradient(s.x, s.y - f * 0.3, f * 0.1, s.x, s.y, f);
|
|
386
|
+
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 : 0.7), t.beginPath(), t.arc(s.x, s.y, f, 0, Math.PI * 2), t.stroke(), l) {
|
|
387
|
+
const m = h * 0.7 + d;
|
|
388
|
+
t.strokeStyle = n.glowColor, t.lineWidth = 1.5 / a, t.globalAlpha = n.opacity * o * 0.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([]);
|
|
365
389
|
}
|
|
366
390
|
if (e.childIds && e.childIds.length > 0) {
|
|
367
|
-
const
|
|
391
|
+
const m = Math.min(e.childIds.length, 8), w = f * 0.55, P = Math.max(1.5, f * 0.06), E = h * 0.15 + d;
|
|
368
392
|
t.globalAlpha = n.opacity * o * 0.6, t.fillStyle = n.color + "90";
|
|
369
|
-
for (let
|
|
370
|
-
const
|
|
393
|
+
for (let C = 0; C < m; C++) {
|
|
394
|
+
const I = E + C / m * Math.PI * 2;
|
|
371
395
|
t.beginPath(), t.arc(
|
|
372
|
-
s.x +
|
|
373
|
-
s.y +
|
|
374
|
-
|
|
396
|
+
s.x + w * Math.cos(I),
|
|
397
|
+
s.y + w * Math.sin(I),
|
|
398
|
+
P,
|
|
375
399
|
0,
|
|
376
400
|
Math.PI * 2
|
|
377
401
|
), t.fill();
|
|
@@ -379,32 +403,32 @@ class W {
|
|
|
379
403
|
}
|
|
380
404
|
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();
|
|
381
405
|
}
|
|
382
|
-
_drawStar(t, e, s, i, o, a,
|
|
383
|
-
const n = this._resolveStyle(e),
|
|
384
|
-
if (t.save(), t.globalAlpha = n.opacity * o,
|
|
385
|
-
for (let
|
|
386
|
-
const
|
|
387
|
-
t.strokeStyle = n.glowColor, t.lineWidth = 1.5 / a * (1 -
|
|
406
|
+
_drawStar(t, e, s, i, o, a, h) {
|
|
407
|
+
const n = this._resolveStyle(e), d = N(e.id), c = i === "selected", f = 1 + (c ? 0.4 : 0.22) * Math.sin(h * 1.7 + d) + 0.08 * Math.sin(h * 3.1 + d * 1.3), l = n.glowRadius * f, _ = n.size * (i === "hovered" ? 1.4 : 1);
|
|
408
|
+
if (t.save(), t.globalAlpha = n.opacity * o, c) {
|
|
409
|
+
for (let g = 0; g < 2; g++) {
|
|
410
|
+
const v = g * 0.9, m = (h + v) % 1.8 / 1.8, w = (_ + l * 0.6) * (1 + m * 2.2), P = (1 - m) * 0.6;
|
|
411
|
+
t.strokeStyle = n.glowColor, t.lineWidth = 1.5 / a * (1 - m * 0.5), t.globalAlpha = P * n.opacity * o, t.beginPath(), t.arc(s.x, s.y, w, 0, Math.PI * 2), t.stroke();
|
|
388
412
|
}
|
|
389
413
|
t.globalAlpha = n.opacity * o;
|
|
390
414
|
}
|
|
391
|
-
if (
|
|
392
|
-
const
|
|
393
|
-
|
|
415
|
+
if (l > 0) {
|
|
416
|
+
const p = t.createRadialGradient(s.x, s.y, 0, s.x, s.y, l + _);
|
|
417
|
+
p.addColorStop(0, n.glowColor + (c ? "ff" : "cc")), p.addColorStop(0.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();
|
|
394
418
|
}
|
|
395
|
-
t.fillStyle =
|
|
419
|
+
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();
|
|
396
420
|
}
|
|
397
421
|
// ─── Edge particles ───────────────────────────────────────────────────────
|
|
398
422
|
_renderEdgeParticles(t, e, s, i, o, a) {
|
|
399
|
-
var
|
|
400
|
-
const
|
|
401
|
-
if (Math.sqrt(
|
|
402
|
-
const
|
|
403
|
-
for (let
|
|
404
|
-
const
|
|
405
|
-
t.save(), t.globalAlpha =
|
|
406
|
-
const
|
|
407
|
-
|
|
423
|
+
var _;
|
|
424
|
+
const h = s.x - e.x, n = s.y - e.y;
|
|
425
|
+
if (Math.sqrt(h * h + n * n) < 1) return;
|
|
426
|
+
const c = 3, b = 0.2, f = 2.5 / a, l = ((_ = i.style) == null ? void 0 : _.color) ?? this._theme.edge.color;
|
|
427
|
+
for (let p = 0; p < c; p++) {
|
|
428
|
+
const g = (o * b + p / c) % 1, v = e.x + h * g, m = e.y + n * g, w = Math.sin(g * Math.PI);
|
|
429
|
+
t.save(), t.globalAlpha = w * 0.85;
|
|
430
|
+
const P = t.createRadialGradient(v, m, 0, v, m, f * 2.5);
|
|
431
|
+
P.addColorStop(0, l + "ff"), P.addColorStop(0.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();
|
|
408
432
|
}
|
|
409
433
|
}
|
|
410
434
|
// ─── Burst animation ──────────────────────────────────────────────────────
|
|
@@ -415,8 +439,8 @@ class W {
|
|
|
415
439
|
this._burst = null;
|
|
416
440
|
return;
|
|
417
441
|
}
|
|
418
|
-
const i = 1 - Math.pow(1 - s, 3), o = Math.hypot(this._canvas.width, this._canvas.height), a = i * o,
|
|
419
|
-
t.save(), t.globalAlpha =
|
|
442
|
+
const i = 1 - Math.pow(1 - s, 3), o = Math.hypot(this._canvas.width, this._canvas.height), a = i * o, h = (1 - i) * 0.35;
|
|
443
|
+
t.save(), t.globalAlpha = h, t.fillStyle = this._burst.color, t.beginPath(), t.arc(
|
|
420
444
|
this._burst.center.x * this._dpr,
|
|
421
445
|
this._burst.center.y * this._dpr,
|
|
422
446
|
a,
|
|
@@ -432,8 +456,8 @@ class W {
|
|
|
432
456
|
this._implode = null;
|
|
433
457
|
return;
|
|
434
458
|
}
|
|
435
|
-
const i = s * s * s, o = Math.hypot(this._canvas.width, this._canvas.height), a = (1 - i) * o,
|
|
436
|
-
t.save(), t.globalAlpha =
|
|
459
|
+
const i = s * s * s, o = Math.hypot(this._canvas.width, this._canvas.height), a = (1 - i) * o, h = (1 - s) * 0.55, n = (1 - i) * 40 * this._dpr, d = this._implode.center.x * this._dpr, c = this._implode.center.y * this._dpr;
|
|
460
|
+
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();
|
|
437
461
|
}
|
|
438
462
|
// ─── Shapes ───────────────────────────────────────────────────────────────
|
|
439
463
|
_resolveStyle(t) {
|
|
@@ -459,16 +483,16 @@ class W {
|
|
|
459
483
|
_polygon(t, e, s, i, o) {
|
|
460
484
|
t.moveTo(e.x + s * Math.cos(o), e.y + s * Math.sin(o));
|
|
461
485
|
for (let a = 1; a < i; a++) {
|
|
462
|
-
const
|
|
463
|
-
t.lineTo(e.x + s * Math.cos(
|
|
486
|
+
const h = o + a * 2 * Math.PI / i;
|
|
487
|
+
t.lineTo(e.x + s * Math.cos(h), e.y + s * Math.sin(h));
|
|
464
488
|
}
|
|
465
489
|
t.closePath();
|
|
466
490
|
}
|
|
467
491
|
_star(t, e, s) {
|
|
468
492
|
const i = s * 0.4, o = 5;
|
|
469
493
|
for (let a = 0; a < o * 2; a++) {
|
|
470
|
-
const
|
|
471
|
-
a === 0 ? t.moveTo(
|
|
494
|
+
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);
|
|
495
|
+
a === 0 ? t.moveTo(d, c) : t.lineTo(d, c);
|
|
472
496
|
}
|
|
473
497
|
t.closePath();
|
|
474
498
|
}
|
|
@@ -483,9 +507,34 @@ class W {
|
|
|
483
507
|
dispose() {
|
|
484
508
|
}
|
|
485
509
|
}
|
|
486
|
-
class
|
|
510
|
+
class V {
|
|
487
511
|
constructor(t) {
|
|
488
|
-
|
|
512
|
+
r(this, "_canvas");
|
|
513
|
+
r(this, "_state");
|
|
514
|
+
r(this, "_isPanning", !1);
|
|
515
|
+
r(this, "_lastPan", { x: 0, y: 0 });
|
|
516
|
+
r(this, "_listeners", /* @__PURE__ */ new Set());
|
|
517
|
+
r(this, "_nodes", []);
|
|
518
|
+
r(this, "_positions", /* @__PURE__ */ new Map());
|
|
519
|
+
r(this, "_nodeSize", 8);
|
|
520
|
+
r(this, "_dpr");
|
|
521
|
+
// Smooth zoom (lerp)
|
|
522
|
+
r(this, "_targetZoom", 1);
|
|
523
|
+
r(this, "_zoomAnchor", null);
|
|
524
|
+
r(this, "_zoomLerpAlpha", 0.14);
|
|
525
|
+
// Timed zoom — takes priority over lerp when active
|
|
526
|
+
r(this, "_timedZoom", null);
|
|
527
|
+
// Multi-touch tracking
|
|
528
|
+
r(this, "_activePointers", /* @__PURE__ */ new Map());
|
|
529
|
+
r(this, "_lastPinchDist", null);
|
|
530
|
+
// Separate from _lastPan (which is updated on every move) — used for drag vs click detection
|
|
531
|
+
r(this, "_pointerDownPos", { x: 0, y: 0 });
|
|
532
|
+
r(this, "_onPointerDown");
|
|
533
|
+
r(this, "_onPointerMove");
|
|
534
|
+
r(this, "_onPointerUp");
|
|
535
|
+
r(this, "_onPointerCancel");
|
|
536
|
+
r(this, "_onWheel");
|
|
537
|
+
this._canvas = t, this._dpr = window.devicePixelRatio ?? 1, this._state = {
|
|
489
538
|
pan: { x: t.width / (2 * this._dpr), y: t.height / (2 * this._dpr) },
|
|
490
539
|
zoom: 1,
|
|
491
540
|
hoveredNodeId: null,
|
|
@@ -536,8 +585,8 @@ class H {
|
|
|
536
585
|
tick() {
|
|
537
586
|
const t = this._state.zoom;
|
|
538
587
|
if (this._timedZoom) {
|
|
539
|
-
const { startZoom: e, target: s, anchor: i, startTime: o, duration: a, easing:
|
|
540
|
-
this._state.zoom = e + (s - e) *
|
|
588
|
+
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;
|
|
589
|
+
this._state.zoom = e + (s - e) * d, n >= 1 ? (this._timedZoom = null, this._zoomAnchor = null) : this._zoomAnchor = i;
|
|
541
590
|
} else {
|
|
542
591
|
const e = this._targetZoom - this._state.zoom;
|
|
543
592
|
if (Math.abs(e) < 1e-4)
|
|
@@ -572,8 +621,8 @@ class H {
|
|
|
572
621
|
for (const i of this._nodes) {
|
|
573
622
|
const o = this._positions.get(i.id);
|
|
574
623
|
if (!o) continue;
|
|
575
|
-
const
|
|
576
|
-
|
|
624
|
+
const h = !!(i.childIds && i.childIds.length > 0) ? k * 1.3 : this._nodeSize * 4, n = t.x - o.x, d = t.y - o.y, c = Math.sqrt(n * n + d * d);
|
|
625
|
+
c < h && c < s && (s = c, e = i.id);
|
|
577
626
|
}
|
|
578
627
|
return e;
|
|
579
628
|
}
|
|
@@ -591,8 +640,8 @@ class H {
|
|
|
591
640
|
if (this._activePointers.set(t.pointerId, e), this._activePointers.size >= 2) {
|
|
592
641
|
const s = [...this._activePointers.values()], i = Math.hypot(s[1].x - s[0].x, s[1].y - s[0].y);
|
|
593
642
|
if (this._lastPinchDist !== null && i > 0) {
|
|
594
|
-
const o = i / this._lastPinchDist, a = (s[0].x + s[1].x) / 2,
|
|
595
|
-
this._targetZoom = Math.max(0.05, Math.min(20, this._targetZoom * o)), this._zoomAnchor = { x: a, y:
|
|
643
|
+
const o = i / this._lastPinchDist, a = (s[0].x + s[1].x) / 2, h = (s[0].y + s[1].y) / 2;
|
|
644
|
+
this._targetZoom = Math.max(0.05, Math.min(20, this._targetZoom * o)), this._zoomAnchor = { x: a, y: h }, this._emit({ type: "zoom:change", zoom: this._targetZoom });
|
|
596
645
|
}
|
|
597
646
|
this._lastPinchDist = i;
|
|
598
647
|
return;
|
|
@@ -616,7 +665,7 @@ class H {
|
|
|
616
665
|
this._isPanning = !1;
|
|
617
666
|
const o = Math.abs(e.x - i.x), a = Math.abs(e.y - i.y);
|
|
618
667
|
if (o < 4 && a < 4) {
|
|
619
|
-
const
|
|
668
|
+
const h = this.canvasToWorld(e.x, e.y), n = this._hitTest(h);
|
|
620
669
|
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" }));
|
|
621
670
|
}
|
|
622
671
|
}
|
|
@@ -630,19 +679,41 @@ class H {
|
|
|
630
679
|
this._targetZoom = Math.max(0.05, Math.min(20, this._targetZoom * s)), this._zoomAnchor = e, this._emit({ type: "zoom:change", zoom: this._targetZoom });
|
|
631
680
|
}
|
|
632
681
|
}
|
|
633
|
-
const
|
|
634
|
-
class
|
|
682
|
+
const $ = 2.5, q = 0.35, S = 700;
|
|
683
|
+
class j {
|
|
635
684
|
constructor(t) {
|
|
636
|
-
|
|
637
|
-
this
|
|
685
|
+
r(this, "_model");
|
|
686
|
+
r(this, "_nav");
|
|
687
|
+
r(this, "_lod");
|
|
688
|
+
r(this, "_layout");
|
|
689
|
+
r(this, "_renderer");
|
|
690
|
+
r(this, "_interaction");
|
|
691
|
+
r(this, "_positions", /* @__PURE__ */ new Map());
|
|
692
|
+
r(this, "_internalStates", /* @__PURE__ */ new Map());
|
|
693
|
+
r(this, "_eventHandlers");
|
|
694
|
+
r(this, "_rafId", null);
|
|
695
|
+
r(this, "_pendingEnter", null);
|
|
696
|
+
r(this, "_pendingExit", null);
|
|
697
|
+
r(this, "_unsubscribers", []);
|
|
698
|
+
r(this, "_resizeObserver");
|
|
699
|
+
// Cached visible node/edge lists — rebuilt on navigation and model changes
|
|
700
|
+
r(this, "_visibleNodes", []);
|
|
701
|
+
r(this, "_visibleEdges", []);
|
|
702
|
+
// Context fade-in
|
|
703
|
+
r(this, "_contextFadeAlpha", 1);
|
|
704
|
+
r(this, "_contextFadeStart", 0);
|
|
705
|
+
r(this, "_contextFadeDuration", 400);
|
|
706
|
+
// Navigation cooldown
|
|
707
|
+
r(this, "_navCooldownUntil", 0);
|
|
708
|
+
var h, n;
|
|
638
709
|
const { canvas: e, data: s, theme: i, lod: o, on: a = {} } = t;
|
|
639
|
-
if (this._eventHandlers = a, this._model = new
|
|
640
|
-
const
|
|
641
|
-
|
|
710
|
+
if (this._eventHandlers = a, this._model = new M(s), this._nav = new O({ nodeId: null, label: s.label }), this._lod = new W(o), this._layout = new H(), this._renderer = new G(e, i ?? s.theme), this._interaction = new V(e), t.initialContextNodeId) {
|
|
711
|
+
const d = this._model.getNode(t.initialContextNodeId);
|
|
712
|
+
d && this._isBubble(d) && this._nav.push({ nodeId: d.id, label: d.label });
|
|
642
713
|
}
|
|
643
714
|
this._rebuildVisibleCache(), this._wireEvents(), this._runLayout(), this._startRenderLoop(), this._resizeObserver = new ResizeObserver(() => {
|
|
644
715
|
this._renderer.resize(), this._runLayout();
|
|
645
|
-
}), this._resizeObserver.observe(e), (n = (
|
|
716
|
+
}), this._resizeObserver.observe(e), (n = (h = this._eventHandlers)["graph:ready"]) == null || n.call(h, s);
|
|
646
717
|
}
|
|
647
718
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
648
719
|
setNodeState(t, e) {
|
|
@@ -672,7 +743,7 @@ class q {
|
|
|
672
743
|
* Silently pops intermediate frames; plays one implode + fade transition.
|
|
673
744
|
*/
|
|
674
745
|
jumpToNavDepth(t) {
|
|
675
|
-
var a,
|
|
746
|
+
var a, h;
|
|
676
747
|
if (this._nav.stack.length <= t) return;
|
|
677
748
|
this._pendingEnter !== null && (clearTimeout(this._pendingEnter), this._pendingEnter = null), this._pendingExit !== null && (clearTimeout(this._pendingExit), this._pendingExit = null);
|
|
678
749
|
let e;
|
|
@@ -680,10 +751,10 @@ class q {
|
|
|
680
751
|
e = this._nav.pop();
|
|
681
752
|
if (!e) return;
|
|
682
753
|
const i = this._renderer.canvas.getBoundingClientRect(), o = { x: i.width / 2, y: i.height / 2 };
|
|
683
|
-
this._navCooldownUntil = Date.now() +
|
|
754
|
+
this._navCooldownUntil = Date.now() + S, this._rebuildVisibleCache(), this._renderer.triggerImplode(o), this._interaction.resetToCenter(1, o.x, o.y), this._lod.setZoom(1);
|
|
684
755
|
for (const n of this._visibleNodes)
|
|
685
756
|
this._positions.set(n.id, { x: (Math.random() - 0.5) * 80, y: (Math.random() - 0.5) * 80 });
|
|
686
|
-
this._contextFadeAlpha = 0, this._contextFadeStart = performance.now(), this._runLayout(), (
|
|
757
|
+
this._contextFadeAlpha = 0, this._contextFadeStart = performance.now(), this._runLayout(), (h = (a = this._eventHandlers)["context:exit"]) == null || h.call(a, e, this._nav.stack);
|
|
687
758
|
}
|
|
688
759
|
_exitWithAnimation() {
|
|
689
760
|
if (!this._nav.canGoBack) return;
|
|
@@ -697,11 +768,11 @@ class q {
|
|
|
697
768
|
const e = this._model.getNode(t);
|
|
698
769
|
if (!e || !this._isBubble(e)) return;
|
|
699
770
|
this._pendingEnter !== null && (clearTimeout(this._pendingEnter), this._pendingEnter = null);
|
|
700
|
-
const i = this._renderer.canvas.getBoundingClientRect(), o = { x: i.width / 2, y: i.height / 2 }, a = this._positions.get(t),
|
|
701
|
-
const { zoom:
|
|
702
|
-
return { x: a.x *
|
|
771
|
+
const i = this._renderer.canvas.getBoundingClientRect(), o = { x: i.width / 2, y: i.height / 2 }, a = this._positions.get(t), h = a ? (() => {
|
|
772
|
+
const { zoom: d, pan: c } = this._interaction.state;
|
|
773
|
+
return { x: a.x * d + c.x, y: a.y * d + c.y };
|
|
703
774
|
})() : void 0, n = 480;
|
|
704
|
-
this._interaction.startTimedZoom(2.5, n,
|
|
775
|
+
this._interaction.startTimedZoom(2.5, n, h), this._pendingEnter = setTimeout(() => {
|
|
705
776
|
this._pendingEnter = null, this._enterContext(e, o);
|
|
706
777
|
}, n);
|
|
707
778
|
}
|
|
@@ -732,11 +803,11 @@ class q {
|
|
|
732
803
|
}
|
|
733
804
|
_enterContext(t, e) {
|
|
734
805
|
var o, a;
|
|
735
|
-
this._nav.push({ nodeId: t.id, label: t.label }), this._navCooldownUntil = Date.now() +
|
|
806
|
+
this._nav.push({ nodeId: t.id, label: t.label }), this._navCooldownUntil = Date.now() + S, this._rebuildVisibleCache(), this._renderer.triggerBurst(e, this._renderer.resolveNodeColor(t));
|
|
736
807
|
const i = this._renderer.canvas.getBoundingClientRect();
|
|
737
808
|
this._interaction.resetToCenter(1, i.width / 2, i.height / 2), this._lod.setZoom(1);
|
|
738
|
-
for (const
|
|
739
|
-
this._positions.set(
|
|
809
|
+
for (const h of this._visibleNodes)
|
|
810
|
+
this._positions.set(h.id, {
|
|
740
811
|
x: (Math.random() - 0.5) * 80,
|
|
741
812
|
y: (Math.random() - 0.5) * 80
|
|
742
813
|
});
|
|
@@ -746,7 +817,7 @@ class q {
|
|
|
746
817
|
var i, o;
|
|
747
818
|
const t = this._nav.pop();
|
|
748
819
|
if (!t) return;
|
|
749
|
-
this._navCooldownUntil = Date.now() +
|
|
820
|
+
this._navCooldownUntil = Date.now() + S, this._rebuildVisibleCache();
|
|
750
821
|
const s = this._renderer.canvas.getBoundingClientRect();
|
|
751
822
|
this._interaction.resetToCenter(1, s.width / 2, s.height / 2), this._lod.setZoom(1);
|
|
752
823
|
for (const a of this._visibleNodes)
|
|
@@ -759,23 +830,23 @@ class q {
|
|
|
759
830
|
_checkNavigation() {
|
|
760
831
|
if (Date.now() < this._navCooldownUntil) return;
|
|
761
832
|
const t = this._interaction.targetZoom, e = this._interaction.zoomAnchor;
|
|
762
|
-
if (t >
|
|
833
|
+
if (t > $ && e) {
|
|
763
834
|
const { zoom: s, pan: i } = this._interaction.state, o = this._findBubbleAtCanvas(e, i, s);
|
|
764
835
|
if (o) {
|
|
765
836
|
this._enterContext(o, e);
|
|
766
837
|
return;
|
|
767
838
|
}
|
|
768
839
|
}
|
|
769
|
-
t <
|
|
840
|
+
t < q && this._nav.canGoBack && this._exitContext();
|
|
770
841
|
}
|
|
771
842
|
_findBubbleAtCanvas(t, e, s) {
|
|
772
843
|
const i = (t.x - e.x) / s, o = (t.y - e.y) / s;
|
|
773
844
|
for (const a of this._visibleNodes) {
|
|
774
845
|
if (!this._isBubble(a)) continue;
|
|
775
|
-
const
|
|
776
|
-
if (!
|
|
777
|
-
const n = i -
|
|
778
|
-
if (Math.sqrt(n * n +
|
|
846
|
+
const h = this._positions.get(a.id);
|
|
847
|
+
if (!h) continue;
|
|
848
|
+
const n = i - h.x, d = o - h.y;
|
|
849
|
+
if (Math.sqrt(n * n + d * d) <= k * 1.3)
|
|
779
850
|
return a;
|
|
780
851
|
}
|
|
781
852
|
return null;
|
|
@@ -788,40 +859,40 @@ class q {
|
|
|
788
859
|
})
|
|
789
860
|
), this._unsubscribers.push(
|
|
790
861
|
this._interaction.onChange((t) => {
|
|
791
|
-
var e, s, i, o, a,
|
|
862
|
+
var e, s, i, o, a, h, n, d;
|
|
792
863
|
switch (t.type) {
|
|
793
864
|
case "zoom:change":
|
|
794
865
|
case "pan:change":
|
|
795
866
|
break;
|
|
796
867
|
case "node:hover": {
|
|
797
868
|
this._internalStates.set(t.nodeId, "hovered");
|
|
798
|
-
const
|
|
799
|
-
|
|
869
|
+
const c = this._model.getNode(t.nodeId);
|
|
870
|
+
c && ((s = (e = this._eventHandlers)["node:hover"]) == null || s.call(e, c));
|
|
800
871
|
break;
|
|
801
872
|
}
|
|
802
873
|
case "node:blur": {
|
|
803
874
|
this._internalStates.get(t.nodeId) === "hovered" && this._internalStates.set(t.nodeId, "idle");
|
|
804
|
-
const
|
|
805
|
-
|
|
875
|
+
const b = this._model.getNode(t.nodeId);
|
|
876
|
+
b && ((o = (i = this._eventHandlers)["node:blur"]) == null || o.call(i, b));
|
|
806
877
|
break;
|
|
807
878
|
}
|
|
808
879
|
case "canvas:click": {
|
|
809
|
-
for (const [
|
|
810
|
-
|
|
811
|
-
(
|
|
880
|
+
for (const [c, b] of this._internalStates)
|
|
881
|
+
b === "selected" && this._internalStates.set(c, "idle");
|
|
882
|
+
(h = (a = this._eventHandlers)["canvas:click"]) == null || h.call(a);
|
|
812
883
|
break;
|
|
813
884
|
}
|
|
814
885
|
case "node:click": {
|
|
815
|
-
const
|
|
816
|
-
if (!
|
|
886
|
+
const c = this._model.getNode(t.nodeId);
|
|
887
|
+
if (!c) break;
|
|
817
888
|
if (this._internalStates.get(t.nodeId) === "selected")
|
|
818
889
|
this._internalStates.set(t.nodeId, "idle");
|
|
819
890
|
else {
|
|
820
|
-
for (const [
|
|
821
|
-
|
|
891
|
+
for (const [l, _] of this._internalStates)
|
|
892
|
+
_ === "selected" && this._internalStates.set(l, "idle");
|
|
822
893
|
this._internalStates.set(t.nodeId, "selected");
|
|
823
894
|
}
|
|
824
|
-
(
|
|
895
|
+
(d = (n = this._eventHandlers)["node:click"]) == null || d.call(n, c);
|
|
825
896
|
break;
|
|
826
897
|
}
|
|
827
898
|
}
|
|
@@ -868,15 +939,15 @@ class q {
|
|
|
868
939
|
}
|
|
869
940
|
}
|
|
870
941
|
export {
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
942
|
+
k as BUBBLE_WORLD_RADIUS,
|
|
943
|
+
G as CanvasRenderer,
|
|
944
|
+
H as ForceLayout,
|
|
945
|
+
V as InteractionController,
|
|
946
|
+
W as LodController,
|
|
947
|
+
O as NavigationController,
|
|
948
|
+
M as SkillGraphModel,
|
|
949
|
+
j as SkillTreeEngine,
|
|
950
|
+
y as defaultTheme,
|
|
951
|
+
x as mergeTheme
|
|
881
952
|
};
|
|
882
953
|
//# sourceMappingURL=index.js.map
|