@pixerate/editor 0.16.2 → 0.18.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.
Files changed (42) hide show
  1. package/dist/agent/index.cjs +32 -29
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.d.cts +9 -3
  4. package/dist/agent/index.d.ts +9 -3
  5. package/dist/agent/index.js +31 -28
  6. package/dist/agent/index.js.map +1 -1
  7. package/dist/canvas/index.cjs +133 -65
  8. package/dist/canvas/index.cjs.map +1 -1
  9. package/dist/canvas/index.d.cts +64 -11
  10. package/dist/canvas/index.d.ts +64 -11
  11. package/dist/canvas/index.js +128 -60
  12. package/dist/canvas/index.js.map +1 -1
  13. package/dist/{chunk-33UVEDOS.cjs → chunk-2TWGLC5K.cjs} +58 -29
  14. package/dist/chunk-2TWGLC5K.cjs.map +1 -0
  15. package/dist/{chunk-3A7KVDEA.js → chunk-AIZUKWTL.js} +527 -114
  16. package/dist/chunk-AIZUKWTL.js.map +1 -0
  17. package/dist/{chunk-GWHJVZPI.js → chunk-KAADNAPF.js} +58 -29
  18. package/dist/chunk-KAADNAPF.js.map +1 -0
  19. package/dist/{chunk-TYG7K6WK.cjs → chunk-YQCI4CCN.cjs} +562 -149
  20. package/dist/chunk-YQCI4CCN.cjs.map +1 -0
  21. package/dist/history/index.cjs +2 -2
  22. package/dist/history/index.d.cts +5 -0
  23. package/dist/history/index.d.ts +5 -0
  24. package/dist/history/index.js +1 -1
  25. package/dist/index.cjs +13 -3
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.cts +1 -1
  28. package/dist/index.d.ts +1 -1
  29. package/dist/index.js +12 -2
  30. package/dist/index.js.map +1 -1
  31. package/dist/spreadsheet/index.cjs +12 -2
  32. package/dist/spreadsheet/index.cjs.map +1 -1
  33. package/dist/spreadsheet/index.d.cts +71 -2
  34. package/dist/spreadsheet/index.d.ts +71 -2
  35. package/dist/spreadsheet/index.js +11 -1
  36. package/dist/{types-DC8b2TsA.d.cts → types-JV3S8VeR.d.cts} +6 -2
  37. package/dist/{types-DC8b2TsA.d.ts → types-JV3S8VeR.d.ts} +6 -2
  38. package/package.json +1 -1
  39. package/dist/chunk-33UVEDOS.cjs.map +0 -1
  40. package/dist/chunk-3A7KVDEA.js.map +0 -1
  41. package/dist/chunk-GWHJVZPI.js.map +0 -1
  42. package/dist/chunk-TYG7K6WK.cjs.map +0 -1
@@ -1,5 +1,5 @@
1
- import { R as Rect, C as CanvasNode, X as XYPosition, a as CanvasEdge, I as IncrementalLayoutOptions, S as Side, b as CanvasLayoutOptions, P as PlaceBesideOptions, T as TrajectoryFunction, F as FanOutTrajectoryOptions, N as NodeTransition, c as ReflowDisplacementOptions, D as DirectionalDisplacementOptions, d as CanvasClipboardPayload } from '../types-DC8b2TsA.cjs';
2
- export { e as CanvasDockingCallbacks, f as CollapseNodeOptions, g as DisplacementDirection, h as DockStrategy, E as ExplodeNodeOptions, i as ExplosionSnapshot, G as GraphDifferences, j as Point, k as Position, l as TrajectoryPoint } from '../types-DC8b2TsA.cjs';
1
+ import { R as Rect, C as CanvasNode, X as XYPosition, a as CanvasEdge, I as IncrementalLayoutOptions, S as Side, b as CanvasLayoutOptions, P as PlaceBesideOptions, T as TrajectoryFunction, F as FanOutTrajectoryOptions, N as NodeTransition, c as CanvasNodeStyle, d as ReflowDisplacementOptions, D as DirectionalDisplacementOptions, e as CanvasClipboardPayload } from '../types-JV3S8VeR.cjs';
2
+ export { f as CanvasDockingCallbacks, g as CollapseNodeOptions, h as DisplacementDirection, i as DockStrategy, E as ExplodeNodeOptions, j as ExplosionSnapshot, G as GraphDifferences, k as Point, l as Position, m as TrajectoryPoint } from '../types-JV3S8VeR.cjs';
3
3
 
4
4
  declare const DEFAULT_NODE_WIDTH = 200;
5
5
  declare const DEFAULT_NODE_HEIGHT = 60;
@@ -131,15 +131,40 @@ declare function createFanOutTrajectory(options?: FanOutTrajectoryOptions): Traj
131
131
  declare function createBezierTrajectory(getControlPoint: (origin: XYPosition, target: XYPosition) => XYPosition): TrajectoryFunction;
132
132
 
133
133
  /**
134
- * Coordinates and animates multiple node transitions simultaneously with zero per-frame array allocations.
134
+ * Returns a copy of `style` with its opacity set to `opacity`.
135
+ *
136
+ * Supports both CSS strings (`"color: red; opacity: 0.5;"`, used by SvelteFlow)
137
+ * and style objects (`{ color: 'red', opacity: 0.5 }`, used by React Flow / xyflow).
138
+ * When `style` is empty, `emptyFormat` decides which representation is produced.
139
+ * Never mutates the input.
135
140
  */
