vizcraft 0.2.2 → 1.0.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.
@@ -0,0 +1,15 @@
1
+ import type { EdgeLabel, VizEdge } from './types';
2
+ import type { EdgePathResult } from './edgePaths';
3
+ /**
4
+ * Resolve the (x, y) position of an edge label given an EdgePathResult.
5
+ * Falls back to `mid` for unknown positions.
6
+ */
7
+ export declare function resolveEdgeLabelPosition(lbl: EdgeLabel, path: EdgePathResult): {
8
+ x: number;
9
+ y: number;
10
+ };
11
+ /**
12
+ * Collect all labels for an edge, preferring `labels[]` when present
13
+ * and falling back to the legacy `label` field.
14
+ */
15
+ export declare function collectEdgeLabels(edge: VizEdge): EdgeLabel[];
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Resolve the (x, y) position of an edge label given an EdgePathResult.
3
+ * Falls back to `mid` for unknown positions.
4
+ */
5
+ export function resolveEdgeLabelPosition(lbl, path) {
6
+ const base = lbl.position === 'start'
7
+ ? path.start
8
+ : lbl.position === 'end'
9
+ ? path.end
10
+ : path.mid;
11
+ return {
12
+ x: base.x + (lbl.dx || 0),
13
+ y: base.y + (lbl.dy || 0),
14
+ };
15
+ }
16
+ /**
17
+ * Collect all labels for an edge, preferring `labels[]` when present
18
+ * and falling back to the legacy `label` field.
19
+ */
20
+ export function collectEdgeLabels(edge) {
21
+ if (edge.labels && edge.labels.length > 0)
22
+ return edge.labels;
23
+ if (edge.label)
24
+ return [edge.label];
25
+ return [];
26
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Edge path computation helpers.
3
+ *
4
+ * Given start/end points, routing mode, and optional waypoints this module
5
+ * produces:
6
+ * 1. An SVG `d` attribute string (for `<path>` elements).
7
+ * 2. A midpoint along the path (for label positioning).
8
+ */
9
+ import type { Vec2, VizNode, VizEdge, EdgeRouting } from './types';
10
+ export interface EdgePathResult {
11
+ /** SVG path `d` attribute. */
12
+ d: string;
13
+ /** Approximate label position along the path (exact for straight/quadratic, approximated for spline and orthogonal-with-waypoints paths). */
14
+ mid: Vec2;
15
+ /** Position near the source end (~15% along the path). */
16
+ start: Vec2;
17
+ /** Position near the target end (~85% along the path). */
18
+ end: Vec2;
19
+ }
20
+ /**
21
+ * Compute anchor-resolved start/end points for an edge.
22
+ *
23
+ * When an edge specifies `fromPort` / `toPort`, the endpoint is resolved
24
+ * to the port's absolute position. Otherwise the legacy `anchor` mode
25
+ * (`'center'` | `'boundary'`) is used.
26
+ *
27
+ * This replicates the logic used internally by the core builder and
28
+ * runtime patcher so that external renderers (e.g. React) can
29
+ * resolve boundary anchors consistently.
30
+ */
31
+ export declare function computeEdgeEndpoints(start: VizNode, end: VizNode, edge: VizEdge): {
32
+ start: Vec2;
33
+ end: Vec2;
34
+ };
35
+ /**
36
+ * Compute the SVG path for an edge.
37
+ *
38
+ * @param start Start point (already anchor-resolved).
39
+ * @param end End point (already anchor-resolved).
40
+ * @param routing Routing algorithm (default `'straight'`).
41
+ * @param waypoints Optional intermediate points.
42
+ */
43
+ export declare function computeEdgePath(start: Vec2, end: Vec2, routing?: EdgeRouting, waypoints?: Vec2[]): EdgePathResult;
@@ -0,0 +1,253 @@
1
+ /**
2
+ * Edge path computation helpers.
3
+ *
4
+ * Given start/end points, routing mode, and optional waypoints this module
5
+ * produces:
6
+ * 1. An SVG `d` attribute string (for `<path>` elements).
7
+ * 2. A midpoint along the path (for label positioning).
8
+ */
9
+ import { computeNodeAnchor, effectivePos, resolvePortPosition } from './shapes';
10
+ /**
11
+ * Compute anchor-resolved start/end points for an edge.
12
+ *
13
+ * When an edge specifies `fromPort` / `toPort`, the endpoint is resolved
14
+ * to the port's absolute position. Otherwise the legacy `anchor` mode
15
+ * (`'center'` | `'boundary'`) is used.
16
+ *
17
+ * This replicates the logic used internally by the core builder and
18
+ * runtime patcher so that external renderers (e.g. React) can
19
+ * resolve boundary anchors consistently.
20
+ */
21
+ export function computeEdgeEndpoints(start, end, edge) {
22
+ const anchor = edge.anchor ?? 'boundary';
23
+ const startPos = effectivePos(start);
24
+ const endPos = effectivePos(end);
25
+ // Port-based resolution takes precedence over anchor mode.
26
+ const startAnchor = edge.fromPort
27
+ ? (resolvePortPosition(start, edge.fromPort) ??
28
+ computeNodeAnchor(start, endPos, anchor))
29
+ : computeNodeAnchor(start, endPos, anchor);
30
+ const endAnchor = edge.toPort
31
+ ? (resolvePortPosition(end, edge.toPort) ??
32
+ computeNodeAnchor(end, startPos, anchor))
33
+ : computeNodeAnchor(end, startPos, anchor);
34
+ return { start: startAnchor, end: endAnchor };
35
+ }
36
+ /**
37
+ * Compute the SVG path for an edge.
38
+ *
39
+ * @param start Start point (already anchor-resolved).
40
+ * @param end End point (already anchor-resolved).
41
+ * @param routing Routing algorithm (default `'straight'`).
42
+ * @param waypoints Optional intermediate points.
43
+ */
44
+ export function computeEdgePath(start, end, routing = 'straight', waypoints) {
45
+ switch (routing) {
46
+ case 'curved':
47
+ return curvedPath(start, end, waypoints);
48
+ case 'orthogonal':
49
+ return orthogonalPath(start, end, waypoints);
50
+ case 'straight':
51
+ default:
52
+ return straightPath(start, end, waypoints);
53
+ }
54
+ }
55
+ // ── Straight routing ────────────────────────────────────────────────────────
56
+ function straightPath(start, end, waypoints) {
57
+ const pts = [start, ...(waypoints ?? []), end];
58
+ const segments = pts.map((p, i) => i === 0 ? `M ${p.x} ${p.y}` : `L ${p.x} ${p.y}`);
59
+ const positions = polylineLabelPositions(pts);
60
+ return {
61
+ d: segments.join(' '),
62
+ ...positions,
63
+ };
64
+ }
65
+ // ── Curved routing ──────────────────────────────────────────────────────────
66
+ function curvedPath(start, end, waypoints) {
67
+ if (waypoints && waypoints.length > 0) {
68
+ // Use waypoints as control/through points – produce a smooth cubic path.
69
+ return curvedThroughPoints(start, end, waypoints);
70
+ }
71
+ // No waypoints: single quadratic bezier with auto-computed control point.
72
+ const cp = autoControlPoint(start, end);
73
+ const d = `M ${start.x} ${start.y} Q ${cp.x} ${cp.y} ${end.x} ${end.y}`;
74
+ const mid = quadraticAt(start, cp, end, 0.5);
75
+ const startPos = quadraticAt(start, cp, end, LABEL_FRACTION_START);
76
+ const endPos = quadraticAt(start, cp, end, LABEL_FRACTION_END);
77
+ return { d, mid, start: startPos, end: endPos };
78
+ }
79
+ /**
80
+ * Auto-compute a control point that creates a gentle arc.
81
+ * The control point is offset perpendicular to the line at the midpoint.
82
+ */
83
+ function autoControlPoint(start, end) {
84
+ const mx = (start.x + end.x) / 2;
85
+ const my = (start.y + end.y) / 2;
86
+ const dx = end.x - start.x;
87
+ const dy = end.y - start.y;
88
+ const len = Math.sqrt(dx * dx + dy * dy) || 1;
89
+ // Perpendicular offset: 20% of the line length
90
+ const offset = len * 0.2;
91
+ return {
92
+ x: mx + (-dy / len) * offset,
93
+ y: my + (dx / len) * offset,
94
+ };
95
+ }
96
+ /** Point on a quadratic bezier at parameter t (0–1). */
97
+ function quadraticAt(p0, cp, p1, t) {
98
+ const mt = 1 - t;
99
+ return {
100
+ x: mt * mt * p0.x + 2 * mt * t * cp.x + t * t * p1.x,
101
+ y: mt * mt * p0.y + 2 * mt * t * cp.y + t * t * p1.y,
102
+ };
103
+ }
104
+ /**
105
+ * Create a smooth cubic bezier path that goes through all waypoints.
106
+ *
107
+ * Strategy: use Catmull-Rom → cubic conversion for smooth interpolation
108
+ * through all points.
109
+ */
110
+ function curvedThroughPoints(start, end, waypoints) {
111
+ const allPts = [start, ...waypoints, end];
112
+ if (allPts.length === 2) {
113
+ // Degenerate — just a quadratic
114
+ const cp = autoControlPoint(start, end);
115
+ return {
116
+ d: `M ${start.x} ${start.y} Q ${cp.x} ${cp.y} ${end.x} ${end.y}`,
117
+ mid: quadraticAt(start, cp, end, 0.5),
118
+ start: quadraticAt(start, cp, end, LABEL_FRACTION_START),
119
+ end: quadraticAt(start, cp, end, LABEL_FRACTION_END),
120
+ };
121
+ }
122
+ // Convert Catmull-Rom through allPts to cubic bezier segments.
123
+ let d = `M ${allPts[0].x} ${allPts[0].y}`;
124
+ const tension = 0.3; // 0 = straight, 0.5 = Catmull-Rom
125
+ for (let i = 0; i < allPts.length - 1; i++) {
126
+ const p0 = allPts[Math.max(i - 1, 0)];
127
+ const p1 = allPts[i];
128
+ const p2 = allPts[i + 1];
129
+ const p3 = allPts[Math.min(i + 2, allPts.length - 1)];
130
+ const cp1x = p1.x + (p2.x - p0.x) * tension;
131
+ const cp1y = p1.y + (p2.y - p0.y) * tension;
132
+ const cp2x = p2.x - (p3.x - p1.x) * tension;
133
+ const cp2y = p2.y - (p3.y - p1.y) * tension;
134
+ d += ` C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${p2.x} ${p2.y}`;
135
+ }
136
+ // Approximate label positions using the through-points polyline
137
+ const positions = polylineLabelPositions(allPts);
138
+ return { d, ...positions };
139
+ }
140
+ // ── Orthogonal routing ──────────────────────────────────────────────────────
141
+ function orthogonalPath(start, end, waypoints) {
142
+ if (waypoints && waypoints.length > 0) {
143
+ // With waypoints, route through each using orthogonal segments.
144
+ return orthogonalThroughWaypoints(start, end, waypoints);
145
+ }
146
+ // Default: auto-compute an L-shaped or Z-shaped orthogonal route.
147
+ return autoOrthogonal(start, end);
148
+ }
149
+ /**
150
+ * Auto-orthogonal routing (no waypoints).
151
+ * Creates an elbow connector: H → V (or V → H depending on direction).
152
+ */
153
+ function autoOrthogonal(start, end) {
154
+ const dx = Math.abs(end.x - start.x);
155
+ const dy = Math.abs(end.y - start.y);
156
+ let d;
157
+ let mid;
158
+ let startPos;
159
+ let endPos;
160
+ if (dx >= dy) {
161
+ // Horizontal-first elbow: start → (midX, start.y) → (midX, end.y) → end
162
+ const midX = (start.x + end.x) / 2;
163
+ d = `M ${start.x} ${start.y} L ${midX} ${start.y} L ${midX} ${end.y} L ${end.x} ${end.y}`;
164
+ const pts = [
165
+ start,
166
+ { x: midX, y: start.y },
167
+ { x: midX, y: end.y },
168
+ end,
169
+ ];
170
+ mid = polylinePointAt(pts, 0.5);
171
+ startPos = polylinePointAt(pts, LABEL_FRACTION_START);
172
+ endPos = polylinePointAt(pts, LABEL_FRACTION_END);
173
+ }
174
+ else {
175
+ // Vertical-first elbow: start → (start.x, midY) → (end.x, midY) → end
176
+ const midY = (start.y + end.y) / 2;
177
+ d = `M ${start.x} ${start.y} L ${start.x} ${midY} L ${end.x} ${midY} L ${end.x} ${end.y}`;
178
+ const pts = [
179
+ start,
180
+ { x: start.x, y: midY },
181
+ { x: end.x, y: midY },
182
+ end,
183
+ ];
184
+ mid = polylinePointAt(pts, 0.5);
185
+ startPos = polylinePointAt(pts, LABEL_FRACTION_START);
186
+ endPos = polylinePointAt(pts, LABEL_FRACTION_END);
187
+ }
188
+ return { d, mid, start: startPos, end: endPos };
189
+ }
190
+ /**
191
+ * Orthogonal routing through user-specified waypoints.
192
+ * Each waypoint pair is connected by an elbow (H-V or V-H).
193
+ */
194
+ function orthogonalThroughWaypoints(start, end, waypoints) {
195
+ const allPts = [start, ...waypoints, end];
196
+ let d = `M ${allPts[0].x} ${allPts[0].y}`;
197
+ // Build the actual rendered points (including elbow intermediaries)
198
+ const renderedPts = [allPts[0]];
199
+ for (let i = 1; i < allPts.length; i++) {
200
+ const prev = allPts[i - 1];
201
+ const cur = allPts[i];
202
+ // Elbow: go horizontal first, then vertical
203
+ d += ` L ${cur.x} ${prev.y} L ${cur.x} ${cur.y}`;
204
+ renderedPts.push({ x: cur.x, y: prev.y });
205
+ renderedPts.push(cur);
206
+ }
207
+ const positions = polylineLabelPositions(renderedPts);
208
+ return { d, ...positions };
209
+ }
210
+ // ── Shared helpers ──────────────────────────────────────────────────────────
211
+ /** Fraction along the path for each label position. */
212
+ const LABEL_FRACTION_START = 0.15;
213
+ const LABEL_FRACTION_END = 0.85;
214
+ /**
215
+ * Point along a polyline at a given fraction (0–1) of total arc length.
216
+ * Fraction 0 = first point, 1 = last point.
217
+ */
218
+ function polylinePointAt(pts, fraction) {
219
+ if (pts.length === 0)
220
+ return { x: 0, y: 0 };
221
+ if (pts.length === 1)
222
+ return pts[0];
223
+ let totalLen = 0;
224
+ for (let i = 1; i < pts.length; i++) {
225
+ const dx = pts[i].x - pts[i - 1].x;
226
+ const dy = pts[i].y - pts[i - 1].y;
227
+ totalLen += Math.sqrt(dx * dx + dy * dy);
228
+ }
229
+ const target = totalLen * fraction;
230
+ let accumulated = 0;
231
+ for (let i = 1; i < pts.length; i++) {
232
+ const dx = pts[i].x - pts[i - 1].x;
233
+ const dy = pts[i].y - pts[i - 1].y;
234
+ const segLen = Math.sqrt(dx * dx + dy * dy);
235
+ if (accumulated + segLen >= target) {
236
+ const t = segLen === 0 ? 0 : (target - accumulated) / segLen;
237
+ return {
238
+ x: pts[i - 1].x + dx * t,
239
+ y: pts[i - 1].y + dy * t,
240
+ };
241
+ }
242
+ accumulated += segLen;
243
+ }
244
+ return pts[pts.length - 1];
245
+ }
246
+ /** Shorthand: compute start, mid, end label positions for a polyline. */
247
+ function polylineLabelPositions(pts) {
248
+ return {
249
+ start: polylinePointAt(pts, LABEL_FRACTION_START),
250
+ mid: polylinePointAt(pts, 0.5),
251
+ end: polylinePointAt(pts, LABEL_FRACTION_END),
252
+ };
253
+ }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,10 @@ export * from './builder';
3
3
  export * from './styles';
4
4
  export * from './animations';
5
5
  export * from './overlays';
6
+ export * from './overlayBuilder';
7
+ export * from './edgePaths';
8
+ export * from './edgeLabels';
9
+ export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, } from './shapes';
6
10
  export * from './anim/spec';
