@pixerate/editor-svelte 0.15.2 → 0.16.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/BubbleMenu.svelte.d.ts +14 -0
- package/dist/BubbleMenu.svelte.d.ts.map +1 -0
- package/{src → dist}/EditableTextNodeEditor.svelte +1 -1
- package/dist/EditableTextNodeEditor.svelte.d.ts +26 -0
- package/dist/EditableTextNodeEditor.svelte.d.ts.map +1 -0
- package/dist/TemplateRenderer.svelte.d.ts +14 -0
- package/dist/TemplateRenderer.svelte.d.ts.map +1 -0
- package/dist/canvas/AgentPresenceLayer.svelte.d.ts +9 -0
- package/dist/canvas/AgentPresenceLayer.svelte.d.ts.map +1 -0
- package/dist/canvas/FloatingHorizontalScrollbar.svelte.d.ts +9 -0
- package/dist/canvas/FloatingHorizontalScrollbar.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasClipboard.svelte.d.ts +32 -0
- package/dist/canvas/createCanvasClipboard.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasClipboard.svelte.js +171 -0
- package/dist/canvas/createCanvasDisplacement.d.ts +32 -0
- package/dist/canvas/createCanvasDisplacement.d.ts.map +1 -0
- package/dist/canvas/createCanvasDisplacement.js +170 -0
- package/dist/canvas/createCanvasDocking.svelte.d.ts +56 -0
- package/dist/canvas/createCanvasDocking.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasDocking.svelte.js +227 -0
- package/dist/canvas/createCanvasExplosion.svelte.d.ts +31 -0
- package/dist/canvas/createCanvasExplosion.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasExplosion.svelte.js +346 -0
- package/dist/canvas/createCanvasGraph.svelte.d.ts +62 -0
- package/dist/canvas/createCanvasGraph.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasGraph.svelte.js +223 -0
- package/dist/canvas/createCanvasInteractions.svelte.d.ts +28 -0
- package/dist/canvas/createCanvasInteractions.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasInteractions.svelte.js +95 -0
- package/dist/canvas/createCanvasLayout.d.ts +22 -0
- package/dist/canvas/createCanvasLayout.d.ts.map +1 -0
- package/dist/canvas/createCanvasLayout.js +76 -0
- package/dist/canvas/createCanvasMultiDrag.svelte.d.ts +61 -0
- package/dist/canvas/createCanvasMultiDrag.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasMultiDrag.svelte.js +105 -0
- package/dist/canvas/createCanvasNodeSync.svelte.d.ts +58 -0
- package/dist/canvas/createCanvasNodeSync.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasNodeSync.svelte.js +82 -0
- package/dist/canvas/createCanvasSelection.svelte.d.ts +26 -0
- package/dist/canvas/createCanvasSelection.svelte.d.ts.map +1 -0
- package/dist/canvas/createCanvasSelection.svelte.js +26 -0
- package/dist/canvas/createCanvasShortcuts.d.ts +9 -0
- package/dist/canvas/createCanvasShortcuts.d.ts.map +1 -0
- package/dist/canvas/createCanvasShortcuts.js +97 -0
- package/dist/canvas/createCanvasTrajectory.d.ts +31 -0
- package/dist/canvas/createCanvasTrajectory.d.ts.map +1 -0
- package/dist/canvas/createCanvasTrajectory.js +141 -0
- package/dist/canvas/createStreamingCanvas.svelte.d.ts +19 -0
- package/dist/canvas/createStreamingCanvas.svelte.d.ts.map +1 -0
- package/dist/canvas/createStreamingCanvas.svelte.js +32 -0
- package/dist/canvas/index.d.ts +18 -688
- package/dist/canvas/index.d.ts.map +1 -0
- package/dist/canvas/index.js +34 -1839
- package/dist/canvas/types.d.ts +183 -0
- package/dist/canvas/types.d.ts.map +1 -0
- package/dist/canvas/types.js +1 -0
- package/dist/createNavigationGuard.svelte.d.ts +24 -0
- package/dist/createNavigationGuard.svelte.d.ts.map +1 -0
- package/dist/createNavigationGuard.svelte.js +65 -0
- package/dist/editor.svelte.d.ts +23 -0
- package/dist/editor.svelte.d.ts.map +1 -0
- package/dist/editor.svelte.js +41 -0
- package/dist/history/createHistoryShortcuts.d.ts +24 -0
- package/dist/history/createHistoryShortcuts.d.ts.map +1 -0
- package/dist/history/createHistoryShortcuts.js +57 -0
- package/dist/history/history.svelte.d.ts +18 -0
- package/dist/history/history.svelte.d.ts.map +1 -0
- package/dist/history/history.svelte.js +43 -0
- package/dist/history/index.d.ts +3 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +2 -0
- package/{src → dist}/image-editor/ImageEditor.svelte +1 -1
- package/dist/image-editor/ImageEditor.svelte.d.ts +17 -0
- package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -0
- package/dist/image-editor/imageEditorState.svelte.d.ts +50 -0
- package/dist/image-editor/imageEditorState.svelte.d.ts.map +1 -0
- package/dist/image-editor/imageEditorState.svelte.js +132 -0
- package/dist/image-editor/index.d.ts +6 -16
- package/dist/image-editor/index.d.ts.map +1 -0
- package/dist/image-editor/index.js +2 -9
- package/dist/index.d.ts +20 -249
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -1119
- package/dist/navigationGuard.d.ts +21 -0
- package/dist/navigationGuard.d.ts.map +1 -0
- package/dist/navigationGuard.js +59 -0
- package/{src → dist}/spreadsheet/FormulaBar.svelte +1 -1
- package/dist/spreadsheet/FormulaBar.svelte.d.ts +17 -0
- package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -0
- package/{src → dist}/spreadsheet/SpreadsheetEditor.svelte +1 -1
- package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts +22 -0
- package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/index.d.ts +9 -0
- package/dist/spreadsheet/index.d.ts.map +1 -0
- package/{src/spreadsheet/index.ts → dist/spreadsheet/index.js} +1 -1
- package/dist/spreadsheet/spreadsheet.svelte.d.ts +33 -0
- package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -0
- package/dist/spreadsheet/spreadsheet.svelte.js +160 -0
- package/package.json +14 -16
- package/dist/canvas/index.js.map +0 -1
- package/dist/chunk-HEOI4UNF.js +0 -1191
- package/dist/chunk-HEOI4UNF.js.map +0 -1
- package/dist/image-editor/index.js.map +0 -1
- package/dist/index.css +0 -50
- package/dist/index.css.map +0 -1
- package/dist/index.js.map +0 -1
- package/src/canvas/__tests__/canvas-clipboard.test.ts +0 -154
- package/src/canvas/__tests__/canvas-docking.test.ts +0 -288
- package/src/canvas/__tests__/canvas-explosion.test.ts +0 -634
- package/src/canvas/__tests__/canvas-graph.test.ts +0 -159
- package/src/canvas/__tests__/canvas-interactions.test.ts +0 -86
- package/src/canvas/__tests__/canvas-layout.test.ts +0 -75
- package/src/canvas/__tests__/canvas-multi-drag.test.ts +0 -112
- package/src/canvas/__tests__/canvas-node-sync.svelte.test.ts +0 -104
- package/src/canvas/__tests__/canvas-shortcuts.test.ts +0 -59
- package/src/canvas/createCanvasClipboard.svelte.ts +0 -219
- package/src/canvas/createCanvasDisplacement.ts +0 -253
- package/src/canvas/createCanvasDocking.svelte.ts +0 -355
- package/src/canvas/createCanvasExplosion.svelte.ts +0 -457
- package/src/canvas/createCanvasGraph.svelte.ts +0 -330
- package/src/canvas/createCanvasInteractions.svelte.ts +0 -112
- package/src/canvas/createCanvasLayout.ts +0 -129
- package/src/canvas/createCanvasMultiDrag.svelte.ts +0 -175
- package/src/canvas/createCanvasNodeSync.svelte.ts +0 -150
- package/src/canvas/createCanvasSelection.svelte.ts +0 -51
- package/src/canvas/createCanvasShortcuts.ts +0 -99
- package/src/canvas/createCanvasTrajectory.ts +0 -207
- package/src/canvas/createStreamingCanvas.svelte.ts +0 -56
- package/src/canvas/index.d.ts +0 -688
- package/src/canvas/index.ts +0 -126
- package/src/canvas/types.ts +0 -223
- package/src/createNavigationGuard.svelte.ts +0 -97
- package/src/editor.svelte.ts +0 -65
- package/src/history/createHistoryShortcuts.ts +0 -95
- package/src/history/history.svelte.ts +0 -68
- package/src/history/index.ts +0 -2
- package/src/image-editor/imageEditorState.svelte.ts +0 -151
- package/src/image-editor/index.d.ts +0 -16
- package/src/image-editor/index.ts +0 -2
- package/src/index.d.ts +0 -249
- package/src/index.ts +0 -89
- package/src/navigationGuard.ts +0 -82
- package/src/spreadsheet/spreadsheet.svelte.ts +0 -176
- package/src/svelte.d.ts +0 -5
- /package/{src → dist}/BubbleMenu.svelte +0 -0
- /package/{src → dist}/TemplateRenderer.svelte +0 -0
- /package/{src → dist}/canvas/AgentPresenceLayer.svelte +0 -0
- /package/{src → dist}/canvas/FloatingHorizontalScrollbar.svelte +0 -0
- /package/{src → dist}/canvas/README.md +0 -0
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import type { XYPosition } from '@xyflow/svelte';
|
|
2
|
-
import type { CanvasNode } from './types.js';
|
|
3
|
-
|
|
4
|
-
export interface MultiDragFollowerOffset {
|
|
5
|
-
deltaX: number;
|
|
6
|
-
deltaY: number;
|
|
7
|
-
originalPosition: XYPosition;
|
|
8
|
-
stackOffsetX: number;
|
|
9
|
-
stackOffsetY: number;
|
|
10
|
-
rotationDeg: number;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface MultiDragState {
|
|
14
|
-
leadId: string;
|
|
15
|
-
offsets: Record<string, MultiDragFollowerOffset>;
|
|
16
|
-
count: number;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface CreateCanvasMultiDragOptions {
|
|
20
|
-
/**
|
|
21
|
-
* Base horizontal fan-out offset per stacked follower card (default: 4px).
|
|
22
|
-
*/
|
|
23
|
-
stackOffsetStepX?: number;
|
|
24
|
-
/**
|
|
25
|
-
* Base vertical fan-out offset per stacked follower card (default: 4px).
|
|
26
|
-
*/
|
|
27
|
-
stackOffsetStepY?: number;
|
|
28
|
-
/**
|
|
29
|
-
* Alternating angular tilt in degrees for follower cards (default: [-2, 2.5, -3, 3]).
|
|
30
|
-
*/
|
|
31
|
-
rotationAngles?: number[];
|
|
32
|
-
/**
|
|
33
|
-
* Base z-index for stacked cards during active dragging (default: 50).
|
|
34
|
-
*/
|
|
35
|
-
baseZIndex?: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export interface FollowerTransform {
|
|
39
|
-
isFollower: boolean;
|
|
40
|
-
stackOffsetX: number;
|
|
41
|
-
stackOffsetY: number;
|
|
42
|
-
rotationDeg: number;
|
|
43
|
-
zIndex: number;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Headless Svelte 5 rune orchestrating multi-node drag operations with stacked visual fanning,
|
|
48
|
-
* dynamic z-index layering, and position restoration.
|
|
49
|
-
*/
|
|
50
|
-
export function createCanvasMultiDrag(options: CreateCanvasMultiDragOptions = {}) {
|
|
51
|
-
const stepX = options.stackOffsetStepX ?? 4;
|
|
52
|
-
const stepY = options.stackOffsetStepY ?? 4;
|
|
53
|
-
const angles = options.rotationAngles ?? [-2, 2.5, -3, 3];
|
|
54
|
-
const baseZIndex = options.baseZIndex ?? 50;
|
|
55
|
-
|
|
56
|
-
let dragState = $state<MultiDragState | null>(null);
|
|
57
|
-
|
|
58
|
-
function startMultiDrag(
|
|
59
|
-
leadNodeId: string,
|
|
60
|
-
selectedNodeIds: string[] | Set<string>,
|
|
61
|
-
nodes: CanvasNode[] | Map<string, CanvasNode>
|
|
62
|
-
): MultiDragState | null {
|
|
63
|
-
const selectedSet =
|
|
64
|
-
selectedNodeIds instanceof Set ? selectedNodeIds : new Set(selectedNodeIds);
|
|
65
|
-
|
|
66
|
-
if (selectedSet.size <= 1 || !selectedSet.has(leadNodeId)) {
|
|
67
|
-
dragState = null;
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const nodeMap =
|
|
72
|
-
nodes instanceof Map ? nodes : new Map(nodes.map((n) => [n.id, n]));
|
|
73
|
-
|
|
74
|
-
const leadNode = nodeMap.get(leadNodeId);
|
|
75
|
-
if (!leadNode) {
|
|
76
|
-
dragState = null;
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const leadPos = leadNode.position ?? { x: 0, y: 0 };
|
|
81
|
-
const offsets: Record<string, MultiDragFollowerOffset> = {};
|
|
82
|
-
let followerIndex = 0;
|
|
83
|
-
|
|
84
|
-
for (const id of selectedSet) {
|
|
85
|
-
if (id === leadNodeId) continue;
|
|
86
|
-
const follower = nodeMap.get(id);
|
|
87
|
-
const fPos = follower?.position ?? { x: 0, y: 0 };
|
|
88
|
-
|
|
89
|
-
const angle = angles[followerIndex % angles.length];
|
|
90
|
-
offsets[id] = {
|
|
91
|
-
deltaX: fPos.x - leadPos.x,
|
|
92
|
-
deltaY: fPos.y - leadPos.y,
|
|
93
|
-
originalPosition: { ...fPos },
|
|
94
|
-
stackOffsetX: (followerIndex + 1) * stepX,
|
|
95
|
-
stackOffsetY: (followerIndex + 1) * stepY,
|
|
96
|
-
rotationDeg: angle
|
|
97
|
-
};
|
|
98
|
-
followerIndex++;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const state: MultiDragState = {
|
|
102
|
-
leadId: leadNodeId,
|
|
103
|
-
offsets,
|
|
104
|
-
count: selectedSet.size
|
|
105
|
-
};
|
|
106
|
-
|
|
107
|
-
dragState = state;
|
|
108
|
-
return state;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function getFollowerTransform(nodeId: string): FollowerTransform {
|
|
112
|
-
if (!dragState || !(nodeId in dragState.offsets)) {
|
|
113
|
-
return {
|
|
114
|
-
isFollower: false,
|
|
115
|
-
stackOffsetX: 0,
|
|
116
|
-
stackOffsetY: 0,
|
|
117
|
-
rotationDeg: 0,
|
|
118
|
-
zIndex: 0
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const offset = dragState.offsets[nodeId];
|
|
123
|
-
const followerKeys = Object.keys(dragState.offsets);
|
|
124
|
-
const index = followerKeys.indexOf(nodeId);
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
isFollower: true,
|
|
128
|
-
stackOffsetX: offset.stackOffsetX,
|
|
129
|
-
stackOffsetY: offset.stackOffsetY,
|
|
130
|
-
rotationDeg: offset.rotationDeg,
|
|
131
|
-
zIndex: baseZIndex - (index + 1)
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function cancelMultiDrag(
|
|
136
|
-
applyRestore?: (restores: { id: string; originalPosition: XYPosition }[]) => void
|
|
137
|
-
): { id: string; originalPosition: XYPosition }[] {
|
|
138
|
-
if (!dragState) return [];
|
|
139
|
-
|
|
140
|
-
const restores = Object.entries(dragState.offsets).map(([id, offset]) => ({
|
|
141
|
-
id,
|
|
142
|
-
originalPosition: { ...offset.originalPosition }
|
|
143
|
-
}));
|
|
144
|
-
|
|
145
|
-
if (applyRestore) {
|
|
146
|
-
applyRestore(restores);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
dragState = null;
|
|
150
|
-
return restores;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function commitMultiDrag(): void {
|
|
154
|
-
dragState = null;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
get dragState() {
|
|
159
|
-
return dragState;
|
|
160
|
-
},
|
|
161
|
-
get isMultiDrag() {
|
|
162
|
-
return Boolean(dragState && dragState.count > 1);
|
|
163
|
-
},
|
|
164
|
-
get count() {
|
|
165
|
-
return dragState ? dragState.count : 0;
|
|
166
|
-
},
|
|
167
|
-
get leadId() {
|
|
168
|
-
return dragState?.leadId ?? null;
|
|
169
|
-
},
|
|
170
|
-
startMultiDrag,
|
|
171
|
-
getFollowerTransform,
|
|
172
|
-
cancelMultiDrag,
|
|
173
|
-
commitMultiDrag
|
|
174
|
-
};
|
|
175
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import { untrack } from 'svelte';
|
|
2
|
-
import type { Position } from '@xyflow/svelte';
|
|
3
|
-
import type { CanvasNode, CanvasEdge } from './types.js';
|
|
4
|
-
|
|
5
|
-
export interface HandleConfig {
|
|
6
|
-
id?: string | null;
|
|
7
|
-
type: 'source' | 'target';
|
|
8
|
-
position: Position;
|
|
9
|
-
x?: number;
|
|
10
|
-
y?: number;
|
|
11
|
-
width?: number;
|
|
12
|
-
height?: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export interface NodeMeasurement {
|
|
16
|
-
width: number;
|
|
17
|
-
height: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Preserves cached .measured dimensions from existing nodes to newly constructed nodes.
|
|
22
|
-
* Resolves measurements without subscribing to reactive updates on the existing collection.
|
|
23
|
-
*/
|
|
24
|
-
export function preserveNodeMeasurements<TNode extends CanvasNode = CanvasNode>(
|
|
25
|
-
newNodes: TNode[],
|
|
26
|
-
existingNodes: TNode[] | Map<string, TNode>
|
|
27
|
-
): TNode[] {
|
|
28
|
-
const map =
|
|
29
|
-
existingNodes instanceof Map
|
|
30
|
-
? existingNodes
|
|
31
|
-
: new Map(existingNodes.map((n) => [n.id, n]));
|
|
32
|
-
|
|
33
|
-
for (const node of newNodes) {
|
|
34
|
-
const prev = map.get(node.id);
|
|
35
|
-
if (prev && (prev as any).measured) {
|
|
36
|
-
(node as any).measured = { ...(prev as any).measured };
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return newNodes;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Pre-populates handle geometry on a node to prevent @xyflow/system's getNodesInside
|
|
44
|
-
* from evaluating `forceInitialRender = !node.internals.handleBounds` and triggering
|
|
45
|
-
* a false full-canvas selection on the first frame of marquee drag-selection.
|
|
46
|
-
*/
|
|
47
|
-
export function normalizeNodeHandles<TNode extends CanvasNode = CanvasNode>(
|
|
48
|
-
node: TNode,
|
|
49
|
-
handles: HandleConfig[] = []
|
|
50
|
-
): TNode {
|
|
51
|
-
(node as any).handles = handles;
|
|
52
|
-
return node;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface CreateCanvasNodeSyncOptions<
|
|
56
|
-
TItem,
|
|
57
|
-
TNode extends CanvasNode = CanvasNode,
|
|
58
|
-
TEdge extends CanvasEdge = CanvasEdge
|
|
59
|
-
> {
|
|
60
|
-
/**
|
|
61
|
-
* Reactive getter returning raw domain items.
|
|
62
|
-
*/
|
|
63
|
-
items: () => TItem[];
|
|
64
|
-
/**
|
|
65
|
-
* Factory function converting a domain item into a CanvasNode.
|
|
66
|
-
*/
|
|
67
|
-
toNode: (
|
|
68
|
-
item: TItem,
|
|
69
|
-
index: number,
|
|
70
|
-
context: { existingNode?: TNode; prevNodesMap: Map<string, TNode> }
|
|
71
|
-
) => TNode;
|
|
72
|
-
/**
|
|
73
|
-
* Optional factory function converting domain items and generated nodes into CanvasEdges.
|
|
74
|
-
*/
|
|
75
|
-
toEdges?: (items: TItem[], nodes: TNode[]) => TEdge[];
|
|
76
|
-
/**
|
|
77
|
-
* Optional default handle configuration provider for connectable nodes.
|
|
78
|
-
*/
|
|
79
|
-
defaultHandles?: (node: TNode) => HandleConfig[] | undefined;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Headless Svelte 5 rune that synchronizes reactive domain items with SvelteFlow nodes/edges,
|
|
84
|
-
* automatically untracking previous node reads, caching .measured dimensions, and normalizing
|
|
85
|
-
* handle bounds to prevent Svelte 5 effect_update_depth_exceeded and marquee drag flashes.
|
|
86
|
-
*/
|
|
87
|
-
export function createCanvasNodeSync<
|
|
88
|
-
TItem,
|
|
89
|
-
TNode extends CanvasNode = CanvasNode,
|
|
90
|
-
TEdge extends CanvasEdge = CanvasEdge
|
|
91
|
-
>(options: CreateCanvasNodeSyncOptions<TItem, TNode, TEdge>) {
|
|
92
|
-
let nodes = $state<TNode[]>([]);
|
|
93
|
-
let edges = $state<TEdge[]>([]);
|
|
94
|
-
|
|
95
|
-
$effect.pre(() => {
|
|
96
|
-
const rawItems = options.items();
|
|
97
|
-
|
|
98
|
-
// Untrack the read of current nodes to avoid recursive effect loops
|
|
99
|
-
const existingNodesMap = untrack(() => new Map(nodes.map((n) => [n.id, n])));
|
|
100
|
-
|
|
101
|
-
const newNodes = rawItems.map((item, index) => {
|
|
102
|
-
const existing = existingNodesMap.get((item as any)?.id);
|
|
103
|
-
const node = options.toNode(item, index, {
|
|
104
|
-
existingNode: existing,
|
|
105
|
-
prevNodesMap: existingNodesMap
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Automatically preserve measured dimensions if available
|
|
109
|
-
if (existing && (existing as any).measured && !(node as any).measured) {
|
|
110
|
-
(node as any).measured = { ...(existing as any).measured };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Pre-populate handles if provided
|
|
114
|
-
if (options.defaultHandles && !(node as any).handles) {
|
|
115
|
-
const handles = options.defaultHandles(node);
|
|
116
|
-
if (handles) {
|
|
117
|
-
(node as any).handles = handles;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
return node;
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const newEdges = options.toEdges ? options.toEdges(rawItems, newNodes) : [];
|
|
125
|
-
|
|
126
|
-
nodes = newNodes;
|
|
127
|
-
edges = newEdges;
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
return {
|
|
131
|
-
get nodes() {
|
|
132
|
-
return nodes;
|
|
133
|
-
},
|
|
134
|
-
set nodes(value: TNode[]) {
|
|
135
|
-
nodes = value;
|
|
136
|
-
},
|
|
137
|
-
get edges() {
|
|
138
|
-
return edges;
|
|
139
|
-
},
|
|
140
|
-
set edges(value: TEdge[]) {
|
|
141
|
-
edges = value;
|
|
142
|
-
},
|
|
143
|
-
updateNodeMeasurements(id: string, width: number, height: number) {
|
|
144
|
-
const node = nodes.find((n) => n.id === id);
|
|
145
|
-
if (node) {
|
|
146
|
-
(node as any).measured = { width, height };
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { CanvasInteractionPresetConfig } from './types.js';
|
|
2
|
-
|
|
3
|
-
export interface CanvasSelectionOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Whether spacebar panning is currently active (suspends marquee selection).
|
|
6
|
-
*/
|
|
7
|
-
isSpacebarPanning?: boolean;
|
|
8
|
-
/**
|
|
9
|
-
* Selection mode: 'partial' (default) selects nodes intersected by selection box,
|
|
10
|
-
* 'full' requires nodes to be fully enclosed.
|
|
11
|
-
*/
|
|
12
|
-
selectionMode?: 'partial' | 'full';
|
|
13
|
-
/**
|
|
14
|
-
* Additional keys that trigger multi-selection (default: ['Shift', 'Meta']).
|
|
15
|
-
*/
|
|
16
|
-
multiSelectionKey?: string[];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Generates an interaction preset configured for left-click marquee drag-selection
|
|
21
|
-
* while preserving middle-mouse and spacebar panning.
|
|
22
|
-
*/
|
|
23
|
-
export function getMarqueeSelectionPreset(
|
|
24
|
-
options: CanvasSelectionOptions = {}
|
|
25
|
-
): CanvasInteractionPresetConfig {
|
|
26
|
-
const isSpacebarPanning = options.isSpacebarPanning ?? false;
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
snapGrid: [25, 25],
|
|
30
|
-
panOnDrag: isSpacebarPanning ? [0, 1, 2] : [1, 2],
|
|
31
|
-
panOnScroll: false,
|
|
32
|
-
selectionOnDrag: !isSpacebarPanning,
|
|
33
|
-
colorMode: 'system' as any,
|
|
34
|
-
multiSelectionKey: options.multiSelectionKey ?? ['Shift', 'Meta'],
|
|
35
|
-
selectionMode: (options.selectionMode ?? 'partial') as any
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Keyboard listener helper to deselect nodes on Escape.
|
|
41
|
-
*/
|
|
42
|
-
export function handleDeselectOnEscape(
|
|
43
|
-
event: KeyboardEvent,
|
|
44
|
-
onDeselect: () => void
|
|
45
|
-
): boolean {
|
|
46
|
-
if (event.key === 'Escape' || event.code === 'Escape') {
|
|
47
|
-
onDeselect();
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { isEventFromTextInput } from './createCanvasClipboard.svelte.js';
|
|
2
|
-
import type { CanvasNode, CanvasShortcutsOptions } from './types.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Attaches keyboard shortcut handlers for canvas interaction.
|
|
6
|
-
* Automatically ignores shortcuts when the user is typing into text inputs, textareas, or rich editors.
|
|
7
|
-
*
|
|
8
|
-
* @returns A cleanup function to remove event listeners.
|
|
9
|
-
*/
|
|
10
|
-
export function createCanvasShortcuts<TNode extends CanvasNode = CanvasNode>(
|
|
11
|
-
options: CanvasShortcutsOptions<TNode>
|
|
12
|
-
): () => void {
|
|
13
|
-
const target = options.targetElement ?? (typeof window !== 'undefined' ? window : null);
|
|
14
|
-
if (!target) return () => {};
|
|
15
|
-
|
|
16
|
-
function handleKeyDown(event: Event) {
|
|
17
|
-
const keyboardEvent = event as KeyboardEvent;
|
|
18
|
-
if (isEventFromTextInput(keyboardEvent)) {
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const isMod = keyboardEvent.metaKey || keyboardEvent.ctrlKey;
|
|
23
|
-
const isShift = keyboardEvent.shiftKey;
|
|
24
|
-
const key = keyboardEvent.key;
|
|
25
|
-
|
|
26
|
-
const selectedNodes = options.getSelectedNodes ? options.getSelectedNodes() : [];
|
|
27
|
-
|
|
28
|
-
if (key === 'Backspace' || key === 'Delete') {
|
|
29
|
-
if (options.onDelete && selectedNodes.length > 0) {
|
|
30
|
-
keyboardEvent.preventDefault();
|
|
31
|
-
options.onDelete(selectedNodes);
|
|
32
|
-
}
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (isMod) {
|
|
37
|
-
const lowerKey = key.toLowerCase();
|
|
38
|
-
|
|
39
|
-
if (lowerKey === 'c' && !isShift) {
|
|
40
|
-
if (options.onCopy) {
|
|
41
|
-
// We let the native copy event fire or call onCopy
|
|
42
|
-
options.onCopy();
|
|
43
|
-
}
|
|
44
|
-
} else if (lowerKey === 'x' && !isShift) {
|
|
45
|
-
if (options.onCut) {
|
|
46
|
-
options.onCut();
|
|
47
|
-
}
|
|
48
|
-
} else if (lowerKey === 'd' && !isShift) {
|
|
49
|
-
if (options.onDuplicate && selectedNodes.length > 0) {
|
|
50
|
-
keyboardEvent.preventDefault();
|
|
51
|
-
options.onDuplicate(selectedNodes);
|
|
52
|
-
}
|
|
53
|
-
} else if (lowerKey === 'a' && !isShift) {
|
|
54
|
-
if (options.onSelectAll) {
|
|
55
|
-
keyboardEvent.preventDefault();
|
|
56
|
-
options.onSelectAll();
|
|
57
|
-
}
|
|
58
|
-
} else if (lowerKey === 'g') {
|
|
59
|
-
if (isShift) {
|
|
60
|
-
if (options.onUngroup && selectedNodes.length > 0) {
|
|
61
|
-
keyboardEvent.preventDefault();
|
|
62
|
-
options.onUngroup(selectedNodes);
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
if (options.onGroup && selectedNodes.length > 0) {
|
|
66
|
-
keyboardEvent.preventDefault();
|
|
67
|
-
options.onGroup(selectedNodes);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
} else if (key === '=' || key === '+') {
|
|
71
|
-
if (options.onZoomIn) {
|
|
72
|
-
keyboardEvent.preventDefault();
|
|
73
|
-
options.onZoomIn();
|
|
74
|
-
}
|
|
75
|
-
} else if (key === '-') {
|
|
76
|
-
if (options.onZoomOut) {
|
|
77
|
-
keyboardEvent.preventDefault();
|
|
78
|
-
options.onZoomOut();
|
|
79
|
-
}
|
|
80
|
-
} else if (key === '0') {
|
|
81
|
-
if (options.onFitView) {
|
|
82
|
-
keyboardEvent.preventDefault();
|
|
83
|
-
options.onFitView();
|
|
84
|
-
}
|
|
85
|
-
} else if (lowerKey === 'f') {
|
|
86
|
-
if (options.onSearch) {
|
|
87
|
-
keyboardEvent.preventDefault();
|
|
88
|
-
options.onSearch();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
target.addEventListener('keydown', handleKeyDown as EventListener);
|
|
95
|
-
|
|
96
|
-
return () => {
|
|
97
|
-
target.removeEventListener('keydown', handleKeyDown as EventListener);
|
|
98
|
-
};
|
|
99
|
-
}
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import { cubicOut } from 'svelte/easing';
|
|
2
|
-
import type { XYPosition } from '@xyflow/svelte';
|
|
3
|
-
import type {
|
|
4
|
-
CanvasNode,
|
|
5
|
-
FanOutTrajectoryOptions,
|
|
6
|
-
NodeTransition,
|
|
7
|
-
TrajectoryFunction,
|
|
8
|
-
TrajectoryPoint
|
|
9
|
-
} from './types.js';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Standard linear trajectory: straight line interpolation with linear scale & opacity progression.
|
|
13
|
-
*/
|
|
14
|
-
export const linearTrajectory: TrajectoryFunction = (
|
|
15
|
-
origin: XYPosition,
|
|
16
|
-
target: XYPosition,
|
|
17
|
-
progress: number
|
|
18
|
-
): TrajectoryPoint => {
|
|
19
|
-
return {
|
|
20
|
-
position: {
|
|
21
|
-
x: origin.x + (target.x - origin.x) * progress,
|
|
22
|
-
y: origin.y + (target.y - origin.y) * progress
|
|
23
|
-
},
|
|
24
|
-
scale: progress,
|
|
25
|
-
opacity: Math.min(1, progress * 1.5)
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Linear position trajectory: pure position interpolation without altering scale or opacity.
|
|
31
|
-
* Used for displaced sibling nodes to ensure their visibility remains unaltered.
|
|
32
|
-
*/
|
|
33
|
-
export const linearPositionTrajectory: TrajectoryFunction = (
|
|
34
|
-
origin: XYPosition,
|
|
35
|
-
target: XYPosition,
|
|
36
|
-
progress: number
|
|
37
|
-
): TrajectoryPoint => {
|
|
38
|
-
return {
|
|
39
|
-
position: {
|
|
40
|
-
x: origin.x + (target.x - origin.x) * progress,
|
|
41
|
-
y: origin.y + (target.y - origin.y) * progress
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Creates a fan-out trajectory where nodes arc outward from origin to target.
|
|
48
|
-
* Matches the curved distribution shown in multi-node branching diagrams.
|
|
49
|
-
*/
|
|
50
|
-
export function createFanOutTrajectory(options: FanOutTrajectoryOptions = {}): TrajectoryFunction {
|
|
51
|
-
const {
|
|
52
|
-
curvature = 0.4,
|
|
53
|
-
startScale = 0,
|
|
54
|
-
endScale = 1,
|
|
55
|
-
startOpacity = 0,
|
|
56
|
-
endOpacity = 1
|
|
57
|
-
} = options;
|
|
58
|
-
|
|
59
|
-
return (
|
|
60
|
-
origin: XYPosition,
|
|
61
|
-
target: XYPosition,
|
|
62
|
-
progress: number,
|
|
63
|
-
index: number,
|
|
64
|
-
total: number
|
|
65
|
-
): TrajectoryPoint => {
|
|
66
|
-
const dx = target.x - origin.x;
|
|
67
|
-
const dy = target.y - origin.y;
|
|
68
|
-
|
|
69
|
-
// Normalized index from -1 (top) to +1 (bottom)
|
|
70
|
-
const normIndex = total > 1 ? (index - (total - 1) / 2) / ((total - 1) / 2) : 0;
|
|
71
|
-
|
|
72
|
-
// Perpendicular control point offset to create smooth fan arc
|
|
73
|
-
const midX = origin.x + dx * 0.4;
|
|
74
|
-
const midY = origin.y + dy * 0.1 - normIndex * Math.abs(dx) * 0.15 * curvature;
|
|
75
|
-
|
|
76
|
-
// Quadratic Bezier interpolation: B(t) = (1-t)^2 * P0 + 2(1-t)t * P1 + t^2 * P2
|
|
77
|
-
const t = progress;
|
|
78
|
-
const oneMinusT = 1 - t;
|
|
79
|
-
|
|
80
|
-
const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * midX + t * t * target.x;
|
|
81
|
-
const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * midY + t * t * target.y;
|
|
82
|
-
|
|
83
|
-
const scale = startScale + (endScale - startScale) * progress;
|
|
84
|
-
const opacity = startOpacity + (endOpacity - startOpacity) * progress;
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
position: { x, y },
|
|
88
|
-
scale,
|
|
89
|
-
opacity
|
|
90
|
-
};
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Creates a custom quadratic Bezier trajectory with a specified or dynamically calculated control point.
|
|
96
|
-
*/
|
|
97
|
-
export function createBezierTrajectory(
|
|
98
|
-
getControlPoint: (origin: XYPosition, target: XYPosition) => XYPosition
|
|
99
|
-
): TrajectoryFunction {
|
|
100
|
-
return (origin: XYPosition, target: XYPosition, progress: number): TrajectoryPoint => {
|
|
101
|
-
const cp = getControlPoint(origin, target);
|
|
102
|
-
const t = progress;
|
|
103
|
-
const oneMinusT = 1 - t;
|
|
104
|
-
|
|
105
|
-
const x = oneMinusT * oneMinusT * origin.x + 2 * oneMinusT * t * cp.x + t * t * target.x;
|
|
106
|
-
const y = oneMinusT * oneMinusT * origin.y + 2 * oneMinusT * t * cp.y + t * t * target.y;
|
|
107
|
-
|
|
108
|
-
return {
|
|
109
|
-
position: { x, y },
|
|
110
|
-
scale: progress,
|
|
111
|
-
opacity: progress
|
|
112
|
-
};
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Coordinates and animates multiple node transitions simultaneously with zero per-frame array allocations.
|
|
118
|
-
* Directly mutates node positions and data (scale, opacity) in-place during the animation loop.
|
|
119
|
-
*/
|
|
120
|
-
export function runMultiNodeTransition<TNode extends CanvasNode = CanvasNode>(
|
|
121
|
-
transitions: NodeTransition<TNode>[],
|
|
122
|
-
options: {
|
|
123
|
-
defaultDuration?: number;
|
|
124
|
-
defaultEasing?: (t: number) => number;
|
|
125
|
-
onUpdate?: () => void;
|
|
126
|
-
onComplete?: () => void;
|
|
127
|
-
} = {}
|
|
128
|
-
): () => void {
|
|
129
|
-
if (transitions.length === 0) {
|
|
130
|
-
options.onComplete?.();
|
|
131
|
-
return () => {};
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const { defaultDuration = 400, defaultEasing = cubicOut, onUpdate, onComplete } = options;
|
|
135
|
-
|
|
136
|
-
let isCancelled = false;
|
|
137
|
-
const startTime = Date.now();
|
|
138
|
-
|
|
139
|
-
const items = transitions.map((tr, i) => ({
|
|
140
|
-
...tr,
|
|
141
|
-
index: tr.index ?? i,
|
|
142
|
-
total: tr.total ?? transitions.length,
|
|
143
|
-
duration: tr.duration ?? defaultDuration,
|
|
144
|
-
easing: tr.easing ?? defaultEasing,
|
|
145
|
-
delay: tr.delay ?? 0,
|
|
146
|
-
trajectory: tr.trajectory ?? linearTrajectory
|
|
147
|
-
}));
|
|
148
|
-
|
|
149
|
-
function frame() {
|
|
150
|
-
if (isCancelled) return;
|
|
151
|
-
|
|
152
|
-
const now = Date.now();
|
|
153
|
-
let allCompleted = true;
|
|
154
|
-
|
|
155
|
-
for (let i = 0; i < items.length; i++) {
|
|
156
|
-
const item = items[i];
|
|
157
|
-
const elapsed = now - (startTime + item.delay);
|
|
158
|
-
|
|
159
|
-
if (elapsed < 0) {
|
|
160
|
-
allCompleted = false;
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const rawProgress = item.duration <= 0 ? 1 : Math.min(elapsed / item.duration, 1);
|
|
165
|
-
const progress = item.duration <= 0 ? 1 : item.easing(rawProgress);
|
|
166
|
-
|
|
167
|
-
const point = item.trajectory(item.from, item.to, progress, item.index, item.total);
|
|
168
|
-
|
|
169
|
-
// Direct in-place mutation avoids array reconstruction during RAF
|
|
170
|
-
item.node.position = { ...point.position };
|
|
171
|
-
|
|
172
|
-
if (point.scale !== undefined || point.opacity !== undefined) {
|
|
173
|
-
if (!item.node.data) {
|
|
174
|
-
item.node.data = {} as any;
|
|
175
|
-
}
|
|
176
|
-
if (point.scale !== undefined) {
|
|
177
|
-
(item.node.data as any).scale = point.scale;
|
|
178
|
-
}
|
|
179
|
-
if (point.opacity !== undefined) {
|
|
180
|
-
(item.node.data as any).opacity = point.opacity;
|
|
181
|
-
const currentStyle = (item.node.style || '').replace(/opacity:\s*[^;]+;?/g, '').trim();
|
|
182
|
-
item.node.style = `${currentStyle ? currentStyle + '; ' : ''}opacity: ${point.opacity};`;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
if (rawProgress < 1) {
|
|
187
|
-
allCompleted = false;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
onUpdate?.();
|
|
192
|
-
|
|
193
|
-
if (!allCompleted) {
|
|
194
|
-
if (typeof requestAnimationFrame !== 'undefined') {
|
|
195
|
-
requestAnimationFrame(frame);
|
|
196
|
-
}
|
|
197
|
-
} else {
|
|
198
|
-
onComplete?.();
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
frame();
|
|
203
|
-
|
|
204
|
-
return () => {
|
|
205
|
-
isCancelled = true;
|
|
206
|
-
};
|
|
207
|
-
}
|