vizcraft 1.7.0 → 1.7.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # vizcraft
2
2
 
3
+ ## 1.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#101](https://github.com/ChipiKaf/vizcraft/pull/101) [`c5084ad`](https://github.com/ChipiKaf/vizcraft/commit/c5084ad12a36b545df987cd425fd431cfb9903d4) Thanks [@ChipiKaf](https://github.com/ChipiKaf)! - Fix `resolveEdgeGeometry`: `startAnchor`/`endAnchor` now return the true boundary/port positions where the edge exits/enters each node, instead of the ~15%/~85% label positions. Added `startLabel` and `endLabel` fields as explicit aliases for the label positions. For self-loops, anchors correspond to the exit/entry points on the node boundary.
8
+
3
9
  ## 1.7.0
4
10
 
5
11
  ### Minor Changes
package/README.md CHANGED
@@ -17,6 +17,7 @@ VizCraft is designed to make creating beautiful, animated node-link diagrams and
17
17
 
18
18
  - **Fluent Builder API**: Define your visualization scene using a readable, chainable API.
19
19
  - **Grid System**: Built-in 2D grid system for easy, structured layout of nodes.
20
+ - **Auto-Layout Algorithms**: Built-in circular and grid layouts, custom sync/async algorithm support (e.g. ELK), and a `getNodeBoundingBox` utility for robust layout integration.
20
21
  - **Two Animation Systems**: Lightweight registry/CSS animations (e.g. edge `flow`) and data-only timeline animations (`AnimationSpec`).
21
22
  - **Framework Agnostic**: The core logic is pure TypeScript and can be used with any framework or Vanilla JS.
22
23
  - **Custom Overlays**: Create complex, custom UI elements that float on top of your visualization.
@@ -338,8 +339,10 @@ if (!geo) return; // edge not found or unresolvable
338
339
 
339
340
  overlayPath.setAttribute('d', geo.d); // SVG path
340
341
  positionToolbar(geo.mid); // midpoint
341
- drawHandle(geo.startAnchor); // source anchor
342
- drawHandle(geo.endAnchor); // target anchor
342
+ drawHandle(geo.startAnchor); // true boundary exit point
343
+ drawHandle(geo.endAnchor); // true boundary entry point
344
+ positionSourceLabel(geo.startLabel); // ~15% along path
345
+ positionTargetLabel(geo.endLabel); // ~85% along path
343
346
  geo.waypoints.forEach(drawDot); // waypoints
344
347
  if (geo.isSelfLoop) {
345
348
  /* ... */
package/dist/builder.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Vec2, VizScene, VizNode, VizEdge, NodeLabel, EdgeLabel, RichText, RichTextToken, AnimationConfig, OverlayId, OverlayParams, VizGridConfig, ContainerConfig, EdgeRouting, EdgeMarkerType, EdgePathResolver, NodeOptions, EdgeOptions, PanZoomOptions, PanZoomController, VizSceneMutator, VizPlugin, VizEventMap, LayoutAlgorithm, SvgExportOptions } from './types';
1
+ import type { Vec2, VizScene, VizNode, VizEdge, NodeLabel, EdgeLabel, RichText, RichTextToken, AnimationConfig, OverlayId, OverlayParams, VizGridConfig, ContainerConfig, EdgeRouting, EdgeMarkerType, EdgePathResolver, NodeOptions, EdgeOptions, PanZoomOptions, PanZoomController, VizSceneMutator, VizPlugin, VizEventMap, SyncLayoutAlgorithm, LayoutAlgorithm, SvgExportOptions } from './types';
2
2
  import { OverlayBuilder } from './overlays/builder';
3
3
  import type { AnimationSpec } from './animation/spec';
4
4
  import { type AnimationBuilder, type AnimatableProps, type TweenOptions } from './animation/builder';
