symbiote-ui 0.3.0-alpha.4

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 (322) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/LICENSE +21 -0
  3. package/README.md +76 -0
  4. package/canvas/AutoLayout.js +731 -0
  5. package/canvas/Breadcrumb/Breadcrumb.css.js +75 -0
  6. package/canvas/Breadcrumb/Breadcrumb.js +96 -0
  7. package/canvas/Breadcrumb/Breadcrumb.tpl.js +7 -0
  8. package/canvas/CanvasConnectionRenderer.js +971 -0
  9. package/canvas/CanvasGraph/CanvasGraph.css.js +29 -0
  10. package/canvas/CanvasGraph/CanvasGraph.js +1697 -0
  11. package/canvas/CanvasGraph/CanvasGraphDrawState.js +280 -0
  12. package/canvas/CanvasGraph/CanvasGraphGeometry.js +194 -0
  13. package/canvas/CanvasViewport.js +550 -0
  14. package/canvas/ConnectionRenderer.js +1283 -0
  15. package/canvas/FlowSimulator.js +326 -0
  16. package/canvas/ForceLayout.js +226 -0
  17. package/canvas/ForceWorker.js +1303 -0
  18. package/canvas/FrameManager.js +223 -0
  19. package/canvas/GraphExplorerShell/GraphExplorerShell.css.js +136 -0
  20. package/canvas/GraphExplorerShell/GraphExplorerShell.js +129 -0
  21. package/canvas/GraphExplorerShell/GraphExplorerShell.tpl.js +12 -0
  22. package/canvas/GraphTabs/GraphTabs.css.js +101 -0
  23. package/canvas/GraphTabs/GraphTabs.js +189 -0
  24. package/canvas/GraphTabs/GraphTabs.tpl.js +12 -0
  25. package/canvas/LODManager.js +88 -0
  26. package/canvas/Minimap/Minimap.css.js +73 -0
  27. package/canvas/Minimap/Minimap.js +210 -0
  28. package/canvas/Minimap/Minimap.tpl.js +7 -0
  29. package/canvas/NodeCanvas/NodeCanvas.css.js +398 -0
  30. package/canvas/NodeCanvas/NodeCanvas.js +1499 -0
  31. package/canvas/NodeCanvas/NodeCanvas.tpl.js +22 -0
  32. package/canvas/NodeSearch/NodeSearch.css.js +97 -0
  33. package/canvas/NodeSearch/NodeSearch.js +140 -0
  34. package/canvas/NodeSearch/NodeSearch.tpl.js +25 -0
  35. package/canvas/NodeViewManager.js +748 -0
  36. package/canvas/PcbRouteDiagnostics.js +463 -0
  37. package/canvas/PcbRouter.js +1127 -0
  38. package/canvas/PinExpansion.js +134 -0
  39. package/canvas/PseudoConnection.js +84 -0
  40. package/canvas/SelectionSync.js +163 -0
  41. package/canvas/SubgraphManager.js +203 -0
  42. package/canvas/SubgraphRouter.js +452 -0
  43. package/canvas/ViewportActions.js +473 -0
  44. package/canvas/graph-explorer.js +339 -0
  45. package/canvas/graph-layout.js +148 -0
  46. package/canvas/graph-model.js +68 -0
  47. package/canvas/html-in-canvas.js +202 -0
  48. package/canvas/project-graph-builder.js +440 -0
  49. package/canvas/project-graph-model.js +183 -0
  50. package/chat/ChatComposer/ChatComposer.css.js +652 -0
  51. package/chat/ChatComposer/ChatComposer.js +304 -0
  52. package/chat/ChatList/ChatList.css.js +102 -0
  53. package/chat/ChatList/ChatList.js +99 -0
  54. package/chat/ChatList/ChatList.tpl.js +20 -0
  55. package/chat/ChatListItem/ChatListItem.css.js +117 -0
  56. package/chat/ChatListItem/ChatListItem.js +32 -0
  57. package/chat/ChatListItem/ChatListItem.tpl.js +17 -0
  58. package/chat/ChatMessageItem/ChatMessageItem.css.js +628 -0
  59. package/chat/ChatMessageItem/ChatMessageItem.js +156 -0
  60. package/chat/ChatSidebar/ChatSidebar.css.js +150 -0
  61. package/chat/ChatSidebar/ChatSidebar.js +230 -0
  62. package/chat/ChatSidebar/ChatSidebar.tpl.js +18 -0
  63. package/chat/ChatSidebar/constants.js +11 -0
  64. package/chat/ChatSidebarItem/ChatSidebarItem.css.js +445 -0
  65. package/chat/ChatSidebarItem/ChatSidebarItem.js +304 -0
  66. package/chat/ChatTranscript/ChatTranscript.css.js +90 -0
  67. package/chat/ChatTranscript/ChatTranscript.js +244 -0
  68. package/chat/chat-context.js +123 -0
  69. package/chat/message-model.js +156 -0
  70. package/cli.js +20 -0
  71. package/control/Button/Button.css.js +93 -0
  72. package/control/Button/Button.js +78 -0
  73. package/control/Button/Button.tpl.js +3 -0
  74. package/control/Field/Field.css.js +91 -0
  75. package/control/Field/Field.js +17 -0
  76. package/control/Field/Field.tpl.js +3 -0
  77. package/core/Connection.js +47 -0
  78. package/core/Editor.js +449 -0
  79. package/core/Frame.js +33 -0
  80. package/core/GraphMermaid.js +348 -0
  81. package/core/GraphText.js +228 -0
  82. package/core/Node.js +145 -0
  83. package/core/Portal.js +106 -0
  84. package/core/Socket.js +187 -0
  85. package/core/SubgraphNode.js +121 -0
  86. package/core/base-path.js +55 -0
  87. package/core/dom-utils.js +14 -0
  88. package/core/index.js +18 -0
  89. package/core/local-cache.js +26 -0
  90. package/core/state-sync.js +227 -0
  91. package/custom-elements.json +6380 -0
  92. package/discover.js +240 -0
  93. package/display/Badge/Badge.css.js +44 -0
  94. package/display/Badge/Badge.js +17 -0
  95. package/display/Badge/Badge.tpl.js +3 -0
  96. package/display/Banner/Banner.css.js +61 -0
  97. package/display/Banner/Banner.js +17 -0
  98. package/display/Banner/Banner.tpl.js +3 -0
  99. package/display/CodeBlock/CodeBlock.css.js +194 -0
  100. package/display/CodeBlock/CodeBlock.js +220 -0
  101. package/display/CodeBlock/CodeBlock.tpl.js +11 -0
  102. package/display/DataTable/DataTable.css.js +101 -0
  103. package/display/DataTable/DataTable.js +136 -0
  104. package/display/DataTable/DataTable.tpl.js +13 -0
  105. package/display/EmptyState/EmptyState.css.js +33 -0
  106. package/display/EmptyState/EmptyState.js +17 -0
  107. package/display/EmptyState/EmptyState.tpl.js +3 -0
  108. package/display/EventFeed/EventFeed.css.js +145 -0
  109. package/display/EventFeed/EventFeed.js +64 -0
  110. package/display/EventFeed/EventFeed.tpl.js +14 -0
  111. package/display/EventFeed/EventFeedItem.js +116 -0
  112. package/display/EventFeed/EventFeedItem.tpl.js +22 -0
  113. package/display/LoadingOverlay/LoadingOverlay.css.js +91 -0
  114. package/display/LoadingOverlay/LoadingOverlay.js +48 -0
  115. package/display/LoadingOverlay/LoadingOverlay.tpl.js +12 -0
  116. package/display/Metric/Metric.css.js +60 -0
  117. package/display/Metric/Metric.js +17 -0
  118. package/display/Metric/Metric.tpl.js +6 -0
  119. package/display/OutputGraphPreview/OutputGraphPreview.css.js +122 -0
  120. package/display/OutputGraphPreview/OutputGraphPreview.js +89 -0
  121. package/display/OutputGraphPreview/OutputGraphPreview.tpl.js +13 -0
  122. package/display/OutputListPreview/OutputListPreview.css.js +109 -0
  123. package/display/OutputListPreview/OutputListPreview.js +77 -0
  124. package/display/OutputListPreview/OutputListPreview.tpl.js +13 -0
  125. package/display/SourceEditor/SourceEditor.css.js +39 -0
  126. package/display/SourceEditor/SourceEditor.js +129 -0
  127. package/display/SourceEditor/SourceEditor.tpl.js +10 -0
  128. package/display/SourceViewer/SourceViewer.css.js +80 -0
  129. package/display/SourceViewer/SourceViewer.js +418 -0
  130. package/display/SourceViewer/SourceViewer.tpl.js +17 -0
  131. package/display/StatusRibbon/StatusRibbon.css.js +73 -0
  132. package/display/StatusRibbon/StatusRibbon.js +87 -0
  133. package/display/StatusRibbon/StatusRibbon.tpl.js +7 -0
  134. package/display/event-feed-adapter.js +72 -0
  135. package/display/format-utils.js +29 -0
  136. package/display/highlight.js +659 -0
  137. package/display/icons.js +37 -0
  138. package/display/markdown-formatter.js +60 -0
  139. package/display/network-approval-page.js +487 -0
  140. package/display/output-preview.js +261 -0
  141. package/effects/CellBg/CellBg.css.js +33 -0
  142. package/effects/CellBg/CellBg.js +410 -0
  143. package/effects/CellBg/CellBg.tpl.js +5 -0
  144. package/graph/canvas-adapter.js +223 -0
  145. package/graph/graph-algorithms.js +31 -0
  146. package/graph/index.js +46 -0
  147. package/graph/model.js +176 -0
  148. package/graph/project-graph-build.js +66 -0
  149. package/graph/project-graph-metadata.js +253 -0
  150. package/graph/project-package.js +128 -0
  151. package/graph/project-runtime.js +116 -0
  152. package/graph/project-transaction.js +284 -0
  153. package/graph/skeleton-utils.js +84 -0
  154. package/graph/theme-contract.js +36 -0
  155. package/graph/transaction-parser.js +56 -0
  156. package/icons/MaterialSymbols.js +69 -0
  157. package/icons/material-symbols-outlined-400.ttf +0 -0
  158. package/icons/material-symbols.css +24 -0
  159. package/index.js +95 -0
  160. package/inspector/InspectorPanel/InspectorPanel.css.js +375 -0
  161. package/inspector/InspectorPanel/InspectorPanel.js +368 -0
  162. package/inspector/InspectorPanel/InspectorPanel.tpl.js +96 -0
  163. package/inspector/TemplatePreview/TemplatePreview.css.js +104 -0
  164. package/inspector/TemplatePreview/TemplatePreview.js +145 -0
  165. package/inspector/TemplatePreview/TemplatePreview.tpl.js +33 -0
  166. package/interactions/ConnectFlow.js +304 -0
  167. package/interactions/Drag.js +104 -0
  168. package/interactions/Selector.js +133 -0
  169. package/interactions/SnapGrid.js +66 -0
  170. package/interactions/Zoom.js +139 -0
  171. package/layout/ActionZone/ActionZone.css.js +88 -0
  172. package/layout/ActionZone/ActionZone.js +261 -0
  173. package/layout/ActionZone/ActionZone.tpl.js +11 -0
  174. package/layout/CrossLayoutPortalBridge/CrossLayoutPortalBridge.js +255 -0
  175. package/layout/Layout/Layout.css.js +91 -0
  176. package/layout/Layout/Layout.js +637 -0
  177. package/layout/Layout/Layout.tpl.js +27 -0
  178. package/layout/LayoutNode/LayoutNode.css.js +302 -0
  179. package/layout/LayoutNode/LayoutNode.js +509 -0
  180. package/layout/LayoutNode/LayoutNode.tpl.js +39 -0
  181. package/layout/LayoutPreview/LayoutPreview.css.js +46 -0
  182. package/layout/LayoutPreview/LayoutPreview.js +102 -0
  183. package/layout/LayoutPreview/LayoutPreview.tpl.js +6 -0
  184. package/layout/LayoutRouter/LayoutRouter.js +274 -0
  185. package/layout/LayoutRouter/SectionRegistry.js +135 -0
  186. package/layout/LayoutRouter/routerSync.js +250 -0
  187. package/layout/LayoutSidebar/LayoutSidebar.css.js +411 -0
  188. package/layout/LayoutSidebar/LayoutSidebar.js +368 -0
  189. package/layout/LayoutSidebar/LayoutSidebar.tpl.js +26 -0
  190. package/layout/LayoutSidebar/SidebarSection.css.js +20 -0
  191. package/layout/LayoutSidebar/SidebarSection.js +184 -0
  192. package/layout/LayoutSidebar/SidebarSection.tpl.js +22 -0
  193. package/layout/LayoutTree.js +373 -0
  194. package/layout/PanelMenu/PanelMenu.css.js +43 -0
  195. package/layout/PanelMenu/PanelMenu.js +95 -0
  196. package/layout/PanelMenu/PanelMenu.tpl.js +17 -0
  197. package/layout/ProjectTabs/ProjectTabs.css.js +188 -0
  198. package/layout/ProjectTabs/ProjectTabs.js +77 -0
  199. package/layout/ProjectTabs/ProjectTabs.tpl.js +15 -0
  200. package/layout/index.js +40 -0
  201. package/list/ListDetailShell/ListDetailShell.css.js +128 -0
  202. package/list/ListDetailShell/ListDetailShell.js +72 -0
  203. package/list/ListDetailShell/ListDetailShell.tpl.js +36 -0
  204. package/list/ListItem/ListItem.css.js +111 -0
  205. package/list/ListItem/ListItem.js +66 -0
  206. package/list/ListItem/ListItem.tpl.js +18 -0
  207. package/locale/index.js +503 -0
  208. package/manifest/component-registry.js +2446 -0
  209. package/manifest/graph-schema.js +285 -0
  210. package/manifest/index.js +6 -0
  211. package/manifest/project-schema-catalog.js +246 -0
  212. package/manifest/rule-catalog.js +201 -0
  213. package/manifest/theme-catalog.js +2149 -0
  214. package/manifest/ui-schema-catalog.js +334 -0
  215. package/menu/ContextMenu/ContextMenu.css.js +61 -0
  216. package/menu/ContextMenu/ContextMenu.js +82 -0
  217. package/menu/ContextMenu/ContextMenu.tpl.js +19 -0
  218. package/navigation/QuickOpen/QuickOpen.css.js +92 -0
  219. package/navigation/QuickOpen/QuickOpen.js +185 -0
  220. package/navigation/QuickOpen/QuickOpen.tpl.js +15 -0
  221. package/navigation/quick-open-utils.js +101 -0
  222. package/node/CtrlItem/CtrlItem.css.js +41 -0
  223. package/node/CtrlItem/CtrlItem.js +24 -0
  224. package/node/CtrlItem/CtrlItem.tpl.js +17 -0
  225. package/node/GraphFrame/GraphFrame.css.js +66 -0
  226. package/node/GraphFrame/GraphFrame.js +32 -0
  227. package/node/GraphFrame/GraphFrame.tpl.js +13 -0
  228. package/node/GraphNode/GraphNode.css.js +815 -0
  229. package/node/GraphNode/GraphNode.js +173 -0
  230. package/node/GraphNode/GraphNode.tpl.js +33 -0
  231. package/node/NodeCallout/NodeCallout.css.js +91 -0
  232. package/node/NodeCallout/NodeCallout.js +281 -0
  233. package/node/NodeCallout/NodeCallout.tpl.js +8 -0
  234. package/node/NodeSocket/NodeSocket.css.js +68 -0
  235. package/node/NodeSocket/NodeSocket.js +26 -0
  236. package/node/NodeSocket/NodeSocket.tpl.js +7 -0
  237. package/node/PortItem/PortItem.css.js +93 -0
  238. package/node/PortItem/PortItem.js +87 -0
  239. package/node/PortItem/PortItem.tpl.js +10 -0
  240. package/package.json +165 -0
  241. package/palette/PaletteBrowser/PaletteBrowser.css.js +143 -0
  242. package/palette/PaletteBrowser/PaletteBrowser.js +152 -0
  243. package/palette/PaletteBrowser/PaletteBrowser.tpl.js +23 -0
  244. package/plugins/History.js +408 -0
  245. package/plugins/Readonly.js +60 -0
  246. package/rules/symbiote-3x.json +170 -0
  247. package/schemas/component-descriptor-v1.json +91 -0
  248. package/schemas/component-descriptor-v2.json +145 -0
  249. package/schemas/graph-model-v1.json +179 -0
  250. package/schemas/graph-v1.json +91 -0
  251. package/schemas/project-package-v1.json +102 -0
  252. package/schemas/project-transaction-v1.json +114 -0
  253. package/schemas/runtime-ui-v1.json +80 -0
  254. package/schemas/theme-rule-block-v1.json +73 -0
  255. package/shapes/CircleShape.js +79 -0
  256. package/shapes/CommentShape.js +35 -0
  257. package/shapes/DiamondShape.js +130 -0
  258. package/shapes/NodeShape.js +79 -0
  259. package/shapes/PillShape.js +91 -0
  260. package/shapes/RectShape.js +84 -0
  261. package/shapes/SVGShape.js +525 -0
  262. package/shapes/index.js +63 -0
  263. package/surface/Card/Card.css.js +57 -0
  264. package/surface/Card/Card.js +17 -0
  265. package/surface/Card/Card.tpl.js +3 -0
  266. package/themes/Palette.js +30 -0
  267. package/themes/Skin.js +113 -0
  268. package/themes/Theme.js +82 -0
  269. package/themes/carbon.js +135 -0
  270. package/themes/dark.js +140 -0
  271. package/themes/default-dark.js +714 -0
  272. package/themes/default-provider.css +635 -0
  273. package/themes/default-provider.js +718 -0
  274. package/themes/ebook.js +136 -0
  275. package/themes/grey.js +137 -0
  276. package/themes/light.js +139 -0
  277. package/themes/neon.js +138 -0
  278. package/themes/pcb.js +273 -0
  279. package/themes/synthwave.js +138 -0
  280. package/tokens/base.json +29 -0
  281. package/tokens/themes/carbon.json +11 -0
  282. package/tokens/themes/dark.json +12 -0
  283. package/tokens/themes/default-dark.json +1543 -0
  284. package/tokens/themes/default-provider.json +1543 -0
  285. package/tokens/themes/ebook.json +11 -0
  286. package/tokens/themes/grey.json +11 -0
  287. package/tokens/themes/light.json +12 -0
  288. package/tokens/themes/neon.json +11 -0
  289. package/tokens/themes/pcb.json +11 -0
  290. package/tokens/themes/synthwave.json +11 -0
  291. package/toolbar/QuickToolbar/QuickToolbar.css.js +152 -0
  292. package/toolbar/QuickToolbar/QuickToolbar.js +529 -0
  293. package/toolbar/QuickToolbar/QuickToolbar.tpl.js +34 -0
  294. package/tree/TreePanel/TreePanel.css.js +112 -0
  295. package/tree/TreePanel/TreePanel.js +147 -0
  296. package/tree/TreePanel/TreePanel.tpl.js +18 -0
  297. package/tree/TreeView/TreeView.css.js +122 -0
  298. package/tree/TreeView/TreeView.js +365 -0
  299. package/tree/TreeView/TreeView.tpl.js +10 -0
  300. package/ui/dialogs.js +221 -0
  301. package/ui/host-adapters.js +114 -0
  302. package/ui/index.js +660 -0
  303. package/ui/locale.js +50 -0
  304. package/ui/overlay-stack.js +89 -0
  305. package/ui/shared-styles.js +26 -0
  306. package/webmcp.js +37 -0
  307. package/xr/deep-graph.js +646 -0
  308. package/xr/emulation.js +198 -0
  309. package/xr/gesture.js +228 -0
  310. package/xr/html-canvas-renderer.js +472 -0
  311. package/xr/index.js +15 -0
  312. package/xr/layout-projection.js +1046 -0
  313. package/xr/panel-frame.js +128 -0
  314. package/xr/panel-host.js +267 -0
  315. package/xr/pointer.js +258 -0
  316. package/xr/scene-controller.js +242 -0
  317. package/xr/spatial-scene.js +212 -0
  318. package/xr/theme-bridge.js +105 -0
  319. package/xr/three-webxr-adapter.js +3439 -0
  320. package/xr/webgl-layer-renderer.js +419 -0
  321. package/xr/webxr.js +679 -0
  322. package/xr/workbench.js +516 -0