7
11
  export * from './anim/animationBuilder';
8
12
  export * from './anim/playback';
package/dist/index.js CHANGED
@@ -3,6 +3,10 @@ export * from './builder';
3
3
  export * from './styles';
4
4
  export * from './animations';
5
5
  export * from './overlays';
6
+ export * from './overlayBuilder';
7
+ export * from './edgePaths';
8
+ export * from './edgeLabels';
9
+ export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, } from './shapes';
6
10
  export * from './anim/spec';
7
11
  export * from './anim/animationBuilder';
8
12
  export * from './anim/playback';
@@ -0,0 +1,50 @@
1
+ import type { OverlayId, OverlayParams, VizOverlaySpec } from './types';
2
+ import type { CircleOverlayParams, GroupOverlayParams, RectOverlayParams, TextOverlayParams } from './overlays';
3
+ export type OverlayAddOptions = {
4
+ /** Optional stable key used to uniquely identify an overlay instance. */
5
+ key?: string;
6
+ /** Optional class applied by the overlay renderer. */
7
+ className?: string;
8
+ };
9
+ /**
10
+ * Fluent overlay authoring.
11
+ *
12
+ * - Produces portable `VizOverlaySpec[]` data (rendering remains registry-driven).
13
+ * - Typed by `OverlayKindRegistry` when available, with a back-compat escape hatch.
14
+ */
15
+ export declare class OverlayBuilder {
16
+ private readonly specs;
17
+ private readonly keyCounters;
18
+ /**
19
+ * Add an overlay spec.
20
+ *
21
+ * Overload 1: typed overlay ids via `OverlayKindRegistry`.
22
+ */
23
+ add<K extends OverlayId>(id: K, params: OverlayParams<K>, options?: OverlayAddOptions): this;
24
+ /**
25
+ * Add an overlay spec.
26
+ *
27
+ * Overload 2: back-compat escape hatch for arbitrary ids.
28
+ */
29
+ add(id: string, params: any, options?: OverlayAddOptions): this;
30
+ /** Remove overlays by key, or (if unkeyed) by id. */
31
+ remove(keyOrId: string): this;
32
+ /** Remove all overlays. */
33
+ clear(): this;
34
+ build(): VizOverlaySpec[];
35
+ /** Add a generic rectangle overlay (built-in, no custom registry needed). */
36
+ rect(params: RectOverlayParams, options?: OverlayAddOptions): this;
37
+ /** Add a generic circle overlay (built-in, no custom registry needed). */
38
+ circle(params: CircleOverlayParams, options?: OverlayAddOptions): this;
39
+ /** Add a generic text overlay (built-in, no custom registry needed). */
40
+ text(params: TextOverlayParams, options?: OverlayAddOptions): this;
41
+ /**
42
+ * Add a composite group overlay (built-in).
43
+ *
44
+ * Children are authored via the callback and rendered inside a single SVG <g>.
45
+ * You can animate the group by targeting its key and tweening `x`, `y`, `scale`, `rotation`, `opacity`.
46
+ */
47
+ group(params: Omit<GroupOverlayParams, 'children'>, buildChildren: (overlay: OverlayBuilder) => unknown, options?: OverlayAddOptions): this;
48
+ }
49
+ /** Convenience helper for one-off overlay list compilation. */
50
+ export declare function buildOverlaySpecs(cb: (overlay: OverlayBuilder) => unknown): VizOverlaySpec[];
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Fluent overlay authoring.
3
+ *
4
+ * - Produces portable `VizOverlaySpec[]` data (rendering remains registry-driven).
5
+ * - Typed by `OverlayKindRegistry` when available, with a back-compat escape hatch.
6
+ */
7
+ export class OverlayBuilder {
8
+ specs = [];
9
+ keyCounters = new Map();
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ add(id, params, options) {
12
+ const className = options?.className;
13
+ // If a user adds multiple overlays of the same id without keys, they will
14
+ // collide at reconcile time (since the DOM layer uses `key || id`).
15
+ // We auto-generate a stable-ish key in that case.
16
+ let key = options?.key;
17
+ if (!key) {
18
+ const hasUnkeyedSameId = this.specs.some((s) => s.id === id && (s.key === undefined || s.key === ''));
19
+ if (hasUnkeyedSameId) {
20
+ const next = (this.keyCounters.get(id) ?? 0) + 1;
21
+ this.keyCounters.set(id, next);
22
+ key = `${id}#${next}`;
23
+ }
24
+ }
25
+ this.specs.push({ id, params, key, className });
26
+ return this;
27
+ }
28
+ /** Remove overlays by key, or (if unkeyed) by id. */
29
+ remove(keyOrId) {
30
+ for (let i = this.specs.length - 1; i >= 0; i--) {
31
+ const s = this.specs[i];
32
+ if (!s)
33
+ continue;
34
+ const matchesKey = s.key === keyOrId;
35
+ const matchesUnkeyedId = !s.key && s.id === keyOrId;
36
+ if (matchesKey || matchesUnkeyedId)
37
+ this.specs.splice(i, 1);
38
+ }
39
+ return this;
40
+ }
41
+ /** Remove all overlays. */
42
+ clear() {
43
+ this.specs.length = 0;
44
+ this.keyCounters.clear();
45
+ return this;
46
+ }
47
+ build() {
48
+ return [...this.specs];
49
+ }
50
+ /** Add a generic rectangle overlay (built-in, no custom registry needed). */
51
+ rect(params, options) {
52
+ return this.add('rect', params, options);
53
+ }
54
+ /** Add a generic circle overlay (built-in, no custom registry needed). */
55
+ circle(params, options) {
56
+ return this.add('circle', params, options);
57
+ }
58
+ /** Add a generic text overlay (built-in, no custom registry needed). */
59
+ text(params, options) {
60
+ return this.add('text', params, options);
61
+ }
62
+ /**
63
+ * Add a composite group overlay (built-in).
64
+ *
65
+ * Children are authored via the callback and rendered inside a single SVG <g>.
66
+ * You can animate the group by targeting its key and tweening `x`, `y`, `scale`, `rotation`, `opacity`.
67
+ */
68
+ group(params, buildChildren, options) {
69
+ const childOverlay = new OverlayBuilder();
70
+ buildChildren(childOverlay);
71
+ const children = childOverlay.build();
72
+ return this.add('group', { ...params, children }, options);
73
+ }
74
+ }
75
+ /** Convenience helper for one-off overlay list compilation. */
76
+ export function buildOverlaySpecs(cb) {
77
+ const overlay = new OverlayBuilder();
78
+ cb(overlay);
79
+ return overlay.build();
80
+ }
@@ -1,9 +1,115 @@
1
1
  import type { VizNode, VizEdge, VizOverlaySpec, VizScene } from './types';