@@ -12,12 +12,19 @@ export interface VizBuilder extends VizSceneMutator {
12
12
  */
13
13
  use<O>(plugin: VizPlugin<O>, options?: O): VizBuilder;
14
14
  /**
15
- * Applies a layout algorithm to the current nodes and edges.
16
- * @param algorithm The layout function to execute
15
+ * Applies a **synchronous** layout algorithm to the current nodes and edges.
16
+ * @param algorithm The layout function to execute (must return synchronously)
17
17
  * @param options Optional configuration for the layout algorithm
18
18
  * @returns The builder, for fluent chaining
19
19
  */
20
- layout<O>(algorithm: LayoutAlgorithm<O>, options?: O): VizBuilder;
20
+ layout<O>(algorithm: SyncLayoutAlgorithm<O>, options?: O): VizBuilder;
21
+ /**
22
+ * Applies a layout algorithm that may be asynchronous (e.g. ELK via web workers).
23
+ * @param algorithm The layout function to execute (may return a Promise)
24
+ * @param options Optional configuration for the layout algorithm
25
+ * @returns A Promise that resolves to the builder, for fluent chaining
26
+ */
27
+ layoutAsync<O>(algorithm: LayoutAlgorithm<O>, options?: O): Promise<VizBuilder>;
21
28
  /**
22
29
  * Listen for lifecycle events (e.g. 'build', 'mount').
23
30
  * @param event The event name
package/dist/builder.js CHANGED
@@ -17,6 +17,15 @@ import { getEffectiveNodeBounds } from './interaction/hitTest';
17
17
  import { defaultCoreIconRegistry } from './shapes/icons';
18
18
  import { NodeBuilderImpl, applyNodeOptions } from './nodes/builder';
19
19
  import { EdgeBuilderImpl, applyEdgeOptions } from './edges/builder';
20
+ /**
21
+ * Runtime check for Promise-like values without `as any` casting.
22
+ */
23
+ function isPromiseLike(value) {
24
+ return (typeof value === 'object' &&
25
+ value !== null &&
26
+ 'then' in value &&
27
+ typeof value.then === 'function');
28
+ }
20
29
  /**
21
30
  * Sanitise a CSS color value for use as a suffix in an SVG marker `id`.
22
31
  * Non-alphanumeric characters are replaced with underscores.
@@ -494,6 +503,26 @@ class VizBuilderImpl {
494
503
  edges: scene.edges,
495
504
  };
496
505
  const result = algorithm(graph, options);
506
+ // Guard: if the algorithm returned a Promise-like, throw a helpful error
507
+ if (isPromiseLike(result)) {
508
+ throw new Error('VizBuilder.layout: received a Promise from the layout algorithm. ' +
509
+ 'Use .layoutAsync() for async layout engines.');
510
+ }
511
+ this._applyLayoutResult(result);
512
+ return this;
513
+ }
514
+ async layoutAsync(algorithm, options) {
515
+ const scene = this.build();
516
+ const graph = {
517
+ nodes: scene.nodes,
518
+ edges: scene.edges,
519
+ };
520
+ const result = await algorithm(graph, options);
521
+ this._applyLayoutResult(result);
522
+ return this;
523
+ }
524
+ /** @internal Apply positions and waypoints from a layout result. */
525
+ _applyLayoutResult(result) {
497
526
  // Apply computed node positions
498
527
  for (const [id, pos] of Object.entries(result.nodes)) {
499
528
  this.updateNode(id, { pos });
@@ -506,7 +535,6 @@ class VizBuilderImpl {
506
535
  }
507
536
  }
508
537
  }
509
- return this;
510
538
  }
