@runfusion/fusion 0.23.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 (172) hide show
  1. package/dist/bin.js +27921 -21003
  2. package/dist/client/assets/AgentDetailView-BwJaLqZh.css +1 -0
  3. package/dist/client/assets/AgentDetailView-ZbHEbYRT.js +18 -0
  4. package/dist/client/assets/AgentsView-B3jYk8Kt.js +29 -0
  5. package/dist/client/assets/{AgentsView-DSGQWObq.css → AgentsView-CV3vm7Qk.css} +1 -1
  6. package/dist/client/assets/ChatView-DhPkiEGs.js +1 -0
  7. package/dist/client/assets/ChatView-DwJAd5G1.css +1 -0
  8. package/dist/client/assets/{DevServerView-C9lzHrcT.js → DevServerView-DyGDEiBP.js} +1 -1
  9. package/dist/client/assets/{DirectoryPicker-aVdFaV37.js → DirectoryPicker-D5UIeIl6.js} +1 -1
  10. package/dist/client/assets/{DocumentsView-DIpg3NSP.js → DocumentsView-DNHu1T8K.js} +1 -1
  11. package/dist/client/assets/{DocumentsView-BrhyOdeE.css → DocumentsView-gv4zG3aT.css} +1 -1
  12. package/dist/client/assets/EvalsView-CUNJ1TLc.css +1 -0
  13. package/dist/client/assets/EvalsView-CpRobtDi.js +1 -0
  14. package/dist/client/assets/{agentSkills-DDHJnrkn.css → ExperimentalAgentOnboardingModal-B-APN_lM.css} +1 -1
  15. package/dist/client/assets/ExperimentalAgentOnboardingModal-DOY_oZi7.js +499 -0
  16. package/dist/client/assets/InsightsView-B0J4mhzV.css +1 -0
  17. package/dist/client/assets/InsightsView-vp0RE8Mg.js +11 -0
  18. package/dist/client/assets/MemoryView-PSc5lGJt.js +2 -0
  19. package/dist/client/assets/MemoryView-zaXewZzi.css +1 -0
  20. package/dist/client/assets/NodesView-DMj6HGeC.js +14 -0
  21. package/dist/client/assets/NodesView-DT4pXowv.css +1 -0
  22. package/dist/client/assets/{PiExtensionsManager-Buopv-jb.js → PiExtensionsManager-DL_QcN56.js} +2 -2
  23. package/dist/client/assets/PluginManager-BtYKm8IT.js +1 -0
  24. package/dist/client/assets/PluginManager-DtRQXia5.css +1 -0
  25. package/dist/client/assets/{ResearchView-_BHXUv2j.js → ResearchView-BhWqfdV0.js} +1 -1
  26. package/dist/client/assets/SettingsModal-BAgB4_AR.js +31 -0
  27. package/dist/client/assets/SettingsModal-CUCyaAyE.js +1 -0
  28. package/dist/client/assets/SettingsModal-DzsLquBu.css +1 -0
  29. package/dist/client/assets/SetupWizardModal-BKscasuh.js +1 -0
  30. package/dist/client/assets/{SkillsView-hDpTBdFT.js → SkillsView-BdELqTy7.js} +1 -1
  31. package/dist/client/assets/TodoView-Cx9cVhq7.css +1 -0
  32. package/dist/client/assets/TodoView-DFNGBDNV.js +6 -0
  33. package/dist/client/assets/{folder-open-usZkXdq2.js → folder-open-k1xmUMyr.js} +1 -1
  34. package/dist/client/assets/index-Qq2JOOWx.css +1 -0
  35. package/dist/client/assets/index-TFYXEVpn.js +692 -0
  36. package/dist/client/assets/projectDetection-G3XuxD2X.js +1 -0
  37. package/dist/client/assets/{star-BAT_ObKE.js → star-ne32r3Y4.js} +1 -1
  38. package/dist/client/assets/{upload-BC2YKNEV.js → upload-MS-2Gx53.js} +1 -1
  39. package/dist/client/assets/{users-Dkd4rtrN.js → users-C519GSjH.js} +1 -1
  40. package/dist/client/index.html +12 -20
  41. package/dist/client/theme-data.css +106 -0
  42. package/dist/client/version.json +1 -1
  43. package/dist/droid-cli/package.json +1 -1
  44. package/dist/extension.js +15395 -9935
  45. package/dist/pi-claude-cli/package.json +1 -1
  46. package/dist/plugins/fusion-plugin-cursor-runtime/bundled.js +216 -0
  47. package/dist/plugins/fusion-plugin-cursor-runtime/manifest.json +6 -0
  48. package/dist/plugins/fusion-plugin-cursor-runtime/package.json +11 -0
  49. package/dist/plugins/fusion-plugin-dependency-graph/bundled.js +30 -0
  50. package/dist/plugins/fusion-plugin-dependency-graph/manifest.json +1 -1
  51. package/dist/plugins/fusion-plugin-dependency-graph/package.json +3 -26
  52. package/dist/plugins/fusion-plugin-droid-runtime/bundled.js +136684 -0
  53. package/dist/plugins/fusion-plugin-droid-runtime/manifest.json +13 -0
  54. package/dist/plugins/fusion-plugin-droid-runtime/mcp-schema-server.cjs +49 -0
  55. package/dist/plugins/fusion-plugin-droid-runtime/package.json +11 -0
  56. package/dist/plugins/fusion-plugin-hermes-runtime/bundled.js +68 -71
  57. package/dist/plugins/fusion-plugin-hermes-runtime/package.json +1 -1
  58. package/dist/plugins/fusion-plugin-openclaw-runtime/bundled.js +137 -53
  59. package/dist/plugins/fusion-plugin-openclaw-runtime/mcp-schema-server.cjs +59 -0
  60. package/dist/plugins/fusion-plugin-openclaw-runtime/package.json +1 -1
  61. package/dist/plugins/fusion-plugin-paperclip-runtime/bundled.js +155 -109
  62. package/dist/plugins/fusion-plugin-paperclip-runtime/package.json +1 -1
  63. package/dist/plugins/fusion-plugin-reports/manifest.json +33 -0
  64. package/dist/plugins/fusion-plugin-reports/package.json +26 -0
  65. package/dist/plugins/fusion-plugin-reports/src/__tests__/manifest.test.ts +51 -0
  66. package/dist/plugins/fusion-plugin-reports/src/__tests__/review-panel.test.ts +166 -0
  67. package/dist/plugins/fusion-plugin-reports/src/__tests__/settings.test.ts +157 -0
  68. package/dist/plugins/fusion-plugin-reports/src/index.ts +87 -0
  69. package/dist/plugins/fusion-plugin-reports/src/report-schema.ts +38 -0
  70. package/dist/plugins/fusion-plugin-reports/src/review-panel.ts +294 -0
  71. package/dist/plugins/fusion-plugin-reports/src/review-types.ts +75 -0
  72. package/dist/plugins/fusion-plugin-reports/src/settings.ts +105 -0
  73. package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-schema.test.ts +66 -0
  74. package/dist/plugins/fusion-plugin-reports/src/store/__tests__/report-store.test.ts +177 -0
  75. package/dist/plugins/fusion-plugin-reports/src/store/report-store.ts +341 -0
  76. package/dist/plugins/fusion-plugin-reports/src/store/report-types.ts +77 -0
  77. package/dist/plugins/fusion-plugin-roadmap/manifest.json +16 -0
  78. package/dist/plugins/fusion-plugin-roadmap/package.json +48 -0
  79. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/api-client.test.ts +101 -0
  80. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/index.test.ts +92 -0
  81. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-routes.test.ts +48 -0
  82. package/dist/plugins/fusion-plugin-roadmap/src/__tests__/roadmap-suggestions.test.ts +31 -0
  83. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.css +1299 -0
  84. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/RoadmapsView.tsx +2559 -0
  85. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/RoadmapsView.test.tsx +1144 -0
  86. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/__tests__/useRoadmaps.test.ts +1756 -0
  87. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/api.ts +70 -0
  88. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/test-setup.ts +7 -0
  89. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/types.ts +1 -0
  90. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useConfirm.ts +8 -0
  91. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useRoadmaps.ts +1188 -0
  92. package/dist/plugins/fusion-plugin-roadmap/src/dashboard/useViewportMode.ts +20 -0
  93. package/dist/plugins/fusion-plugin-roadmap/src/dashboard-view.tsx +6 -0
  94. package/dist/plugins/fusion-plugin-roadmap/src/index.ts +74 -0
  95. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-routes.ts +1 -0
  96. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-schema.ts +41 -0
  97. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.d.ts +15 -0
  98. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-suggestions.ts +15 -0
  99. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts +283 -0
  100. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.d.ts.map +1 -0
  101. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js +21 -0
  102. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.js.map +1 -0
  103. package/dist/plugins/fusion-plugin-roadmap/src/roadmap-types.ts +310 -0
  104. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts +5 -0
  105. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.d.ts.map +1 -0
  106. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js +361 -0
  107. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.js.map +1 -0
  108. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-routes.ts +408 -0
  109. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts +68 -0
  110. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.d.ts.map +1 -0
  111. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js +300 -0
  112. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.js.map +1 -0
  113. package/dist/plugins/fusion-plugin-roadmap/src/routes/roadmap-suggestions.ts +381 -0
  114. package/dist/plugins/fusion-plugin-roadmap/src/server/index.d.ts +3 -0
  115. package/dist/plugins/fusion-plugin-roadmap/src/server/index.ts +1 -0
  116. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-handoff.test.ts +445 -0
  117. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-ordering.test.ts +334 -0
  118. package/dist/plugins/fusion-plugin-roadmap/src/store/__tests__/roadmap-store.test.ts +1318 -0
  119. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-handoff.ts +163 -0
  120. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts +37 -0
  121. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.d.ts.map +1 -0
  122. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js +188 -0
  123. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.js.map +1 -0
  124. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-ordering.ts +311 -0
  125. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts +299 -0
  126. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.d.ts.map +1 -0
  127. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js +765 -0
  128. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.js.map +1 -0
  129. package/dist/plugins/fusion-plugin-roadmap/src/store/roadmap-store.ts +1001 -0
  130. package/dist/plugins/fusion-plugin-whatsapp-chat/manifest.json +8 -0
  131. package/dist/plugins/fusion-plugin-whatsapp-chat/package.json +34 -0
  132. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/auth-state.test.ts +99 -0
  133. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/connection.test.ts +145 -0
  134. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/index.test.ts +216 -0
  135. package/dist/plugins/fusion-plugin-whatsapp-chat/src/__tests__/reply.test.ts +52 -0
  136. package/dist/plugins/fusion-plugin-whatsapp-chat/src/auth-state.ts +89 -0
  137. package/dist/plugins/fusion-plugin-whatsapp-chat/src/connection.ts +253 -0
  138. package/dist/plugins/fusion-plugin-whatsapp-chat/src/index.ts +262 -0
  139. package/dist/plugins/fusion-plugin-whatsapp-chat/src/qrcode.d.ts +1 -0
  140. package/dist/plugins/fusion-plugin-whatsapp-chat/src/reply.ts +37 -0
  141. package/package.json +2 -2
  142. package/skill/fusion/SKILL.md +2 -2
  143. package/skill/fusion/references/engine-tools.md +3 -0
  144. package/skill/fusion/references/extension-tools.md +39 -0
  145. package/skill/fusion/references/fusion-capabilities.md +3 -0
  146. package/dist/client/assets/AgentDetailView-C1XceMgi.js +0 -18
  147. package/dist/client/assets/AgentDetailView-CeO_1MK7.css +0 -1
  148. package/dist/client/assets/AgentsView-Deh125ss.js +0 -527
  149. package/dist/client/assets/ChatView-7D_RQDqT.js +0 -1
  150. package/dist/client/assets/InsightsView-AWo5o_81.css +0 -1
  151. package/dist/client/assets/InsightsView-jKjEFAx_.js +0 -11
  152. package/dist/client/assets/MemoryView-DiajLXby.css +0 -1
  153. package/dist/client/assets/MemoryView-nXlTqebk.js +0 -2
  154. package/dist/client/assets/NodesView-Di2SvOhg.js +0 -14
  155. package/dist/client/assets/NodesView-fXqDk9ur.css +0 -1
  156. package/dist/client/assets/PluginManager-B9-NbQ8f.js +0 -1
  157. package/dist/client/assets/PluginManager-C1DbPaar.css +0 -1
  158. package/dist/client/assets/RoadmapsView-DHWjUoc8.js +0 -6
  159. package/dist/client/assets/RoadmapsView-DdGlfuu-.css +0 -1
  160. package/dist/client/assets/SettingsModal-C89Ikhfm.js +0 -1
  161. package/dist/client/assets/SettingsModal-DHitIpsa.css +0 -1
  162. package/dist/client/assets/SettingsModal-DR_yirvK.js +0 -31
  163. package/dist/client/assets/SetupWizardModal-BtDMY9pa.js +0 -1
  164. package/dist/client/assets/agentSkills-B-w5wFHh.js +0 -1
  165. package/dist/client/assets/index-Bc6ZdGMz.css +0 -1
  166. package/dist/client/assets/index-D__RMku8.js +0 -694
  167. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.css +0 -141
  168. package/dist/plugins/fusion-plugin-dependency-graph/src/DependencyGraphView.tsx +0 -428
  169. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/DependencyGraphView.test.tsx +0 -261
  170. package/dist/plugins/fusion-plugin-dependency-graph/src/__tests__/storage.test.ts +0 -41
  171. package/dist/plugins/fusion-plugin-dependency-graph/src/index.ts +0 -25
  172. package/dist/plugins/fusion-plugin-dependency-graph/src/storage.ts +0 -22
