@pixerate/editor-svelte 0.15.3 → 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.
Files changed (149) hide show
  1. package/dist/BubbleMenu.svelte.d.ts +14 -0
  2. package/dist/BubbleMenu.svelte.d.ts.map +1 -0
  3. package/{src → dist}/EditableTextNodeEditor.svelte +1 -1
  4. package/dist/EditableTextNodeEditor.svelte.d.ts +26 -0
  5. package/dist/EditableTextNodeEditor.svelte.d.ts.map +1 -0
  6. package/dist/TemplateRenderer.svelte.d.ts +14 -0
  7. package/dist/TemplateRenderer.svelte.d.ts.map +1 -0
  8. package/dist/canvas/AgentPresenceLayer.svelte.d.ts +9 -0
  9. package/dist/canvas/AgentPresenceLayer.svelte.d.ts.map +1 -0
  10. package/dist/canvas/FloatingHorizontalScrollbar.svelte.d.ts +9 -0
  11. package/dist/canvas/FloatingHorizontalScrollbar.svelte.d.ts.map +1 -0
  12. package/dist/canvas/createCanvasClipboard.svelte.d.ts +32 -0
  13. package/dist/canvas/createCanvasClipboard.svelte.d.ts.map +1 -0
  14. package/dist/canvas/createCanvasClipboard.svelte.js +171 -0
  15. package/dist/canvas/createCanvasDisplacement.d.ts +32 -0
  16. package/dist/canvas/createCanvasDisplacement.d.ts.map +1 -0
  17. package/dist/canvas/createCanvasDisplacement.js +170 -0
  18. package/dist/canvas/createCanvasDocking.svelte.d.ts +56 -0
  19. package/dist/canvas/createCanvasDocking.svelte.d.ts.map +1 -0
  20. package/dist/canvas/createCanvasDocking.svelte.js +227 -0
  21. package/dist/canvas/createCanvasExplosion.svelte.d.ts +31 -0
  22. package/dist/canvas/createCanvasExplosion.svelte.d.ts.map +1 -0
  23. package/dist/canvas/createCanvasExplosion.svelte.js +346 -0
  24. package/dist/canvas/createCanvasGraph.svelte.d.ts +62 -0
  25. package/dist/canvas/createCanvasGraph.svelte.d.ts.map +1 -0
  26. package/dist/canvas/createCanvasGraph.svelte.js +223 -0
  27. package/dist/canvas/createCanvasInteractions.svelte.d.ts +28 -0
  28. package/dist/canvas/createCanvasInteractions.svelte.d.ts.map +1 -0
  29. package/dist/canvas/createCanvasInteractions.svelte.js +95 -0
  30. package/dist/canvas/createCanvasLayout.d.ts +22 -0
  31. package/dist/canvas/createCanvasLayout.d.ts.map +1 -0
  32. package/dist/canvas/createCanvasLayout.js +76 -0
  33. package/dist/canvas/createCanvasMultiDrag.svelte.d.ts +61 -0
  34. package/dist/canvas/createCanvasMultiDrag.svelte.d.ts.map +1 -0
  35. package/dist/canvas/createCanvasMultiDrag.svelte.js +105 -0
  36. package/dist/canvas/createCanvasNodeSync.svelte.d.ts +58 -0
  37. package/dist/canvas/createCanvasNodeSync.svelte.d.ts.map +1 -0
  38. package/dist/canvas/createCanvasNodeSync.svelte.js +82 -0
  39. package/dist/canvas/createCanvasSelection.svelte.d.ts +26 -0
  40. package/dist/canvas/createCanvasSelection.svelte.d.ts.map +1 -0
  41. package/dist/canvas/createCanvasSelection.svelte.js +26 -0
  42. package/dist/canvas/createCanvasShortcuts.d.ts +9 -0
  43. package/dist/canvas/createCanvasShortcuts.d.ts.map +1 -0
  44. package/dist/canvas/createCanvasShortcuts.js +97 -0
  45. package/dist/canvas/createCanvasTrajectory.d.ts +31 -0
  46. package/dist/canvas/createCanvasTrajectory.d.ts.map +1 -0
  47. package/dist/canvas/createCanvasTrajectory.js +141 -0
  48. package/dist/canvas/createStreamingCanvas.svelte.d.ts +19 -0
  49. package/dist/canvas/createStreamingCanvas.svelte.d.ts.map +1 -0
  50. package/dist/canvas/createStreamingCanvas.svelte.js +32 -0
  51. package/dist/canvas/index.d.ts +18 -688
  52. package/dist/canvas/index.d.ts.map +1 -0
  53. package/dist/canvas/index.js +34 -1839
  54. package/dist/canvas/types.d.ts +183 -0
  55. package/dist/canvas/types.d.ts.map +1 -0
  56. package/dist/canvas/types.js +1 -0
  57. package/dist/createNavigationGuard.svelte.d.ts +24 -0
  58. package/dist/createNavigationGuard.svelte.d.ts.map +1 -0
  59. package/dist/createNavigationGuard.svelte.js +65 -0
  60. package/dist/editor.svelte.d.ts +23 -0
  61. package/dist/editor.svelte.d.ts.map +1 -0
  62. package/dist/editor.svelte.js +41 -0
  63. package/dist/history/createHistoryShortcuts.d.ts +24 -0
  64. package/dist/history/createHistoryShortcuts.d.ts.map +1 -0
  65. package/dist/history/createHistoryShortcuts.js +57 -0
  66. package/dist/history/history.svelte.d.ts +18 -0
  67. package/dist/history/history.svelte.d.ts.map +1 -0
  68. package/dist/history/history.svelte.js +43 -0
  69. package/dist/history/index.d.ts +3 -0
  70. package/dist/history/index.d.ts.map +1 -0
  71. package/dist/history/index.js +2 -0
  72. package/{src → dist}/image-editor/ImageEditor.svelte +1 -1
  73. package/dist/image-editor/ImageEditor.svelte.d.ts +17 -0
  74. package/dist/image-editor/ImageEditor.svelte.d.ts.map +1 -0
  75. package/dist/image-editor/imageEditorState.svelte.d.ts +50 -0
  76. package/dist/image-editor/imageEditorState.svelte.d.ts.map +1 -0
  77. package/dist/image-editor/imageEditorState.svelte.js +132 -0
  78. package/dist/image-editor/index.d.ts +6 -16
  79. package/dist/image-editor/index.d.ts.map +1 -0
  80. package/dist/image-editor/index.js +2 -9
  81. package/dist/index.d.ts +20 -249
  82. package/dist/index.d.ts.map +1 -0
  83. package/dist/index.js +10 -1119
  84. package/dist/navigationGuard.d.ts +21 -0
  85. package/dist/navigationGuard.d.ts.map +1 -0
  86. package/dist/navigationGuard.js +59 -0
  87. package/{src → dist}/spreadsheet/FormulaBar.svelte +1 -1
  88. package/dist/spreadsheet/FormulaBar.svelte.d.ts +17 -0
  89. package/dist/spreadsheet/FormulaBar.svelte.d.ts.map +1 -0
  90. package/{src → dist}/spreadsheet/SpreadsheetEditor.svelte +1 -1
  91. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts +22 -0
  92. package/dist/spreadsheet/SpreadsheetEditor.svelte.d.ts.map +1 -0
  93. package/dist/spreadsheet/index.d.ts +9 -0
  94. package/dist/spreadsheet/index.d.ts.map +1 -0
  95. package/{src/spreadsheet/index.ts → dist/spreadsheet/index.js} +1 -1
  96. package/dist/spreadsheet/spreadsheet.svelte.d.ts +33 -0
  97. package/dist/spreadsheet/spreadsheet.svelte.d.ts.map +1 -0
  98. package/dist/spreadsheet/spreadsheet.svelte.js +160 -0
  99. package/package.json +13 -15
  100. package/dist/canvas/index.js.map +0 -1
  101. package/dist/chunk-HEOI4UNF.js +0 -1191
  102. package/dist/chunk-HEOI4UNF.js.map +0 -1
  103. package/dist/image-editor/index.js.map +0 -1
  104. package/dist/index.css +0 -50
  105. package/dist/index.css.map +0 -1
  106. package/dist/index.js.map +0 -1
  107. package/src/canvas/__tests__/canvas-clipboard.test.ts +0 -154
  108. package/src/canvas/__tests__/canvas-docking.test.ts +0 -288
  109. package/src/canvas/__tests__/canvas-explosion.test.ts +0 -634
  110. package/src/canvas/__tests__/canvas-graph.test.ts +0 -159
  111. package/src/canvas/__tests__/canvas-interactions.test.ts +0 -86
  112. package/src/canvas/__tests__/canvas-layout.test.ts +0 -75
  113. package/src/canvas/__tests__/canvas-multi-drag.test.ts +0 -112
  114. package/src/canvas/__tests__/canvas-node-sync.svelte.test.ts +0 -104
  115. package/src/canvas/__tests__/canvas-shortcuts.test.ts +0 -59
  116. package/src/canvas/createCanvasClipboard.svelte.ts +0 -219
  117. package/src/canvas/createCanvasDisplacement.ts +0 -253
  118. package/src/canvas/createCanvasDocking.svelte.ts +0 -355
  119. package/src/canvas/createCanvasExplosion.svelte.ts +0 -457
  120. package/src/canvas/createCanvasGraph.svelte.ts +0 -330
  121. package/src/canvas/createCanvasInteractions.svelte.ts +0 -112
  122. package/src/canvas/createCanvasLayout.ts +0 -129
  123. package/src/canvas/createCanvasMultiDrag.svelte.ts +0 -175
  124. package/src/canvas/createCanvasNodeSync.svelte.ts +0 -150
  125. package/src/canvas/createCanvasSelection.svelte.ts +0 -51
  126. package/src/canvas/createCanvasShortcuts.ts +0 -99
  127. package/src/canvas/createCanvasTrajectory.ts +0 -207
  128. package/src/canvas/createStreamingCanvas.svelte.ts +0 -56
  129. package/src/canvas/index.d.ts +0 -688
  130. package/src/canvas/index.ts +0 -126
  131. package/src/canvas/types.ts +0 -223
  132. package/src/createNavigationGuard.svelte.ts +0 -97
  133. package/src/editor.svelte.ts +0 -65
  134. package/src/history/createHistoryShortcuts.ts +0 -95
  135. package/src/history/history.svelte.ts +0 -68
  136. package/src/history/index.ts +0 -2
  137. package/src/image-editor/imageEditorState.svelte.ts +0 -151
  138. package/src/image-editor/index.d.ts +0 -16
  139. package/src/image-editor/index.ts +0 -2
  140. package/src/index.d.ts +0 -249
  141. package/src/index.ts +0 -89
  142. package/src/navigationGuard.ts +0 -82
  143. package/src/spreadsheet/spreadsheet.svelte.ts +0 -176
  144. package/src/svelte.d.ts +0 -5
  145. /package/{src → dist}/BubbleMenu.svelte +0 -0
  146. /package/{src → dist}/TemplateRenderer.svelte +0 -0
  147. /package/{src → dist}/canvas/AgentPresenceLayer.svelte +0 -0
  148. /package/{src → dist}/canvas/FloatingHorizontalScrollbar.svelte +0 -0
  149. /package/{src → dist}/canvas/README.md +0 -0
