@runfusion/fusion 0.24.0 → 0.25.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 (106) hide show
  1. package/dist/bin.js +2254 -1349
  2. package/dist/client/assets/AgentDetailView-ZbHEbYRT.js +18 -0
  3. package/dist/client/assets/{AgentsView-BkB9FiMT.js → AgentsView-B3jYk8Kt.js} +3 -3
  4. package/dist/client/assets/ChatView-DhPkiEGs.js +1 -0
  5. package/dist/client/assets/{DevServerView-BkvtjZBa.js → DevServerView-DyGDEiBP.js} +1 -1
  6. package/dist/client/assets/{DirectoryPicker-BK-KbnhP.js → DirectoryPicker-D5UIeIl6.js} +1 -1
  7. package/dist/client/assets/{DocumentsView-BEg1CQAk.js → DocumentsView-DNHu1T8K.js} +1 -1
  8. package/dist/client/assets/{EvalsView-Berf9bQm.js → EvalsView-CpRobtDi.js} +1 -1
  9. package/dist/client/assets/{ExperimentalAgentOnboardingModal-jcInE50G.js → ExperimentalAgentOnboardingModal-DOY_oZi7.js} +1 -1
  10. package/dist/client/assets/{InsightsView-BX5bSF1J.js → InsightsView-vp0RE8Mg.js} +1 -1
  11. package/dist/client/assets/MemoryView-PSc5lGJt.js +2 -0
  12. package/dist/client/assets/MemoryView-zaXewZzi.css +1 -0
  13. package/dist/client/assets/{NodesView-DLUOBLf6.js → NodesView-DMj6HGeC.js} +1 -1
  14. package/dist/client/assets/{PiExtensionsManager-COlJf0Kx.js → PiExtensionsManager-DL_QcN56.js} +2 -2
  15. package/dist/client/assets/PluginManager-BtYKm8IT.js +1 -0
  16. package/dist/client/assets/{ResearchView-B256Lr8I.js → ResearchView-BhWqfdV0.js} +1 -1
  17. package/dist/client/assets/{SettingsModal-BeA_nQtW.js → SettingsModal-BAgB4_AR.js} +4 -4
  18. package/dist/client/assets/{SettingsModal-yRqM4DV8.js → SettingsModal-CUCyaAyE.js} +1 -1
  19. package/dist/client/assets/{SetupWizardModal-uUZk3TKT.js → SetupWizardModal-BKscasuh.js} +1 -1
  20. package/dist/client/assets/{SkillsView-CP8JX0P_.js → SkillsView-BdELqTy7.js} +1 -1
  21. package/dist/client/assets/{TodoView-DCRIkDZ-.js → TodoView-DFNGBDNV.js} +1 -1
  22. package/dist/client/assets/{folder-open-DHjELt8-.js → folder-open-k1xmUMyr.js} +1 -1
  23. package/dist/client/assets/index-Qq2JOOWx.css +1 -0
  24. package/dist/client/assets/{index-CQyVRLOb.js → index-TFYXEVpn.js} +160 -160
  25. package/dist/client/assets/{star-DYesq1AV.js → star-ne32r3Y4.js} +1 -1
  26. package/dist/client/assets/{upload-DTWF3Db5.js → upload-MS-2Gx53.js} +1 -1
  27. package/dist/client/assets/{users--syrel4l.js → users-C519GSjH.js} +1 -1
  28. package/dist/client/index.html +2 -2
  29. package/dist/client/version.json +1 -1
  30. package/dist/droid-cli/package.json +1 -1
  31. package/dist/extension.js +1370 -629
  32. package/dist/pi-claude-cli/package.json +1 -1
  33. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +9 -11
  34. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +1 -1
  35. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +30 -0
  36. package/dist/plugins/fusion-plugin-dependency-graph/package.json +3 -28
  37. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +899 -895
  38. package/dist/plugins/fusion-plugin-droid-runtime/package.json +1 -1
  39. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +68 -71
  40. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  41. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +47 -50
  42. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  43. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +155 -109
  44. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  45. package/dist/plugins/fusion-plugin-reports/package.json +1 -1
  46. package/dist/plugins/fusion-plugin-reports/src/index.ts +49 -3
  47. package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +38 -0
  48. package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-schema.test.ts +66 -0
  49. package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-store.test.ts +177 -0
  50. package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +341 -0
  51. package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +77 -0
  52. package/dist/plugins/fusion-plugin-roadmap/package.json +1 -1
  53. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +1 -1
  54. package/package.json +1 -1
  55. package/dist/client/assets/AgentDetailView-gy_5SUj2.js +0 -18
  56. package/dist/client/assets/ChatView-B_-B8fqu.js +0 -1
  57. package/dist/client/assets/MemoryView-CKElJY_3.js +0 -2
  58. package/dist/client/assets/MemoryView-DiajLXby.css +0 -1
  59. package/dist/client/assets/PluginManager-CfW55BF4.js +0 -1
  60. package/dist/client/assets/createLucideIcon-BazL2hk5.js +0 -21
  61. package/dist/client/assets/dashboard-view-BkTMSZYn.css +0 -1
  62. package/dist/client/assets/dashboard-view-CyWN-d02.js +0 -63
  63. package/dist/client/assets/dashboard-view-DdGlfuu-.css +0 -1
  64. package/dist/client/assets/dashboard-view-lR7YYmSC.js +0 -21
  65. package/dist/client/assets/index-CxA2Nn0_.css +0 -1
  66. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.css +0 -58
  67. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraph.tsx +0 -301
  68. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphHighlight.css +0 -27
  69. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.css +0 -157
  70. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphTaskNode.tsx +0 -126
  71. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.css +0 -35
  72. package/dist/plugins/fusion-plugin-dependency-graph/src/GraphToolbar.tsx +0 -36
  73. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.highlighting.test.tsx +0 -112
  74. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.persistence.test.tsx +0 -115
  75. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraph.test.tsx +0 -128
  76. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.drag.test.tsx +0 -82
  77. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphTaskNode.test.tsx +0 -307
  78. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/GraphToolbar.test.tsx +0 -60
  79. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/edges.test.tsx +0 -75
  80. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filtering.test.tsx +0 -62
  81. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/filters.test.ts +0 -78
  82. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/graphPositionStorage.test.ts +0 -95
  83. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/host-integration.test.ts +0 -74
  84. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/index.test.ts +0 -58
  85. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/interactions.test.tsx +0 -121
  86. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/layout.test.ts +0 -70
  87. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/persistence.test.tsx +0 -89
  88. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphData.test.ts +0 -86
  89. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphInteraction.test.ts +0 -167
  90. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useGraphPositions.test.ts +0 -66
  91. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/useNodeDrag.test.ts +0 -81
  92. package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-interop.d.ts +0 -35
  93. package/dist/plugins/fusion-plugin-dependency-graph/src/dashboard-view.tsx +0 -19
  94. package/dist/plugins/fusion-plugin-dependency-graph/src/edges.tsx +0 -70
  95. package/dist/plugins/fusion-plugin-dependency-graph/src/filters.ts +0 -8
  96. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/__tests__/useDependencyChain.test.ts +0 -53
  97. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useDependencyChain.ts +0 -60
  98. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useGraphPositions.ts +0 -45
  99. package/dist/plugins/fusion-plugin-dependency-graph/src/hooks/useNodeDrag.ts +0 -114
  100. package/dist/plugins/fusion-plugin-dependency-graph/src/index.ts +0 -24
  101. package/dist/plugins/fusion-plugin-dependency-graph/src/layout.ts +0 -91
  102. package/dist/plugins/fusion-plugin-dependency-graph/src/styles/drag.css +0 -15
  103. package/dist/plugins/fusion-plugin-dependency-graph/src/types.ts +0 -21
  104. package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphData.ts +0 -17
  105. package/dist/plugins/fusion-plugin-dependency-graph/src/useGraphInteraction.ts +0 -292
  106. package/dist/plugins/fusion-plugin-dependency-graph/src/utils/graphPositionStorage.ts +0 -65