2
+ export type SignalOverlayParams = {
3
+ from: string;
4
+ to: string;
5
+ progress: number;
6
+ magnitude?: number;
7
+ };
8
+ export type GridLabelsOverlayParams = {
9
+ colLabels?: Record<number, string>;
10
+ rowLabels?: Record<number, string>;
11
+ yOffset?: number;
12
+ xOffset?: number;
13
+ };
14
+ export interface DataPoint {
15
+ id: string;
16
+ currentNodeId: string;
17
+ [key: string]: any;
18
+ }
19
+ export type DataPointsOverlayParams = {
20
+ points: DataPoint[];
21
+ };
22
+ export type RectOverlayParams = {
23
+ x: number;
24
+ y: number;
25
+ w: number;
26
+ h: number;
27
+ rx?: number;
28
+ ry?: number;
29
+ opacity?: number;
30
+ /** SVG fill (defaults to a visible blue). Can be overridden by CSS via className. */
31
+ fill?: string;
32
+ /** SVG stroke (defaults to a visible blue). Can be overridden by CSS via className. */
33
+ stroke?: string;
34
+ /** SVG stroke-width (defaults to 3). Can be overridden by CSS via className. */
35
+ strokeWidth?: number;
36
+ };
37
+ export type CircleOverlayParams = {
38
+ x: number;
39
+ y: number;
40
+ r: number;
41
+ opacity?: number;
42
+ /** SVG fill (defaults to a visible blue). Can be overridden by CSS via className. */
43
+ fill?: string;
44
+ /** SVG stroke (defaults to a visible blue). Can be overridden by CSS via className. */
45
+ stroke?: string;
46
+ /** SVG stroke-width (defaults to 3). Can be overridden by CSS via className. */
47
+ strokeWidth?: number;
48
+ };
49
+ export type TextOverlayParams = {
50
+ x: number;
51
+ y: number;
52
+ text: string;
53
+ opacity?: number;
54
+ /** SVG fill color (defaults to #111). Can be overridden by CSS via className. */
55
+ fill?: string;
56
+ fontSize?: number;
57
+ fontWeight?: string | number;
58
+ textAnchor?: 'start' | 'middle' | 'end';
59
+ dominantBaseline?: string;
60
+ };
61
+ export type GroupOverlayParams = {
62
+ /**
63
+ * Translate (group-local origin).
64
+ *
65
+ * If `from`/`to` are provided, these act as an additional offset.
66
+ */
67
+ x?: number;
68
+ y?: number;
69
+ /**
70
+ * Optional node ids used to drive the group's position via `progress`.
71
+ *
72
+ * When set, the group will translate along the line from `from` to `to`.
73
+ */
74
+ from?: string;
75
+ to?: string;
76
+ /** Interpolation 0..1 used when `from`/`to` are set. */
77
+ progress?: number;
78
+ /**
79
+ * Optional "pulse" value 0..1.
80
+ *
81
+ * When provided, it scales the group slightly (in addition to `scale`).
82
+ */
83
+ magnitude?: number;
84
+ /** Scale around group origin. */
85
+ scale?: number;
86
+ /** Rotate (degrees) around group origin. */
87
+ rotation?: number;
88
+ /** Group opacity (multiplies with child opacity). */
89
+ opacity?: number;
90
+ /** Child overlays rendered inside this group. Coordinates are group-local. */
91
+ children: VizOverlaySpec[];
92
+ };
93
+ declare module './types' {
94
+ interface OverlayKindRegistry {
95
+ signal: SignalOverlayParams;
96
+ 'grid-labels': GridLabelsOverlayParams;
97
+ 'data-points': DataPointsOverlayParams;
98
+ /** Generic overlay primitives (no custom registry needed). */
99
+ rect: RectOverlayParams;
100
+ circle: CircleOverlayParams;
101
+ text: TextOverlayParams;
102
+ /** Overlay container that can hold child overlays and be animated as a unit. */
103
+ group: GroupOverlayParams;
104
+ }
105
+ }
2
106
  export interface CoreOverlayRenderContext<T = any> {
3
107
  spec: VizOverlaySpec<T>;
4
108
  nodesById: Map<string, VizNode>;
5
109
  edgesById: Map<string, VizEdge>;
6
110
  scene: VizScene;
111
+ /** Registry reference (useful for composite overlays like `group`). */
112
+ registry?: CoreOverlayRegistry;
7
113
  }