136
- declare function runMultiNodeTransition<TNode extends CanvasNode = CanvasNode>(transitions: NodeTransition<TNode>[], options?: {
141
+ declare function withStyleOpacity(style: CanvasNodeStyle | undefined | null, opacity: number, emptyFormat?: 'string' | 'object'): CanvasNodeStyle;
142
+ interface MultiNodeTransitionOptions<TNode extends CanvasNode = CanvasNode> {
137
143
  defaultDuration?: number;
138
144
  defaultEasing?: (t: number) => number;
139
145
  clock?: Clock;
140
- onUpdate?: () => void;
141
- onComplete?: () => void;
142
- }): () => void;
146
+ /**
147
+ * Style representation to create for nodes that have no `style` yet when an opacity
148
+ * is applied. Defaults to `'string'` (SvelteFlow); pass `'object'` for React Flow.
149
+ * Existing styles always keep their representation.
150
+ */
151
+ styleFormat?: 'string' | 'object';
152
+ /**
153
+ * Called on every frame with updated copies of the transitioning nodes, in the same
154
+ * order as the `transitions` array. Input nodes are never mutated.
155
+ */
156
+ onUpdate?: (nodes: TNode[]) => void;
157
+ /** Called once all transitions finished, with the final node copies. */
158
+ onComplete?: (nodes: TNode[]) => void;
159
+ }
160
+ /**
161
+ * Coordinates and animates multiple node transitions simultaneously.
162
+ *
163
+ * The input nodes are treated as immutable: every frame produces updated copies
164
+ * (position, `data.scale` / `data.opacity`, and `style` opacity) which are handed to
165
+ * `onUpdate` and finally `onComplete`. Merge them into your node state by `id`.
166
+ */
167
+ declare function runMultiNodeTransition<TNode extends CanvasNode = CanvasNode>(transitions: NodeTransition<TNode>[], options?: MultiNodeTransitionOptions<TNode>): () => void;
143
168
 
144
169
  /**
145
170
  * Calculates a bounding rectangle for a set of nodes if not provided by getNodesBounds.
@@ -175,9 +200,14 @@ declare function isEventFromTextInput(event: Event | {
175
200
  target: EventTarget | null;
176
201
  }): boolean;
177
202
  /**
178
- * Validates that an object conforms to minimal CanvasNode shape.
203
+ * Validates that an object conforms to minimal CanvasNode shape:
204
+ * a string `id` and a `position` whose `x` / `y` are finite numbers.
179
205
  */
180
206
  declare function isValidCanvasNode(obj: unknown): obj is CanvasNode;
207
+ /**
208
+ * Validates that an object conforms to minimal CanvasEdge shape (string `id`, `source`, `target`).
209
+ */
210
+ declare function isValidCanvasEdge(obj: unknown): obj is CanvasEdge;
181
211
  /**
182
212
  * Checks if a string is a valid HTTP/HTTPS URL.
183
213
  */
@@ -192,8 +222,14 @@ declare function serializeCanvasNodes<TNode extends CanvasNode = CanvasNode, TEd
192
222
  declare function parseCanvasClipboardData<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(raw: string): CanvasClipboardPayload<TNode, TEdge> | null;
193
223
  /**
194
224
  * Generates unique new IDs for pasted nodes and updates edge references accordingly.
225
+ *
226
+ * - Nodes without a valid id / finite numeric position are skipped.
227
+ * - `parentId` (and legacy `parentNode`) references to other pasted nodes are remapped to
228
+ * the new IDs; such children keep their parent-relative position (no extra offset).
229
+ * References to parents outside the pasted set are left unchanged.
230
+ * - Edges are kept only when both endpoints were pasted; a non-array `edges` is ignored.
195
231
  */