@@ -1,159 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import {
3
- createCanvasGraph,
4
- calculateGraphDifferences,
5
- replaceNodeInGraph
6
- } from '../createCanvasGraph.svelte.js';
7
- import type { CanvasNode, CanvasEdge } from '../types.js';
8
-
9
- describe('createCanvasGraph & Graph Operations', () => {
10
- const createSampleNode = (id: string, x = 0, y = 0): CanvasNode => ({
11
- id,
12
- position: { x, y },
13
- data: { label: `Node ${id}` }
14
- });
15
-
16
- const createSampleEdge = (id: string, source: string, target: string): CanvasEdge => ({
17
- id,
18
- source,
19
- target
20
- });
21
-
22
- it('initializes with default empty or provided nodes and edges', () => {
23
- const graph = createCanvasGraph({
24
- initialNodes: [createSampleNode('1')],
25
- initialEdges: [createSampleEdge('e1', '1', '2')]
26
- });
27
-
28
- expect(graph.nodes).toHaveLength(1);
29
- expect(graph.edges).toHaveLength(1);
30
- expect(graph.nodes[0].id).toBe('1');
31
- });
32
-
33
- it('adds nodes and ignores duplicates', () => {
34
- const onNodesChange = vi.fn();
35
- const graph = createCanvasGraph({ onNodesChange });
36
-
37
- graph.addNodeOrNodes(createSampleNode('1'));
38
- expect(graph.nodes).toHaveLength(1);
39
- expect(onNodesChange).toHaveBeenCalledTimes(1);
40
-
41
- // Adding existing node does not duplicate
42
- graph.addNodeOrNodes(createSampleNode('1'));
43
- expect(graph.nodes).toHaveLength(1);
44
-
45
- // Adding multiple nodes
46
- graph.addNodeOrNodes([createSampleNode('2'), createSampleNode('3')]);
47
- expect(graph.nodes).toHaveLength(3);
48
- });
49
-
50
- it('adds and removes edges cleanly', () => {
51
- const onEdgesChange = vi.fn();
52
- const graph = createCanvasGraph({ onEdgesChange });
53
-
54
- const e1 = createSampleEdge('e1', '1', '2');
55
- const e2 = createSampleEdge('e2', '2', '3');
56
-
57
- graph.addEdge(e1);
58
- expect(graph.edges).toHaveLength(1);
59
-
60
- graph.addEdges([e2]);
61
- expect(graph.edges).toHaveLength(2);
62
-
63
- graph.removeEdges([e1]);
64
- expect(graph.edges).toHaveLength(1);
65
- expect(graph.edges[0].id).toBe('e2');
66
- });
67
-
68
- it('removes nodes by reference or ID matching', () => {
69
- const n1 = createSampleNode('1');
70
- const n2 = createSampleNode('2');
71
- const graph = createCanvasGraph({ initialNodes: [n1, n2] });
72
-
73
- graph.removeNodes([n1]);
74
- expect(graph.nodes).toHaveLength(1);
75
- expect(graph.nodes[0].id).toBe('2');
76
- });
77
-
78
- it('replaces a node and preserves connected edges when keepEdges is true', () => {
79
- const n1 = createSampleNode('1');
80
- const n2 = createSampleNode('2');
81
- const e1 = createSampleEdge('e1', '1', '2');
82
- const n1Replacement = createSampleNode('new-1', 10, 10);
83
-
84
- const result = replaceNodeInGraph([n1, n2], [e1], n1, n1Replacement, true);
85
- expect(result.nodes.some((n) => n.id === 'new-1')).toBe(true);
86
- expect(result.nodes.some((n) => n.id === '1')).toBe(false);
87
-
88
- // Edge should have updated source to new-1
89
- expect(result.edges).toHaveLength(1);
90
- expect(result.edges[0].source).toBe('new-1');
91
- expect(result.edges[0].target).toBe('2');
92
- });
93
-
94
- it('replaces a node without preserving edges when keepEdges is false', () => {
95
- const n1 = createSampleNode('1');
96
- const n2 = createSampleNode('2');
97
- const e1 = createSampleEdge('e1', '1', '2');
98
- const n1Replacement = createSampleNode('new-1', 10, 10);
99
-
100
- const result = replaceNodeInGraph([n1, n2], [e1], n1, n1Replacement, false);
101
- expect(result.nodes.some((n) => n.id === 'new-1')).toBe(true);
102
- expect(result.edges).toHaveLength(1);
103
- expect(result.edges[0].source).toBe('1'); // Unchanged
104
- });
105
-
106
- it('adds connected nodes connecting inbound and outbound targets', () => {
107
- const graph = createCanvasGraph();
108
- const inNode = createSampleNode('in');
109
- const outNode = createSampleNode('out');
110
- const midNode = createSampleNode('mid');
111
-
112
- graph.addNodeOrNodes([inNode, outNode]);
113
-
114
- graph.addConnectedNodes({
115
- nodesToAdd: [midNode],
116
- inboundNodes: [inNode],
117
- outboundNodes: [outNode],
118
- getEdgeLabel: (src, tgt) => `${src.id}->${tgt.id}`
119
- });
120
-
121
- expect(graph.nodes).toHaveLength(3);
122
- expect(graph.edges).toHaveLength(2);
123
- expect(graph.edges.find((e) => e.source === 'in' && e.target === 'mid')?.label).toBe('in->mid');
124
- expect(graph.edges.find((e) => e.source === 'mid' && e.target === 'out')?.label).toBe('mid->out');
125
- });
126
-
127
- it('accurately calculates graph differences against a baseline', () => {
128
- const n1 = createSampleNode('1', 0, 0);
129
- const n2 = createSampleNode('2', 50, 50);
130
- const e1 = createSampleEdge('e1', '1', '2');
131
-
132
- const baseline = {
133
- nodes: [n1, n2],
134
- edges: [e1]
135
- };
136
-
137
- // Node 2 position changed, node 3 added, edge 1 removed, edge 2 added
138
- const n2Changed = createSampleNode('2', 100, 100);
139
- const n3Added = createSampleNode('3', 200, 200);
140
- const e2Added = createSampleEdge('e2', '2', '3');
141
-
142
- const current = {
143
- nodes: [n1, n2Changed, n3Added],
144
- edges: [e2Added]
145
- };
146
-
147
- const diff = calculateGraphDifferences(current, baseline);
148
-
149
- expect(diff.changedNodes).toHaveLength(2); // n2 changed + n3 newly added
150
- expect(diff.changedNodes.map((n) => n.id)).toContain('2');
151
- expect(diff.changedNodes.map((n) => n.id)).toContain('3');
152
- expect(diff.removedNodes).toHaveLength(0);
153
-
154
- expect(diff.removedEdges).toHaveLength(1);
155
- expect(diff.removedEdges[0].id).toBe('e1');
156
- expect(diff.changedEdges).toHaveLength(1);
157
- expect(diff.changedEdges[0].id).toBe('e2');
158
- });
159
- });
@@ -1,86 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import {
3
- createCanvasInteractions,
4
- defaultSvelteFlowPreset,
5
- miroCompatiblePreset,
6
- restorePanelPointerEvents
7
- } from '../createCanvasInteractions.svelte.js';
8
-
9
- describe('createCanvasInteractions', () => {
10
- it('exposes valid default presets', () => {
11
- expect(defaultSvelteFlowPreset.snapGrid).toEqual([25, 25]);
12
- expect(defaultSvelteFlowPreset.panOnScroll).toBe(false);
13
- expect(defaultSvelteFlowPreset.selectionOnDrag).toBe(false);
14
-
15
- expect(miroCompatiblePreset.selectionOnDrag).toBe(true);
16
- expect(miroCompatiblePreset.panOnDrag).toEqual([1, 2]);
17
- });
18
-
19
- it('toggles spacebar panning mode on Space keydown and keyup', () => {
20
- const target = new EventTarget();
21
- const onPanModeChange = vi.fn();
22
-
23
- const interactions = createCanvasInteractions({
24
- targetElement: target as unknown as HTMLElement,
25
- onPanModeChange
26
- });
27
-
28
- const cleanup = interactions.attachListeners(target as unknown as HTMLElement);
29
-
30
- // Press Space
31
- const downEvent = new KeyboardEvent('keydown', { key: ' ' });
32
- target.dispatchEvent(downEvent);
33
-
34
- expect(interactions.isSpacebarPanning).toBe(true);
35
- expect(onPanModeChange).toHaveBeenCalledWith(true);
36
-
37
- // Release Space
38
- const upEvent = new KeyboardEvent('keyup', { key: ' ' });
39
- target.dispatchEvent(upEvent);
40
-
41
- expect(interactions.isSpacebarPanning).toBe(false);
42
- expect(onPanModeChange).toHaveBeenCalledWith(false);
43
-
44
- cleanup();
45
- });
46
-
47
- it('detects trackpad pinch on wheel event with ctrlKey', () => {
48
- const target = new EventTarget();
49
- const onTrackpadDetected = vi.fn();
50
-
51
- const interactions = createCanvasInteractions({
52
- targetElement: target as unknown as HTMLElement,
53
- onTrackpadDetected
54
- });
55
-
56
- const cleanup = interactions.attachListeners(target as unknown as HTMLElement);
57
-
58
- expect(interactions.trackpadDetected).toBe(false);
59
-
60
- // Normal wheel without ctrlKey
61
- const wheelEvent = new WheelEvent('wheel', { ctrlKey: false });
62
- target.dispatchEvent(wheelEvent);
63
- expect(interactions.trackpadDetected).toBe(false);
64
- expect(onTrackpadDetected).not.toHaveBeenCalled();
65
-
66
- // Trackpad pinch with ctrlKey
67
- const pinchEvent = new WheelEvent('wheel', { ctrlKey: true });
68
- target.dispatchEvent(pinchEvent);
69
- expect(interactions.trackpadDetected).toBe(true);
70
- expect(onTrackpadDetected).toHaveBeenCalledTimes(1);
71
-
72
- cleanup();
73
- });
74
-
75
- it('restores pointer events on DOM panels', () => {
76
- const panel = document.createElement('div');
77
- panel.className = 'svelte-flow__panel';
78
- panel.style.pointerEvents = 'none';
79
- document.body.appendChild(panel);
80
-
81
- restorePanelPointerEvents();
82
-
83
- expect(panel.style.pointerEvents).toBe('auto');
84
- document.body.removeChild(panel);
85
- });
86
- });
@@ -1,75 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- getCenteredNodePosition,
4
- centerNodes,
5
- getLayoutedNodes
6
- } from '../createCanvasLayout.js';
7
- import type { CanvasNode, CanvasEdge } from '../types.js';
8
-
9
- describe('createCanvasLayout & Geometry Helpers', () => {
10
- it('calculates centered node position correctly', () => {
11
- const screenDimensions = { width: 1000, height: 800 };
12
- const screenToFlowPosition = (pos: { x: number; y: number }) => ({ x: pos.x, y: pos.y });
13
-
14
- const pos = getCenteredNodePosition(200, 100, screenToFlowPosition, screenDimensions);
15
- // Center of 1000x800 is (500, 400). Top-left of 200x100 is (500 - 100, 400 - 50) = (400, 350)
16
- expect(pos.x).toBe(400);
17
- expect(pos.y).toBe(350);
18
- });
19
-
20
- it('centers multiple nodes using centerNodes', () => {
21
- const screenDimensions = { width: 1000, height: 800 };
22
- const screenToFlowPosition = (pos: { x: number; y: number }) => pos;
23
-
24
- const nodes: CanvasNode[] = [
25
- { id: '1', position: { x: 0, y: 0 }, data: {} },
26
- { id: '2', position: { x: 200, y: 100 }, data: {} }
27
- ];
28
-
29
- const getNodesBounds = () => ({ x: 0, y: 0, width: 300, height: 200 });
30
-
31
- const centered = centerNodes(nodes, screenToFlowPosition, getNodesBounds, screenDimensions);
32
-
33
- // Center of viewport is (500, 400). Upper left for 300x200 bounding box is (350, 300).
34
- // DeltaX = 350 - 0 = 350, DeltaY = 300 - 0 = 300.
35
- expect(centered[0].position.x).toBe(350);
36
- expect(centered[0].position.y).toBe(300);
37
- expect(centered[1].position.x).toBe(550);
38
- expect(centered[1].position.y).toBe(400);
39
- });
40
-
41
- it('computes Dagre hierarchical layout positions for nodes and edges', () => {
42
- const nodes: CanvasNode[] = [
43
- { id: 'root', position: { x: 0, y: 0 }, data: {}, measured: { width: 100, height: 100 } },
44
- { id: 'child1', position: { x: 0, y: 0 }, data: {}, measured: { width: 100, height: 100 } },
45
- { id: 'child2', position: { x: 0, y: 0 }, data: {}, measured: { width: 100, height: 100 } }
46
- ];
47
-
48
- const edges: CanvasEdge[] = [
49
- { id: 'e1', source: 'root', target: 'child1' },
50
- { id: 'e2', source: 'root', target: 'child2' }
51
- ];
52
-
53
- const getNodesBounds = () => ({ x: 100, y: 100, width: 400, height: 400 });
54
-
55
- const layouted = getLayoutedNodes(nodes, edges, getNodesBounds, {
56
- direction: 'LR',
57
- ranksep: 80,
58
- nodesep: 40
59
- });
60
-
61
- expect(layouted).toHaveLength(3);
62
-
63
- const root = layouted.find((n) => n.id === 'root')!;
64
- const child1 = layouted.find((n) => n.id === 'child1')!;
65
- const child2 = layouted.find((n) => n.id === 'child2')!;
66
-
67
- // Root should be to the left of child nodes in 'LR' layout
68
- expect(root.position.x).toBeLessThan(child1.position.x);
69
- expect(root.position.x).toBeLessThan(child2.position.x);
70
-
71
- // Target and source positions should be configured for horizontal layout
72
- expect(root.sourcePosition).toBe('right');
73
- expect(child1.targetPosition).toBe('left');
74
- });
75
- });
@@ -1,112 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createCanvasMultiDrag } from '../createCanvasMultiDrag.svelte.js';
3
- import {
4
- getMarqueeSelectionPreset,
5
- handleDeselectOnEscape
6
- } from '../createCanvasSelection.svelte.js';
7
- import type { CanvasNode } from '../types.js';
8
-
9
- describe('createCanvasMultiDrag & Selection Helpers', () => {
10
- const sampleNodes: CanvasNode[] = [
11
- { id: 'node-lead', position: { x: 100, y: 100 }, data: {} },
12
- { id: 'node-sub-1', position: { x: 100, y: 180 }, data: {} },
13
- { id: 'node-sub-2', position: { x: 100, y: 260 }, data: {} }
14
- ];
15
-
16
- it('returns null and does not activate multi-drag if only 1 node is selected', () => {
17
- const multiDrag = createCanvasMultiDrag();
18
- const state = multiDrag.startMultiDrag('node-lead', ['node-lead'], sampleNodes);
19
-
20
- expect(state).toBeNull();
21
- expect(multiDrag.isMultiDrag).toBe(false);
22
- expect(multiDrag.count).toBe(0);
23
- });
24
-
25
- it('computes relative follower offsets, stack fan-out, and rotational tilt for multi-selection', () => {
26
- const multiDrag = createCanvasMultiDrag({
27
- stackOffsetStepX: 5,
28
- stackOffsetStepY: 6,
29
- rotationAngles: [-2, 3]
30
- });
31
-
32
- const state = multiDrag.startMultiDrag(
33
- 'node-lead',
34
- ['node-lead', 'node-sub-1', 'node-sub-2'],
35
- sampleNodes
36
- );
37
-
38
- expect(state).not.toBeNull();
39
- expect(multiDrag.isMultiDrag).toBe(true);
40
- expect(multiDrag.count).toBe(3);
41
- expect(multiDrag.leadId).toBe('node-lead');
42
-
43
- // Check follower 1
44
- const f1 = multiDrag.getFollowerTransform('node-sub-1');
45
- expect(f1.isFollower).toBe(true);
46
- expect(f1.stackOffsetX).toBe(5);
47
- expect(f1.stackOffsetY).toBe(6);
48
- expect(f1.rotationDeg).toBe(-2);
49
- expect(f1.zIndex).toBe(49); // baseZIndex (50) - 1
50
-
51
- // Check follower 2
52
- const f2 = multiDrag.getFollowerTransform('node-sub-2');
53
- expect(f2.isFollower).toBe(true);
54
- expect(f2.stackOffsetX).toBe(10);
55
- expect(f2.stackOffsetY).toBe(12);
56
- expect(f2.rotationDeg).toBe(3);
57
- expect(f2.zIndex).toBe(48); // baseZIndex (50) - 2
58
-
59
- // Non-follower check
60
- const fLead = multiDrag.getFollowerTransform('node-lead');
61
- expect(fLead.isFollower).toBe(false);
62
- });
63
-
64
- it('restores original positions upon drag cancellation', () => {
65
- const multiDrag = createCanvasMultiDrag();
66
- multiDrag.startMultiDrag(
67
- 'node-lead',
68
- ['node-lead', 'node-sub-1'],
69
- sampleNodes
70
- );
71
-
72
- let restoredItems: any[] = [];
73
- const restores = multiDrag.cancelMultiDrag((items) => {
74
- restoredItems = items;
75
- });
76
-
77
- expect(restores).toHaveLength(1);
78
- expect(restores[0].id).toBe('node-sub-1');
79
- expect(restores[0].originalPosition).toEqual({ x: 100, y: 180 });
80
- expect(restoredItems).toEqual(restores);
81
- expect(multiDrag.isMultiDrag).toBe(false);
82
- });
83
-
84
- it('generates marquee selection preset with spacebar panning awareness', () => {
85
- const normalPreset = getMarqueeSelectionPreset({ isSpacebarPanning: false });
86
- expect(normalPreset.selectionOnDrag).toBe(true);
87
- expect(normalPreset.panOnDrag).toEqual([1, 2]);
88
-
89
- const panPreset = getMarqueeSelectionPreset({ isSpacebarPanning: true });
90
- expect(panPreset.selectionOnDrag).toBe(false);
91
- expect(panPreset.panOnDrag).toEqual([0, 1, 2]);
92
- });
93
-
94
- it('handles deselect on Escape key press', () => {
95
- let deselected = false;
96
- const handled = handleDeselectOnEscape(
97
- { key: 'Escape', code: 'Escape' } as KeyboardEvent,
98
- () => {
99
- deselected = true;
100
- }
101
- );
102
-
103
- expect(handled).toBe(true);
104
- expect(deselected).toBe(true);
105
-
106
- const ignored = handleDeselectOnEscape(
107
- { key: 'Enter', code: 'Enter' } as KeyboardEvent,
108
- () => {}
109
- );
110
- expect(ignored).toBe(false);
111
- });
112
- });
@@ -1,104 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { flushSync } from 'svelte';
3
- import { Position } from '@xyflow/svelte';
4
- import {
5
- preserveNodeMeasurements,
6
- normalizeNodeHandles,
7
- createCanvasNodeSync,
8
- type HandleConfig
9
- } from '../createCanvasNodeSync.svelte.js';
10
- import type { CanvasNode } from '../types.js';
11
-
12
- describe('createCanvasNodeSync & Node Measurement Caching', () => {
13
- it('preserves measured dimensions from existing nodes to newly created nodes', () => {
14
- const existingNodes: CanvasNode[] = [
15
- {
16
- id: 'node-1',
17
- position: { x: 100, y: 100 },
18
- data: {},
19
- measured: { width: 280, height: 160 }
20
- } as any,
21
- {
22
- id: 'node-2',
23
- position: { x: 200, y: 200 },
24
- data: {}
25
- } as any
26
- ];
27
-
28
- const freshNodes: CanvasNode[] = [
29
- { id: 'node-1', position: { x: 100, y: 100 }, data: { title: 'Updated' } } as any,
30
- { id: 'node-2', position: { x: 200, y: 200 }, data: { title: 'Fresh' } } as any,
31
- { id: 'node-3', position: { x: 300, y: 300 }, data: { title: 'Brand New' } } as any
32
- ];
33
-
34
- const preserved = preserveNodeMeasurements(freshNodes, existingNodes);
35
-
36
- expect((preserved[0] as any).measured).toEqual({ width: 280, height: 160 });
37
- expect((preserved[1] as any).measured).toBeUndefined();
38
- expect((preserved[2] as any).measured).toBeUndefined();
39
- });
40
-
41
- it('normalizes node handles to prevent xyflow forceInitialRender selection flash', () => {
42
- const rawNode: CanvasNode = {
43
- id: 'node-1',
44
- position: { x: 0, y: 0 },
45
- data: {}
46
- };
47
-
48
- const defaultHandles: HandleConfig[] = [
49
- { id: null, type: 'target', position: Position.Left, x: 0, y: 50, width: 12, height: 12 },
50
- { id: null, type: 'source', position: Position.Right, x: 260, y: 50, width: 12, height: 12 }
51
- ];
52
-
53
- const normalized = normalizeNodeHandles(rawNode, defaultHandles);
54
-
55
- expect((normalized as any).handles).toHaveLength(2);
56
- expect((normalized as any).handles[0].position).toBe(Position.Left);
57
- expect((normalized as any).handles[1].position).toBe(Position.Right);
58
- });
59
-
60
- it('synchronizes domain items to nodes and edges while caching measurements across ticks', () => {
61
- let domainItems = [
62
- { id: 'item-1', name: 'Task 1', dependencies: [] },
63
- { id: 'item-2', name: 'Task 2', dependencies: ['item-1'] }
64
- ];
65
-
66
- let sync: ReturnType<typeof createCanvasNodeSync<any>>;
67
-
68
- const cleanup = $effect.root(() => {
69
- sync = createCanvasNodeSync({
70
- items: () => domainItems,
71
- toNode: (item) => ({
72
- id: item.id,
73
- position: { x: 10, y: 20 },
74
- data: { label: item.name }
75
- }),
76
- toEdges: (items) =>
77
- items.flatMap((item) =>
78
- item.dependencies.map((depId) => ({
79
- id: `edge-${depId}-${item.id}`,
80
- source: depId,
81
- target: item.id
82
- }))
83
- ),
84
- defaultHandles: () => [
85
- { id: 'in', type: 'target', position: Position.Left },
86
- { id: 'out', type: 'source', position: Position.Right }
87
- ]
88
- });
89
- });
90
-
91
- flushSync();
92
-
93
- expect(sync!.nodes).toHaveLength(2);
94
- expect(sync!.edges).toHaveLength(1);
95
- expect(sync!.edges[0].id).toBe('edge-item-1-item-2');
96
- expect((sync!.nodes[0] as any).handles).toHaveLength(2);
97
-
98
- // Simulate xyflow measuring node-1
99
- sync!.updateNodeMeasurements('item-1', 250, 120);
100
- expect((sync!.nodes[0] as any).measured).toEqual({ width: 250, height: 120 });
101
-
102
- cleanup();
103
- });
104
- });
@@ -1,59 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { createCanvasShortcuts } from '../createCanvasShortcuts.js';
3
- import type { CanvasNode } from '../types.js';
4
-
5
- describe('createCanvasShortcuts', () => {
6
- it('attaches listener, handles hotkeys, and cleans up', () => {
7
- const target = new EventTarget();
8
- const onDelete = vi.fn();
9
- const onDuplicate = vi.fn();
10
- const onSelectAll = vi.fn();
11
-
12
- const selectedNodes: CanvasNode[] = [{ id: 'node-1', position: { x: 0, y: 0 }, data: {} }];
13
-
14
- const destroy = createCanvasShortcuts({
15
- targetElement: target as unknown as HTMLElement,
16
- getSelectedNodes: () => selectedNodes,
17
- onDelete,
18
- onDuplicate,
19
- onSelectAll
20
- });
21
-
22
- // Trigger Backspace -> onDelete
23
- const deleteEvent = new CustomEvent('keydown', { cancelable: true });
24
- Object.assign(deleteEvent, { key: 'Backspace' });
25
- target.dispatchEvent(deleteEvent);
26
-
27
- expect(onDelete).toHaveBeenCalledWith(selectedNodes);
28
-
29
- // Trigger Meta + D -> onDuplicate
30
- const dupEvent = new CustomEvent('keydown', { cancelable: true });
31
- Object.assign(dupEvent, { key: 'd', metaKey: true });
32
- target.dispatchEvent(dupEvent);
33
-
34
- expect(onDuplicate).toHaveBeenCalledWith(selectedNodes);
35
-
36
- // Trigger Meta + A -> onSelectAll
37
- const allEvent = new CustomEvent('keydown', { cancelable: true });
38
- Object.assign(allEvent, { key: 'a', metaKey: true });
39
- target.dispatchEvent(allEvent);
40
-
41
- expect(onSelectAll).toHaveBeenCalledTimes(1);
42
-
43
- // Trigger shortcut while targeting an input -> should be ignored
44
- const inputEl = { tagName: 'INPUT' } as unknown as HTMLElement;
45
- const ignoredEvent = new CustomEvent('keydown', { cancelable: true });
46
- Object.defineProperty(ignoredEvent, 'target', { value: inputEl });
47
- Object.assign(ignoredEvent, { key: 'Backspace' });
48
- target.dispatchEvent(ignoredEvent);
49
-
50
- expect(onDelete).toHaveBeenCalledTimes(1); // Not called again
51
-
52
- // Destroy listeners
53
- destroy();
54
-
55
- // Dispatching after destroy should do nothing
56
- target.dispatchEvent(deleteEvent);
57
- expect(onDelete).toHaveBeenCalledTimes(1);
58
- });
59
- });