511
539
  setEdgePathResolver(resolver) {
512
540
  this._edgePathResolver = resolver;
@@ -43,11 +43,15 @@ export declare function computeEdgeEndpoints(start: VizNode | null, end: VizNode
43
43
  * @param waypoints Optional intermediate points.
44
44
  */
45
45
  export declare function computeEdgePath(start: Vec2, end: Vec2, routing?: EdgeRouting, waypoints?: Vec2[]): EdgePathResult;
46
+ /** Extends EdgePathResult with true boundary exit/entry points for self-loops. */
47
+ export interface SelfLoopResult extends EdgePathResult {
48
+ /** Boundary point where the loop departs the node. */
49
+ exitPoint: Vec2;
50
+ /** Boundary point where the loop returns to the node. */
51
+ entryPoint: Vec2;
52
+ }
46
53
  /**
47
- * Compute the SVG path and label positions for a self-loop edge.
48
- * A self-loop exits and enters the same node on the specified side.
49
- *
50
- * @param node The target node
51
- * @param edge The self-referencing VizEdge
54
+ * Compute the SVG path and positions for a self-loop edge.
55
+ * Exits and enters the same node on the specified side.
52
56
  */
53
- export declare function computeSelfLoop(node: VizNode, edge: VizEdge): EdgePathResult;
57
+ export declare function computeSelfLoop(node: VizNode, edge: VizEdge): SelfLoopResult;
@@ -297,11 +297,8 @@ function estimateNodeDims(shape) {
297
297
  return { w: 60, h: 60 };
298
298
  }
299
299
  /**
300
- * Compute the SVG path and label positions for a self-loop edge.
301
- * A self-loop exits and enters the same node on the specified side.
302
- *
303
- * @param node The target node
304
- * @param edge The self-referencing VizEdge
300
+ * Compute the SVG path and positions for a self-loop edge.
301
+ * Exits and enters the same node on the specified side.
305
302
  */
306
303
  export function computeSelfLoop(node, edge) {
307
304
  const c = effectivePos(node);
@@ -314,6 +311,7 @@ export function computeSelfLoop(node, edge) {
314
311
  const spread = Math.min(20, (side === 'top' || side === 'bottom' ? w : h) * 0.8);
315
312
  let d = '';
316
313
  let start, mid, end;
314
+ let exitPt, entryPt;
317
315
  switch (side) {
318
316
  case 'top': {
319
317
  const sx = c.x - spread / 2;
@@ -322,6 +320,8 @@ export function computeSelfLoop(node, edge) {
322
320
  const ey = c.y - h / 2;
323
321
  const cpY = sy - size * 1.5;
324
322
  d = `M ${sx} ${sy} C ${sx - spread / 2} ${cpY}, ${ex + spread / 2} ${cpY}, ${ex} ${ey}`;
323
+ exitPt = { x: sx, y: sy };
324
+ entryPt = { x: ex, y: ey };
325
325
  start = { x: sx, y: sy - size * 0.2 };
326
326
  mid = { x: c.x, y: sy - size };
327
327
  end = { x: ex, y: ey - size * 0.2 };
@@ -334,6 +334,8 @@ export function computeSelfLoop(node, edge) {
334
334
  const ey = c.y + h / 2;
335
335
  const cpY = sy + size * 1.5;
336
336
  d = `M ${sx} ${sy} C ${sx - spread / 2} ${cpY}, ${ex + spread / 2} ${cpY}, ${ex} ${ey}`;
337
+ exitPt = { x: sx, y: sy };
338
+ entryPt = { x: ex, y: ey };
337
339
  start = { x: sx, y: sy + size * 0.2 };
338
340
  mid = { x: c.x, y: sy + size };
339
341
  end = { x: ex, y: ey + size * 0.2 };
@@ -346,6 +348,8 @@ export function computeSelfLoop(node, edge) {
346
348
  const ey = c.y + spread / 2;
347
349
  const cpX = sx - size * 1.5;
348
350
  d = `M ${sx} ${sy} C ${cpX} ${sy - spread / 2}, ${cpX} ${ey + spread / 2}, ${ex} ${ey}`;
351
+ exitPt = { x: sx, y: sy };
352
+ entryPt = { x: ex, y: ey };
349
353
  start = { x: sx - size * 0.2, y: sy };
350
354
  mid = { x: sx - size, y: c.y };
351
355
  end = { x: ex - size * 0.2, y: ey };
@@ -358,11 +362,13 @@ export function computeSelfLoop(node, edge) {
358
362
  const ey = c.y + spread / 2;
359
363
  const cpX = sx + size * 1.5;
360
364
  d = `M ${sx} ${sy} C ${cpX} ${sy - spread / 2}, ${cpX} ${ey + spread / 2}, ${ex} ${ey}`;
365
+ exitPt = { x: sx, y: sy };
366
+ entryPt = { x: ex, y: ey };
361
367
  start = { x: sx + size * 0.2, y: sy };
362
368
  mid = { x: sx + size, y: c.y };
363
369
  end = { x: ex + size * 0.2, y: ey };
364
370
  break;
365
371
  }
366
372
  }
367
- return { d, start, mid, end };
373
+ return { d, start, mid, end, exitPoint: exitPt, entryPoint: entryPt };
368
374
  }
@@ -1,26 +1,19 @@
1
- /**
2
- * Convenience function that resolves the full rendered geometry for an edge
3
- * in a single call. Handles node lookup, dangling edges, self-loop detection,
4
- * port/angle/boundary anchors, waypoints, and routing.
5
- *
6
- * @module
7
- */
1
+ /** Resolves full rendered geometry for an edge in a single call. @module */
8
2
  import type { Vec2, VizScene, VizEdge, VizNode } from '../types';
9
3
  import type { EdgePathResult } from './paths';
10
- /**
11
- * Fully resolved edge geometry returned by {@link resolveEdgeGeometry}.
12
- *
13
- * Extends `EdgePathResult` with extra convenience fields so consumers
14
- * never need to orchestrate multiple helpers manually.
15
- */
4
+ /** Fully resolved edge geometry. Extends `EdgePathResult` with anchor and label positions. */
16
5
  export interface ResolvedEdgeGeometry extends EdgePathResult {
17
- /** Source anchor position (alias of `start` from EdgePathResult, ~15% along path). */
6
+ /** Boundary/port position where the edge exits the source node. */
18
7
  startAnchor: Vec2;
19
- /** Target anchor position (alias of `end` from EdgePathResult, ~85% along path). */
8
+ /** Boundary/port position where the edge enters the target node. */
20
9
  endAnchor: Vec2;
10
+ /** Label position ~15% along the path (alias of `start`). */
11
+ startLabel: Vec2;
12
+ /** Label position ~85% along the path (alias of `end`). */
13
+ endLabel: Vec2;
21
14
  /** Waypoints used for the path (empty array when none). */
22
15
  waypoints: Vec2[];
23
- /** Whether this edge is a self-loop (same source and target node). */
16
+ /** Whether this edge is a self-loop. */
24
17
  isSelfLoop: boolean;
25
18
  }
26
19
  /**
@@ -43,12 +36,8 @@ export interface ResolvedEdgeGeometry extends EdgePathResult {
43
36
  */
44
37
  export declare function resolveEdgeGeometry(scene: VizScene, edgeId: string): ResolvedEdgeGeometry | null;
45
38
  /**
46
- * Lower-level helper: resolve geometry from an edge + a node lookup map.
47
- *
48
- * Useful when the caller already has a `Map` built (e.g. inside a render loop
49
- * processing many edges).
50
- *
51
- * @internal exported for advanced consumers and testing — prefer
52
- * {@link resolveEdgeGeometry} for typical usage.
39
+ * Resolve geometry from an edge + a pre-built node map.
40
+ * Prefer {@link resolveEdgeGeometry} unless you already hold the map.
41
+ * @internal
53
42
  */
54
43
  export declare function resolveEdgeGeometryFromData(edge: VizEdge, nodesById: Map<string, VizNode>): ResolvedEdgeGeometry | null;
@@ -1,12 +1,5 @@
1
- /**
2
- * Convenience function that resolves the full rendered geometry for an edge
3
- * in a single call. Handles node lookup, dangling edges, self-loop detection,
4
- * port/angle/boundary anchors, waypoints, and routing.
5
- *
6
- * @module
7
- */
1
+ /** Resolves full rendered geometry for an edge in a single call. @module */
8
2
  import { computeEdgeEndpoints, computeEdgePath, computeSelfLoop, } from './paths';
9
- // ── Implementation ──────────────────────────────────────────────────────────
10
3
  /**
11
4
  * Resolve all rendered geometry for a single edge in one call.
12
5
  *
@@ -33,40 +26,41 @@ export function resolveEdgeGeometry(scene, edgeId) {
33
26
  return resolveEdgeGeometryFromData(edge, nodesById);
34
27
  }
35
28
  /**
36
- * Lower-level helper: resolve geometry from an edge + a node lookup map.
37
- *
38
- * Useful when the caller already has a `Map` built (e.g. inside a render loop
39
- * processing many edges).
40
- *
41
- * @internal exported for advanced consumers and testing — prefer
42
- * {@link resolveEdgeGeometry} for typical usage.
29
+ * Resolve geometry from an edge + a pre-built node map.
30
+ * Prefer {@link resolveEdgeGeometry} unless you already hold the map.
31
+ * @internal
43
32
  */
44
33
  export function resolveEdgeGeometryFromData(edge, nodesById) {
45
- // ── Node lookup (null-safe for dangling edges) ──────────────────────────
46
34
  const startNode = edge.from ? (nodesById.get(edge.from) ?? null) : null;
47
35
  const endNode = edge.to ? (nodesById.get(edge.to) ?? null) : null;
48
- // Bail out if a referenced node id doesn't exist.
49
36
  if (edge.from && !startNode)
50
37
  return null;
51
38
  if (edge.to && !endNode)
52
39
  return null;
53
- // Bail out if both endpoints are entirely unresolvable.
54
40
  if (!startNode && !edge.fromAt && !endNode && !edge.toAt)
55
41
  return null;
56
- // ── Self-loop detection ─────────────────────────────────────────────────
57
42
  const isSelfLoop = !!(startNode && endNode && startNode === endNode);
58
43
  let pathResult;
44
+ let anchorStart;
45
+ let anchorEnd;
59
46
  if (isSelfLoop) {
60
- pathResult = computeSelfLoop(startNode, edge);
47
+ const selfLoop = computeSelfLoop(startNode, edge);
48
+ pathResult = selfLoop;
49
+ anchorStart = selfLoop.exitPoint;
50
+ anchorEnd = selfLoop.entryPoint;
61
51
  }
62
52
  else {
63
53
  const endpoints = computeEdgeEndpoints(startNode, endNode, edge);
64
54
  pathResult = computeEdgePath(endpoints.start, endpoints.end, edge.routing, edge.waypoints);
55
+ anchorStart = endpoints.start;
56
+ anchorEnd = endpoints.end;
65
57
  }
66
58
  return {
67
59
  ...pathResult,
68
- startAnchor: pathResult.start,
69
- endAnchor: pathResult.end,
60
+ startAnchor: anchorStart,
61
+ endAnchor: anchorEnd,
62
+ startLabel: pathResult.start,
63
+ endLabel: pathResult.end,
70
64
  waypoints: edge.waypoints ?? [],
71
65
  isSelfLoop,
72
66
  };
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ export * from './edges/paths';
9
9
  export * from './edges/labels';
10
10
  export * from './edges/styles';
11
11
  export * from './edges/resolveEdgeGeometry';
12
- export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, } from './shapes/geometry';
12
+ export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, getNodeBoundingBox, } from './shapes/geometry';
13
13
  export * from './animation/spec';
14
14
  export * from './animation/builder';
15
15
  export * from './animation/playback';
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ export * from './edges/paths';
9
9
  export * from './edges/labels';
10
10
  export * from './edges/styles';
11
11
  export * from './edges/resolveEdgeGeometry';
12
- export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, } from './shapes/geometry';
12
+ export { getDefaultPorts, getNodePorts, findPort, resolvePortPosition, computeNodeAnchorAtAngle, getNodeBoundingBox, } from './shapes/geometry';
13
13
  export * from './animation/spec';
14
14
  export * from './animation/builder';
15
15
  export * from './animation/playback';
@@ -52,3 +52,15 @@ export declare function findPort(node: VizNode, portId: string): NodePort | unde
52
52
  * Returns `undefined` if the port id is not found.
53
53
  */
54
54
  export declare function resolvePortPosition(node: VizNode, portId: string): Vec2 | undefined;
55
+ /**
56
+ * Compute the tight axis-aligned bounding-box size of a node shape.
57
+ *
58
+ * Returns `{ width, height }` that enclose the rendered shape, accounting for
59
+ * orientation, direction, pointer height, and other shape-specific parameters.
60
+ * Intended as a convenience helper for layout algorithms and collision
61
+ * detection, to avoid duplicating per-shape switch logic.
62
+ */
63
+ export declare function getNodeBoundingBox(shape: NodeShape): {
64
+ width: number;
65
+ height: number;
66
+ };
@@ -1404,7 +1404,7 @@ export function resolvePortPosition(node, portId) {
1404
1404
  return { x: pos.x + port.offset.x, y: pos.y + port.offset.y };
1405
1405
  }
1406
1406
  // ── Helpers ─────────────────────────────────────────────────────────────────
1407
- /** Approximate half-width / half-height for shapes that have a bounding box. */
1407
+ /** Axis-aligned half-width / half-height for each node shape. */
1408
1408
  function shapeBoundingBox(shape) {
1409
1409
  switch (shape.kind) {
1410
1410
  case 'circle':
@@ -1415,16 +1415,32 @@ function shapeBoundingBox(shape) {
1415
1415
  return { hw: shape.w / 2, hh: shape.h / 2 };
1416
1416
  case 'ellipse':
1417
1417
  return { hw: shape.rx, hh: shape.ry };
1418
- case 'hexagon':
1419
- return { hw: shape.r, hh: shape.r };
1418
+ case 'hexagon': {
1419
+ const orientation = shape.orientation ?? 'pointy';
1420
+ // pointy-top: width = r·√3, height = 2r
1421
+ // flat-top: width = 2r, height = r·√3
1422
+ const SQRT3_2 = Math.sqrt(3) / 2;
1423
+ return orientation === 'pointy'
1424
+ ? { hw: shape.r * SQRT3_2, hh: shape.r }
1425
+ : { hw: shape.r, hh: shape.r * SQRT3_2 };
1426
+ }
1420
1427
  case 'cylinder':
1421
1428
  return { hw: shape.w / 2, hh: shape.h / 2 };
1422
1429
  case 'arc':
1423
1430
  return { hw: shape.r, hh: shape.r };
1424
- case 'blockArrow':
1425
- return { hw: shape.length / 2, hh: shape.headWidth / 2 };
1426
- case 'callout':
1427
- return { hw: shape.w / 2, hh: shape.h / 2 };
1431
+ case 'blockArrow': {
1432
+ const dir = shape.direction ?? 'right';
1433
+ return dir === 'up' || dir === 'down'
1434
+ ? { hw: shape.headWidth / 2, hh: shape.length / 2 }
1435
+ : { hw: shape.length / 2, hh: shape.headWidth / 2 };
1436
+ }
1437
+ case 'callout': {
1438
+ const side = shape.pointerSide ?? 'bottom';
1439
+ const pH = shape.pointerHeight ?? Math.round(shape.h * 0.25);
1440
+ const hw = shape.w / 2 + (side === 'left' || side === 'right' ? pH : 0);
1441
+ const hh = shape.h / 2 + (side === 'top' || side === 'bottom' ? pH : 0);
1442
+ return { hw, hh };
1443
+ }
1428
1444
  case 'cloud':
1429
1445
  return { hw: shape.w / 2, hh: shape.h / 2 };
1430
1446
  case 'cross':
@@ -1458,3 +1474,15 @@ function shapeBoundingBox(shape) {
1458
1474
  return { hw: 0, hh: 0 };
1459
1475
  }
1460
1476
  }
1477
+ /**
1478
+ * Compute the tight axis-aligned bounding-box size of a node shape.
1479
+ *
1480
+ * Returns `{ width, height }` that enclose the rendered shape, accounting for
1481
+ * orientation, direction, pointer height, and other shape-specific parameters.
1482
+ * Intended as a convenience helper for layout algorithms and collision
1483
+ * detection, to avoid duplicating per-shape switch logic.
1484
+ */
1485
+ export function getNodeBoundingBox(shape) {
1486
+ const { hw, hh } = shapeBoundingBox(shape);
1487
+ return { width: hw * 2, height: hh * 2 };
1488
+ }
package/dist/types.d.ts CHANGED
@@ -862,6 +862,16 @@ export interface LayoutResult {
862
862
  }>;
863
863
  }
864
864
  /**
865
- * A layout algorithm computes positions for nodes in a graph.
865
+ * A synchronous layout algorithm that computes positions for nodes in a graph.
866
+ *
867
+ * Use this type for algorithms that return results immediately.
868
+ * See also {@link LayoutAlgorithm} for algorithms that may be async.
869
+ */
870
+ export type SyncLayoutAlgorithm<Options = any> = (graph: LayoutGraph, options?: Options) => LayoutResult;
871
+ /**
872
+ * A layout algorithm that may be synchronous or asynchronous.
873
+ *
874
+ * Use with {@link VizBuilder.layoutAsync} for async engines (e.g. ELK via
875
+ * web workers). For the sync-only variant, see {@link SyncLayoutAlgorithm}.
866
876
  */
867
- export type LayoutAlgorithm<Options = any> = (graph: LayoutGraph, options?: Options) => LayoutResult;
877
+ export type LayoutAlgorithm<Options = any> = (graph: LayoutGraph, options?: Options) => LayoutResult | Promise<LayoutResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vizcraft",
3
- "version": "1.7.0",
3
+ "version": "1.7.1",
4
4
  "description": "A fluent, type-safe SVG scene builder for composing nodes, edges, animations, and overlays with incremental DOM updates and no framework dependency.",
5
5
  "keywords": [
6
6
  "visualization",