196
- declare function remapPastedNodes<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(nodes: TNode[], edges?: TEdge[], offset?: XYPosition, idGenerator?: () => string): {
232
+ declare function remapPastedNodes<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(nodes: TNode[], edges?: TEdge[] | null | undefined, offset?: XYPosition, idGenerator?: () => string): {
197
233
  nodes: TNode[];
198
234
  edges: TEdge[];
199
235
  };
@@ -213,6 +249,15 @@ interface StreamingLayoutManagerOptions {
213
249
  /**
214
250
  * Manages throttled incremental layout recalculation and smooth tweened node gliding
215
251
  * during active streaming (e.g. LLM streaming or graph generation).
252
+ *
253
+ * - `pushStreamUpdate` is a true throttle (leading + trailing edge): the first update
254
+ * lays out immediately, further updates within `throttleMs` are coalesced and the
255
+ * latest one is laid out when the window closes. Layouts therefore keep running at
256
+ * most once per `throttleMs` while a stream is active, instead of being postponed
257
+ * until the stream goes quiet.
258
+ * - Every layout is computed from the graph (respecting `layoutOptions.storedPositions`
259
+ * as pinned/manual coordinates) and each known node glides from its *current*
260
+ * position (the `currentPositions` argument) to its newly computed position.
216
261
  */
217
262
  declare class StreamingLayoutManager<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge> {
218
263
  private throttleMs;
@@ -222,18 +267,26 @@ declare class StreamingLayoutManager<TNode extends CanvasNode = CanvasNode, TEdg
222
267
  private layoutOptions;
223
268
  private onLayoutUpdated?;
224
269
  private pendingTimer;
270
+ private pendingArgs;
225
271
  private activeTransitionCancel;
226
272
  private targetPositions;
273
+ private livePositions;
227
274
  constructor(options?: StreamingLayoutManagerOptions);
228
275
  /**
229
276
  * Feed incoming nodes and edges into the streaming manager.
230
- * Coalesces rapid streaming bursts and triggers smooth layout gliding.
277
+ * Throttles rapid streaming bursts (leading + trailing edge) and triggers smooth layout gliding.
231
278
  */
232
279
  pushStreamUpdate(nodes: TNode[], edges: TEdge[], currentPositions: Map<string, XYPosition>): void;
280
+ private startWindow;
233
281
  /**
234
282
  * Forces immediate calculation and cancels pending throttled timers.
235
283
  */
236
284
  flush(nodes: TNode[], edges: TEdge[], currentPositions: Map<string, XYPosition>): void;
285
+ /**
286
+ * Latest computed layout targets (the positions nodes are gliding towards).
287
+ */
288
+ getTargetPositions(): Map<string, XYPosition>;
289
+ private clearPending;
237
290
  private recalculateAndGlide;
238
291
  destroy(): void;
239
292
  }
@@ -274,4 +327,4 @@ declare function blendLayout<TNode extends CanvasNode = CanvasNode>(from: TNode[
274
327
  */
275
328
  declare function connectedOnly<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(nodes: TNode[], edges: TEdge[]): TNode[];
276
329
 
277
- export { CanvasClipboardPayload, CanvasEdge, type CanvasHeightOptions, CanvasLayoutOptions, CanvasNode, type Clock, DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH, DEFAULT_PLACEMENT_GAP, DirectionalDisplacementOptions, FanOutTrajectoryOptions, IncrementalLayoutOptions, ManualClock, NodeTransition, PlaceBesideOptions, Rect, ReflowDisplacementOptions, Side, StreamingLayoutManager, type StreamingLayoutManagerOptions, TrajectoryFunction, XYPosition, blendLayout, boundsOf, calculateDagreReflowDisplacement, calculateDirectionalDisplacement, canvasHeight, centerNodes, centre, computeNodesBoundingBox, connectedOnly, createBezierTrajectory, createFanOutTrajectory, defaultEasing, ensureLayout, estimateCanvasHeight, estimateNodeSize, flowSide, getCenteredNodePosition, getLayoutedNodes, isEventFromTextInput, isValidCanvasNode, isValidUrl, layoutBounds, linearPositionTrajectory, linearTrajectory, overlaps, pageHidden, parseCanvasClipboardData, placeBeside, realClock, remapPastedNodes, repositionNodesTo, runMultiNodeTransition, serializeCanvasNodes };
330
+ export { CanvasClipboardPayload, CanvasEdge, type CanvasHeightOptions, CanvasLayoutOptions, CanvasNode, CanvasNodeStyle, type Clock, DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH, DEFAULT_PLACEMENT_GAP, DirectionalDisplacementOptions, FanOutTrajectoryOptions, IncrementalLayoutOptions, ManualClock, type MultiNodeTransitionOptions, NodeTransition, PlaceBesideOptions, Rect, ReflowDisplacementOptions, Side, StreamingLayoutManager, type StreamingLayoutManagerOptions, TrajectoryFunction, XYPosition, blendLayout, boundsOf, calculateDagreReflowDisplacement, calculateDirectionalDisplacement, canvasHeight, centerNodes, centre, computeNodesBoundingBox, connectedOnly, createBezierTrajectory, createFanOutTrajectory, defaultEasing, ensureLayout, estimateCanvasHeight, estimateNodeSize, flowSide, getCenteredNodePosition, getLayoutedNodes, isEventFromTextInput, isValidCanvasEdge, isValidCanvasNode, isValidUrl, layoutBounds, linearPositionTrajectory, linearTrajectory, overlaps, pageHidden, parseCanvasClipboardData, placeBeside, realClock, remapPastedNodes, repositionNodesTo, runMultiNodeTransition, serializeCanvasNodes, withStyleOpacity };
@@ -1,5 +1,5 @@
1
- import { R as Rect, C as CanvasNode, X as XYPosition, a as CanvasEdge, I as IncrementalLayoutOptions, S as Side, b as CanvasLayoutOptions, P as PlaceBesideOptions, T as TrajectoryFunction, F as FanOutTrajectoryOptions, N as NodeTransition, c as ReflowDisplacementOptions, D as DirectionalDisplacementOptions, d as CanvasClipboardPayload } from '../types-DC8b2TsA.js';
2
- export { e as CanvasDockingCallbacks, f as CollapseNodeOptions, g as DisplacementDirection, h as DockStrategy, E as ExplodeNodeOptions, i as ExplosionSnapshot, G as GraphDifferences, j as Point, k as Position, l as TrajectoryPoint } from '../types-DC8b2TsA.js';
1
+ import { R as Rect, C as CanvasNode, X as XYPosition, a as CanvasEdge, I as IncrementalLayoutOptions, S as Side, b as CanvasLayoutOptions, P as PlaceBesideOptions, T as TrajectoryFunction, F as FanOutTrajectoryOptions, N as NodeTransition, c as CanvasNodeStyle, d as ReflowDisplacementOptions, D as DirectionalDisplacementOptions, e as CanvasClipboardPayload } from '../types-JV3S8VeR.js';
2
+ export { f as CanvasDockingCallbacks, g as CollapseNodeOptions, h as DisplacementDirection, i as DockStrategy, E as ExplodeNodeOptions, j as ExplosionSnapshot, G as GraphDifferences, k as Point, l as Position, m as TrajectoryPoint } from '../types-JV3S8VeR.js';
3
3
 
4
4
  declare const DEFAULT_NODE_WIDTH = 200;
5
5
  declare const DEFAULT_NODE_HEIGHT = 60;
@@ -131,15 +131,40 @@ declare function createFanOutTrajectory(options?: FanOutTrajectoryOptions): Traj
131
131
  declare function createBezierTrajectory(getControlPoint: (origin: XYPosition, target: XYPosition) => XYPosition): TrajectoryFunction;
132
132
 
133
133
  /**
134
- * Coordinates and animates multiple node transitions simultaneously with zero per-frame array allocations.
134
+ * Returns a copy of `style` with its opacity set to `opacity`.
135
+ *
136
+ * Supports both CSS strings (`"color: red; opacity: 0.5;"`, used by SvelteFlow)
137
+ * and style objects (`{ color: 'red', opacity: 0.5 }`, used by React Flow / xyflow).
138
+ * When `style` is empty, `emptyFormat` decides which representation is produced.
139
+ * Never mutates the input.
135
140
  */
136
- declare function runMultiNodeTransition<TNode extends CanvasNode = CanvasNode>(transitions: NodeTransition<TNode>[], options?: {
141
+ declare function withStyleOpacity(style: CanvasNodeStyle | undefined | null, opacity: number, emptyFormat?: 'string' | 'object'): CanvasNodeStyle;
142
+ interface MultiNodeTransitionOptions<TNode extends CanvasNode = CanvasNode> {
137
143
  defaultDuration?: number;
138
144
  defaultEasing?: (t: number) => number;
139
145
  clock?: Clock;
140
- onUpdate?: () => void;
141
- onComplete?: () => void;
142
- }): () => void;
146
+ /**
147
+ * Style representation to create for nodes that have no `style` yet when an opacity
148
+ * is applied. Defaults to `'string'` (SvelteFlow); pass `'object'` for React Flow.
149
+ * Existing styles always keep their representation.
150
+ */
151
+ styleFormat?: 'string' | 'object';
152
+ /**
153
+ * Called on every frame with updated copies of the transitioning nodes, in the same
154
+ * order as the `transitions` array. Input nodes are never mutated.
155
+ */
156
+ onUpdate?: (nodes: TNode[]) => void;
157
+ /** Called once all transitions finished, with the final node copies. */
158
+ onComplete?: (nodes: TNode[]) => void;
159
+ }
160
+ /**
161
+ * Coordinates and animates multiple node transitions simultaneously.
162
+ *
163
+ * The input nodes are treated as immutable: every frame produces updated copies
164
+ * (position, `data.scale` / `data.opacity`, and `style` opacity) which are handed to
165
+ * `onUpdate` and finally `onComplete`. Merge them into your node state by `id`.
166
+ */
167
+ declare function runMultiNodeTransition<TNode extends CanvasNode = CanvasNode>(transitions: NodeTransition<TNode>[], options?: MultiNodeTransitionOptions<TNode>): () => void;
143
168
 
144
169
  /**
145
170
  * Calculates a bounding rectangle for a set of nodes if not provided by getNodesBounds.
@@ -175,9 +200,14 @@ declare function isEventFromTextInput(event: Event | {
175
200
  target: EventTarget | null;
176
201
  }): boolean;
177
202
  /**
178
- * Validates that an object conforms to minimal CanvasNode shape.
203
+ * Validates that an object conforms to minimal CanvasNode shape:
204
+ * a string `id` and a `position` whose `x` / `y` are finite numbers.
179
205
  */
180
206
  declare function isValidCanvasNode(obj: unknown): obj is CanvasNode;
207
+ /**
208
+ * Validates that an object conforms to minimal CanvasEdge shape (string `id`, `source`, `target`).
209
+ */
210
+ declare function isValidCanvasEdge(obj: unknown): obj is CanvasEdge;
181
211
  /**
182
212
  * Checks if a string is a valid HTTP/HTTPS URL.
183
213
  */
@@ -192,8 +222,14 @@ declare function serializeCanvasNodes<TNode extends CanvasNode = CanvasNode, TEd
192
222
  declare function parseCanvasClipboardData<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(raw: string): CanvasClipboardPayload<TNode, TEdge> | null;
193
223
  /**
194
224
  * Generates unique new IDs for pasted nodes and updates edge references accordingly.
225
+ *
226
+ * - Nodes without a valid id / finite numeric position are skipped.
227
+ * - `parentId` (and legacy `parentNode`) references to other pasted nodes are remapped to
228
+ * the new IDs; such children keep their parent-relative position (no extra offset).
229
+ * References to parents outside the pasted set are left unchanged.
230
+ * - Edges are kept only when both endpoints were pasted; a non-array `edges` is ignored.
195
231
  */
196
- declare function remapPastedNodes<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(nodes: TNode[], edges?: TEdge[], offset?: XYPosition, idGenerator?: () => string): {
232
+ declare function remapPastedNodes<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(nodes: TNode[], edges?: TEdge[] | null | undefined, offset?: XYPosition, idGenerator?: () => string): {
197
233
  nodes: TNode[];
198
234
  edges: TEdge[];
199
235
  };
@@ -213,6 +249,15 @@ interface StreamingLayoutManagerOptions {
213
249
  /**
214
250
  * Manages throttled incremental layout recalculation and smooth tweened node gliding
215
251
  * during active streaming (e.g. LLM streaming or graph generation).
252
+ *
253
+ * - `pushStreamUpdate` is a true throttle (leading + trailing edge): the first update
254
+ * lays out immediately, further updates within `throttleMs` are coalesced and the
255
+ * latest one is laid out when the window closes. Layouts therefore keep running at
256
+ * most once per `throttleMs` while a stream is active, instead of being postponed
257
+ * until the stream goes quiet.
258
+ * - Every layout is computed from the graph (respecting `layoutOptions.storedPositions`
259
+ * as pinned/manual coordinates) and each known node glides from its *current*
260
+ * position (the `currentPositions` argument) to its newly computed position.
216
261
  */
217
262
  declare class StreamingLayoutManager<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge> {
218
263
  private throttleMs;
@@ -222,18 +267,26 @@ declare class StreamingLayoutManager<TNode extends CanvasNode = CanvasNode, TEdg
222
267
  private layoutOptions;
223
268
  private onLayoutUpdated?;
224
269
  private pendingTimer;
270
+ private pendingArgs;
225
271
  private activeTransitionCancel;
226
272
  private targetPositions;
273
+ private livePositions;
227
274
  constructor(options?: StreamingLayoutManagerOptions);
228
275
  /**
229
276
  * Feed incoming nodes and edges into the streaming manager.
230
- * Coalesces rapid streaming bursts and triggers smooth layout gliding.
277
+ * Throttles rapid streaming bursts (leading + trailing edge) and triggers smooth layout gliding.
231
278
  */
232
279
  pushStreamUpdate(nodes: TNode[], edges: TEdge[], currentPositions: Map<string, XYPosition>): void;
280
+ private startWindow;
233
281
  /**
234
282
  * Forces immediate calculation and cancels pending throttled timers.
235
283
  */
236
284
  flush(nodes: TNode[], edges: TEdge[], currentPositions: Map<string, XYPosition>): void;
285
+ /**
286
+ * Latest computed layout targets (the positions nodes are gliding towards).
287
+ */
288
+ getTargetPositions(): Map<string, XYPosition>;
289
+ private clearPending;
237
290
  private recalculateAndGlide;
238
291
  destroy(): void;
239
292
  }
@@ -274,4 +327,4 @@ declare function blendLayout<TNode extends CanvasNode = CanvasNode>(from: TNode[
274
327
  */
275
328
  declare function connectedOnly<TNode extends CanvasNode = CanvasNode, TEdge extends CanvasEdge = CanvasEdge>(nodes: TNode[], edges: TEdge[]): TNode[];
276
329
 
277
- export { CanvasClipboardPayload, CanvasEdge, type CanvasHeightOptions, CanvasLayoutOptions, CanvasNode, type Clock, DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH, DEFAULT_PLACEMENT_GAP, DirectionalDisplacementOptions, FanOutTrajectoryOptions, IncrementalLayoutOptions, ManualClock, NodeTransition, PlaceBesideOptions, Rect, ReflowDisplacementOptions, Side, StreamingLayoutManager, type StreamingLayoutManagerOptions, TrajectoryFunction, XYPosition, blendLayout, boundsOf, calculateDagreReflowDisplacement, calculateDirectionalDisplacement, canvasHeight, centerNodes, centre, computeNodesBoundingBox, connectedOnly, createBezierTrajectory, createFanOutTrajectory, defaultEasing, ensureLayout, estimateCanvasHeight, estimateNodeSize, flowSide, getCenteredNodePosition, getLayoutedNodes, isEventFromTextInput, isValidCanvasNode, isValidUrl, layoutBounds, linearPositionTrajectory, linearTrajectory, overlaps, pageHidden, parseCanvasClipboardData, placeBeside, realClock, remapPastedNodes, repositionNodesTo, runMultiNodeTransition, serializeCanvasNodes };
330
+ export { CanvasClipboardPayload, CanvasEdge, type CanvasHeightOptions, CanvasLayoutOptions, CanvasNode, CanvasNodeStyle, type Clock, DEFAULT_NODE_HEIGHT, DEFAULT_NODE_WIDTH, DEFAULT_PLACEMENT_GAP, DirectionalDisplacementOptions, FanOutTrajectoryOptions, IncrementalLayoutOptions, ManualClock, type MultiNodeTransitionOptions, NodeTransition, PlaceBesideOptions, Rect, ReflowDisplacementOptions, Side, StreamingLayoutManager, type StreamingLayoutManagerOptions, TrajectoryFunction, XYPosition, blendLayout, boundsOf, calculateDagreReflowDisplacement, calculateDirectionalDisplacement, canvasHeight, centerNodes, centre, computeNodesBoundingBox, connectedOnly, createBezierTrajectory, createFanOutTrajectory, defaultEasing, ensureLayout, estimateCanvasHeight, estimateNodeSize, flowSide, getCenteredNodePosition, getLayoutedNodes, isEventFromTextInput, isValidCanvasEdge, isValidCanvasNode, isValidUrl, layoutBounds, linearPositionTrajectory, linearTrajectory, overlaps, pageHidden, parseCanvasClipboardData, placeBeside, realClock, remapPastedNodes, repositionNodesTo, runMultiNodeTransition, serializeCanvasNodes, withStyleOpacity };
@@ -148,9 +148,19 @@ function createBezierTrajectory(getControlPoint) {
148
148
  };
149
149
  };
150
150
  }
151
+ function withStyleOpacity(style, opacity, emptyFormat = "string") {
152
+ if (style !== null && typeof style === "object") {
153
+ return { ...style, opacity };
154
+ }
155
+ if (typeof style === "string" && style.trim() !== "") {
156
+ const rest = style.replace(/(^|;)\s*opacity\s*:\s*[^;]*;?/gi, "$1").trim().replace(/;+\s*$/, "").trim();
157
+ return `${rest ? rest + "; " : ""}opacity: ${opacity};`;
158
+ }
159
+ return emptyFormat === "object" ? { opacity } : `opacity: ${opacity};`;
160
+ }
151
161
  function runMultiNodeTransition(transitions, options = {}) {
152
162
  if (transitions.length === 0) {
153
- options.onComplete?.();
163
+ options.onComplete?.([]);
154
164
  return () => {
155
165
  };
156
166
  }
@@ -158,6 +168,7 @@ function runMultiNodeTransition(transitions, options = {}) {
158
168
  defaultDuration = 400,
159
169
  defaultEasing: easingFunc = defaultEasing,
160
170
  clock = realClock,
171
+ styleFormat = "string",
161
172
  onUpdate,
162
173
  onComplete
163
174
  } = options;
@@ -172,6 +183,7 @@ function runMultiNodeTransition(transitions, options = {}) {
172
183
  delay: tr.delay ?? 0,
173
184
  trajectory: tr.trajectory ?? linearTrajectory
174
185
  }));
186
+ const current = items.map((item) => item.node);
175
187
  function frame() {
176
188
  if (isCancelled) return;
177
189
  const now = clock.now();
@@ -186,29 +198,28 @@ function runMultiNodeTransition(transitions, options = {}) {
186
198
  const rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);
187
199
  const progress = item.duration <= 0 ? 1 : item.easing(rawProgress);
188
200
  const point = item.trajectory(item.from, item.to, progress, item.index, item.total);
189
- item.node.position = { ...point.position };
201
+ const next = { ...item.node, position: { ...point.position } };
190
202
  if (point.scale !== void 0 || point.opacity !== void 0) {
191
- if (!item.node.data) {
192
- item.node.data = {};
193
- }
203
+ const data = { ...item.node.data ?? {} };
194
204
  if (point.scale !== void 0) {
195
- item.node.data.scale = point.scale;
205
+ data.scale = point.scale;
196
206
  }
197
207
  if (point.opacity !== void 0) {
198
- item.node.data.opacity = point.opacity;
199
- const currentStyle = (item.node.style || "").replace(/opacity:\s*[^;]+;?/g, "").trim();
200
- item.node.style = `${currentStyle ? currentStyle + "; " : ""}opacity: ${point.opacity};`;
208
+ data.opacity = point.opacity;
209
+ next.style = withStyleOpacity(item.node.style, point.opacity, styleFormat);
201
210
  }
211
+ next.data = data;
202
212
  }
213
+ current[i] = next;
203
214
  if (rawProgress < 1) {
204
215
  allCompleted = false;
205
216
  }
206
217
  }
207
- onUpdate?.();
218
+ onUpdate?.(current.slice());
208
219
  if (!allCompleted) {
209
220
  clock.frame().then(frame);
210
221
  } else {
211
- onComplete?.();
222
+ onComplete?.(current.slice());
212
223
  }
213
224
  }
214
225
  frame();
@@ -417,7 +428,16 @@ function isEventFromTextInput(event) {
417
428
  return false;
418
429
  }
419
430
  function isValidCanvasNode(obj) {
420
- return typeof obj === "object" && obj !== null && "id" in obj && typeof obj.id === "string" && "position" in obj && typeof obj.position === "object" && obj.position !== null && "x" in obj.position && "y" in obj.position;
431
+ if (typeof obj !== "object" || obj === null) return false;
432
+ const candidate = obj;
433
+ if (typeof candidate.id !== "string") return false;
434
+ const position = candidate.position;
435
+ return typeof position === "object" && position !== null && typeof position.x === "number" && Number.isFinite(position.x) && typeof position.y === "number" && Number.isFinite(position.y);
436
+ }
437
+ function isValidCanvasEdge(obj) {
438
+ if (typeof obj !== "object" || obj === null) return false;
439
+ const candidate = obj;
440
+ return typeof candidate.id === "string" && typeof candidate.source === "string" && typeof candidate.target === "string";
421
441
  }
422
442
  function isValidUrl(url) {
423
443
  try {
@@ -439,28 +459,46 @@ function parseCanvasClipboardData(raw) {
439
459
  try {
440
460
  const parsed = JSON.parse(raw);
441
461
  if (typeof parsed === "object" && parsed !== null && Array.isArray(parsed.nodes) && parsed.nodes.length > 0 && parsed.nodes.every(isValidCanvasNode)) {
442
- return parsed;
462
+ const edges = Array.isArray(parsed.edges) ? parsed.edges.filter(isValidCanvasEdge) : void 0;
463
+ return { ...parsed, edges };
443
464
  }
444
465
  } catch {
445
466
  }
446
467
  return null;
447
468
  }
448
469
  function remapPastedNodes(nodes, edges = [], offset = { x: 30, y: 30 }, idGenerator = () => `node-${Math.random().toString(36).substring(2, 9)}`) {
470
+ const validNodes = (Array.isArray(nodes) ? nodes : []).filter(isValidCanvasNode);
471
+ const validEdges = (Array.isArray(edges) ? edges : []).filter(isValidCanvasEdge);
472
+ const dx = Number.isFinite(offset?.x) ? offset.x : 0;
473
+ const dy = Number.isFinite(offset?.y) ? offset.y : 0;
449
474
  const idMap = /* @__PURE__ */ new Map();
450
- const newNodes = nodes.map((node) => {
451
- const newId = idGenerator();
452
- idMap.set(node.id, newId);
453
- return {
475
+ for (const node of validNodes) {
476
+ idMap.set(node.id, idGenerator());
477
+ }
478
+ const newNodes = validNodes.map((node) => {
479
+ const next = {
454
480
  ...node,
455
- id: newId,
481
+ id: idMap.get(node.id),
456
482
  position: {
457
- x: node.position.x + offset.x,
458
- y: node.position.y + offset.y
483
+ x: node.position.x + dx,
484
+ y: node.position.y + dy
459
485
  },
460
486
  selected: true
461
487
  };
488
+ let parentRemapped = false;
489
+ for (const key of ["parentId", "parentNode"]) {
490
+ const parent = node[key];
491
+ if (typeof parent === "string" && idMap.has(parent)) {
492
+ next[key] = idMap.get(parent);
493
+ parentRemapped = true;
494
+ }
495
+ }
496
+ if (parentRemapped) {
497
+ next.position = { x: node.position.x, y: node.position.y };
498
+ }
499
+ return next;
462
500
  });
463
- const newEdges = edges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((edge) => ({
501
+ const newEdges = validEdges.filter((e) => idMap.has(e.source) && idMap.has(e.target)).map((edge) => ({
464
502
  ...edge,
465
503
  id: `edge-${Math.random().toString(36).substring(2, 9)}`,
466
504
  source: idMap.get(edge.source),
@@ -557,8 +595,10 @@ var StreamingLayoutManager = class {
557
595
  layoutOptions;
558
596
  onLayoutUpdated;
559
597
  pendingTimer = null;
598
+ pendingArgs = null;
560
599
  activeTransitionCancel = null;
561
600
  targetPositions = /* @__PURE__ */ new Map();
601
+ livePositions = /* @__PURE__ */ new Map();
562
602
  constructor(options = {}) {
563
603
  this.throttleMs = options.throttleMs ?? 200;
564
604
  this.glideDurationMs = options.glideDurationMs ?? 300;
@@ -569,52 +609,70 @@ var StreamingLayoutManager = class {
569
609
  }
570
610
  /**
571
611
  * Feed incoming nodes and edges into the streaming manager.
572
- * Coalesces rapid streaming bursts and triggers smooth layout gliding.
612
+ * Throttles rapid streaming bursts (leading + trailing edge) and triggers smooth layout gliding.
573
613
  */
574
614
  pushStreamUpdate(nodes, edges, currentPositions) {
575
- if (this.pendingTimer) {
576
- clearTimeout(this.pendingTimer);
615
+ if (this.pendingTimer !== null) {
616
+ this.pendingArgs = [nodes, edges, currentPositions];
617
+ return;
577
618
  }
619
+ this.recalculateAndGlide(nodes, edges, currentPositions);
620
+ this.startWindow();
621
+ }
622
+ startWindow() {
578
623
  this.pendingTimer = setTimeout(() => {
579
624
  this.pendingTimer = null;
580
- this.recalculateAndGlide(nodes, edges, currentPositions);
581
- }, this.throttleMs);
625
+ const args = this.pendingArgs;
626
+ this.pendingArgs = null;
627
+ if (args) {
628
+ this.recalculateAndGlide(...args);
629
+ this.startWindow();
630
+ }
631
+ }, Math.max(0, this.throttleMs));
582
632
  }
583
633
  /**
584
634
  * Forces immediate calculation and cancels pending throttled timers.
585
635
  */
586
636
  flush(nodes, edges, currentPositions) {
587
- if (this.pendingTimer) {
637
+ this.clearPending();
638
+ this.recalculateAndGlide(nodes, edges, currentPositions);
639
+ }
640
+ /**
641
+ * Latest computed layout targets (the positions nodes are gliding towards).
642
+ */
643
+ getTargetPositions() {
644
+ return new Map(this.targetPositions);
645
+ }
646
+ clearPending() {
647
+ if (this.pendingTimer !== null) {
588
648
  clearTimeout(this.pendingTimer);
589
649
  this.pendingTimer = null;
590
650
  }
591
- this.recalculateAndGlide(nodes, edges, currentPositions);
651
+ this.pendingArgs = null;
592
652
  }
593
653
  recalculateAndGlide(nodes, edges, currentPositions) {
594
654
  const targetNodes = this.connectedOnly ? connectedOnly(nodes, edges) : nodes;
595
655
  if (targetNodes.length === 0) return;
656
+ const startPositions = new Map(currentPositions);
596
657
  if (this.activeTransitionCancel) {
658
+ for (const [id, pos] of this.livePositions) {
659
+ startPositions.set(id, pos);
660
+ }
597
661
  this.activeTransitionCancel();
598
662
  this.activeTransitionCancel = null;
599
663
  }
600
- const stored = {};
601
- for (const [id, pos] of currentPositions.entries()) {
602
- stored[id] = pos;
603
- }
604
- const layouted = ensureLayout(targetNodes, edges, {
605
- ...this.layoutOptions,
606
- storedPositions: stored
607
- });
664
+ const layouted = ensureLayout(targetNodes, edges, this.layoutOptions);
608
665
  const nextPositions = /* @__PURE__ */ new Map();
609
666
  const transitions = [];
610
667
  for (const node of layouted) {
611
- const target = node.position;
668
+ const target = { ...node.position };
612
669
  nextPositions.set(node.id, target);
613
- const current = currentPositions.get(node.id) ?? target;
670
+ const current = startPositions.get(node.id);
671
+ if (!current) continue;
614
672
  const distance = Math.hypot(target.x - current.x, target.y - current.y);
615
673
  if (distance > 2) {
616
674
  transitions.push({
617
- node,
675
+ node: { ...node, position: { ...current } },
618
676
  from: current,
619
677
  to: target,
620
678
  trajectory: linearPositionTrajectory,
@@ -623,30 +681,38 @@ var StreamingLayoutManager = class {
623
681
  }
624
682
  }
625
683
  this.targetPositions = nextPositions;
626
- if (transitions.length > 0) {
627
- this.activeTransitionCancel = runMultiNodeTransition(transitions, {
628
- clock: this.clock,
629
- onUpdate: () => {
630
- const liveMap = new Map(currentPositions);
631
- for (const trans of transitions) {
632
- liveMap.set(trans.node.id, { ...trans.node.position });
633
- }
634
- this.onLayoutUpdated?.(liveMap);
635
- },
636
- onComplete: () => {
637
- this.activeTransitionCancel = null;
638
- this.onLayoutUpdated?.(nextPositions);
639
- }
640
- });
641
- } else {
684
+ if (transitions.length === 0) {
685
+ this.livePositions = new Map(nextPositions);
642
686
  this.onLayoutUpdated?.(nextPositions);
687
+ return;
643
688
  }
689
+ const live = new Map(nextPositions);
690
+ for (const trans of transitions) {
691
+ live.set(trans.node.id, { ...trans.from });
692
+ }
693
+ this.livePositions = live;
694
+ let finished = false;
695
+ const cancel = runMultiNodeTransition(transitions, {
696
+ clock: this.clock,
697
+ onUpdate: (updated) => {
698
+ const frame = new Map(this.livePositions);
699
+ for (const node of updated) {
700
+ frame.set(node.id, { ...node.position });
701
+ }
702
+ this.livePositions = frame;
703
+ this.onLayoutUpdated?.(frame);
704
+ },
705
+ onComplete: () => {
706
+ finished = true;
707
+ this.activeTransitionCancel = null;
708
+ this.livePositions = new Map(nextPositions);
709
+ this.onLayoutUpdated?.(nextPositions);
710
+ }
711
+ });
712
+ this.activeTransitionCancel = finished ? null : cancel;
644
713
  }
645
714
  destroy() {
646
- if (this.pendingTimer) {
647
- clearTimeout(this.pendingTimer);
648
- this.pendingTimer = null;
649
- }
715
+ this.clearPending();
650
716
  if (this.activeTransitionCancel) {
651
717
  this.activeTransitionCancel();
652
718
  this.activeTransitionCancel = null;
@@ -678,6 +744,7 @@ export {
678
744
  getCenteredNodePosition,
679
745
  getLayoutedNodes,
680
746
  isEventFromTextInput,
747
+ isValidCanvasEdge,
681
748
  isValidCanvasNode,
682
749
  isValidUrl,
683
750
  layoutBounds,
@@ -691,6 +758,7 @@ export {
691
758
  remapPastedNodes,
692
759
  repositionNodesTo,
693
760
  runMultiNodeTransition,
694
- serializeCanvasNodes
761
+ serializeCanvasNodes,
762
+ withStyleOpacity
695
763
  };
696
764
  //# sourceMappingURL=index.js.map