@@ -1,261 +0,0 @@
1
- import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
2
- import { render, fireEvent, screen, act, cleanup } from "@testing-library/react";
3
- import * as React from "react";
4
- import { DependencyGraphView } from "../DependencyGraphView";
5
- import type { DependencyGraphHostContext, PluginDashboardViewComponentProps } from "../DependencyGraphView";
6
- import type { Task } from "@fusion/core";
7
-
8
- // Mock storage to avoid localStorage dependency
9
- vi.mock("../storage", () => ({
10
- loadPositions: () => ({}),
11
- savePositions: () => {},
12
- }));
13
-
14
- // Helper to create a minimal Task
15
- function createTask(overrides: Partial<Task> & { id: string; column: Task["column"] }): Task {
16
- return {
17
- description: overrides.description ?? `Task ${overrides.id}`,
18
- column: overrides.column,
19
- dependencies: overrides.dependencies ?? [],
20
- steps: overrides.steps ?? [],
21
- currentStep: overrides.currentStep ?? 0,
22
- log: overrides.log ?? [],
23
- ...overrides,
24
- } as Task;
25
- }
26
-
27
- function createMockContext(tasks: Task[] = []): DependencyGraphHostContext {
28
- return {
29
- projectId: "test-project",
30
- tasks,
31
- openTaskDetail: vi.fn(),
32
- renderTaskCard: (task: Task) => React.createElement("div", { "data-testid": "task-card" }, task.id),
33
- };
34
- }
35
-
36
- function renderView(context?: DependencyGraphHostContext) {
37
- const props: PluginDashboardViewComponentProps = {
38
- context: context ?? createMockContext(),
39
- };
40
- return render(React.createElement(DependencyGraphView, props));
41
- }
42
-
43
- describe("DependencyGraphView", () => {
44
- beforeEach(() => {
45
- // Mock getComputedStyle for rem calculations
46
- vi.spyOn(window, "getComputedStyle").mockImplementation((() => {
47
- return { fontSize: "16px" } as CSSStyleDeclaration;
48
- }) as typeof window.getComputedStyle);
49
-
50
- // Mock matchMedia to default to desktop
51
- window.matchMedia = vi.fn().mockImplementation((query: string) => ({
52
- matches: false,
53
- media: query,
54
- onchange: null,
55
- addListener: vi.fn(),
56
- removeListener: vi.fn(),
57
- addEventListener: vi.fn(),
58
- removeEventListener: vi.fn(),
59
- dispatchEvent: vi.fn(),
60
- }));
61
- });
62
-
63
- afterEach(() => {
64
- cleanup();
65
- vi.restoreAllMocks();
66
- });
67
-
68
- it("renders empty state when no active-column tasks provided", () => {
69
- const { container } = renderView(createMockContext([]));
70
- const empty = container.querySelector(".dependency-graph-empty");
71
- expect(empty).toBeTruthy();
72
- expect(empty?.textContent).toContain("No tasks to display");
73
- });
74
-
75
- it("renders nodes for active-column tasks", () => {
76
- const tasks = [
77
- createTask({ id: "FN-001", column: "todo" }),
78
- createTask({ id: "FN-002", column: "in-progress" }),
79
- ];
80
- const { container } = renderView(createMockContext(tasks));
81
- const nodes = container.querySelectorAll(".dependency-graph-node");
82
- expect(nodes.length).toBe(2);
83
- });
84
-
85
- it("filters out tasks not in active columns", () => {
86
- const tasks = [
87
- createTask({ id: "FN-001", column: "todo" }),
88
- createTask({ id: "FN-002", column: "done" }),
89
- createTask({ id: "FN-003", column: "archived" }),
90
- ];
91
- const { container } = renderView(createMockContext(tasks));
92
- const nodes = container.querySelectorAll(".dependency-graph-node");
93
- expect(nodes.length).toBe(1);
94
- });
95
-
96
- it("renders edges for tasks with dependencies in the active set", () => {
97
- const tasks = [
98
- createTask({ id: "FN-001", column: "todo" }),
99
- createTask({ id: "FN-002", column: "todo", dependencies: ["FN-001"] }),
100
- ];
101
- const { container } = renderView(createMockContext(tasks));
102
- const edges = container.querySelectorAll(".dependency-graph-edge");
103
- expect(edges.length).toBe(1);
104
- });
105
-
106
- it("does not render edges for dependencies not in the active set", () => {
107
- const tasks = [
108
- createTask({ id: "FN-002", column: "todo", dependencies: ["FN-999"] }),
109
- ];
110
- const { container } = renderView(createMockContext(tasks));
111
- const edges = container.querySelectorAll(".dependency-graph-edge");
112
- expect(edges.length).toBe(0);
113
- });
114
-
115
- it("zoom-in button increases scale", () => {
116
- const tasks = [createTask({ id: "FN-001", column: "todo" })];
117
- const { container, unmount } = renderView(createMockContext(tasks));
118
- const zoomInBtn = Array.from(container.querySelectorAll("button")).find((b) => b.textContent === "Zoom In")!;
119
- fireEvent.click(zoomInBtn);
120
- const scene = container.querySelector(".dependency-graph-scene") as HTMLElement;
121
- expect(scene).toBeTruthy();
122
- const transform = scene.style.transform;
123
- expect(transform).toMatch(/scale\([1-9]/);
124
- });
125
-
126
- it("zoom-out button decreases scale", () => {
127
- const tasks = [createTask({ id: "FN-001", column: "todo" })];
128
- const { container, unmount } = renderView(createMockContext(tasks));
129
- const zoomOutBtn = Array.from(container.querySelectorAll("button")).find((b) => b.textContent === "Zoom Out")!;
130
- fireEvent.click(zoomOutBtn);
131
- const scene = container.querySelector(".dependency-graph-scene") as HTMLElement;
132
- expect(scene).toBeTruthy();
133
- const transform = scene.style.transform;
134
- expect(transform).toMatch(/scale\(0\./);
135
- });
136
-
137
- it("fit-to-graph button produces a valid scale", () => {
138
- const tasks = [createTask({ id: "FN-001", column: "todo" })];
139
- const { container, unmount } = renderView(createMockContext(tasks));
140
-
141
- // Mock canvas dimensions for fitToGraph
142
- const canvas = container.querySelector(".dependency-graph-canvas") as HTMLElement;
143
- if (canvas) {
144
- Object.defineProperty(canvas, "clientWidth", { value: 800, configurable: true });
145
- Object.defineProperty(canvas, "clientHeight", { value: 600, configurable: true });
146
- }
147
-
148
- const fitBtn = Array.from(container.querySelectorAll("button")).find((b) => b.textContent === "Fit")!;
149
- fireEvent.click(fitBtn);
150
-
151
- const scene = container.querySelector(".dependency-graph-scene") as HTMLElement;
152
- expect(scene).toBeTruthy();
153
- const transform = scene.style.transform;
154
- const scaleMatch = transform.match(/scale\(([\d.]+)\)/);
155
- expect(scaleMatch).toBeTruthy();
156
- const scaleValue = parseFloat(scaleMatch![1]);
157
- expect(scaleValue).toBeGreaterThanOrEqual(0.4);
158
- expect(scaleValue).toBeLessThanOrEqual(2);
159
- });
160
-
161
- it("wheel event zooms the graph", () => {
162
- const tasks = [createTask({ id: "FN-001", column: "todo" })];
163
- const { container } = renderView(createMockContext(tasks));
164
- const canvas = container.querySelector(".dependency-graph-canvas") as HTMLElement;
165
-
166
- expect(canvas).toBeTruthy();
167
-
168
- // Mock getBoundingClientRect on the canvas
169
- canvas.getBoundingClientRect = vi.fn().mockReturnValue({
170
- left: 0, top: 0, right: 800, bottom: 600, width: 800, height: 600, x: 0, y: 0,
171
- });
172
-
173
- const initialScene = container.querySelector(".dependency-graph-scene") as HTMLElement;
174
- const initialTransform = initialScene.style.transform;
175
-
176
- // Negative deltaY = zoom in
177
- fireEvent.wheel(canvas, { deltaY: -100, clientX: 400, clientY: 300 });
178
-
179
- const updatedScene = container.querySelector(".dependency-graph-scene") as HTMLElement;
180
- const updatedTransform = updatedScene.style.transform;
181
-
182
- expect(updatedTransform).not.toBe(initialTransform);
183
- const scaleMatch = updatedTransform.match(/scale\(([\d.]+)\)/);
184
- expect(scaleMatch).toBeTruthy();
185
- expect(parseFloat(scaleMatch![1])).toBeGreaterThan(1);
186
- });
187
-
188
- it("pinch gesture changes scale", () => {
189
- const tasks = [createTask({ id: "FN-001", column: "todo" })];
190
- const { container } = renderView(createMockContext(tasks));
191
- const canvas = container.querySelector(".dependency-graph-canvas") as HTMLElement;
192
-
193
- expect(canvas).toBeTruthy();
194
-
195
- // First pointer down (finger 1)
196
- fireEvent.pointerDown(canvas, {
197
- pointerId: 1,
198
- pointerType: "touch",
199
- button: 0,
200
- clientX: 200,
201
- clientY: 300,
202
- });
203
-
204
- // Second pointer down (finger 2) — starts pinch
205
- fireEvent.pointerDown(canvas, {
206
- pointerId: 2,
207
- pointerType: "touch",
208
- button: 0,
209
- clientX: 600,
210
- clientY: 300,
211
- });
212
-
213
- // Move fingers apart (increasing distance)
214
- fireEvent.pointerMove(canvas, {
215
- pointerId: 1,
216
- pointerType: "touch",
217
- clientX: 100,
218
- clientY: 300,
219
- });
220
-
221
- fireEvent.pointerMove(canvas, {
222
- pointerId: 2,
223
- pointerType: "touch",
224
- clientX: 700,
225
- clientY: 300,
226
- });
227
-
228
- const scene = container.querySelector(".dependency-graph-scene") as HTMLElement;
229
- const transform = scene.style.transform;
230
- const scaleMatch = transform.match(/scale\(([\d.]+)\)/);
231
- expect(scaleMatch).toBeTruthy();
232
- expect(parseFloat(scaleMatch![1])).toBeGreaterThan(1);
233
- });
234
-
235
- it("scale is clamped between MIN_SCALE and MAX_SCALE", () => {
236
- const tasks = [createTask({ id: "FN-001", column: "todo" })];
237
- const { container, unmount } = renderView(createMockContext(tasks));
238
-
239
- // Try to zoom in beyond MAX_SCALE (2.0)
240
- const zoomInBtn = Array.from(container.querySelectorAll("button")).find((b) => b.textContent === "Zoom In")!;
241
- for (let i = 0; i < 50; i++) {
242
- fireEvent.click(zoomInBtn);
243
- }
244
-
245
- const scene = container.querySelector(".dependency-graph-scene") as HTMLElement;
246
- let scaleMatch = scene.style.transform.match(/scale\(([\d.]+)\)/);
247
- expect(scaleMatch).toBeTruthy();
248
- expect(parseFloat(scaleMatch![1])).toBeLessThanOrEqual(2);
249
-
250
- // Try to zoom out beyond MIN_SCALE (0.4)
251
- const zoomOutBtn = Array.from(container.querySelectorAll("button")).find((b) => b.textContent === "Zoom Out")!;
252
- for (let i = 0; i < 100; i++) {
253
- fireEvent.click(zoomOutBtn);
254
- }
255
-
256
- const updatedScene = container.querySelector(".dependency-graph-scene") as HTMLElement;
257
- scaleMatch = updatedScene.style.transform.match(/scale\(([\d.]+)\)/);
258
- expect(scaleMatch).toBeTruthy();
259
- expect(parseFloat(scaleMatch![1])).toBeGreaterThanOrEqual(0.4);
260
- });
261
- });
@@ -1,41 +0,0 @@
1
- import { describe, expect, it, beforeEach } from "vitest";
2
- import { projectScopedKey, loadPositions, savePositions } from "../storage";
3
-
4
- const createMemoryStorage = () => {
5
- const map = new Map<string, string>();
6
- return {
7
- getItem: (key: string) => map.get(key) ?? null,
8
- setItem: (key: string, value: string) => {
9
- map.set(key, value);
10
- },
11
- clear: () => map.clear(),
12
- };
13
- };
14
-
15
- describe("storage", () => {
16
- const localStorage = createMemoryStorage();
17
-
18
- beforeEach(() => {
19
- (globalThis as { window?: { localStorage?: typeof localStorage } }).window = { localStorage };
20
- localStorage.clear();
21
- });
22
-
23
- it("builds project-scoped key with canonical base key", () => {
24
- expect(projectScopedKey("proj_123")).toBe("kb:proj_123:fusion-plugin-dependency-graph:positions");
25
- });
26
-
27
- it("falls back to unscoped key when projectId is missing or empty", () => {
28
- expect(projectScopedKey()).toBe("fusion-plugin-dependency-graph:positions");
29
- expect(projectScopedKey("")).toBe("fusion-plugin-dependency-graph:positions");
30
- });
31
-
32
- it("persists and restores positions", () => {
33
- savePositions("proj_123", { "FN-1": { x: 10, y: 20 } });
34
- expect(loadPositions("proj_123")).toEqual({ "FN-1": { x: 10, y: 20 } });
35
- });
36
-
37
- it("returns empty object for invalid JSON", () => {
38
- localStorage.setItem("kb:proj_123:fusion-plugin-dependency-graph:positions", "not-json");
39
- expect(loadPositions("proj_123")).toEqual({});
40
- });
41
- });
@@ -1,25 +0,0 @@
1
- import { definePlugin } from "@fusion/plugin-sdk";
2
-
3
- const plugin = definePlugin({
4
- manifest: {
5
- id: "fusion-plugin-dependency-graph",
6
- name: "Dependency Graph",
7
- version: "0.1.0",
8
- description: "Top-level dependency graph dashboard view",
9
- },
10
- state: "installed",
11
- hooks: {},
12
- dashboardViews: [
13
- {
14
- viewId: "graph",
15
- label: "Graph",
16
- componentPath: "./src/DependencyGraphView.tsx",
17
- icon: "Network",
18
- placement: "more",
19
- order: 40,
20
- },
21
- ],
22
- });
23
-
24
- export default plugin;
25
- export { DependencyGraphView } from "./DependencyGraphView";
@@ -1,22 +0,0 @@
1
- import { getScopedItem, scopedKey, setScopedItem } from "../../../packages/dashboard/app/utils/projectStorage";
2
-
3
- const BASE_KEY = "fusion-plugin-dependency-graph:positions";
4
-
5
- export function projectScopedKey(projectId?: string): string {
6
- return scopedKey(BASE_KEY, projectId);
7
- }
8
-
9
- export function loadPositions(projectId?: string): Record<string, { x: number; y: number }> {
10
- try {
11
- const raw = getScopedItem(BASE_KEY, projectId);
12
- if (!raw) return {};
13
- const parsed = JSON.parse(raw) as Record<string, { x: number; y: number }>;
14
- return parsed ?? {};
15
- } catch {
16
- return {};
17
- }
18
- }
19
-
20
- export function savePositions(projectId: string | undefined, positions: Record<string, { x: number; y: number }>): void {
21
- setScopedItem(BASE_KEY, JSON.stringify(positions), projectId);
22
- }