@@ -1,15 +0,0 @@
1
- .graph-node--draggable {
2
- cursor: grab;
3
- touch-action: none;
4
- }
5
-
6
- .graph-node--dragging {
7
- z-index: 3;
8
- cursor: grabbing;
9
- box-shadow: var(--shadow-lg);
10
- transform: scale(1.02);
11
- transition:
12
- transform var(--transition-fast),
13
- box-shadow var(--transition-fast),
14
- z-index var(--transition-fast);
15
- }
@@ -1,21 +0,0 @@
1
- import type { Task } from "@fusion/core";
2
-
3
- export interface GraphPosition {
4
- x: number;
5
- y: number;
6
- }
7
-
8
- export interface GraphNode {
9
- task: Task;
10
- position?: GraphPosition;
11
- }
12
-
13
- export interface GraphEdge {
14
- source: string;
15
- target: string;
16
- }
17
-
18
- export interface GraphData {
19
- nodes: GraphNode[];
20
- edges: GraphEdge[];
21
- }
@@ -1,17 +0,0 @@
1
- import { useMemo } from "react";
2
- import type { Task } from "@fusion/core";
3
- import type { GraphData, GraphNode } from "./types";
4
-
5
- export function useGraphData(tasks: Task[]): GraphData {
6
- return useMemo(() => {
7
- const nodes: GraphNode[] = tasks.map((task) => ({ task }));
8
- const taskIds = new Set(tasks.map((task) => task.id));
9
- const edges = tasks.flatMap((task) =>
10
- (task.dependencies ?? [])
11
- .filter((dependencyId) => taskIds.has(dependencyId))
12
- .map((dependencyId) => ({ source: task.id, target: dependencyId })),
13
- );
14
-
15
- return { nodes, edges };
16
- }, [tasks]);
17
- }
@@ -1,292 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
2
- import type { KeyboardEvent as ReactKeyboardEvent } from "react";
3
- import type { LayoutOptions } from "./layout";
4
- import type { GraphPosition } from "./types";
5
-
6
- const MIN_ZOOM = 0.1;
7
- const MAX_ZOOM = 3;
8
- const FIT_PADDING = 40;
9
- const TRANSITION_TIMEOUT_MS = 200;
10
-
11
- interface PointerPoint {
12
- x: number;
13
- y: number;
14
- }
15
-
16
- interface PinchState {
17
- distance: number;
18
- zoom: number;
19
- pan: PointerPoint;
20
- midpoint: PointerPoint;
21
- }
22
-
23
- function clamp(value: number, min: number, max: number): number {
24
- return Math.min(max, Math.max(min, value));
25
- }
26
-
27
- function isEditableTarget(target: EventTarget | null): boolean {
28
- if (!(target instanceof HTMLElement)) return false;
29
- const tagName = target.tagName.toLowerCase();
30
- return target.isContentEditable || tagName === "input" || tagName === "textarea" || tagName === "select";
31
- }
32
-
33
- export function useGraphInteraction() {
34
- const [pan, setPan] = useState<PointerPoint>({ x: 0, y: 0 });
35
- const [zoom, setZoom] = useState(1);
36
- const [transitioning, setTransitioning] = useState(false);
37
-
38
- const panRef = useRef(pan);
39
- const zoomRef = useRef(zoom);
40
- const transitionTimerRef = useRef<number | null>(null);
41
- const dragStateRef = useRef<{ start: PointerPoint; panStart: PointerPoint } | null>(null);
42
- const pointersRef = useRef<Map<number, PointerPoint>>(new Map());
43
- const pinchRef = useRef<PinchState | null>(null);
44
-
45
- useEffect(() => {
46
- panRef.current = pan;
47
- }, [pan]);
48
-
49
- useEffect(() => {
50
- zoomRef.current = zoom;
51
- }, [zoom]);
52
-
53
- useEffect(() => () => {
54
- if (transitionTimerRef.current !== null) {
55
- window.clearTimeout(transitionTimerRef.current);
56
- }
57
- }, []);
58
-
59
- const transform = useMemo(() => `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`, [pan.x, pan.y, zoom]);
60
- const zoomPercent = useMemo(() => Math.round(zoom * 100), [zoom]);
61
-
62
- const setAnimate = useCallback((enabled: boolean) => {
63
- if (!enabled) {
64
- if (transitionTimerRef.current !== null) {
65
- window.clearTimeout(transitionTimerRef.current);
66
- transitionTimerRef.current = null;
67
- }
68
- setTransitioning(false);
69
- return;
70
- }
71
-
72
- setTransitioning(true);
73
- if (transitionTimerRef.current !== null) {
74
- window.clearTimeout(transitionTimerRef.current);
75
- }
76
- transitionTimerRef.current = window.setTimeout(() => {
77
- setTransitioning(false);
78
- transitionTimerRef.current = null;
79
- }, TRANSITION_TIMEOUT_MS);
80
- }, []);
81
-
82
- const clampPan = useCallback((nextPan: PointerPoint, viewportWidth: number, viewportHeight: number) => ({
83
- x: clamp(nextPan.x, -viewportWidth, viewportWidth),
84
- y: clamp(nextPan.y, -viewportHeight, viewportHeight),
85
- }), []);
86
-
87
- const zoomAtPoint = useCallback((
88
- nextZoomRaw: number,
89
- anchor: PointerPoint,
90
- viewportWidth: number,
91
- viewportHeight: number,
92
- ) => {
93
- const currentZoom = zoomRef.current;
94
- const currentPan = panRef.current;
95
- const nextZoom = clamp(nextZoomRaw, MIN_ZOOM, MAX_ZOOM);
96
- const scaleRatio = nextZoom / currentZoom;
97
-
98
- const nextPan = clampPan({
99
- x: anchor.x - (anchor.x - currentPan.x) * scaleRatio,
100
- y: anchor.y - (anchor.y - currentPan.y) * scaleRatio,
101
- }, viewportWidth, viewportHeight);
102
-
103
- setZoom(nextZoom);
104
- setPan(nextPan);
105
- }, [clampPan]);
106
-
107
- const zoomByFactor = useCallback((factor: number, viewportWidth: number, viewportHeight: number, anchor?: PointerPoint) => {
108
- setAnimate(false);
109
- const point = anchor ?? { x: viewportWidth / 2, y: viewportHeight / 2 };
110
- zoomAtPoint(zoomRef.current * factor, point, viewportWidth, viewportHeight);
111
- }, [setAnimate, zoomAtPoint]);
112
-
113
- const zoomIn = useCallback((viewportWidth?: number, viewportHeight?: number) => {
114
- if (viewportWidth && viewportHeight) {
115
- zoomByFactor(1.2, viewportWidth, viewportHeight);
116
- return;
117
- }
118
- setZoom((current) => clamp(current + 0.1, MIN_ZOOM, MAX_ZOOM));
119
- }, [zoomByFactor]);
120
-
121
- const zoomOut = useCallback((viewportWidth?: number, viewportHeight?: number) => {
122
- if (viewportWidth && viewportHeight) {
123
- zoomByFactor(1 / 1.2, viewportWidth, viewportHeight);
124
- return;
125
- }
126
- setZoom((current) => clamp(current - 0.1, MIN_ZOOM, MAX_ZOOM));
127
- }, [zoomByFactor]);
128
-
129
- const resetView = useCallback(() => {
130
- setAnimate(true);
131
- setPan({ x: 0, y: 0 });
132
- setZoom(1);
133
- }, [setAnimate]);
134
-
135
- const fitToGraph = useCallback((
136
- positions: Map<string, GraphPosition>,
137
- viewportWidth: number,
138
- viewportHeight: number,
139
- layoutOptions?: LayoutOptions,
140
- ) => {
141
- setAnimate(true);
142
- if (positions.size === 0) {
143
- setPan({ x: 0, y: 0 });
144
- setZoom(1);
145
- return;
146
- }
147
-
148
- const nodeWidth = layoutOptions?.nodeWidth ?? 280;
149
- const nodeHeight = layoutOptions?.nodeHeight ?? 100;
150
-
151
- const entries = Array.from(positions.values());
152
- const minX = Math.min(...entries.map((p) => p.x));
153
- const minY = Math.min(...entries.map((p) => p.y));
154
- const maxX = Math.max(...entries.map((p) => p.x + nodeWidth));
155
- const maxY = Math.max(...entries.map((p) => p.y + nodeHeight));
156
-
157
- const graphWidth = Math.max(1, maxX - minX);
158
- const graphHeight = Math.max(1, maxY - minY);
159
- const availableWidth = Math.max(1, viewportWidth - FIT_PADDING * 2);
160
- const availableHeight = Math.max(1, viewportHeight - FIT_PADDING * 2);
161
- const nextZoom = clamp(Math.min(availableWidth / graphWidth, availableHeight / graphHeight), MIN_ZOOM, MAX_ZOOM);
162
-
163
- const panX = (viewportWidth - graphWidth * nextZoom) / 2 - minX * nextZoom;
164
- const panY = (viewportHeight - graphHeight * nextZoom) / 2 - minY * nextZoom;
165
-
166
- setZoom(nextZoom);
167
- setPan(clampPan({ x: panX, y: panY }, viewportWidth, viewportHeight));
168
- }, [clampPan, setAnimate]);
169
-
170
- const onPointerDown = useCallback((pointerId: number, point: PointerPoint) => {
171
- pointersRef.current.set(pointerId, point);
172
- if (pointersRef.current.size === 2) {
173
- const [a, b] = Array.from(pointersRef.current.values());
174
- pinchRef.current = {
175
- distance: Math.hypot(a.x - b.x, a.y - b.y),
176
- zoom: zoomRef.current,
177
- pan: panRef.current,
178
- midpoint: { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 },
179
- };
180
- dragStateRef.current = null;
181
- return;
182
- }
183
- dragStateRef.current = { start: point, panStart: panRef.current };
184
- }, []);
185
-
186
- const onPointerMove = useCallback((pointerId: number, point: PointerPoint, viewportWidth: number, viewportHeight: number) => {
187
- if (pointersRef.current.has(pointerId)) pointersRef.current.set(pointerId, point);
188
-
189
- if (pointersRef.current.size >= 2 && pinchRef.current) {
190
- setAnimate(false);
191
- const [a, b] = Array.from(pointersRef.current.values());
192
- const distance = Math.hypot(a.x - b.x, a.y - b.y);
193
- const factor = distance / Math.max(1, pinchRef.current.distance);
194
- const midpoint = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };
195
- const nextZoom = clamp(pinchRef.current.zoom * factor, MIN_ZOOM, MAX_ZOOM);
196
- const ratio = nextZoom / pinchRef.current.zoom;
197
- const nextPan = clampPan({
198
- x: midpoint.x - (pinchRef.current.midpoint.x - pinchRef.current.pan.x) * ratio,
199
- y: midpoint.y - (pinchRef.current.midpoint.y - pinchRef.current.pan.y) * ratio,
200
- }, viewportWidth, viewportHeight);
201
- setZoom(nextZoom);
202
- setPan(nextPan);
203
- return;
204
- }
205
-
206
- const dragState = dragStateRef.current;
207
- if (!dragState) return;
208
- setAnimate(false);
209
- const nextPan = {
210
- x: dragState.panStart.x + (point.x - dragState.start.x),
211
- y: dragState.panStart.y + (point.y - dragState.start.y),
212
- };
213
- setPan(clampPan(nextPan, viewportWidth, viewportHeight));
214
- }, [clampPan, setAnimate]);
215
-
216
- const onPointerUp = useCallback((pointerId: number) => {
217
- pointersRef.current.delete(pointerId);
218
- if (pointersRef.current.size < 2) pinchRef.current = null;
219
- if (pointersRef.current.size === 0) dragStateRef.current = null;
220
- }, []);
221
-
222
- const onWheelZoom = useCallback((
223
- deltaY: number,
224
- point: PointerPoint,
225
- viewportWidth: number,
226
- viewportHeight: number,
227
- ) => {
228
- const factor = deltaY < 0 ? 1.1 : 0.9;
229
- setAnimate(false);
230
- zoomAtPoint(zoomRef.current * factor, point, viewportWidth, viewportHeight);
231
- }, [setAnimate, zoomAtPoint]);
232
-
233
- const handleKeyDown = useCallback((
234
- event: ReactKeyboardEvent,
235
- viewportWidth: number,
236
- viewportHeight: number,
237
- positions: Map<string, GraphPosition>,
238
- layoutOptions?: LayoutOptions,
239
- ) => {
240
- if (isEditableTarget(event.target)) return;
241
-
242
- const modifier = event.metaKey || event.ctrlKey;
243
- if (event.key === "Escape") {
244
- event.preventDefault();
245
- resetView();
246
- return;
247
- }
248
-
249
- if (!modifier) return;
250
-
251
- if (event.key === "=" || event.key === "+") {
252
- event.preventDefault();
253
- zoomByFactor(1.2, viewportWidth, viewportHeight);
254
- return;
255
- }
256
-
257
- if (event.key === "-") {
258
- event.preventDefault();
259
- zoomByFactor(1 / 1.2, viewportWidth, viewportHeight);
260
- return;
261
- }
262
-
263
- if (event.key === "0") {
264
- event.preventDefault();
265
- resetView();
266
- return;
267
- }
268
-
269
- if ((event.key === "f" || event.key === "F") && event.shiftKey) {
270
- event.preventDefault();
271
- fitToGraph(positions, viewportWidth, viewportHeight, layoutOptions);
272
- }
273
- }, [fitToGraph, resetView, zoomByFactor]);
274
-
275
- return {
276
- pan,
277
- zoom,
278
- zoomPercent,
279
- transform,
280
- transitioning,
281
- zoomIn,
282
- zoomOut,
283
- resetView,
284
- fitToGraph,
285
- setAnimate,
286
- onPointerDown,
287
- onPointerMove,
288
- onPointerUp,
289
- onWheelZoom,
290
- handleKeyDown,
291
- };
292
- }
@@ -1,65 +0,0 @@
1
- import { getScopedItem, removeScopedItem, setScopedItem } from "@fusion/dashboard/app/utils/projectStorage";
2
-
3
- export type NodePositions = Record<string, { x: number; y: number }>;
4
-
5
- const STORAGE_KEY = "fusion-plugin-dependency-graph:positions";
6
-
7
- function isPosition(value: unknown): value is { x: number; y: number } {
8
- if (!value || typeof value !== "object") return false;
9
- const candidate = value as { x?: unknown; y?: unknown };
10
- return typeof candidate.x === "number" && Number.isFinite(candidate.x) && typeof candidate.y === "number" && Number.isFinite(candidate.y);
11
- }
12
-
13
- export function loadPositions(projectId?: string): NodePositions {
14
- const raw = getScopedItem(STORAGE_KEY, projectId);
15
- if (!raw) return {};
16
-
17
- try {
18
- const parsed = JSON.parse(raw) as Record<string, unknown>;
19
- if (!parsed || typeof parsed !== "object") return {};
20
-
21
- const result: NodePositions = {};
22
- for (const [taskId, value] of Object.entries(parsed)) {
23
- if (isPosition(value)) {
24
- result[taskId] = value;
25
- }
26
- }
27
-
28
- return result;
29
- } catch {
30
- return {};
31
- }
32
- }
33
-
34
- export function savePositions(positions: NodePositions, visibleTaskIds: Set<string>, projectId?: string): void {
35
- const filtered: NodePositions = {};
36
- for (const [taskId, position] of Object.entries(positions)) {
37
- if (visibleTaskIds.has(taskId) && isPosition(position)) {
38
- filtered[taskId] = position;
39
- }
40
- }
41
-
42
- setScopedItem(STORAGE_KEY, JSON.stringify(filtered), projectId);
43
- }
44
-
45
- export function clearPositions(projectId?: string): void {
46
- removeScopedItem(STORAGE_KEY, projectId);
47
- }
48
-
49
- export function mergePositions(autoLayoutPositions: NodePositions, savedPositions: NodePositions, visibleTaskIds: Set<string>): NodePositions {
50
- const merged: NodePositions = {};
51
-
52
- for (const [taskId, position] of Object.entries(autoLayoutPositions)) {
53
- if (visibleTaskIds.has(taskId) && isPosition(position)) {
54
- merged[taskId] = position;
55
- }
56
- }
57
-
58
- for (const [taskId, position] of Object.entries(savedPositions)) {
59
- if (visibleTaskIds.has(taskId) && isPosition(position)) {
60
- merged[taskId] = position;
61
- }
62
- }
63
-
64
- return merged;
65
- }