package/core/Editor.js ADDED
@@ -0,0 +1,449 @@
1
+ /**
2
+ * NodeEditor — central graph manager with event system
3
+ *
4
+ * Provides CRUD operations for nodes and connections with
5
+ * pre/post event hooks via callback-based event emitter.
6
+ * Replaces Rete.js Scope/Signal with simpler emit pattern.
7
+ *
8
+ * @module symbiote-node/core/Editor
9
+ */
10
+
11
+ import { Connection } from './Connection.js';
12
+ import { Node } from './Node.js';
13
+ import { Socket, Input, Output, InputControl } from './Socket.js';
14
+ import { Frame } from './Frame.js';
15
+
16
+ /**
17
+ * @typedef {'nodecreate'|'nodecreated'|'noderemove'|'noderemoved'|
18
+ * 'connectioncreate'|'connectioncreated'|'connectionremove'|'connectionremoved'|
19
+ * 'framecreate'|'framecreated'|'frameremove'|'frameremoved'|
20
+ * 'clear'|'cleared'|'nodeselect'|'nodedeselect'} EditorEvent
21
+ */
22
+
23
+ export class NodeEditor {
24
+ constructor() {
25
+ /** @type {Map<string, import('./Node.js').Node>} */
26
+ this.nodes = new Map();
27
+
28
+ /** @type {Map<string, Connection>} */
29
+ this.connections = new Map();
30
+
31
+ /** @type {Map<string, import('./Frame.js').Frame>} */
32
+ this.frames = new Map();
33
+
34
+ /** @type {Object<string, Set<function>>} */
35
+ this._listeners = {};
36
+ }
37
+
38
+
39
+ /**
40
+ * Subscribe to editor event
41
+ * @param {EditorEvent} event
42
+ * @param {function} handler
43
+ * @returns {function} Unsubscribe function
44
+ */
45
+ on(event, handler) {
46
+ if (!this._listeners[event]) {
47
+ this._listeners[event] = new Set();
48
+ }
49
+ this._listeners[event].add(handler);
50
+ return () => this._listeners[event].delete(handler);
51
+ }
52
+
53
+ /**
54
+ * Emit event to all listeners
55
+ * @param {EditorEvent} event
56
+ * @param {*} data
57
+ * @returns {boolean} - false if any listener returned false (cancel)
58
+ */
59
+ emit(event, data) {
60
+ let handlers = this._listeners[event];
61
+ if (!handlers) return true;
62
+ for (const handler of handlers) {
63
+ if (handler(data) === false) return false;
64
+ }
65
+ return true;
66
+ }
67
+
68
+ /**
69
+ * Remove all event listeners (for clean teardown)
70
+ */
71
+ removeAllListeners() {
72
+ this._listeners = {};
73
+ }
74
+
75
+
76
+ /**
77
+ * Get node by ID
78
+ * @param {string} id
79
+ * @returns {import('./Node.js').Node|undefined}
80
+ */
81
+ getNode(id) {
82
+ return this.nodes.get(id);
83
+ }
84
+
85
+ /**
86
+ * Get all nodes
87
+ * @returns {import('./Node.js').Node[]}
88
+ */
89
+ getNodes() {
90
+ return [...this.nodes.values()];
91
+ }
92
+
93
+ /**
94
+ * Add node to editor
95
+ * @param {import('./Node.js').Node} node
96
+ * @returns {boolean}
97
+ */
98
+ addNode(node) {
99
+ if (this.nodes.has(node.id)) throw new Error('node already added');
100
+ if (!this.emit('nodecreate', node)) return false;
101
+ this.nodes.set(node.id, node);
102
+ this.emit('nodecreated', node);
103
+ return true;
104
+ }
105
+
106
+ /**
107
+ * Remove node and all its connections
108
+ * @param {string} id
109
+ * @returns {boolean}
110
+ */
111
+ removeNode(id) {
112
+ let node = this.nodes.get(id);
113
+ if (!node) throw new Error('node not found');
114
+ if (!this.emit('noderemove', node)) return false;
115
+
116
+
117
+ for (const [connId, conn] of this.connections) {
118
+ if (conn.from === id || conn.to === id) {
119
+ this.removeConnection(connId);
120
+ }
121
+ }
122
+
123
+ this.nodes.delete(id);
124
+ this.emit('noderemoved', node);
125
+ return true;
126
+ }
127
+
128
+
129
+ /**
130
+ * Get connection by ID
131
+ * @param {string} id
132
+ * @returns {Connection|undefined}
133
+ */
134
+ getConnection(id) {
135
+ return this.connections.get(id);
136
+ }
137
+
138
+ /**
139
+ * Get all connections
140
+ * @returns {Connection[]}
141
+ */
142
+ getConnections() {
143
+ return [...this.connections.values()];
144
+ }
145
+
146
+ /**
147
+ * Get connections for a specific node
148
+ * @param {string} nodeId
149
+ * @returns {Connection[]}
150
+ */
151
+ getNodeConnections(nodeId) {
152
+ return this.getConnections().filter((c) => c.from === nodeId || c.to === nodeId);
153
+ }
154
+
155
+ /**
156
+ * Add connection
157
+ * @param {Connection} connection
158
+ * @returns {boolean}
159
+ */
160
+ addConnection(connection) {
161
+ if (this.connections.has(connection.id)) throw new Error('connection already added');
162
+ if (!this.emit('connectioncreate', connection)) return false;
163
+ this.connections.set(connection.id, connection);
164
+ this.emit('connectioncreated', connection);
165
+ return true;
166
+ }
167
+
168
+ /**
169
+ * Remove connection
170
+ * @param {string} id
171
+ * @returns {boolean}
172
+ */
173
+ removeConnection(id) {
174
+ let conn = this.connections.get(id);
175
+ if (!conn) return false;
176
+ if (!this.emit('connectionremove', conn)) return false;
177
+ this.connections.delete(id);
178
+ this.emit('connectionremoved', conn);
179
+ return true;
180
+ }
181
+
182
+
183
+ /**
184
+ * Clear all nodes and connections
185
+ * @returns {boolean}
186
+ */
187
+ clear() {
188
+ if (!this.emit('clear', null)) return false;
189
+ for (const id of [...this.connections.keys()]) {
190
+ this.removeConnection(id);
191
+ }
192
+ for (const id of [...this.nodes.keys()]) {
193
+ this.removeNode(id);
194
+ }
195
+ this.emit('cleared', null);
196
+ return true;
197
+ }
198
+
199
+
200
+ /**
201
+ * Get frame by ID
202
+ * @param {string} id
203
+ * @returns {import('./Frame.js').Frame|undefined}
204
+ */
205
+ getFrame(id) {
206
+ return this.frames.get(id);
207
+ }
208
+
209
+ /**
210
+ * Get all frames
211
+ * @returns {import('./Frame.js').Frame[]}
212
+ */
213
+ getFrames() {
214
+ return [...this.frames.values()];
215
+ }
216
+
217
+ /**
218
+ * Add frame
219
+ * @param {import('./Frame.js').Frame} frame
220
+ * @returns {boolean}
221
+ */
222
+ addFrame(frame) {
223
+ if (this.frames.has(frame.id)) throw new Error('frame already added');
224
+ if (!this.emit('framecreate', frame)) return false;
225
+ this.frames.set(frame.id, frame);
226
+ this.emit('framecreated', frame);
227
+ return true;
228
+ }
229
+
230
+ /**
231
+ * Remove frame
232
+ * @param {string} id
233
+ * @returns {boolean}
234
+ */
235
+ removeFrame(id) {
236
+ let frame = this.frames.get(id);
237
+ if (!frame) return false;
238
+ if (!this.emit('frameremove', frame)) return false;
239
+ this.frames.delete(id);
240
+ this.emit('frameremoved', frame);
241
+ return true;
242
+ }
243
+
244
+
245
+ /**
246
+ * Serialize editor state to symbiote-node workflow JSON format.
247
+ * Output is directly compatible with engine/Graph.fromJSON().
248
+ * @param {Object<string, number[]>} [positions] - Node positions {nodeId: [x, y]}
249
+ * @returns {object} Workflow JSON
250
+ */
251
+ toJSON(positions = {}) {
252
+ return {
253
+ version: 1,
254
+ nodes: this.getNodes().map((n) => {
255
+ let obj = {
256
+ id: n.id,
257
+ type: n.type,
258
+ name: n.label,
259
+ params: { ...n.params },
260
+ };
261
+ if (n.category && n.category !== 'default') obj.category = n.category;
262
+ if (n.shape && n.shape !== 'rect') obj.shape = n.shape;
263
+ if (n.icon) obj.icon = n.icon;
264
+ if (n.cacheMode && n.cacheMode !== 'auto') obj.cacheMode = n.cacheMode;
265
+
266
+
267
+ let inputs = Object.entries(n.inputs);
268
+ if (inputs.length > 0) {
269
+ obj.inputs = inputs.map(([key, inp]) => ({
270
+ name: key,
271
+ type: inp.socket?.name || 'any',
272
+ label: inp.label || '',
273
+ }));
274
+ }
275
+ let outputs = Object.entries(n.outputs);
276
+ if (outputs.length > 0) {
277
+ obj.outputs = outputs.map(([key, out]) => ({
278
+ name: key,
279
+ type: out.socket?.name || 'any',
280
+ label: out.label || '',
281
+ }));
282
+ }
283
+ return obj;
284
+ }),
285
+ connections: this.getConnections().map((c) => ({
286
+ id: c.id,
287
+ from: c.from,
288
+ out: c.out,
289
+ to: c.to,
290
+ in: c.in,
291
+ })),
292
+ frames: this.getFrames().map((f) => ({
293
+ id: f.id,
294
+ label: f.label,
295
+ x: f.x,
296
+ y: f.y,
297
+ width: f.width,
298
+ height: f.height,
299
+ color: f.color,
300
+ })),
301
+ ui: {
302
+ positions: { ...positions },
303
+ },
304
+ };
305
+ }
306
+
307
+ /**
308
+ * Reconstruct editor state from symbiote-node workflow JSON.
309
+ * Enables round-trip: Editor → toJSON → fromJSON → Editor.
310
+ * Also accepts output from engine/Graph.toJSON().
311
+ * @param {object} data - Workflow JSON
312
+ * @param {Object<string, number[]>} [positionsOut] - Optional object to populate with positions
313
+ * @returns {NodeEditor} this
314
+ */
315
+ fromJSON(data, positionsOut) {
316
+
317
+ this.nodes.clear();
318
+ this.connections.clear();
319
+ this.frames.clear();
320
+
321
+
322
+ for (const nd of data.nodes || []) {
323
+ let node = new Node(nd.name || nd.type, {
324
+ id: nd.id,
325
+ type: nd.type,
326
+ category: nd.category || 'default',
327
+ shape: nd.shape || 'rect',
328
+ icon: nd.icon || '',
329
+ });
330
+ node.params = { ...nd.params };
331
+ if (nd.cacheMode) node.cacheMode = nd.cacheMode;
332
+
333
+
334
+ let driverParams = nd.driver?.params;
335
+ if (driverParams && !nd.params) nd.params = {};
336
+ if (driverParams) {
337
+ for (const [key, def] of Object.entries(driverParams)) {
338
+ if (!(key in nd.params) && def.default !== undefined) {
339
+ nd.params[key] = def.default;
340
+ }
341
+ }
342
+ }
343
+
344
+ if (nd.params) {
345
+ for (const [key, value] of Object.entries(nd.params)) {
346
+ /** @type {'text'|'number'|'textarea'|'select'|'boolean'} */
347
+ let controlType = 'text';
348
+ let displayValue = value;
349
+ let controlOptions = [];
350
+
351
+
352
+ let paramMeta = driverParams?.[key];
353
+ if (paramMeta?.type === 'boolean' || typeof value === 'boolean') {
354
+ controlType = 'boolean';
355
+ } else if (paramMeta?.type === 'number' || typeof value === 'number') {
356
+ controlType = 'number';
357
+ } else if (typeof value === 'string' && value.includes('\n')) {
358
+ controlType = 'textarea';
359
+ } else if (typeof value === 'object') {
360
+ controlType = 'textarea';
361
+ displayValue = JSON.stringify(value, null, 2);
362
+ }
363
+
364
+
365
+ if (paramMeta?.options) {
366
+ controlType = 'select';
367
+ controlOptions = paramMeta.options;
368
+ }
369
+
370
+ node.addControl(
371
+ key,
372
+ new InputControl(controlType, {
373
+ initial: displayValue,
374
+ label: paramMeta?.description || key,
375
+ options: controlOptions,
376
+ })
377
+ );
378
+ }
379
+ }
380
+
381
+
382
+ if (nd.inputs) {
383
+ for (const inp of nd.inputs) {
384
+ node.addInput(inp.name, new Input(new Socket(inp.type || 'any'), inp.label || ''));
385
+ }
386
+ }
387
+ if (nd.outputs) {
388
+ for (const out of nd.outputs) {
389
+ node.addOutput(out.name, new Output(new Socket(out.type || 'any'), out.label || ''));
390
+ }
391
+ }
392
+
393
+ this.nodes.set(node.id, node);
394
+ }
395
+
396
+
397
+ for (const cd of data.connections || []) {
398
+ let srcNode = this.nodes.get(cd.from);
399
+ let tgtNode = this.nodes.get(cd.to);
400
+ if (!srcNode || !tgtNode) continue;
401
+
402
+
403
+ if (!srcNode.outputs[cd.out]) {
404
+ srcNode.addOutput(cd.out, new Output(new Socket('any'), cd.out));
405
+ }
406
+ if (!tgtNode.inputs[cd.in]) {
407
+ tgtNode.addInput(cd.in, new Input(new Socket('any'), cd.in));
408
+ }
409
+
410
+ let conn = new Connection(srcNode, cd.out, tgtNode, cd.in);
411
+ if (cd.id) conn.id = cd.id;
412
+ this.connections.set(conn.id, conn);
413
+ }
414
+
415
+
416
+ for (const fd of data.frames || []) {
417
+ let frame = new Frame(fd.label, {
418
+ id: fd.id,
419
+ x: fd.x,
420
+ y: fd.y,
421
+ width: fd.width,
422
+ height: fd.height,
423
+ color: fd.color,
424
+ });
425
+ this.frames.set(frame.id, frame);
426
+ }
427
+
428
+
429
+ if (positionsOut && data.ui?.positions) {
430
+ Object.assign(positionsOut, data.ui.positions);
431
+ }
432
+
433
+ return this;
434
+ }
435
+
436
+ /**
437
+ * Convert editor state to an engine Graph instance for server-side execution.
438
+ * The Graph can be passed directly to Executor.run().
439
+ * @param {Object<string, number[]>} [positions] - Node positions
440
+ * @returns {Promise<import('symbiote-engine').Graph>}
441
+ */
442
+ async toGraph(positions = {}) {
443
+ let { Graph } = await import('symbiote-engine');
444
+ let json = this.toJSON(positions);
445
+ return new Graph(json);
446
+ }
447
+ }
448
+
449
+ export { NodeEditor as default };
package/core/Frame.js ADDED
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Frame — visual grouping rectangle for nodes
3
+ *
4
+ * Pure data class (like Node). Stores position, size, color, label.
5
+ * Frame does not own nodes — containment is determined by spatial overlap.
6
+ *
7
+ * @module symbiote-node/core/Frame
8
+ */
9
+
10
+ import { uid } from './Socket.js';
11
+
12
+ export class Frame {
13
+ /**
14
+ * @param {string} label - Display label
15
+ * @param {object} [options]
16
+ * @param {string} [options.color='#4a9eff'] - Border/header color
17
+ * @param {number} [options.x=0] - X position
18
+ * @param {number} [options.y=0] - Y position
19
+ * @param {number} [options.width=400] - Width
20
+ * @param {number} [options.height=300] - Height
21
+ */
22
+ constructor(label, options = {}) {
23
+ this.id = options.id || uid();
24
+ this.label = label;
25
+ this.color = options.color || '#4a9eff';
26
+ this.x = options.x || 0;
27
+ this.y = options.y || 0;
28
+ this.width = options.width || 400;
29
+ this.height = options.height || 300;
30
+ }
31
+ }
32
+
33
+ export { Frame as default };