8
114
  export interface CoreOverlayRenderer<T = any> {
9
115
  render: (ctx: CoreOverlayRenderContext<T>) => string;
@@ -14,25 +120,11 @@ export declare class CoreOverlayRegistry {
14
120
  register(id: string, renderer: CoreOverlayRenderer): this;
15
121
  get(id: string): CoreOverlayRenderer<any> | undefined;
16
122
  }
17
- export declare const coreSignalOverlay: CoreOverlayRenderer<{
18
- from: string;
19
- to: string;
20
- progress: number;
21
- magnitude?: number;
22
- }>;
23
- export declare const coreGridLabelsOverlay: CoreOverlayRenderer<{
24
- colLabels?: Record<number, string>;
25
- rowLabels?: Record<number, string>;
26
- yOffset?: number;
27
- xOffset?: number;
28
- }>;
29
- interface DataPoint {
30
- id: string;
31
- currentNodeId: string;
32
- [key: string]: any;
33
- }
34
- export declare const coreDataPointOverlay: CoreOverlayRenderer<{
35
- points: DataPoint[];
36
- }>;
123
+ export declare const coreSignalOverlay: CoreOverlayRenderer<SignalOverlayParams>;
124
+ export declare const coreGridLabelsOverlay: CoreOverlayRenderer<GridLabelsOverlayParams>;
125
+ export declare const coreDataPointOverlay: CoreOverlayRenderer<DataPointsOverlayParams>;
126
+ export declare const coreRectOverlay: CoreOverlayRenderer<RectOverlayParams>;
127
+ export declare const coreCircleOverlay: CoreOverlayRenderer<CircleOverlayParams>;
128
+ export declare const coreTextOverlay: CoreOverlayRenderer<TextOverlayParams>;
129
+ export declare const coreGroupOverlay: CoreOverlayRenderer<GroupOverlayParams>;
37
130
  export declare const defaultCoreOverlayRegistry: CoreOverlayRegistry;
38
- export {};