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
@@ -0,0 +1,33 @@
1
+ /**
2
+ * TemplatePreview template
3
+ * @module symbiote-node/inspector/TemplatePreview.tpl
4
+ */
5
+ import { html } from '@symbiotejs/symbiote';
6
+
7
+ export let template = html`
8
+ <div class="tpl-preview-section">
9
+ <div class="tpl-chips-label">
10
+ <span class="material-symbols-outlined">sell</span> <span ${{ textContent: 'placeholdersLabel' }}></span>
11
+ </div>
12
+ <div class="tpl-chips" itemize="placeholderChips">
13
+ <template>
14
+ <span class="tpl-chip">{{name}}</span>
15
+ </template>
16
+ </div>
17
+ <div class="tpl-chips-empty" ${{ '@hidden': '!noPlaceholders' }}>
18
+ <span ${{ textContent: 'emptyLabel' }}></span>
19
+ </div>
20
+ </div>
21
+ <div class="tpl-preview-section">
22
+ <div class="tpl-preview-label">
23
+ <span class="material-symbols-outlined">data_object</span> <span ${{ textContent: 'testDataLabel' }}></span>
24
+ </div>
25
+ <textarea class="tpl-test-data" rows="3" spellcheck="false"></textarea>
26
+ </div>
27
+ <div class="tpl-preview-section">
28
+ <div class="tpl-preview-label">
29
+ <span class="material-symbols-outlined">visibility</span> <span ${{ textContent: 'previewLabel' }}></span>
30
+ </div>
31
+ <div class="tpl-preview-result" ${{ textContent: 'previewText' }}></div>
32
+ </div>
33
+ `;
@@ -0,0 +1,304 @@
1
+ /**
2
+ * ConnectFlow — interactive socket-to-socket connection creation
3
+ *
4
+ * Simplified version of Rete.js ClassicFlow.
5
+ * Handles: pointerdown on socket → drag pseudo-line → pointerup on target socket → create connection.
6
+ *
7
+ * @module symbiote-node/interactions/ConnectFlow
8
+ */
9
+
10
+ import { Connection } from '../core/Connection.js';
11
+
12
+ /**
13
+ * @typedef {object} SocketData
14
+ * @property {string} nodeId - Node ID
15
+ * @property {string} key - Port key
16
+ * @property {'input'|'output'} side - Port side
17
+ * @property {HTMLElement} element - Socket DOM element
18
+ */
19
+
20
+ export class ConnectFlow {
21
+ /** @type {SocketData|null} */
22
+ #picked = null;
23
+
24
+ /** @type {import('../core/Editor.js').NodeEditor} */
25
+ #editor;
26
+
27
+ /** @type {function} */
28
+ #getNodePosition;
29
+
30
+ /** @type {function} */
31
+ #getNodeSize;
32
+
33
+ /** @type {function} */
34
+ #getTransform;
35
+
36
+ /** @type {function|null} */
37
+ #onPseudoStart = null;
38
+
39
+ /** @type {function|null} */
40
+ #onPseudoMove = null;
41
+
42
+ /** @type {function|null} */
43
+ #onPseudoEnd = null;
44
+
45
+ /** @type {function|null} */
46
+ #onDropEmpty = null;
47
+
48
+ /** @type {function|null} - called during drag with world XY + picked socket */
49
+ #onCompatibleMove = null;
50
+
51
+ /** @type {function|null} - find nearest SVG dot as drop target */
52
+ #findNearestDot = null;
53
+
54
+ /** @type {number} - last time compatible move was emitted (ms) */
55
+ #lastMoveTime = 0;
56
+
57
+ /** @type {{ x: number, y: number }|null} - cached start position from pick */
58
+ #pickedStartPos = null;
59
+
60
+ /** @type {Set<SocketData>} */
61
+ #sockets = new Set();
62
+
63
+ /**
64
+ * @param {import('../core/Editor.js').NodeEditor} editor
65
+ * @param {object} callbacks
66
+ * @param {function} callbacks.getNodePosition
67
+ * @param {function} callbacks.getNodeSize
68
+ * @param {function} callbacks.getTransform - Returns { x, y, k, rect }
69
+ * @param {function} callbacks.onPseudoStart
70
+ * @param {function} callbacks.onPseudoMove
71
+ * @param {function} callbacks.onPseudoEnd
72
+ * @param {function} [callbacks.onDropEmpty] - Called when connection dropped in empty space
73
+ */
74
+ constructor(editor, callbacks) {
75
+ this.#editor = editor;
76
+ this.#getNodePosition = callbacks.getNodePosition;
77
+ this.#getNodeSize = callbacks.getNodeSize;
78
+ this.#getTransform = callbacks.getTransform;
79
+ this.#onPseudoStart = callbacks.onPseudoStart;
80
+ this.#onPseudoMove = callbacks.onPseudoMove;
81
+ this.#onPseudoEnd = callbacks.onPseudoEnd;
82
+ this.#onDropEmpty = callbacks.onDropEmpty || null;
83
+ this.#onCompatibleMove = callbacks.onCompatibleMove || null;
84
+ this.#findNearestDot = callbacks.findNearestDot || null;
85
+
86
+ window.addEventListener('pointermove', this.#onMove);
87
+ window.addEventListener('pointerup', this.#onUp);
88
+ }
89
+
90
+ /**
91
+ * Register a socket element for connection interaction
92
+ * @param {HTMLElement} socketEl
93
+ * @param {SocketData} data
94
+ */
95
+ registerSocket(socketEl, data) {
96
+ this.#sockets.add(data);
97
+ socketEl.addEventListener('pointerdown', (e) => {
98
+ e.stopPropagation();
99
+ e.preventDefault();
100
+ this.#pick(data);
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Whether a connection drag is in progress
106
+ * @returns {boolean}
107
+ */
108
+ isPicking() {
109
+ return this.#picked !== null;
110
+ }
111
+
112
+ /**
113
+ * Get the currently picked socket data (during drag)
114
+ * @returns {SocketData|null}
115
+ */
116
+ getPickedSocket() {
117
+ return this.#picked;
118
+ }
119
+
120
+ /**
121
+ * Externally initiate a connection drag from socket data
122
+ * @param {SocketData} data
123
+ */
124
+ pickSocket(data) {
125
+
126
+ if (!this.#sockets.has(data)) {
127
+ this.#sockets.add(data);
128
+ }
129
+ this.#pick(data);
130
+ }
131
+
132
+ #pick(data) {
133
+ this.#picked = data;
134
+ let pos = this.#getSocketWorldPosition(data);
135
+ if (this.#onPseudoStart) this.#onPseudoStart(pos.x, pos.y, data);
136
+ }
137
+
138
+ #onMove = (e) => {
139
+ if (!this.#picked) return;
140
+ e.preventDefault();
141
+
142
+ let startPos = this.#getSocketWorldPosition(this.#picked);
143
+ let t = this.#getTransform();
144
+
145
+ let endX = (e.clientX - t.rect.left - t.x) / t.k;
146
+ let endY = (e.clientY - t.rect.top - t.y) / t.k;
147
+
148
+ if (this.#onPseudoMove) this.#onPseudoMove(startPos.x, startPos.y, endX, endY);
149
+
150
+
151
+ let now = performance.now();
152
+ if (this.#onCompatibleMove && now - this.#lastMoveTime > 16) {
153
+ this.#lastMoveTime = now;
154
+ this.#onCompatibleMove(endX, endY, this.#picked);
155
+ }
156
+ };
157
+
158
+ #onUp = (e) => {
159
+ if (!this.#picked) return;
160
+
161
+
162
+ let t = this.#getTransform();
163
+ let pointerX = (e.clientX - t.rect.left - t.x) / t.k;
164
+ let pointerY = (e.clientY - t.rect.top - t.y) / t.k;
165
+ let target = this.#findNearestSocket(pointerX, pointerY);
166
+
167
+ if (target && this.#canConnect(this.#picked, target)) {
168
+ this.#makeConnection(this.#picked, target);
169
+ } else if (this.#findNearestDot) {
170
+
171
+ let dotTarget = this.#findNearestDot(pointerX, pointerY);
172
+ if (dotTarget) {
173
+ let dotSocket = { nodeId: dotTarget.nodeId, key: dotTarget.key, side: dotTarget.side };
174
+ if (this.#canConnect(this.#picked, dotSocket)) {
175
+ this.#makeConnection(this.#picked, dotSocket);
176
+ } else if (this.#onDropEmpty) {
177
+ this.#onDropEmpty(pointerX, pointerY, this.#picked);
178
+ }
179
+ } else if (this.#onDropEmpty) {
180
+ this.#onDropEmpty(pointerX, pointerY, this.#picked);
181
+ }
182
+ } else if (this.#onDropEmpty) {
183
+
184
+ this.#onDropEmpty(pointerX, pointerY, this.#picked);
185
+ }
186
+
187
+ this.#picked = null;
188
+ if (this.#onPseudoEnd) this.#onPseudoEnd();
189
+ };
190
+ /**
191
+ * Get socket position in graph coordinate space
192
+ * Uses getBoundingClientRect with zoom compensation
193
+ * @param {SocketData} data
194
+ * @returns {{ x: number, y: number }}
195
+ */
196
+ #getSocketWorldPosition(data) {
197
+
198
+ if (data.worldX !== undefined && data.worldY !== undefined) {
199
+ return { x: data.worldX, y: data.worldY };
200
+ }
201
+
202
+ let pos = this.#getNodePosition(data.nodeId);
203
+ if (!pos) return { x: 0, y: 0 };
204
+
205
+ if (data.element) {
206
+ let graphNode = data.element.closest('graph-node');
207
+ if (graphNode) {
208
+ let t = this.#getTransform();
209
+ let nodeRect = graphNode.getBoundingClientRect();
210
+ let socketRect = data.element.getBoundingClientRect();
211
+
212
+ let offsetX = (socketRect.left - nodeRect.left + socketRect.width / 2) / t.k;
213
+ let offsetY = (socketRect.top - nodeRect.top + socketRect.height / 2) / t.k;
214
+ return { x: pos.x + offsetX, y: pos.y + offsetY };
215
+ }
216
+ }
217
+
218
+
219
+ let size = this.#getNodeSize(data.nodeId);
220
+ if (!size) return { x: 0, y: 0 };
221
+ return {
222
+ x: data.side === 'output' ? pos.x + size.width : pos.x,
223
+ y: pos.y + size.height / 2,
224
+ };
225
+ }
226
+
227
+ /**
228
+ * Find nearest registered socket within snap distance
229
+ * Uses registered socket collection instead of DOM hit-testing
230
+ * @param {number} worldX - Pointer X in graph coordinates
231
+ * @param {number} worldY - Pointer Y in graph coordinates
232
+ * @returns {SocketData|null}
233
+ */
234
+ #findNearestSocket(worldX, worldY) {
235
+ const SNAP_DISTANCE = 30;
236
+ let nearest = null;
237
+ let nearestDist = SNAP_DISTANCE;
238
+
239
+ for (const socket of this.#sockets) {
240
+
241
+ if (socket === this.#picked) continue;
242
+
243
+ let pos = this.#getSocketWorldPosition(socket);
244
+ let dx = worldX - pos.x;
245
+ let dy = worldY - pos.y;
246
+ let dist = Math.sqrt(dx * dx + dy * dy);
247
+
248
+ if (dist < nearestDist) {
249
+ nearestDist = dist;
250
+ nearest = socket;
251
+ }
252
+ }
253
+
254
+ return nearest;
255
+ }
256
+
257
+ /**
258
+ * Check if two sockets can be connected
259
+ * @param {SocketData} from
260
+ * @param {SocketData} to
261
+ * @returns {boolean}
262
+ */
263
+ #canConnect(from, to) {
264
+ if (from.side === to.side) return false;
265
+ if (from.nodeId === to.nodeId) return false;
266
+
267
+ let fromNode = this.#editor.getNode(from.nodeId);
268
+ let toNode = this.#editor.getNode(to.nodeId);
269
+ if (!fromNode || !toNode) return false;
270
+
271
+ let isFromOutput = from.side === 'output';
272
+ let output = isFromOutput ? fromNode.outputs[from.key] : toNode.outputs[to.key];
273
+ let input = isFromOutput ? toNode.inputs[to.key] : fromNode.inputs[from.key];
274
+
275
+ if (!output || !input) return false;
276
+
277
+ return output.socket.isCompatibleWith(input.socket);
278
+ }
279
+
280
+ /**
281
+ * Create the connection
282
+ * @param {SocketData} from
283
+ * @param {SocketData} to
284
+ */
285
+ #makeConnection(from, to) {
286
+ let sourceData = from.side === 'output' ? from : to;
287
+ let targetData = from.side === 'input' ? from : to;
288
+
289
+ let sourceNode = this.#editor.getNode(sourceData.nodeId);
290
+ let targetNode = this.#editor.getNode(targetData.nodeId);
291
+ if (!sourceNode || !targetNode) return;
292
+
293
+ let conn = new Connection(sourceNode, sourceData.key, targetNode, targetData.key);
294
+ this.#editor.addConnection(conn);
295
+ }
296
+
297
+ /** Cleanup */
298
+ destroy() {
299
+ window.removeEventListener('pointermove', this.#onMove);
300
+ window.removeEventListener('pointerup', this.#onUp);
301
+ }
302
+ }
303
+
304
+ export { ConnectFlow as default };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Drag handler — pointer-based drag with zoom compensation
3
+ *
4
+ * Adapted from Rete.js area-plugin/drag.ts (86 LOC).
5
+ * Handles both canvas panning and node dragging.
6
+ *
7
+ * @module symbiote-node/interactions/Drag
8
+ */
9
+
10
+ export class Drag {
11
+ /** @type {{ x: number, y: number }|null} */
12
+ #pointerStart = null;
13
+
14
+ /** @type {{ x: number, y: number }|null} */
15
+ #startPosition = null;
16
+
17
+ /** @type {function|null} */
18
+ #onTranslate = null;
19
+
20
+ /** @type {function|null} */
21
+ #onStart = null;
22
+
23
+ /** @type {function|null} */
24
+ #onDrop = null;
25
+
26
+ /** @type {function} */
27
+ #getZoom = () => 1;
28
+
29
+ /** @type {function} */
30
+ #getPosition = () => ({ x: 0, y: 0 });
31
+
32
+ /** @type {HTMLElement|null} */
33
+ #element = null;
34
+
35
+ /** @type {function|null} - optional hit-test before starting drag */
36
+ #shouldStart = null;
37
+
38
+ /**
39
+ * Initialize drag handler on element
40
+ * @param {HTMLElement} element - Element to attach drag to
41
+ * @param {object} config
42
+ * @param {function} config.getPosition - Returns current {x, y}
43
+ * @param {function} config.getZoom - Returns current zoom level
44
+ * @param {object} callbacks
45
+ * @param {function} callbacks.onStart - Called on drag start
46
+ * @param {function} callbacks.onTranslate - Called with (x, y) during drag
47
+ * @param {function} callbacks.onDrop - Called on drag end
48
+ */
49
+ initialize(element, config, callbacks) {
50
+ this.#element = element;
51
+ this.#getPosition = config.getPosition;
52
+ this.#getZoom = config.getZoom;
53
+ this.#onStart = callbacks.onStart || null;
54
+ this.#onTranslate = callbacks.onTranslate;
55
+ this.#onDrop = callbacks.onDrop || null;
56
+ this.#shouldStart = callbacks.shouldStart || null;
57
+
58
+ element.style.touchAction = 'none';
59
+ element.addEventListener('pointerdown', this.#down);
60
+ window.addEventListener('pointermove', this.#move);
61
+ window.addEventListener('pointerup', this.#up);
62
+ }
63
+
64
+ #down = (e) => {
65
+ if (e.pointerType === 'mouse' && e.button !== 0) return;
66
+
67
+ if (e.target.closest && e.target.closest('.sn-socket')) return;
68
+
69
+ if (this.#shouldStart && !this.#shouldStart(e)) return;
70
+ e.stopPropagation();
71
+ this.#pointerStart = { x: e.pageX, y: e.pageY };
72
+ this.#startPosition = { ...this.#getPosition() };
73
+ if (this.#onStart) this.#onStart(e);
74
+ };
75
+
76
+ #move = (e) => {
77
+ if (!this.#pointerStart || !this.#startPosition) return;
78
+ e.preventDefault();
79
+ let dx = e.pageX - this.#pointerStart.x;
80
+ let dy = e.pageY - this.#pointerStart.y;
81
+ let zoom = this.#getZoom();
82
+ let x = this.#startPosition.x + dx / zoom;
83
+ let y = this.#startPosition.y + dy / zoom;
84
+ if (this.#onTranslate) this.#onTranslate(x, y, e);
85
+ };
86
+
87
+ #up = (e) => {
88
+ if (!this.#pointerStart) return;
89
+ this.#pointerStart = null;
90
+ this.#startPosition = null;
91
+ if (this.#onDrop) this.#onDrop(e);
92
+ };
93
+
94
+ /** Cleanup event listeners */
95
+ destroy() {
96
+ if (this.#element) {
97
+ this.#element.removeEventListener('pointerdown', this.#down);
98
+ }
99
+ window.removeEventListener('pointermove', this.#move);
100
+ window.removeEventListener('pointerup', this.#up);
101
+ }
102
+ }
103
+
104
+ export { Drag as default };
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Selector — multi-select manager for nodes and connections
3
+ *
4
+ * Handles single select, Ctrl/Meta+click accumulation,
5
+ * canvas deselect, and group translate of selected nodes.
6
+ *
7
+ * Adapted from Rete.js selectable extension (208 LOC).
8
+ * @module symbiote-node/interactions/Selector
9
+ */
10
+
11
+ /** @type {number} Minimum pointer movement to consider a drag (pixels) */
12
+ const TWITCH_THRESHOLD = 4;
13
+
14
+ export class Selector {
15
+ /** @type {Set<string>} */
16
+ #selectedNodes = new Set();
17
+
18
+ /** @type {Set<string>} */
19
+ #selectedConnections = new Set();
20
+
21
+ /** @type {function|null} */
22
+ #onChange = null;
23
+
24
+ /**
25
+ * @param {object} config
26
+ * @param {function} config.onChange - Called when selection changes: (nodes: Set, connections: Set)
27
+ */
28
+ constructor(config = {}) {
29
+ this.#onChange = config.onChange || null;
30
+ }
31
+
32
+ /**
33
+ * Select a node (single or accumulate with Ctrl/Meta)
34
+ * @param {string} nodeId
35
+ * @param {boolean} accumulate - Ctrl/Meta held
36
+ */
37
+ selectNode(nodeId, accumulate = false) {
38
+ if (!accumulate) {
39
+ this.#selectedNodes.clear();
40
+ this.#selectedConnections.clear();
41
+ }
42
+
43
+ if (this.#selectedNodes.has(nodeId) && accumulate) {
44
+ this.#selectedNodes.delete(nodeId);
45
+ } else {
46
+ this.#selectedNodes.add(nodeId);
47
+ }
48
+
49
+ this.#notify();
50
+ }
51
+
52
+ /**
53
+ * Select a connection
54
+ * @param {string} connId
55
+ * @param {boolean} accumulate
56
+ */
57
+ selectConnection(connId, accumulate = false) {
58
+ if (!accumulate) {
59
+ this.#selectedNodes.clear();
60
+ this.#selectedConnections.clear();
61
+ }
62
+
63
+ if (this.#selectedConnections.has(connId) && accumulate) {
64
+ this.#selectedConnections.delete(connId);
65
+ } else {
66
+ this.#selectedConnections.add(connId);
67
+ }
68
+
69
+ this.#notify();
70
+ }
71
+
72
+ /**
73
+ * Deselect everything
74
+ */
75
+ unselectAll() {
76
+ if (this.#selectedNodes.size === 0 && this.#selectedConnections.size === 0) return;
77
+ this.#selectedNodes.clear();
78
+ this.#selectedConnections.clear();
79
+ this.#notify();
80
+ }
81
+
82
+ /**
83
+ * @param {string} nodeId
84
+ * @returns {boolean}
85
+ */
86
+ isNodeSelected(nodeId) {
87
+ return this.#selectedNodes.has(nodeId);
88
+ }
89
+
90
+ /**
91
+ * @param {string} connId
92
+ * @returns {boolean}
93
+ */
94
+ isConnectionSelected(connId) {
95
+ return this.#selectedConnections.has(connId);
96
+ }
97
+
98
+ /**
99
+ * Get all selected node IDs
100
+ * @returns {Set<string>}
101
+ */
102
+ getSelectedNodes() {
103
+ return this.#selectedNodes;
104
+ }
105
+
106
+ /**
107
+ * Get all selected connection IDs
108
+ * @returns {Set<string>}
109
+ */
110
+ getSelectedConnections() {
111
+ return this.#selectedConnections;
112
+ }
113
+
114
+ /**
115
+ * Check if pointer was a twitch (< 4px movement)
116
+ * @param {{ x: number, y: number }} start
117
+ * @param {{ x: number, y: number }} end
118
+ * @returns {boolean}
119
+ */
120
+ static isTwitch(start, end) {
121
+ let dx = Math.abs(end.x - start.x);
122
+ let dy = Math.abs(end.y - start.y);
123
+ return dx < TWITCH_THRESHOLD && dy < TWITCH_THRESHOLD;
124
+ }
125
+
126
+ #notify() {
127
+ if (this.#onChange) {
128
+ this.#onChange(this.#selectedNodes, this.#selectedConnections);
129
+ }
130
+ }
131
+ }
132
+
133
+ export { Selector as default };
@@ -0,0 +1,66 @@
1
+ /**
2
+ * SnapGrid — grid snapping for node positions
3
+ *
4
+ * Rounds coordinates to nearest grid cell.
5
+ * Supports dynamic (during drag) and static (on drop) modes.
6
+ *
7
+ * Adapted from Rete.js snap grid (62 LOC).
8
+ * @module symbiote-node/interactions/SnapGrid
9
+ */
10
+
11
+ export class SnapGrid {
12
+ /** @type {number} */
13
+ #size;
14
+
15
+ /** @type {boolean} */
16
+ #dynamic;
17
+
18
+ /**
19
+ * @param {object} config
20
+ * @param {number} [config.size=16] - Grid cell size in pixels
21
+ * @param {boolean} [config.dynamic=true] - Snap during drag (true) or only on drop (false)
22
+ */
23
+ constructor(config = {}) {
24
+ this.#size = config.size || 16;
25
+ this.#dynamic = config.dynamic !== false;
26
+ }
27
+
28
+ /**
29
+ * Snap coordinates to nearest grid cell
30
+ * @param {number} x
31
+ * @param {number} y
32
+ * @returns {{ x: number, y: number }}
33
+ */
34
+ snap(x, y) {
35
+ return {
36
+ x: Math.round(x / this.#size) * this.#size,
37
+ y: Math.round(y / this.#size) * this.#size,
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Whether to snap during drag or only on drop
43
+ * @returns {boolean}
44
+ */
45
+ get isDynamic() {
46
+ return this.#dynamic;
47
+ }
48
+
49
+ /**
50
+ * Get grid size
51
+ * @returns {number}
52
+ */
53
+ get size() {
54
+ return this.#size;
55
+ }
56
+
57
+ /**
58
+ * Update grid size
59
+ * @param {number} size
60
+ */
61
+ setSize(size) {
62
+ this.#size = size;
63
+ }
64
+ }
65
+
66
+ export { SnapGrid as default };