@expertrees/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +428 -0
- package/dist/index.js +882 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { SimulationLinkDatum } from 'd3-force';
|
|
2
|
+
import { SimulationNodeDatum } from 'd3-force';
|
|
3
|
+
|
|
4
|
+
export declare const BUBBLE_WORLD_RADIUS = 40;
|
|
5
|
+
|
|
6
|
+
export declare class CanvasRenderer {
|
|
7
|
+
private _canvas;
|
|
8
|
+
private _ctx;
|
|
9
|
+
private _theme;
|
|
10
|
+
private _dpr;
|
|
11
|
+
private _bgStars;
|
|
12
|
+
private _burst;
|
|
13
|
+
private readonly _burstDuration;
|
|
14
|
+
private _implode;
|
|
15
|
+
private readonly _implodeDuration;
|
|
16
|
+
constructor(canvas: HTMLCanvasElement, theme?: ThemeInput);
|
|
17
|
+
get canvas(): HTMLCanvasElement;
|
|
18
|
+
get isBurstActive(): boolean;
|
|
19
|
+
/** Sync canvas physical pixel size to its CSS size. Call when the container resizes. */
|
|
20
|
+
resize(): void;
|
|
21
|
+
/** Returns the resolved fill color for a node (used for burst color matching). */
|
|
22
|
+
resolveNodeColor(node: SkillNode): string;
|
|
23
|
+
updateTheme(theme: ThemeInput): void;
|
|
24
|
+
triggerBurst(centerCanvas: Position, color: string): void;
|
|
25
|
+
triggerImplode(centerCanvas: Position): void;
|
|
26
|
+
render(nodes: SkillNode[], edges: SkillEdge[], state: RenderState): void;
|
|
27
|
+
private _renderBgStars;
|
|
28
|
+
private _drawNode;
|
|
29
|
+
private _drawBubble;
|
|
30
|
+
private _drawStar;
|
|
31
|
+
private _renderEdgeParticles;
|
|
32
|
+
private _renderBurst;
|
|
33
|
+
private _renderImplode;
|
|
34
|
+
private _resolveStyle;
|
|
35
|
+
private _drawShape;
|
|
36
|
+
private _polygon;
|
|
37
|
+
private _star;
|
|
38
|
+
private _drawEdge;
|
|
39
|
+
private _resize;
|
|
40
|
+
dispose(): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export declare const defaultTheme: Theme;
|
|
44
|
+
|
|
45
|
+
export declare interface EdgeStyle {
|
|
46
|
+
color: string;
|
|
47
|
+
width: number;
|
|
48
|
+
opacity: number;
|
|
49
|
+
dashPattern?: number[];
|
|
50
|
+
animated: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare type EventListener_2<T> = T extends void ? () => void : (payload: T) => void;
|
|
54
|
+
|
|
55
|
+
export declare interface Evidence {
|
|
56
|
+
id: string;
|
|
57
|
+
type: EvidenceType;
|
|
58
|
+
label: string;
|
|
59
|
+
url?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
date?: string;
|
|
62
|
+
tags?: string[];
|
|
63
|
+
thumbnail?: string;
|
|
64
|
+
meta?: Record<string, unknown>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export declare type EvidenceType = 'link' | 'text' | 'image' | 'video' | 'file';
|
|
68
|
+
|
|
69
|
+
export declare class ForceLayout {
|
|
70
|
+
private _simulation;
|
|
71
|
+
run(nodes: SkillNode[], edges: SkillEdge[], options: ForceLayoutOptions): void;
|
|
72
|
+
stop(): void;
|
|
73
|
+
reheat(): void;
|
|
74
|
+
private _collectPositions;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export declare interface ForceLayoutOptions {
|
|
78
|
+
width: number;
|
|
79
|
+
height: number;
|
|
80
|
+
/** Charge strength — negative = repulsion */
|
|
81
|
+
chargeStrength?: number;
|
|
82
|
+
/** Collision radius multiplier relative to node size */
|
|
83
|
+
collisionPadding?: number;
|
|
84
|
+
alphaDecay?: number;
|
|
85
|
+
onTick?: (positions: Map<string, Position>) => void;
|
|
86
|
+
onEnd?: (positions: Map<string, Position>) => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
declare type GraphEvents = {
|
|
90
|
+
nodeStateChanged: {
|
|
91
|
+
nodeId: string;
|
|
92
|
+
state: NodeState;
|
|
93
|
+
};
|
|
94
|
+
nodeAdded: {
|
|
95
|
+
node: SkillNode;
|
|
96
|
+
};
|
|
97
|
+
nodeRemoved: {
|
|
98
|
+
nodeId: string;
|
|
99
|
+
};
|
|
100
|
+
edgeAdded: {
|
|
101
|
+
edge: SkillEdge;
|
|
102
|
+
};
|
|
103
|
+
edgeRemoved: {
|
|
104
|
+
edgeId: string;
|
|
105
|
+
};
|
|
106
|
+
evidenceAdded: {
|
|
107
|
+
nodeId: string;
|
|
108
|
+
evidence: Evidence;
|
|
109
|
+
};
|
|
110
|
+
evidenceRemoved: {
|
|
111
|
+
nodeId: string;
|
|
112
|
+
evidenceId: string;
|
|
113
|
+
};
|
|
114
|
+
changed: void;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export declare class InteractionController {
|
|
118
|
+
private _canvas;
|
|
119
|
+
private _state;
|
|
120
|
+
private _isPanning;
|
|
121
|
+
private _lastPan;
|
|
122
|
+
private _listeners;
|
|
123
|
+
private _nodes;
|
|
124
|
+
private _positions;
|
|
125
|
+
private _nodeSize;
|
|
126
|
+
private _dpr;
|
|
127
|
+
private _targetZoom;
|
|
128
|
+
private _zoomAnchor;
|
|
129
|
+
private readonly _zoomLerpAlpha;
|
|
130
|
+
private _timedZoom;
|
|
131
|
+
private _activePointers;
|
|
132
|
+
private _lastPinchDist;
|
|
133
|
+
private _pointerDownPos;
|
|
134
|
+
private _onPointerDown;
|
|
135
|
+
private _onPointerMove;
|
|
136
|
+
private _onPointerUp;
|
|
137
|
+
private _onPointerCancel;
|
|
138
|
+
private _onWheel;
|
|
139
|
+
constructor(canvas: HTMLCanvasElement);
|
|
140
|
+
get state(): Readonly<InteractionState>;
|
|
141
|
+
get targetZoom(): number;
|
|
142
|
+
get zoomAnchor(): Position | null;
|
|
143
|
+
setTargetZoom(value: number, anchor?: Position): void;
|
|
144
|
+
/**
|
|
145
|
+
* Animate zoom to `target` over `duration` ms.
|
|
146
|
+
* `easing: 'in'` — cubic ease-in (slow start, accelerates toward target).
|
|
147
|
+
* `easing: 'out'` — cubic ease-out (fast start, decelerates toward target).
|
|
148
|
+
*/
|
|
149
|
+
startTimedZoom(target: number, duration: number, anchor?: Position, easing?: 'in' | 'out'): void;
|
|
150
|
+
/** Instantly snap zoom and pan to a new center — used on navigation transitions. */
|
|
151
|
+
resetToCenter(zoom: number, cx: number, cy: number): void;
|
|
152
|
+
updateNodes(nodes: SkillNode[], positions: Map<string, Position>, nodeSize: number): void;
|
|
153
|
+
onChange(listener: (event: InteractionEvent) => void): () => void;
|
|
154
|
+
/**
|
|
155
|
+
* Advance zoom animation one frame. Called every RAF frame by the engine.
|
|
156
|
+
* Returns true while the animation is still in progress.
|
|
157
|
+
*/
|
|
158
|
+
tick(): boolean;
|
|
159
|
+
dispose(): void;
|
|
160
|
+
canvasToWorld(canvasX: number, canvasY: number): Position;
|
|
161
|
+
private _getCanvasPos;
|
|
162
|
+
private _hitTest;
|
|
163
|
+
private _emit;
|
|
164
|
+
private _handlePointerDown;
|
|
165
|
+
private _handlePointerMove;
|
|
166
|
+
private _handlePointerUp;
|
|
167
|
+
private _handlePointerCancel;
|
|
168
|
+
private _handleWheel;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export declare type InteractionEvent = {
|
|
172
|
+
type: 'node:click';
|
|
173
|
+
nodeId: string;
|
|
174
|
+
} | {
|
|
175
|
+
type: 'node:hover';
|
|
176
|
+
nodeId: string;
|
|
177
|
+
} | {
|
|
178
|
+
type: 'node:blur';
|
|
179
|
+
nodeId: string;
|
|
180
|
+
} | {
|
|
181
|
+
type: 'canvas:click';
|
|
182
|
+
} | {
|
|
183
|
+
type: 'zoom:change';
|
|
184
|
+
zoom: number;
|
|
185
|
+
} | {
|
|
186
|
+
type: 'pan:change';
|
|
187
|
+
pan: Position;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export declare interface InteractionState {
|
|
191
|
+
pan: Position;
|
|
192
|
+
zoom: number;
|
|
193
|
+
hoveredNodeId: string | null;
|
|
194
|
+
selectedNodeId: string | null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export declare type InternalNodeState = 'idle' | 'hovered' | 'selected' | 'expanded';
|
|
198
|
+
|
|
199
|
+
export declare interface LayoutLink extends SimulationLinkDatum<LayoutNode> {
|
|
200
|
+
id: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export declare interface LayoutNode extends SimulationNodeDatum {
|
|
204
|
+
id: string;
|
|
205
|
+
depth: number;
|
|
206
|
+
x: number;
|
|
207
|
+
y: number;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export declare class LodController {
|
|
211
|
+
private _thresholds;
|
|
212
|
+
private _zoom;
|
|
213
|
+
private _listeners;
|
|
214
|
+
constructor(thresholds?: LodThreshold[]);
|
|
215
|
+
get zoom(): number;
|
|
216
|
+
get thresholds(): readonly LodThreshold[];
|
|
217
|
+
setZoom(zoom: number): void;
|
|
218
|
+
onChange(listener: (zoom: number) => void): () => void;
|
|
219
|
+
private _notify;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export declare interface LodThreshold {
|
|
223
|
+
depth: number;
|
|
224
|
+
minZoom: number;
|
|
225
|
+
maxZoom: number;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export declare function mergeTheme(overrides?: ThemeInput): Theme;
|
|
229
|
+
|
|
230
|
+
export declare class NavigationController {
|
|
231
|
+
private _stack;
|
|
232
|
+
private _listeners;
|
|
233
|
+
constructor(root?: NavigationFrame);
|
|
234
|
+
get current(): NavigationFrame;
|
|
235
|
+
get stack(): readonly NavigationFrame[];
|
|
236
|
+
get canGoBack(): boolean;
|
|
237
|
+
push(frame: NavigationFrame): void;
|
|
238
|
+
pop(): NavigationFrame | undefined;
|
|
239
|
+
reset(): void;
|
|
240
|
+
onChange(listener: (stack: readonly NavigationFrame[]) => void): () => void;
|
|
241
|
+
private _notify;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export declare interface NavigationFrame {
|
|
245
|
+
nodeId: string | null;
|
|
246
|
+
label: string;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export declare type NodeShape = 'circle' | 'star' | 'hexagon' | 'diamond';
|
|
250
|
+
|
|
251
|
+
export declare type NodeState = 'default' | 'active' | 'locked' | 'unlocked' | 'highlighted';
|
|
252
|
+
|
|
253
|
+
export declare interface NodeStyle {
|
|
254
|
+
color: string;
|
|
255
|
+
glowColor: string;
|
|
256
|
+
glowRadius: number;
|
|
257
|
+
size: number;
|
|
258
|
+
shape: NodeShape;
|
|
259
|
+
opacity: number;
|
|
260
|
+
labelColor: string;
|
|
261
|
+
labelSize: number;
|
|
262
|
+
labelFont: string;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export declare interface Position {
|
|
266
|
+
x: number;
|
|
267
|
+
y: number;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export declare interface RenderState {
|
|
271
|
+
positions: Map<string, Position>;
|
|
272
|
+
internalStates: Map<string, InternalNodeState>;
|
|
273
|
+
contextFadeAlpha: number;
|
|
274
|
+
pan: Position;
|
|
275
|
+
zoom: number;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export declare interface SkillEdge {
|
|
279
|
+
id: string;
|
|
280
|
+
source: string;
|
|
281
|
+
target: string;
|
|
282
|
+
directed: boolean;
|
|
283
|
+
label?: string;
|
|
284
|
+
style?: Partial<EdgeStyle>;
|
|
285
|
+
meta?: Record<string, unknown>;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export declare interface SkillGraph {
|
|
289
|
+
id: string;
|
|
290
|
+
label: string;
|
|
291
|
+
nodes: SkillNode[];
|
|
292
|
+
edges: SkillEdge[];
|
|
293
|
+
theme?: ThemeInput;
|
|
294
|
+
meta?: Record<string, unknown>;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export declare class SkillGraphModel {
|
|
298
|
+
private _graph;
|
|
299
|
+
private _meta;
|
|
300
|
+
private _label;
|
|
301
|
+
private _id;
|
|
302
|
+
private _listeners;
|
|
303
|
+
constructor(data: SkillGraph);
|
|
304
|
+
get id(): string;
|
|
305
|
+
get label(): string;
|
|
306
|
+
get meta(): Record<string, unknown> | undefined;
|
|
307
|
+
getNode(id: string): SkillNode | undefined;
|
|
308
|
+
getEdge(id: string): SkillEdge | undefined;
|
|
309
|
+
getNodes(): SkillNode[];
|
|
310
|
+
getEdges(): SkillEdge[];
|
|
311
|
+
getChildren(nodeId: string): SkillNode[];
|
|
312
|
+
getNodesAtDepth(depth: number): SkillNode[];
|
|
313
|
+
getNodesUpToDepth(depth: number): SkillNode[];
|
|
314
|
+
setNodeState(nodeId: string, state: NodeState): void;
|
|
315
|
+
addNode(node: SkillNode): void;
|
|
316
|
+
removeNode(nodeId: string): void;
|
|
317
|
+
addEdge(edge: SkillEdge): void;
|
|
318
|
+
removeEdge(edgeId: string): void;
|
|
319
|
+
addEvidence(nodeId: string, evidence: Evidence): void;
|
|
320
|
+
removeEvidence(nodeId: string, evidenceId: string): void;
|
|
321
|
+
toJSON(): SkillGraph;
|
|
322
|
+
static fromJSON(data: SkillGraph): SkillGraphModel;
|
|
323
|
+
on<K extends keyof GraphEvents>(event: K, listener: EventListener_2<GraphEvents[K]>): () => void;
|
|
324
|
+
private _emit;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export declare interface SkillNode {
|
|
328
|
+
id: string;
|
|
329
|
+
label: string;
|
|
330
|
+
description?: string;
|
|
331
|
+
depth: number;
|
|
332
|
+
parentId?: string;
|
|
333
|
+
childIds?: string[];
|
|
334
|
+
position?: Position;
|
|
335
|
+
state?: NodeState;
|
|
336
|
+
evidence?: Evidence[];
|
|
337
|
+
style?: Partial<NodeStyle>;
|
|
338
|
+
meta?: Record<string, unknown>;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
export declare class SkillTreeEngine {
|
|
342
|
+
private _model;
|
|
343
|
+
private _nav;
|
|
344
|
+
private _lod;
|
|
345
|
+
private _layout;
|
|
346
|
+
private _renderer;
|
|
347
|
+
private _interaction;
|
|
348
|
+
private _positions;
|
|
349
|
+
private _internalStates;
|
|
350
|
+
private _eventHandlers;
|
|
351
|
+
private _rafId;
|
|
352
|
+
private _pendingEnter;
|
|
353
|
+
private _pendingExit;
|
|
354
|
+
private _unsubscribers;
|
|
355
|
+
private _resizeObserver;
|
|
356
|
+
private _visibleNodes;
|
|
357
|
+
private _visibleEdges;
|
|
358
|
+
private _contextFadeAlpha;
|
|
359
|
+
private _contextFadeStart;
|
|
360
|
+
private readonly _contextFadeDuration;
|
|
361
|
+
private _navCooldownUntil;
|
|
362
|
+
constructor(options: SkillTreeEngineOptions);
|
|
363
|
+
setNodeState(nodeId: string, state: NodeState): void;
|
|
364
|
+
addEvidence(nodeId: string, evidence: Evidence): void;
|
|
365
|
+
removeEvidence(nodeId: string, evidenceId: string): void;
|
|
366
|
+
updateTheme(theme: ThemeInput): void;
|
|
367
|
+
zoomIn(): void;
|
|
368
|
+
zoomOut(): void;
|
|
369
|
+
goBack(): void;
|
|
370
|
+
/**
|
|
371
|
+
* Atomically jump to a specific nav stack depth, firing a single animation.
|
|
372
|
+
* `targetLength` is the desired `stack.length` after the jump.
|
|
373
|
+
* Silently pops intermediate frames; plays one implode + fade transition.
|
|
374
|
+
*/
|
|
375
|
+
jumpToNavDepth(targetLength: number): void;
|
|
376
|
+
private _exitWithAnimation;
|
|
377
|
+
enterContext(nodeId: string): void;
|
|
378
|
+
getGraph(): SkillGraph;
|
|
379
|
+
getNavigationStack(): readonly NavigationFrame[];
|
|
380
|
+
dispose(): void;
|
|
381
|
+
private _rebuildVisibleCache;
|
|
382
|
+
private _isBubble;
|
|
383
|
+
private _enterContext;
|
|
384
|
+
private _exitContext;
|
|
385
|
+
private _checkNavigation;
|
|
386
|
+
private _findBubbleAtCanvas;
|
|
387
|
+
private _wireEvents;
|
|
388
|
+
private _runLayout;
|
|
389
|
+
private _startRenderLoop;
|
|
390
|
+
private _draw;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export declare interface SkillTreeEngineOptions {
|
|
394
|
+
canvas: HTMLCanvasElement;
|
|
395
|
+
data: SkillGraph;
|
|
396
|
+
theme?: ThemeInput;
|
|
397
|
+
lod?: LodThreshold[];
|
|
398
|
+
on?: Partial<SkillTreeEvents>;
|
|
399
|
+
/** Silently start inside this bubble node's context — no burst or fade animation */
|
|
400
|
+
initialContextNodeId?: string;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export declare interface SkillTreeEvents {
|
|
404
|
+
'node:click': (node: SkillNode) => void;
|
|
405
|
+
'node:hover': (node: SkillNode) => void;
|
|
406
|
+
'node:blur': (node: SkillNode) => void;
|
|
407
|
+
'canvas:click': () => void;
|
|
408
|
+
'zoom:change': (zoom: number) => void;
|
|
409
|
+
'context:enter': (node: SkillNode, stack: readonly NavigationFrame[]) => void;
|
|
410
|
+
'context:exit': (frame: NavigationFrame, stack: readonly NavigationFrame[]) => void;
|
|
411
|
+
'graph:ready': (graph: SkillGraph) => void;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export declare interface Theme {
|
|
415
|
+
background: string;
|
|
416
|
+
node: NodeStyle;
|
|
417
|
+
edge: EdgeStyle;
|
|
418
|
+
states: Record<NodeState, Partial<NodeStyle>>;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export declare interface ThemeInput {
|
|
422
|
+
background?: string;
|
|
423
|
+
node?: Partial<NodeStyle>;
|
|
424
|
+
edge?: Partial<EdgeStyle>;
|
|
425
|
+
states?: Partial<Record<NodeState, Partial<NodeStyle>>>;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export { }
|