@r2digisolutions/components 0.1.6 → 0.2.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 (389) hide show
  1. package/README.md +1 -1
  2. package/dist/components/atoms/Checkbox/Checkbox.svelte +10 -3
  3. package/dist/components/atoms/Checkbox/Checkbox.svelte.d.ts +3 -0
  4. package/dist/components/atoms/IconButton/IconButton.svelte +1 -1
  5. package/dist/components/atoms/Input/Input.svelte +17 -2
  6. package/dist/components/atoms/Input/Input.svelte.d.ts +6 -1
  7. package/dist/components/atoms/MarqueeRect/MarqueeRect.svelte +21 -0
  8. package/dist/components/atoms/MarqueeRect/MarqueeRect.svelte.d.ts +8 -0
  9. package/dist/components/atoms/MediaKindIcon/MediaKindIcon.stories.js +8 -0
  10. package/dist/components/atoms/MediaKindIcon/MediaKindIcon.svelte +122 -0
  11. package/dist/components/atoms/MediaKindIcon/MediaKindIcon.svelte.d.ts +11 -0
  12. package/dist/components/atoms/MediaKindIcon/MediaKindIconStory.svelte +12 -0
  13. package/dist/components/atoms/MediaKindIcon/MediaKindIconStory.svelte.d.ts +18 -0
  14. package/dist/components/atoms/PanKnob/PanKnob.stories.d.ts +9 -0
  15. package/dist/components/atoms/PanKnob/PanKnob.stories.js +8 -0
  16. package/dist/components/atoms/PanKnob/PanKnob.svelte +218 -0
  17. package/dist/components/atoms/PanKnob/PanKnob.svelte.d.ts +19 -0
  18. package/dist/components/atoms/PanKnob/PanKnobStory.svelte +15 -0
  19. package/dist/components/atoms/PanKnob/PanKnobStory.svelte.d.ts +3 -0
  20. package/dist/components/atoms/Playhead/Playhead.stories.js +8 -0
  21. package/dist/components/atoms/Playhead/Playhead.svelte +59 -0
  22. package/dist/components/atoms/Playhead/Playhead.svelte.d.ts +13 -0
  23. package/dist/components/atoms/Playhead/PlayheadStory.svelte +9 -0
  24. package/dist/components/atoms/Playhead/PlayheadStory.svelte.d.ts +18 -0
  25. package/dist/components/atoms/Textarea/Textarea.svelte +4 -1
  26. package/dist/components/atoms/Textarea/Textarea.svelte.d.ts +1 -0
  27. package/dist/components/atoms/TimeRuler/TimeRuler.stories.js +8 -0
  28. package/dist/components/atoms/TimeRuler/TimeRuler.svelte +58 -0
  29. package/dist/components/atoms/TimeRuler/TimeRuler.svelte.d.ts +10 -0
  30. package/dist/components/atoms/TimeRuler/TimeRulerStory.svelte +7 -0
  31. package/dist/components/atoms/TimeRuler/TimeRulerStory.svelte.d.ts +18 -0
  32. package/dist/components/atoms/Toggle/Toggle.svelte +1 -1
  33. package/dist/components/atoms/Tooltip/Tooltip.svelte +1 -1
  34. package/dist/components/atoms/TrimHandle/TrimHandle.stories.js +8 -0
  35. package/dist/components/atoms/TrimHandle/TrimHandle.svelte +63 -0
  36. package/dist/components/atoms/TrimHandle/TrimHandle.svelte.d.ts +12 -0
  37. package/dist/components/atoms/TrimHandle/TrimHandleStory.svelte +11 -0
  38. package/dist/components/atoms/TrimHandle/TrimHandleStory.svelte.d.ts +18 -0
  39. package/dist/components/molecules/AiAssistPanel/AiAssistPanel.stories.d.ts +9 -0
  40. package/dist/components/molecules/AiAssistPanel/AiAssistPanel.stories.js +8 -0
  41. package/dist/components/molecules/AiAssistPanel/AiAssistPanel.svelte +102 -0
  42. package/dist/components/molecules/AiAssistPanel/AiAssistPanel.svelte.d.ts +20 -0
  43. package/dist/components/molecules/AiAssistPanel/AiAssistPanelStory.svelte +34 -0
  44. package/dist/components/molecules/AiAssistPanel/AiAssistPanelStory.svelte.d.ts +3 -0
  45. package/dist/components/molecules/BulkActionBar/BulkActionBar.svelte +112 -62
  46. package/dist/components/molecules/BulkActionBar/BulkActionBar.svelte.d.ts +2 -0
  47. package/dist/components/molecules/BulkEditPanel/BulkEditPanel.stories.js +8 -0
  48. package/dist/components/molecules/BulkEditPanel/BulkEditPanel.svelte +117 -0
  49. package/dist/components/molecules/BulkEditPanel/BulkEditPanel.svelte.d.ts +23 -0
  50. package/dist/components/molecules/BulkEditPanel/BulkEditPanelStory.svelte +18 -0
  51. package/dist/components/molecules/BulkEditPanel/BulkEditPanelStory.svelte.d.ts +18 -0
  52. package/dist/components/molecules/CanvasElementsPanel/CanvasElementsPanel.stories.js +8 -0
  53. package/dist/components/molecules/CanvasElementsPanel/CanvasElementsPanel.svelte +858 -0
  54. package/dist/components/molecules/CanvasElementsPanel/CanvasElementsPanel.svelte.d.ts +43 -0
  55. package/dist/components/molecules/CanvasElementsPanel/CanvasElementsPanelStory.svelte +7 -0
  56. package/dist/components/molecules/CanvasElementsPanel/CanvasElementsPanelStory.svelte.d.ts +18 -0
  57. package/dist/components/molecules/CanvasInspector/CanvasInspector.stories.d.ts +9 -0
  58. package/dist/components/molecules/CanvasInspector/CanvasInspector.stories.js +8 -0
  59. package/dist/components/molecules/CanvasInspector/CanvasInspector.svelte +1140 -0
  60. package/dist/components/molecules/CanvasInspector/CanvasInspector.svelte.d.ts +28 -0
  61. package/dist/components/molecules/CanvasInspector/CanvasInspectorStory.svelte +20 -0
  62. package/dist/components/molecules/CanvasInspector/CanvasInspectorStory.svelte.d.ts +3 -0
  63. package/dist/components/molecules/CodeEditor/CodeEditor.svelte +13 -10
  64. package/dist/components/molecules/ColorPicker/ColorPicker.svelte +167 -97
  65. package/dist/components/molecules/ColumnVisibilityMenu/ColumnVisibilityMenu.stories.d.ts +9 -0
  66. package/dist/components/molecules/ColumnVisibilityMenu/ColumnVisibilityMenu.stories.js +8 -0
  67. package/dist/components/molecules/ColumnVisibilityMenu/ColumnVisibilityMenu.svelte +108 -0
  68. package/dist/components/molecules/ColumnVisibilityMenu/ColumnVisibilityMenu.svelte.d.ts +18 -0
  69. package/dist/components/molecules/ColumnVisibilityMenu/ColumnVisibilityMenuStory.svelte +15 -0
  70. package/dist/components/molecules/ColumnVisibilityMenu/ColumnVisibilityMenuStory.svelte.d.ts +3 -0
  71. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.stories.d.ts +9 -0
  72. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.stories.js +8 -0
  73. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.svelte +412 -0
  74. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanel.svelte.d.ts +14 -0
  75. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanelStory.svelte +27 -0
  76. package/dist/components/molecules/ConditionalFormatPanel/ConditionalFormatPanelStory.svelte.d.ts +3 -0
  77. package/dist/components/molecules/ConflictResolver/ConflictResolver.stories.js +8 -0
  78. package/dist/components/molecules/ConflictResolver/ConflictResolver.svelte +73 -0
  79. package/dist/components/molecules/ConflictResolver/ConflictResolver.svelte.d.ts +16 -0
  80. package/dist/components/molecules/ConflictResolver/ConflictResolverStory.svelte +11 -0
  81. package/dist/components/molecules/ConflictResolver/ConflictResolverStory.svelte.d.ts +18 -0
  82. package/dist/components/molecules/ContextMenu/ContextMenu.svelte +237 -41
  83. package/dist/components/molecules/ContextMenu/ContextMenu.svelte.d.ts +17 -1
  84. package/dist/components/molecules/DateTimePicker/DateTimePicker.svelte.d.ts +1 -1
  85. package/dist/components/molecules/ExpressionEditor/ExpressionEditor.stories.d.ts +9 -0
  86. package/dist/components/molecules/ExpressionEditor/ExpressionEditor.stories.js +8 -0
  87. package/dist/components/molecules/ExpressionEditor/ExpressionEditor.svelte +56 -0
  88. package/dist/components/molecules/ExpressionEditor/ExpressionEditor.svelte.d.ts +12 -0
  89. package/dist/components/molecules/ExpressionEditor/ExpressionEditorStory.svelte +12 -0
  90. package/dist/components/molecules/ExpressionEditor/ExpressionEditorStory.svelte.d.ts +3 -0
  91. package/dist/components/molecules/FilterList/FilterList.stories.d.ts +9 -0
  92. package/dist/components/molecules/FilterList/FilterList.stories.js +8 -0
  93. package/dist/components/molecules/FilterList/FilterList.svelte +101 -0
  94. package/dist/components/molecules/FilterList/FilterList.svelte.d.ts +18 -0
  95. package/dist/components/molecules/FilterList/FilterListStory.svelte +31 -0
  96. package/dist/components/molecules/FilterList/FilterListStory.svelte.d.ts +3 -0
  97. package/dist/components/molecules/FormDateTimePicker/FormDateTimePicker.svelte.d.ts +1 -1
  98. package/dist/components/molecules/Gauge/Gauge.stories.d.ts +1 -1
  99. package/dist/components/molecules/InfiniteScroll/InfiniteScroll.stories.d.ts +1 -1
  100. package/dist/components/molecules/MediaAssetBrowser/MediaAssetBrowser.stories.d.ts +9 -0
  101. package/dist/components/molecules/MediaAssetBrowser/MediaAssetBrowser.stories.js +8 -0
  102. package/dist/components/molecules/MediaAssetBrowser/MediaAssetBrowser.svelte +94 -0
  103. package/dist/components/molecules/MediaAssetBrowser/MediaAssetBrowser.svelte.d.ts +15 -0
  104. package/dist/components/molecules/MediaAssetBrowser/MediaAssetBrowserStory.svelte +18 -0
  105. package/dist/components/molecules/MediaAssetBrowser/MediaAssetBrowserStory.svelte.d.ts +3 -0
  106. package/dist/components/molecules/MediaAssetCard/MediaAssetCard.stories.js +8 -0
  107. package/dist/components/molecules/MediaAssetCard/MediaAssetCard.svelte +50 -0
  108. package/dist/components/molecules/MediaAssetCard/MediaAssetCard.svelte.d.ts +11 -0
  109. package/dist/components/molecules/MediaAssetCard/MediaAssetCardStory.svelte +13 -0
  110. package/dist/components/molecules/MediaAssetCard/MediaAssetCardStory.svelte.d.ts +18 -0
  111. package/dist/components/molecules/MediaLayerItem/MediaLayerItem.stories.js +8 -0
  112. package/dist/components/molecules/MediaLayerItem/MediaLayerItem.svelte +380 -0
  113. package/dist/components/molecules/MediaLayerItem/MediaLayerItem.svelte.d.ts +33 -0
  114. package/dist/components/molecules/MediaLayerItem/MediaLayerItemStory.svelte +14 -0
  115. package/dist/components/molecules/MediaLayerItem/MediaLayerItemStory.svelte.d.ts +18 -0
  116. package/dist/components/molecules/MediaLayerList/MediaLayerList.stories.d.ts +9 -0
  117. package/dist/components/molecules/MediaLayerList/MediaLayerList.stories.js +8 -0
  118. package/dist/components/molecules/MediaLayerList/MediaLayerList.svelte +399 -0
  119. package/dist/components/molecules/MediaLayerList/MediaLayerList.svelte.d.ts +37 -0
  120. package/dist/components/molecules/MediaLayerList/MediaLayerListStory.svelte +24 -0
  121. package/dist/components/molecules/MediaLayerList/MediaLayerListStory.svelte.d.ts +3 -0
  122. package/dist/components/molecules/MediaStage/MediaStage.stories.d.ts +9 -0
  123. package/dist/components/molecules/MediaStage/MediaStage.stories.js +8 -0
  124. package/dist/components/molecules/MediaStage/MediaStage.svelte +1498 -0
  125. package/dist/components/molecules/MediaStage/MediaStage.svelte.d.ts +45 -0
  126. package/dist/components/molecules/MediaStage/MediaStageStory.svelte +35 -0
  127. package/dist/components/molecules/MediaStage/MediaStageStory.svelte.d.ts +3 -0
  128. package/dist/components/molecules/MediaTimeline/MediaTimeline.stories.d.ts +9 -0
  129. package/dist/components/molecules/MediaTimeline/MediaTimeline.stories.js +8 -0
  130. package/dist/components/molecules/MediaTimeline/MediaTimeline.svelte +131 -0
  131. package/dist/components/molecules/MediaTimeline/MediaTimeline.svelte.d.ts +19 -0
  132. package/dist/components/molecules/MediaTimeline/MediaTimelineStory.svelte +20 -0
  133. package/dist/components/molecules/MediaTimeline/MediaTimelineStory.svelte.d.ts +3 -0
  134. package/dist/components/molecules/MixerChannel/MixerChannel.stories.d.ts +9 -0
  135. package/dist/components/molecules/MixerChannel/MixerChannel.stories.js +8 -0
  136. package/dist/components/molecules/MixerChannel/MixerChannel.svelte +92 -0
  137. package/dist/components/molecules/MixerChannel/MixerChannel.svelte.d.ts +11 -0
  138. package/dist/components/molecules/MixerChannel/MixerChannelStory.svelte +13 -0
  139. package/dist/components/molecules/MixerChannel/MixerChannelStory.svelte.d.ts +3 -0
  140. package/dist/components/molecules/NodeGraph/GraphBlueprintNode.svelte +454 -0
  141. package/dist/components/molecules/NodeGraph/GraphBlueprintNode.svelte.d.ts +8 -0
  142. package/dist/components/molecules/NodeGraph/GraphFlowBridge.svelte +26 -0
  143. package/dist/components/molecules/NodeGraph/GraphFlowBridge.svelte.d.ts +22 -0
  144. package/dist/components/molecules/NodeGraph/NodeGraph.stories.d.ts +12 -0
  145. package/dist/components/molecules/NodeGraph/NodeGraph.stories.js +9 -0
  146. package/dist/components/molecules/NodeGraph/NodeGraph.svelte +1409 -0
  147. package/dist/components/molecules/NodeGraph/NodeGraph.svelte.d.ts +33 -0
  148. package/dist/components/molecules/NodeGraph/NodeGraphStory.svelte +90 -0
  149. package/dist/components/molecules/NodeGraph/NodeGraphStory.svelte.d.ts +3 -0
  150. package/dist/components/molecules/NodePalette/NodePalette.stories.js +8 -0
  151. package/dist/components/molecules/NodePalette/NodePalette.svelte +86 -0
  152. package/dist/components/molecules/NodePalette/NodePalette.svelte.d.ts +11 -0
  153. package/dist/components/molecules/NodePalette/NodePaletteStory.svelte +11 -0
  154. package/dist/components/molecules/NodePalette/NodePaletteStory.svelte.d.ts +18 -0
  155. package/dist/components/molecules/Poll/Poll.stories.d.ts +1 -1
  156. package/dist/components/molecules/ProgramMonitor/ProgramMonitor.stories.js +8 -0
  157. package/dist/components/molecules/ProgramMonitor/ProgramMonitor.svelte +72 -0
  158. package/dist/components/molecules/ProgramMonitor/ProgramMonitor.svelte.d.ts +12 -0
  159. package/dist/components/molecules/ProgramMonitor/ProgramMonitorStory.svelte +16 -0
  160. package/dist/components/molecules/ProgramMonitor/ProgramMonitorStory.svelte.d.ts +18 -0
  161. package/dist/components/molecules/PropertyField/PropertyField.svelte +57 -3
  162. package/dist/components/molecules/PropertyField/PropertyField.svelte.d.ts +10 -0
  163. package/dist/components/molecules/PropertyField/PropertyFieldStory.svelte +21 -0
  164. package/dist/components/molecules/PropertyGroup/PropertyGroup.svelte +1 -1
  165. package/dist/components/molecules/QueryBuilder/QueryBuilder.stories.d.ts +9 -0
  166. package/dist/components/molecules/QueryBuilder/QueryBuilder.stories.js +8 -0
  167. package/dist/components/molecules/QueryBuilder/QueryBuilder.svelte +52 -0
  168. package/dist/components/molecules/QueryBuilder/QueryBuilder.svelte.d.ts +18 -0
  169. package/dist/components/molecules/QueryBuilder/QueryBuilderStory.svelte +62 -0
  170. package/dist/components/molecules/QueryBuilder/QueryBuilderStory.svelte.d.ts +3 -0
  171. package/dist/components/molecules/RelationPicker/RelationPicker.stories.d.ts +9 -0
  172. package/dist/components/molecules/RelationPicker/RelationPicker.stories.js +8 -0
  173. package/dist/components/molecules/RelationPicker/RelationPicker.svelte +180 -0
  174. package/dist/components/molecules/RelationPicker/RelationPicker.svelte.d.ts +24 -0
  175. package/dist/components/molecules/RelationPicker/RelationPickerStory.svelte +16 -0
  176. package/dist/components/molecules/RelationPicker/RelationPickerStory.svelte.d.ts +3 -0
  177. package/dist/components/molecules/SchemaFieldBuilder/SchemaFieldBuilder.stories.d.ts +9 -0
  178. package/dist/components/molecules/SchemaFieldBuilder/SchemaFieldBuilder.stories.js +8 -0
  179. package/dist/components/molecules/SchemaFieldBuilder/SchemaFieldBuilder.svelte +154 -0
  180. package/dist/components/molecules/SchemaFieldBuilder/SchemaFieldBuilder.svelte.d.ts +12 -0
  181. package/dist/components/molecules/SchemaFieldBuilder/SchemaFieldBuilderStory.svelte +16 -0
  182. package/dist/components/molecules/SchemaFieldBuilder/SchemaFieldBuilderStory.svelte.d.ts +3 -0
  183. package/dist/components/molecules/SecretField/SecretField.stories.d.ts +9 -0
  184. package/dist/components/molecules/SecretField/SecretField.stories.js +8 -0
  185. package/dist/components/molecules/SecretField/SecretField.svelte +58 -0
  186. package/dist/components/molecules/SecretField/SecretField.svelte.d.ts +15 -0
  187. package/dist/components/molecules/SecretField/SecretFieldStory.svelte +8 -0
  188. package/dist/components/molecules/SecretField/SecretFieldStory.svelte.d.ts +3 -0
  189. package/dist/components/molecules/SplitPane/SplitPane.svelte +96 -37
  190. package/dist/components/molecules/SplitPane/SplitPane.svelte.d.ts +10 -2
  191. package/dist/components/molecules/TableToolbar/TableToolbar.svelte.d.ts +1 -1
  192. package/dist/components/molecules/Terminal/Terminal.stories.js +20 -0
  193. package/dist/components/molecules/Terminal/Terminal.svelte +238 -0
  194. package/dist/components/molecules/Terminal/Terminal.svelte.d.ts +19 -0
  195. package/dist/components/molecules/Terminal/TerminalStory.svelte +47 -0
  196. package/dist/components/molecules/Terminal/TerminalStory.svelte.d.ts +6 -0
  197. package/dist/components/molecules/TimelineClip/TimelineClip.stories.js +8 -0
  198. package/dist/components/molecules/TimelineClip/TimelineClip.svelte +116 -0
  199. package/dist/components/molecules/TimelineClip/TimelineClip.svelte.d.ts +15 -0
  200. package/dist/components/molecules/TimelineClip/TimelineClipStory.svelte +18 -0
  201. package/dist/components/molecules/TimelineClip/TimelineClipStory.svelte.d.ts +18 -0
  202. package/dist/components/molecules/TimelineTrack/TimelineTrack.stories.js +8 -0
  203. package/dist/components/molecules/TimelineTrack/TimelineTrack.svelte +56 -0
  204. package/dist/components/molecules/TimelineTrack/TimelineTrack.svelte.d.ts +16 -0
  205. package/dist/components/molecules/TimelineTrack/TimelineTrackStory.svelte +35 -0
  206. package/dist/components/molecules/TimelineTrack/TimelineTrackStory.svelte.d.ts +18 -0
  207. package/dist/components/molecules/TraceWaterfall/TraceWaterfall.stories.js +8 -0
  208. package/dist/components/molecules/TraceWaterfall/TraceWaterfall.svelte +158 -0
  209. package/dist/components/molecules/TraceWaterfall/TraceWaterfall.svelte.d.ts +18 -0
  210. package/dist/components/molecules/TraceWaterfall/TraceWaterfallStory.svelte +55 -0
  211. package/dist/components/molecules/TraceWaterfall/TraceWaterfallStory.svelte.d.ts +18 -0
  212. package/dist/components/molecules/TrackHeader/TrackHeader.stories.d.ts +9 -0
  213. package/dist/components/molecules/TrackHeader/TrackHeader.stories.js +8 -0
  214. package/dist/components/molecules/TrackHeader/TrackHeader.svelte +54 -0
  215. package/dist/components/molecules/TrackHeader/TrackHeader.svelte.d.ts +9 -0
  216. package/dist/components/molecules/TrackHeader/TrackHeaderStory.svelte +10 -0
  217. package/dist/components/molecules/TrackHeader/TrackHeaderStory.svelte.d.ts +3 -0
  218. package/dist/components/molecules/TransportControls/TransportControls.stories.d.ts +9 -0
  219. package/dist/components/molecules/TransportControls/TransportControls.stories.js +8 -0
  220. package/dist/components/molecules/TransportControls/TransportControls.svelte +53 -0
  221. package/dist/components/molecules/TransportControls/TransportControls.svelte.d.ts +12 -0
  222. package/dist/components/molecules/TransportControls/TransportControlsStory.svelte +18 -0
  223. package/dist/components/molecules/TransportControls/TransportControlsStory.svelte.d.ts +3 -0
  224. package/dist/components/molecules/VersionHistoryPanel/VersionHistoryPanel.stories.js +8 -0
  225. package/dist/components/molecules/VersionHistoryPanel/VersionHistoryPanel.svelte +117 -0
  226. package/dist/components/molecules/VersionHistoryPanel/VersionHistoryPanel.svelte.d.ts +20 -0
  227. package/dist/components/molecules/VersionHistoryPanel/VersionHistoryPanelStory.svelte +36 -0
  228. package/dist/components/molecules/VersionHistoryPanel/VersionHistoryPanelStory.svelte.d.ts +18 -0
  229. package/dist/components/molecules/VideoPlayer/VideoPlayer.stories.d.ts +1 -1
  230. package/dist/components/molecules/VideoTrackHeader/VideoTrackHeader.stories.d.ts +9 -0
  231. package/dist/components/molecules/VideoTrackHeader/VideoTrackHeader.stories.js +8 -0
  232. package/dist/components/molecules/VideoTrackHeader/VideoTrackHeader.svelte +59 -0
  233. package/dist/components/molecules/VideoTrackHeader/VideoTrackHeader.svelte.d.ts +9 -0
  234. package/dist/components/molecules/VideoTrackHeader/VideoTrackHeaderStory.svelte +10 -0
  235. package/dist/components/molecules/VideoTrackHeader/VideoTrackHeaderStory.svelte.d.ts +3 -0
  236. package/dist/components/molecules/WaveformRegion/WaveformRegion.stories.js +8 -0
  237. package/dist/components/molecules/WaveformRegion/WaveformRegion.svelte +82 -0
  238. package/dist/components/molecules/WaveformRegion/WaveformRegion.svelte.d.ts +13 -0
  239. package/dist/components/molecules/WaveformRegion/WaveformRegionStory.svelte +17 -0
  240. package/dist/components/molecules/WaveformRegion/WaveformRegionStory.svelte.d.ts +18 -0
  241. package/dist/components/molecules/WidgetCanvas/WidgetCanvas.svelte +13 -4
  242. package/dist/components/molecules/WidgetCanvas/WidgetCanvas.svelte.d.ts +3 -1
  243. package/dist/components/molecules/WidgetCanvas/widgetCanvasContext.d.ts +2 -0
  244. package/dist/components/molecules/WidgetFrame/WidgetFrame.svelte +206 -37
  245. package/dist/components/molecules/WidgetFrame/WidgetFrame.svelte.d.ts +31 -0
  246. package/dist/components/organisms/AssetPickerPage/AssetPickerPage.svelte.d.ts +1 -1
  247. package/dist/components/organisms/AudioEditor/AudioEditor.stories.d.ts +12 -0
  248. package/dist/components/organisms/AudioEditor/AudioEditor.stories.js +9 -0
  249. package/dist/components/organisms/AudioEditor/AudioEditor.svelte +273 -0
  250. package/dist/components/organisms/AudioEditor/AudioEditor.svelte.d.ts +13 -0
  251. package/dist/components/organisms/AudioEditor/AudioEditorStory.svelte +10 -0
  252. package/dist/components/organisms/AudioEditor/AudioEditorStory.svelte.d.ts +3 -0
  253. package/dist/components/organisms/BacklogPage/BacklogPage.svelte.d.ts +1 -1
  254. package/dist/components/organisms/BlogEditor/BlogEditor.stories.d.ts +12 -0
  255. package/dist/components/organisms/BlogEditor/BlogEditor.stories.js +9 -0
  256. package/dist/components/organisms/BlogEditor/BlogEditor.svelte +1208 -0
  257. package/dist/components/organisms/BlogEditor/BlogEditor.svelte.d.ts +16 -0
  258. package/dist/components/organisms/BlogEditor/BlogEditorStory.svelte +144 -0
  259. package/dist/components/organisms/BlogEditor/BlogEditorStory.svelte.d.ts +3 -0
  260. package/dist/components/organisms/BlogEditor/DemoBlogCard.svelte +28 -0
  261. package/dist/components/organisms/BlogEditor/DemoBlogCard.svelte.d.ts +10 -0
  262. package/dist/components/organisms/BlogEditor/DemoBlogStats.svelte +34 -0
  263. package/dist/components/organisms/BlogEditor/DemoBlogStats.svelte.d.ts +11 -0
  264. package/dist/components/organisms/BlogEditor/DemoBlogWidget.svelte +13 -0
  265. package/dist/components/organisms/BlogEditor/DemoBlogWidget.svelte.d.ts +8 -0
  266. package/dist/components/organisms/BlogRenderer/BlogRenderer.stories.js +8 -0
  267. package/dist/components/organisms/BlogRenderer/BlogRenderer.svelte +469 -0
  268. package/dist/components/organisms/BlogRenderer/BlogRenderer.svelte.d.ts +14 -0
  269. package/dist/components/organisms/BlogRenderer/BlogRendererStory.svelte +20 -0
  270. package/dist/components/organisms/BlogRenderer/BlogRendererStory.svelte.d.ts +18 -0
  271. package/dist/components/organisms/BlueprintEditor/BlueprintEditor.stories.d.ts +12 -0
  272. package/dist/components/organisms/BlueprintEditor/BlueprintEditor.stories.js +9 -0
  273. package/dist/components/organisms/BlueprintEditor/BlueprintEditor.svelte +1012 -0
  274. package/dist/components/organisms/BlueprintEditor/BlueprintEditor.svelte.d.ts +16 -0
  275. package/dist/components/organisms/BlueprintEditor/BlueprintEditorStory.svelte +76 -0
  276. package/dist/components/organisms/BlueprintEditor/BlueprintEditorStory.svelte.d.ts +3 -0
  277. package/dist/components/organisms/CalendarApp/CalendarApp.svelte.d.ts +1 -1
  278. package/dist/components/organisms/CanvasEditor/CanvasEditor.stories.d.ts +12 -0
  279. package/dist/components/organisms/CanvasEditor/CanvasEditor.stories.js +9 -0
  280. package/dist/components/organisms/CanvasEditor/CanvasEditor.svelte +1680 -0
  281. package/dist/components/organisms/CanvasEditor/CanvasEditor.svelte.d.ts +18 -0
  282. package/dist/components/organisms/CanvasEditor/CanvasEditorStory.svelte +329 -0
  283. package/dist/components/organisms/CanvasEditor/CanvasEditorStory.svelte.d.ts +3 -0
  284. package/dist/components/organisms/DashboardBuilderPage/DashboardBuilderPage.svelte.d.ts +1 -1
  285. package/dist/components/organisms/DataGrid/DataGrid.stories.d.ts +65 -0
  286. package/dist/components/organisms/DataGrid/DataGrid.stories.js +95 -0
  287. package/dist/components/organisms/DataGrid/DataGrid.svelte +2264 -0
  288. package/dist/components/organisms/DataGrid/DataGrid.svelte.d.ts +99 -0
  289. package/dist/components/organisms/DataGrid/DataGridStory.svelte +659 -0
  290. package/dist/components/organisms/DataGrid/DataGridStory.svelte.d.ts +11 -0
  291. package/dist/components/organisms/DataGrid/breakpoints.d.ts +10 -0
  292. package/dist/components/organisms/DataGrid/breakpoints.js +20 -0
  293. package/dist/components/organisms/DataGrid/cellEditors.d.ts +8 -0
  294. package/dist/components/organisms/DataGrid/cellEditors.js +59 -0
  295. package/dist/components/organisms/DataGrid/conditionalFormat.d.ts +11 -0
  296. package/dist/components/organisms/DataGrid/conditionalFormat.js +98 -0
  297. package/dist/components/organisms/DataGrid/filterRows.d.ts +5 -0
  298. package/dist/components/organisms/DataGrid/filterRows.js +60 -0
  299. package/dist/components/organisms/DataGrid/marks.d.ts +9 -0
  300. package/dist/components/organisms/DataGrid/marks.js +91 -0
  301. package/dist/components/organisms/DataGrid/types.d.ts +126 -0
  302. package/dist/components/organisms/DataGrid/types.js +21 -0
  303. package/dist/components/organisms/DesktopShell/DesktopShell.svelte.d.ts +1 -1
  304. package/dist/components/organisms/EditorShell/EditorShell.svelte +123 -28
  305. package/dist/components/organisms/EditorShell/EditorShell.svelte.d.ts +21 -1
  306. package/dist/components/organisms/FileUploader/FileUploader.svelte +77 -15
  307. package/dist/components/organisms/FileUploader/FileUploader.svelte.d.ts +11 -0
  308. package/dist/components/organisms/ImportWizard/ImportWizard.stories.js +8 -0
  309. package/dist/components/organisms/ImportWizard/ImportWizard.svelte +209 -0
  310. package/dist/components/organisms/ImportWizard/ImportWizard.svelte.d.ts +22 -0
  311. package/dist/components/organisms/ImportWizard/ImportWizardStory.svelte +16 -0
  312. package/dist/components/organisms/ImportWizard/ImportWizardStory.svelte.d.ts +18 -0
  313. package/dist/components/organisms/ResourceStudio/ResourceStudio.stories.d.ts +33 -0
  314. package/dist/components/organisms/ResourceStudio/ResourceStudio.stories.js +56 -0
  315. package/dist/components/organisms/ResourceStudio/ResourceStudio.svelte +830 -0
  316. package/dist/components/organisms/ResourceStudio/ResourceStudio.svelte.d.ts +68 -0
  317. package/dist/components/organisms/ResourceStudio/ResourceStudioStory.svelte +600 -0
  318. package/dist/components/organisms/ResourceStudio/ResourceStudioStory.svelte.d.ts +7 -0
  319. package/dist/components/organisms/ResourceStudio/fieldsToColumns.d.ts +4 -0
  320. package/dist/components/organisms/ResourceStudio/fieldsToColumns.js +20 -0
  321. package/dist/components/organisms/ResourceStudio/fieldsToSchema.d.ts +6 -0
  322. package/dist/components/organisms/ResourceStudio/fieldsToSchema.js +53 -0
  323. package/dist/components/organisms/ResourceStudio/types.d.ts +69 -0
  324. package/dist/components/organisms/ResourceStudio/types.js +15 -0
  325. package/dist/components/organisms/Scheduler/Scheduler.svelte.d.ts +1 -1
  326. package/dist/components/organisms/SchemaForm/SchemaForm.svelte +10 -3
  327. package/dist/components/organisms/SchemaForm/SchemaForm.svelte.d.ts +1 -0
  328. package/dist/components/organisms/VideoEditor/VideoEditor.stories.d.ts +12 -0
  329. package/dist/components/organisms/VideoEditor/VideoEditor.stories.js +9 -0
  330. package/dist/components/organisms/VideoEditor/VideoEditor.svelte +308 -0
  331. package/dist/components/organisms/VideoEditor/VideoEditor.svelte.d.ts +13 -0
  332. package/dist/components/organisms/VideoEditor/VideoEditorStory.svelte +10 -0
  333. package/dist/components/organisms/VideoEditor/VideoEditorStory.svelte.d.ts +3 -0
  334. package/dist/components/templates/AudioEditorTemplate/AudioEditorTemplate.stories.d.ts +12 -0
  335. package/dist/components/templates/AudioEditorTemplate/AudioEditorTemplate.stories.js +9 -0
  336. package/dist/components/templates/AudioEditorTemplate/AudioEditorTemplate.svelte +22 -0
  337. package/dist/components/templates/AudioEditorTemplate/AudioEditorTemplate.svelte.d.ts +13 -0
  338. package/dist/components/templates/AudioEditorTemplate/AudioEditorTemplateStory.svelte +8 -0
  339. package/dist/components/templates/AudioEditorTemplate/AudioEditorTemplateStory.svelte.d.ts +3 -0
  340. package/dist/components/templates/CanvasEditorTemplate/CanvasEditorTemplate.stories.d.ts +12 -0
  341. package/dist/components/templates/CanvasEditorTemplate/CanvasEditorTemplate.stories.js +9 -0
  342. package/dist/components/templates/CanvasEditorTemplate/CanvasEditorTemplate.svelte +32 -0
  343. package/dist/components/templates/CanvasEditorTemplate/CanvasEditorTemplate.svelte.d.ts +17 -0
  344. package/dist/components/templates/CanvasEditorTemplate/CanvasEditorTemplateStory.svelte +21 -0
  345. package/dist/components/templates/CanvasEditorTemplate/CanvasEditorTemplateStory.svelte.d.ts +3 -0
  346. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.stories.d.ts +25 -0
  347. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.stories.js +18 -0
  348. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.svelte +192 -0
  349. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplate.svelte.d.ts +62 -0
  350. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplateStory.svelte +129 -0
  351. package/dist/components/templates/ResourceStudioTemplate/ResourceStudioTemplateStory.svelte.d.ts +7 -0
  352. package/dist/components/templates/VideoEditorTemplate/VideoEditorTemplate.stories.d.ts +12 -0
  353. package/dist/components/templates/VideoEditorTemplate/VideoEditorTemplate.stories.js +9 -0
  354. package/dist/components/templates/VideoEditorTemplate/VideoEditorTemplate.svelte +22 -0
  355. package/dist/components/templates/VideoEditorTemplate/VideoEditorTemplate.svelte.d.ts +13 -0
  356. package/dist/components/templates/VideoEditorTemplate/VideoEditorTemplateStory.svelte +8 -0
  357. package/dist/components/templates/VideoEditorTemplate/VideoEditorTemplateStory.svelte.d.ts +3 -0
  358. package/dist/index.d.ts +94 -1
  359. package/dist/index.js +68 -0
  360. package/dist/types/xyflow-svelte.d.ts +62 -0
  361. package/dist/utils/audioProject.d.ts +19 -0
  362. package/dist/utils/audioProject.js +70 -0
  363. package/dist/utils/blogDocument.d.ts +175 -0
  364. package/dist/utils/blogDocument.js +220 -0
  365. package/dist/utils/blueprintRuntime.d.ts +34 -0
  366. package/dist/utils/blueprintRuntime.js +289 -0
  367. package/dist/utils/canvasDocument.d.ts +239 -0
  368. package/dist/utils/canvasDocument.js +443 -0
  369. package/dist/utils/canvasExport.d.ts +10 -0
  370. package/dist/utils/canvasExport.js +469 -0
  371. package/dist/utils/canvasHierarchy.d.ts +189 -0
  372. package/dist/utils/canvasHierarchy.js +674 -0
  373. package/dist/utils/canvasHistory.d.ts +19 -0
  374. package/dist/utils/canvasHistory.js +48 -0
  375. package/dist/utils/canvasWidget.d.ts +81 -0
  376. package/dist/utils/canvasWidget.js +597 -0
  377. package/dist/utils/marqueeSelect.d.ts +48 -0
  378. package/dist/utils/marqueeSelect.js +189 -0
  379. package/dist/utils/mediaTime.d.ts +16 -0
  380. package/dist/utils/mediaTime.js +57 -0
  381. package/dist/utils/mediaTracks.d.ts +61 -0
  382. package/dist/utils/mediaTracks.js +102 -0
  383. package/dist/utils/nodeGraph.d.ts +354 -0
  384. package/dist/utils/nodeGraph.js +1453 -0
  385. package/dist/utils/queryBridge.d.ts +18 -0
  386. package/dist/utils/queryBridge.js +118 -0
  387. package/dist/utils/videoSequence.d.ts +13 -0
  388. package/dist/utils/videoSequence.js +63 -0
  389. package/package.json +5 -2
@@ -0,0 +1,1409 @@
1
+ <script lang="ts">
2
+ import {
3
+ SvelteFlow,
4
+ Background,
5
+ BackgroundVariant,
6
+ Controls,
7
+ MiniMap,
8
+ MarkerType,
9
+ type Connection,
10
+ type Edge,
11
+ type NodeTypes
12
+ } from '@xyflow/svelte';
13
+ import '@xyflow/svelte/dist/style.css';
14
+
15
+ import Button from '../../atoms/Button/Button.svelte';
16
+ import Input from '../../atoms/Input/Input.svelte';
17
+ import SearchInput from '../SearchInput/SearchInput.svelte';
18
+ import GraphBlueprintNode from './GraphBlueprintNode.svelte';
19
+ import GraphFlowBridge from './GraphFlowBridge.svelte';
20
+ import {
21
+ DEFAULT_NODE_TYPES,
22
+ alignGraphNodes,
23
+ breakLinksAtHandle,
24
+ collapseSelectionToFunction,
25
+ createCommentFromSelection,
26
+ createGraphEdge,
27
+ createGraphNode,
28
+ createVarGetNode,
29
+ createVarSetNode,
30
+ decodeGraphClipboard,
31
+ distributeGraphNodes,
32
+ duplicateGraphSelection,
33
+ emptyGraphDocument,
34
+ encodeGraphClipboard,
35
+ expandCollapsedGraph,
36
+ extractGraphSelection,
37
+ filterNodeTypesForGraph,
38
+ flowDocumentFromFlow,
39
+ flowPortOnNode,
40
+ groupNodeTypesByCategory,
41
+ insertRerouteOnEdge,
42
+ isExecPort,
43
+ nodeTypeAcceptsPort,
44
+ pasteGraphSelection,
45
+ pinColor,
46
+ pinsCompatible,
47
+ toFlowEdges,
48
+ toFlowNodes,
49
+ validateGraph,
50
+ variablePlaceItems,
51
+ variablePinColor,
52
+ wiredInputIdsByNode,
53
+ type AlignAxis,
54
+ type BlueprintFlowEdge,
55
+ type BlueprintFlowNode,
56
+ type BlueprintVariable,
57
+ type DistributeAxis,
58
+ type GraphDocument,
59
+ type GraphEdge,
60
+ type GraphNode,
61
+ type GraphNodeContext,
62
+ type GraphNodeTypeDef,
63
+ type GraphPort,
64
+ type GraphValidationIssue
65
+ } from '../../../utils/nodeGraph.js';
66
+
67
+ export type { GraphDocument, GraphNode, GraphEdge, GraphNodeTypeDef, GraphPort };
68
+
69
+ const MIME_NODE_TYPE = 'application/x-node-type';
70
+ const MIME_BP_VAR = 'application/x-blueprint-var';
71
+ const HISTORY_LIMIT = 80;
72
+
73
+ interface NodeGraphProps {
74
+ value?: GraphDocument;
75
+ nodeTypes?: GraphNodeTypeDef[];
76
+ /** Blueprint variables → Get/Set in place menu + drop targets. */
77
+ variables?: BlueprintVariable[];
78
+ /** Graph kind filter (Events only on event_graph, etc.). */
79
+ graphContext?: GraphNodeContext | null;
80
+ readonly?: boolean;
81
+ showMinimap?: boolean;
82
+ showToolbar?: boolean;
83
+ showGrid?: boolean;
84
+ snapToGrid?: boolean;
85
+ gridSize?: number;
86
+ class?: string;
87
+ selectedIds?: string[];
88
+ onchange?: (doc: GraphDocument) => void;
89
+ onselect?: (id: string | null) => void;
90
+ onselectmany?: (ids: string[]) => void;
91
+ onconnect?: (edge: GraphEdge) => void;
92
+ onvalidate?: (issues: GraphValidationIssue[]) => void;
93
+ onplace?: (type: string, x: number, y: number) => void;
94
+ }
95
+
96
+ let {
97
+ value = $bindable(emptyGraphDocument()),
98
+ nodeTypes = DEFAULT_NODE_TYPES,
99
+ variables = [],
100
+ graphContext = null,
101
+ readonly = false,
102
+ showMinimap = true,
103
+ showToolbar = true,
104
+ showGrid = $bindable(true),
105
+ snapToGrid = $bindable(true),
106
+ gridSize = 16,
107
+ class: className = '',
108
+ selectedIds = $bindable<string[]>([]),
109
+ onchange,
110
+ onselect,
111
+ onselectmany,
112
+ onconnect,
113
+ onvalidate,
114
+ onplace
115
+ }: NodeGraphProps = $props();
116
+
117
+ const flowNodeTypes = {
118
+ blueprint: GraphBlueprintNode
119
+ } as unknown as NodeTypes;
120
+
121
+ let nodes = $state.raw<BlueprintFlowNode[]>([]);
122
+ let edges = $state.raw<BlueprintFlowEdge[]>([]);
123
+ let selectedId = $state<string | null>(null);
124
+ let labelDraft = $state('');
125
+ let issues = $state<GraphValidationIssue[]>([]);
126
+ let addOpen = $state(false);
127
+ let alignOpen = $state(false);
128
+ let lastSerialized = $state('');
129
+ let historyPast = $state<string[]>([]);
130
+ let historyFuture = $state<string[]>([]);
131
+ let memoryClipboard = $state<string | null>(null);
132
+ let lastEdgeClick = $state<{ id: string; at: number } | null>(null);
133
+ let commentDrag = $state<{
134
+ commentId: string;
135
+ startPos: { x: number; y: number };
136
+ memberStarts: Map<string, { x: number; y: number }>;
137
+ } | null>(null);
138
+ let flowApi = $state<{
139
+ fitView: (opts?: {
140
+ padding?: number;
141
+ duration?: number;
142
+ maxZoom?: number;
143
+ nodes?: { id: string }[];
144
+ }) => void;
145
+ screenToFlowPosition: (pos: { x: number; y: number }) => { x: number; y: number };
146
+ } | null>(null);
147
+
148
+ let pendingLink = $state<{
149
+ nodeId: string;
150
+ handleId: string;
151
+ port: GraphPort;
152
+ } | null>(null);
153
+ let placeMenuPos = $state({ x: 0, y: 0 });
154
+ let placeAt = $state<{ x: number; y: number } | null>(null);
155
+ let placeMenuQuery = $state('');
156
+ let placeMenuEl = $state<HTMLDivElement | null>(null);
157
+
158
+ const selectedNodeIds = $derived(
159
+ selectedIds.filter((id) => value.nodes.some((n) => n.id === id))
160
+ );
161
+ const selectedEdgeIds = $derived(
162
+ selectedIds.filter((id) => value.edges.some((e) => e.id === id))
163
+ );
164
+ const selectedNode = $derived(value.nodes.find((n) => n.id === selectedId) ?? null);
165
+ const selectedEdge = $derived(value.edges.find((e) => e.id === selectedId) ?? null);
166
+ const hasSelection = $derived(selectedIds.length > 0);
167
+ const canAlign = $derived(selectedNodeIds.length >= 2);
168
+ const canDistribute = $derived(selectedNodeIds.length >= 3);
169
+ const canCollapse = $derived(
170
+ selectedNodeIds.filter((id) => {
171
+ const n = value.nodes.find((x) => x.id === id);
172
+ return n && n.type !== 'comment' && n.type !== 'collapsed_graph';
173
+ }).length >= 1
174
+ );
175
+ const selectedCollapsed = $derived(
176
+ selectedNodeIds.length === 1
177
+ ? (value.nodes.find((n) => n.id === selectedNodeIds[0] && n.type === 'collapsed_graph') ??
178
+ null)
179
+ : null
180
+ );
181
+ const canUndo = $derived(historyPast.length > 0);
182
+ const canRedo = $derived(historyFuture.length > 0);
183
+ const menuSourcePort = $derived(pendingLink?.port ?? null);
184
+
185
+ const catalogTypes = $derived(
186
+ filterNodeTypesForGraph(nodeTypes, { context: graphContext, includeHidden: false })
187
+ );
188
+
189
+ const placeableTypes = $derived.by(() => {
190
+ const base = catalogTypes.filter((t) => t.type !== 'comment');
191
+ if (!menuSourcePort) return base;
192
+ return base.filter((t) => nodeTypeAcceptsPort(t, menuSourcePort));
193
+ });
194
+
195
+ const placeMenuFiltered = $derived.by(() => {
196
+ const q = placeMenuQuery.trim().toLowerCase();
197
+ if (!q) return placeableTypes;
198
+ return placeableTypes.filter(
199
+ (t) =>
200
+ t.label.toLowerCase().includes(q) ||
201
+ t.type.toLowerCase().includes(q) ||
202
+ t.category?.toLowerCase().includes(q) ||
203
+ t.description?.toLowerCase().includes(q)
204
+ );
205
+ });
206
+
207
+ const placeMenuGroups = $derived(groupNodeTypesByCategory(placeMenuFiltered));
208
+
209
+ const variableMenuItems = $derived.by(() => {
210
+ const items = variablePlaceItems(variables);
211
+ const q = placeMenuQuery.trim().toLowerCase();
212
+ const filtered = q
213
+ ? items.filter(
214
+ (it) =>
215
+ it.label.toLowerCase().includes(q) ||
216
+ it.variable.name.toLowerCase().includes(q) ||
217
+ it.variable.dataType.toLowerCase().includes(q)
218
+ )
219
+ : items;
220
+ if (!menuSourcePort) return filtered;
221
+ return filtered.filter((it) => {
222
+ if (it.kind === 'get') return false;
223
+ const probe = createVarSetNode(it.variable, { x: 0, y: 0 });
224
+ return (probe.inputs ?? []).some((p) => pinsCompatible(menuSourcePort!, p));
225
+ });
226
+ });
227
+
228
+ let pendingVarChooser = $state<BlueprintVariable | null>(null);
229
+
230
+ const issueNodeIds = $derived(
231
+ new Set(issues.filter((i) => i.nodeId).map((i) => i.nodeId as string))
232
+ );
233
+
234
+ function decorate(list: BlueprintFlowNode[]): BlueprintFlowNode[] {
235
+ return list.map((n) => ({
236
+ ...n,
237
+ data: { ...n.data, _alert: issueNodeIds.has(n.id) }
238
+ }));
239
+ }
240
+
241
+ function styleEdges(list: BlueprintFlowEdge[], nodeList = nodes): BlueprintFlowEdge[] {
242
+ return list.map((e) => {
243
+ const src = nodeList.find((n) => n.id === e.source);
244
+ const out = flowPortOnNode(src, e.sourceHandle, 'output');
245
+ const color = !out ? '#94a3b8' : isExecPort(out) ? '#94a3b8' : pinColor(out);
246
+ return {
247
+ ...e,
248
+ style: `stroke:${color};stroke-width:2`,
249
+ markerEnd: {
250
+ type: MarkerType.ArrowClosed,
251
+ width: 16,
252
+ height: 16,
253
+ color
254
+ }
255
+ } as BlueprintFlowEdge;
256
+ });
257
+ }
258
+
259
+ function serializeDoc(doc: GraphDocument): string {
260
+ return JSON.stringify({
261
+ nodes: doc.nodes.map((n) => ({
262
+ id: n.id,
263
+ type: n.type,
264
+ x: n.x,
265
+ y: n.y,
266
+ data: n.data,
267
+ inputs: n.inputs,
268
+ outputs: n.outputs
269
+ })),
270
+ edges: doc.edges
271
+ });
272
+ }
273
+
274
+ function emitDoc(doc: GraphDocument, opts?: { recordHistory?: boolean }) {
275
+ const record = opts?.recordHistory !== false;
276
+ const nextSerial = serializeDoc(doc);
277
+ if (record && lastSerialized && nextSerial !== lastSerialized) {
278
+ historyPast = [...historyPast, lastSerialized].slice(-HISTORY_LIMIT);
279
+ historyFuture = [];
280
+ }
281
+ lastSerialized = nextSerial;
282
+ value = doc;
283
+ onchange?.(doc);
284
+ }
285
+
286
+ function restoreSerialized(serial: string) {
287
+ const doc = JSON.parse(serial) as GraphDocument;
288
+ lastSerialized = serial;
289
+ value = doc;
290
+ onchange?.(doc);
291
+ hydrateFromValue(doc);
292
+ }
293
+
294
+ function undo() {
295
+ if (!historyPast.length) return;
296
+ historyFuture = [lastSerialized || serializeDoc(value), ...historyFuture].slice(
297
+ 0,
298
+ HISTORY_LIMIT
299
+ );
300
+ const prev = historyPast[historyPast.length - 1];
301
+ historyPast = historyPast.slice(0, -1);
302
+ restoreSerialized(prev);
303
+ applySelection([]);
304
+ }
305
+
306
+ function redo() {
307
+ if (!historyFuture.length) return;
308
+ historyPast = [...historyPast, lastSerialized || serializeDoc(value)].slice(-HISTORY_LIMIT);
309
+ const next = historyFuture[0];
310
+ historyFuture = historyFuture.slice(1);
311
+ restoreSerialized(next);
312
+ applySelection([]);
313
+ }
314
+
315
+ function emitFromFlow(nextNodes = nodes, nextEdges = edges) {
316
+ const doc = flowDocumentFromFlow(nextNodes, nextEdges);
317
+ emitDoc(doc);
318
+ hydrateFromValue(doc);
319
+ }
320
+
321
+ function updatePortLiteral(nodeId: string, portId: string, literal: unknown) {
322
+ if (readonly) return;
323
+ const next: GraphDocument = {
324
+ ...value,
325
+ nodes: value.nodes.map((n) =>
326
+ n.id !== nodeId
327
+ ? n
328
+ : {
329
+ ...n,
330
+ inputs: (n.inputs ?? []).map((p) =>
331
+ p.id === portId ? { ...p, value: literal } : p
332
+ )
333
+ }
334
+ )
335
+ };
336
+ emitDoc(next);
337
+ hydrateFromValue(next);
338
+ }
339
+
340
+ function hydrateFromValue(doc: GraphDocument) {
341
+ const wired = wiredInputIdsByNode(doc.edges);
342
+ const flowNodes = decorate(toFlowNodes(doc, nodeTypes, selectedIds)).map((n) => ({
343
+ ...n,
344
+ data: {
345
+ ...n.data,
346
+ _wiredInputs: [...(wired.get(n.id) ?? [])],
347
+ _onLiteral: readonly
348
+ ? undefined
349
+ : (portId: string, literal: unknown) => updatePortLiteral(n.id, portId, literal)
350
+ }
351
+ }));
352
+ nodes = flowNodes;
353
+ edges = styleEdges(toFlowEdges(doc, selectedIds), flowNodes);
354
+ }
355
+
356
+ $effect(() => {
357
+ const serialized = serializeDoc(value);
358
+ if (serialized === lastSerialized) return;
359
+ // External updates (inspector / parent) — keep undo stack in sync.
360
+ if (lastSerialized) {
361
+ historyPast = [...historyPast, lastSerialized].slice(-HISTORY_LIMIT);
362
+ historyFuture = [];
363
+ }
364
+ lastSerialized = serialized;
365
+ hydrateFromValue(value);
366
+ });
367
+
368
+ $effect(() => {
369
+ if (selectedNode) labelDraft = String(selectedNode.data?.label ?? selectedNode.type);
370
+ else if (selectedEdge) labelDraft = selectedEdge.label ?? '';
371
+ else labelDraft = '';
372
+ });
373
+
374
+ function applySelection(ids: string[], primary?: string | null) {
375
+ selectedIds = ids;
376
+ selectedId = primary !== undefined ? primary : (ids[0] ?? null);
377
+ onselect?.(selectedId);
378
+ onselectmany?.(ids);
379
+ const set = new Set(ids);
380
+ nodes = nodes.map((n) => ({ ...n, selected: set.has(n.id) }));
381
+ edges = edges.map((e) => ({ ...e, selected: set.has(e.id) }));
382
+ }
383
+
384
+ function select(id: string | null) {
385
+ applySelection(id ? [id] : [], id);
386
+ }
387
+
388
+ function selectAll() {
389
+ if (readonly) return;
390
+ applySelection(value.nodes.map((n) => n.id));
391
+ }
392
+
393
+ function placeNode(type: string, at?: { x: number; y: number }) {
394
+ if (readonly) return null;
395
+ const origin = at ?? placeAt ?? { x: 120, y: 120 };
396
+ const n = createGraphNode({ type, x: origin.x, y: origin.y }, nodeTypes);
397
+ return commitPlacedNode(n, origin);
398
+ }
399
+
400
+ function placeVariableAccess(kind: 'get' | 'set', variable: BlueprintVariable, at?: { x: number; y: number }) {
401
+ if (readonly) return null;
402
+ const origin = at ?? placeAt ?? { x: 120, y: 120 };
403
+ const n =
404
+ kind === 'get' ? createVarGetNode(variable, origin) : createVarSetNode(variable, origin);
405
+ return commitPlacedNode(n, origin);
406
+ }
407
+
408
+ function commitPlacedNode(n: GraphNode, origin: { x: number; y: number }) {
409
+ let doc: GraphDocument = {
410
+ ...value,
411
+ nodes: [...value.nodes, n]
412
+ };
413
+ if (pendingLink) {
414
+ const inn = (n.inputs ?? []).find((p) => pinsCompatible(pendingLink!.port, p));
415
+ if (inn) {
416
+ const edge = createGraphEdge(pendingLink.nodeId, n.id, {
417
+ sourceHandle: pendingLink.handleId,
418
+ targetHandle: inn.id
419
+ });
420
+ doc = { ...doc, edges: [...doc.edges, edge] };
421
+ onconnect?.(edge);
422
+ }
423
+ pendingLink = null;
424
+ }
425
+ emitDoc(doc);
426
+ // Keep xyflow `nodes`/`edges` in sync — emitDoc alone skips hydrate ($effect sees same serial).
427
+ hydrateFromValue(doc);
428
+ select(n.id);
429
+ onplace?.(n.type, origin.x, origin.y);
430
+ addOpen = false;
431
+ pendingVarChooser = null;
432
+ closePlaceMenu(false);
433
+ return n;
434
+ }
435
+
436
+ function duplicateSelected() {
437
+ if (readonly || !selectedNodeIds.length) return;
438
+ const { doc, newNodeIds } = duplicateGraphSelection(value, selectedNodeIds);
439
+ if (!newNodeIds.length) return;
440
+ emitDoc(doc);
441
+ hydrateFromValue(doc);
442
+ applySelection(newNodeIds);
443
+ }
444
+
445
+ function deleteSelected() {
446
+ if (readonly || !hasSelection) return;
447
+ const delNodes = new Set(selectedNodeIds);
448
+ const delEdges = new Set(selectedEdgeIds);
449
+ const next: GraphDocument = {
450
+ ...value,
451
+ nodes: value.nodes.filter((n) => !delNodes.has(n.id)),
452
+ edges: value.edges.filter(
453
+ (e) =>
454
+ !delEdges.has(e.id) && !delNodes.has(e.source) && !delNodes.has(e.target)
455
+ )
456
+ };
457
+ emitDoc(next);
458
+ hydrateFromValue(next);
459
+ applySelection([]);
460
+ }
461
+
462
+ async function copySelected() {
463
+ if (!selectedNodeIds.length) return;
464
+ const fragment = extractGraphSelection(value, selectedNodeIds);
465
+ const encoded = encodeGraphClipboard(fragment);
466
+ memoryClipboard = encoded;
467
+ try {
468
+ await navigator.clipboard.writeText(encoded);
469
+ } catch {
470
+ /* memory clipboard fallback */
471
+ }
472
+ }
473
+
474
+ async function cutSelected() {
475
+ if (readonly || !selectedNodeIds.length) return;
476
+ await copySelected();
477
+ deleteSelected();
478
+ }
479
+
480
+ async function pasteClipboard(at?: { x: number; y: number }) {
481
+ if (readonly) return;
482
+ let raw = memoryClipboard;
483
+ try {
484
+ const fromNav = await navigator.clipboard.readText();
485
+ if (fromNav) raw = fromNav;
486
+ } catch {
487
+ /* use memory */
488
+ }
489
+ if (!raw) return;
490
+ const fragment = decodeGraphClipboard(raw);
491
+ if (!fragment?.nodes.length) return;
492
+ const { doc, newNodeIds } = pasteGraphSelection(value, fragment, {
493
+ origin: at,
494
+ offsetX: 40,
495
+ offsetY: 40
496
+ });
497
+ emitDoc(doc);
498
+ hydrateFromValue(doc);
499
+ applySelection(newNodeIds);
500
+ }
501
+
502
+ function applyDoc(doc: GraphDocument, selectIds?: string[]) {
503
+ emitDoc(doc);
504
+ hydrateFromValue(doc);
505
+ if (selectIds) applySelection(selectIds);
506
+ }
507
+
508
+ function alignSelected(align: AlignAxis) {
509
+ if (readonly || !canAlign) return;
510
+ applyDoc(alignGraphNodes(value, selectedNodeIds, align), selectedNodeIds);
511
+ alignOpen = false;
512
+ }
513
+
514
+ function distributeSelected(axis: DistributeAxis) {
515
+ if (readonly || !canDistribute) return;
516
+ applyDoc(distributeGraphNodes(value, selectedNodeIds, axis), selectedNodeIds);
517
+ alignOpen = false;
518
+ }
519
+
520
+ function commentFromSelection() {
521
+ if (readonly || !selectedNodeIds.length) return;
522
+ const result = createCommentFromSelection(value, selectedNodeIds);
523
+ if (!result) return;
524
+ applyDoc(result.doc, [result.commentId, ...selectedNodeIds]);
525
+ }
526
+
527
+ function collapseSelected() {
528
+ if (readonly || !canCollapse) return;
529
+ const result = collapseSelectionToFunction(value, selectedNodeIds);
530
+ if (!result) return;
531
+ applyDoc(result.doc, [result.collapsedId]);
532
+ }
533
+
534
+ function expandSelected() {
535
+ if (readonly || !selectedCollapsed) return;
536
+ const result = expandCollapsedGraph(value, selectedCollapsed.id);
537
+ if (!result) return;
538
+ applyDoc(result.doc, result.newNodeIds);
539
+ }
540
+
541
+ function insertRerouteOnSelectedEdge(at?: { x: number; y: number }) {
542
+ if (readonly) return;
543
+ const edgeId = selectedEdgeIds[0] ?? (selectedEdge?.id ?? null);
544
+ if (!edgeId) return;
545
+ const edge = value.edges.find((e) => e.id === edgeId);
546
+ if (!edge) return;
547
+ const source = value.nodes.find((n) => n.id === edge.source);
548
+ const target = value.nodes.find((n) => n.id === edge.target);
549
+ const mid = at ?? {
550
+ x: ((source?.x ?? 0) + (target?.x ?? 0)) / 2,
551
+ y: ((source?.y ?? 0) + (target?.y ?? 0)) / 2
552
+ };
553
+ const result = insertRerouteOnEdge(value, edgeId, mid);
554
+ if (!result) return;
555
+ applyDoc(result.doc, [result.rerouteId]);
556
+ }
557
+
558
+ function zoomToSelection() {
559
+ if (selectedNodeIds.length) {
560
+ flowApi?.fitView({
561
+ padding: 0.25,
562
+ duration: 200,
563
+ maxZoom: 1,
564
+ nodes: selectedNodeIds.map((id) => ({ id }))
565
+ });
566
+ return;
567
+ }
568
+ doFitView();
569
+ }
570
+
571
+ function applyLabel() {
572
+ if (readonly || !selectedId) return;
573
+ if (selectedNode) {
574
+ emitDoc({
575
+ ...value,
576
+ nodes: value.nodes.map((n) =>
577
+ n.id === selectedId ? { ...n, data: { ...n.data, label: labelDraft } } : n
578
+ )
579
+ });
580
+ } else if (selectedEdge) {
581
+ emitDoc({
582
+ ...value,
583
+ edges: value.edges.map((e) =>
584
+ e.id === selectedId ? { ...e, label: labelDraft || undefined } : e
585
+ )
586
+ });
587
+ }
588
+ }
589
+
590
+ function runValidate() {
591
+ const next = validateGraph(value);
592
+ issues = next;
593
+ onvalidate?.(next);
594
+ nodes = decorate(nodes);
595
+ return next;
596
+ }
597
+
598
+ function isValidConnection(connection: Connection | Edge): boolean {
599
+ const source = nodes.find((n) => n.id === connection.source);
600
+ const target = nodes.find((n) => n.id === connection.target);
601
+ const out = flowPortOnNode(source, connection.sourceHandle, 'output');
602
+ const inn = flowPortOnNode(target, connection.targetHandle, 'input');
603
+ if (!out || !inn) return false;
604
+ return pinsCompatible(out, inn);
605
+ }
606
+
607
+ function onConnect(connection: Connection) {
608
+ if (readonly) return;
609
+ const edge = createGraphEdge(connection.source, connection.target, {
610
+ sourceHandle: connection.sourceHandle ?? undefined,
611
+ targetHandle: connection.targetHandle ?? undefined
612
+ });
613
+ // Replace existing wires into the same target handle (UE-style single pin link).
614
+ const filtered = edges.filter(
615
+ (e) =>
616
+ !(
617
+ e.target === edge.target &&
618
+ (e.targetHandle ?? '') === (edge.targetHandle ?? '')
619
+ )
620
+ );
621
+ const nextEdges = styleEdges([
622
+ ...filtered,
623
+ {
624
+ id: edge.id,
625
+ source: edge.source,
626
+ target: edge.target,
627
+ sourceHandle: edge.sourceHandle ?? null,
628
+ targetHandle: edge.targetHandle ?? null,
629
+ label: edge.label
630
+ }
631
+ ]);
632
+ edges = nextEdges;
633
+ emitFromFlow(nodes, nextEdges);
634
+ onconnect?.(edge);
635
+ select(edge.id);
636
+ }
637
+
638
+ function onConnectStart(
639
+ _event: MouseEvent | TouchEvent,
640
+ params: { nodeId: string | null; handleId: string | null; handleType: string | null }
641
+ ) {
642
+ if (readonly) return;
643
+ const ev = _event as MouseEvent;
644
+ if (!ev.altKey || !params.nodeId) return;
645
+ const next = breakLinksAtHandle(
646
+ value,
647
+ params.nodeId,
648
+ params.handleId,
649
+ params.handleType === 'source' || params.handleType === 'target'
650
+ ? params.handleType
651
+ : null
652
+ );
653
+ if (next.edges.length === value.edges.length) return;
654
+ emitDoc(next);
655
+ hydrateFromValue(next);
656
+ }
657
+
658
+ function onEdgeClick({ edge, event }: { edge: BlueprintFlowEdge; event: MouseEvent }) {
659
+ const now = performance.now();
660
+ const dbl =
661
+ lastEdgeClick && lastEdgeClick.id === edge.id && now - lastEdgeClick.at < 320;
662
+ lastEdgeClick = { id: edge.id, at: now };
663
+ if (!dbl || readonly) return;
664
+ event.preventDefault();
665
+ const flowPos = flowApi?.screenToFlowPosition({ x: event.clientX, y: event.clientY }) ?? {
666
+ x: 0,
667
+ y: 0
668
+ };
669
+ const result = insertRerouteOnEdge(value, edge.id, flowPos);
670
+ if (!result) return;
671
+ applyDoc(result.doc, [result.rerouteId]);
672
+ }
673
+
674
+ function onConnectEnd(
675
+ event: MouseEvent | TouchEvent,
676
+ state: {
677
+ isValid: boolean | null;
678
+ fromNode: BlueprintFlowNode | null;
679
+ fromHandle: { id: string | null; type: string | null } | null;
680
+ }
681
+ ) {
682
+ if (readonly) return;
683
+ if (state.isValid) return;
684
+ if (!state.fromNode || !state.fromHandle || state.fromHandle.type !== 'source') return;
685
+ const port = flowPortOnNode(state.fromNode, state.fromHandle.id, 'output');
686
+ if (!port) return;
687
+ const clientX = 'changedTouches' in event ? event.changedTouches[0]?.clientX : event.clientX;
688
+ const clientY = 'changedTouches' in event ? event.changedTouches[0]?.clientY : event.clientY;
689
+ if (clientX == null || clientY == null) return;
690
+ pendingLink = {
691
+ nodeId: state.fromNode.id,
692
+ handleId: state.fromHandle.id ?? port.id,
693
+ port
694
+ };
695
+ const flowPos = flowApi?.screenToFlowPosition({ x: clientX, y: clientY }) ?? {
696
+ x: 120,
697
+ y: 120
698
+ };
699
+ openPlaceMenu(clientX, clientY, flowPos);
700
+ }
701
+
702
+ function onNodeDragStart({
703
+ targetNode
704
+ }: {
705
+ targetNode: BlueprintFlowNode | null;
706
+ }) {
707
+ if (!targetNode || targetNode.data?.graphType !== 'comment') {
708
+ commentDrag = null;
709
+ return;
710
+ }
711
+ const memberIds = Array.isArray(targetNode.data.memberIds)
712
+ ? (targetNode.data.memberIds as string[])
713
+ : [];
714
+ const memberStarts = new Map<string, { x: number; y: number }>();
715
+ for (const id of memberIds) {
716
+ const n = nodes.find((x) => x.id === id);
717
+ if (n) memberStarts.set(id, { x: n.position.x, y: n.position.y });
718
+ }
719
+ commentDrag = {
720
+ commentId: targetNode.id,
721
+ startPos: { x: targetNode.position.x, y: targetNode.position.y },
722
+ memberStarts
723
+ };
724
+ }
725
+
726
+ function onNodeDrag({ targetNode }: { targetNode: BlueprintFlowNode | null }) {
727
+ if (!commentDrag || !targetNode || targetNode.id !== commentDrag.commentId) return;
728
+ if (!commentDrag.memberStarts.size) return;
729
+ const dx = targetNode.position.x - commentDrag.startPos.x;
730
+ const dy = targetNode.position.y - commentDrag.startPos.y;
731
+ nodes = nodes.map((n) => {
732
+ const start = commentDrag!.memberStarts.get(n.id);
733
+ if (!start) return n;
734
+ return {
735
+ ...n,
736
+ position: { x: start.x + dx, y: start.y + dy }
737
+ };
738
+ });
739
+ }
740
+
741
+ function onNodeDragStop() {
742
+ commentDrag = null;
743
+ emitFromFlow();
744
+ }
745
+
746
+ function onSelectionChange({
747
+ nodes: selNodes,
748
+ edges: selEdges
749
+ }: {
750
+ nodes: BlueprintFlowNode[];
751
+ edges: BlueprintFlowEdge[];
752
+ }) {
753
+ const ids = [...selNodes.map((n) => n.id), ...selEdges.map((e) => e.id)];
754
+ selectedIds = ids;
755
+ selectedId = selNodes[0]?.id ?? selEdges[0]?.id ?? null;
756
+ onselect?.(selectedId);
757
+ onselectmany?.(ids);
758
+ }
759
+
760
+ function onDelete({
761
+ nodes: delNodes,
762
+ edges: delEdges
763
+ }: {
764
+ nodes: BlueprintFlowNode[];
765
+ edges: BlueprintFlowEdge[];
766
+ }) {
767
+ if (readonly) return;
768
+ const delNodeIds = new Set(delNodes.map((n) => n.id));
769
+ const delEdgeIds = new Set(delEdges.map((e) => e.id));
770
+ const nextNodes = nodes.filter((n) => !delNodeIds.has(n.id));
771
+ const nextEdges = edges.filter(
772
+ (e) => !delEdgeIds.has(e.id) && !delNodeIds.has(e.source) && !delNodeIds.has(e.target)
773
+ );
774
+ nodes = nextNodes;
775
+ edges = nextEdges;
776
+ emitFromFlow(nextNodes, nextEdges);
777
+ applySelection([]);
778
+ }
779
+
780
+ function openPlaceMenu(clientX: number, clientY: number, world: { x: number; y: number }) {
781
+ placeAt = world;
782
+ placeMenuPos = { x: clientX, y: clientY };
783
+ placeMenuQuery = '';
784
+ requestAnimationFrame(() => {
785
+ if (!placeMenuEl) return;
786
+ const vw = window.innerWidth;
787
+ const vh = window.innerHeight;
788
+ const { offsetWidth: mw, offsetHeight: mh } = placeMenuEl;
789
+ placeMenuPos = {
790
+ x: clientX + mw > vw - 8 ? Math.max(8, clientX - mw) : clientX,
791
+ y: clientY + mh > vh - 8 ? Math.max(8, clientY - mh) : clientY
792
+ };
793
+ if (!placeMenuEl.matches(':popover-open')) placeMenuEl.showPopover();
794
+ // Ignore the pointerup that follows contextmenu, then listen for outside dismiss.
795
+ ignorePlaceMenuDismissUntil = performance.now() + 320;
796
+ attachPlaceMenuOutsideClose();
797
+ });
798
+ }
799
+
800
+ let ignorePlaceMenuDismissUntil = 0;
801
+ let placeMenuOutsideCleanup: (() => void) | null = null;
802
+
803
+ function attachPlaceMenuOutsideClose() {
804
+ placeMenuOutsideCleanup?.();
805
+ const onPointerDown = (event: PointerEvent) => {
806
+ if (performance.now() < ignorePlaceMenuDismissUntil) {
807
+ event.preventDefault();
808
+ event.stopPropagation();
809
+ return;
810
+ }
811
+ const t = event.target as Node | null;
812
+ if (placeMenuEl && t && placeMenuEl.contains(t)) return;
813
+ closePlaceMenu(true);
814
+ };
815
+ const onKey = (event: KeyboardEvent) => {
816
+ if (event.key === 'Escape') closePlaceMenu(true);
817
+ };
818
+ document.addEventListener('pointerdown', onPointerDown, true);
819
+ document.addEventListener('keydown', onKey, true);
820
+ placeMenuOutsideCleanup = () => {
821
+ document.removeEventListener('pointerdown', onPointerDown, true);
822
+ document.removeEventListener('keydown', onKey, true);
823
+ placeMenuOutsideCleanup = null;
824
+ };
825
+ }
826
+
827
+ function closePlaceMenu(clearPending = true) {
828
+ placeMenuOutsideCleanup?.();
829
+ if (placeMenuEl?.matches(':popover-open')) placeMenuEl.hidePopover();
830
+ placeMenuQuery = '';
831
+ pendingVarChooser = null;
832
+ if (clearPending) pendingLink = null;
833
+ }
834
+
835
+ function onPlaceMenuToggle(event: ToggleEvent) {
836
+ if (event.newState === 'open') return;
837
+ placeMenuOutsideCleanup?.();
838
+ placeMenuQuery = '';
839
+ pendingVarChooser = null;
840
+ pendingLink = null;
841
+ }
842
+
843
+ function onPlaceMenuSelect(type: string) {
844
+ placeNode(type, placeAt ?? undefined);
845
+ }
846
+
847
+ function onPaneContextMenu({ event }: { event: MouseEvent }) {
848
+ if (readonly) return;
849
+ event.preventDefault();
850
+ event.stopPropagation();
851
+ pendingLink = null;
852
+ pendingVarChooser = null;
853
+ const clientX = event.clientX;
854
+ const clientY = event.clientY;
855
+ const flowPos = flowApi?.screenToFlowPosition({ x: clientX, y: clientY }) ?? {
856
+ x: clientX,
857
+ y: clientY
858
+ };
859
+ // Open after the contextmenu gesture ends — otherwise the following mouseup dismisses it.
860
+ let opened = false;
861
+ const open = () => {
862
+ if (opened) return;
863
+ opened = true;
864
+ window.removeEventListener('pointerup', open);
865
+ window.removeEventListener('mouseup', open);
866
+ openPlaceMenu(clientX, clientY, flowPos);
867
+ };
868
+ window.addEventListener('pointerup', open, { once: true });
869
+ window.addEventListener('mouseup', open, { once: true });
870
+ setTimeout(open, 50);
871
+ }
872
+
873
+ function flowPosFromClient(clientX: number, clientY: number) {
874
+ return (
875
+ flowApi?.screenToFlowPosition({ x: clientX, y: clientY }) ?? {
876
+ x: clientX,
877
+ y: clientY
878
+ }
879
+ );
880
+ }
881
+
882
+ function onCanvasDragOver(event: DragEvent) {
883
+ if (readonly) return;
884
+ const types = event.dataTransfer?.types ?? [];
885
+ if (
886
+ [...types].includes(MIME_NODE_TYPE) ||
887
+ [...types].includes(MIME_BP_VAR) ||
888
+ [...types].includes('text/plain')
889
+ ) {
890
+ event.preventDefault();
891
+ if (event.dataTransfer) event.dataTransfer.dropEffect = 'copy';
892
+ }
893
+ }
894
+
895
+ function onCanvasDrop(event: DragEvent) {
896
+ if (readonly) return;
897
+ event.preventDefault();
898
+ const flowPos = flowPosFromClient(event.clientX, event.clientY);
899
+ const varRaw = event.dataTransfer?.getData(MIME_BP_VAR);
900
+ if (varRaw) {
901
+ try {
902
+ const parsed = JSON.parse(varRaw) as BlueprintVariable;
903
+ const variable = variables.find((v) => v.id === parsed.id) ?? parsed;
904
+ pendingLink = null;
905
+ pendingVarChooser = variable;
906
+ openPlaceMenu(event.clientX, event.clientY, flowPos);
907
+ return;
908
+ } catch {
909
+ /* ignore bad payload */
910
+ }
911
+ }
912
+ const type =
913
+ event.dataTransfer?.getData(MIME_NODE_TYPE) || event.dataTransfer?.getData('text/plain');
914
+ if (type && catalogTypes.some((t) => t.type === type)) {
915
+ placeNode(type, flowPos);
916
+ }
917
+ }
918
+
919
+ function doFitView() {
920
+ flowApi?.fitView({ padding: 0.2, duration: 200, maxZoom: 1 });
921
+ }
922
+
923
+ function onGraphKeyDown(event: KeyboardEvent) {
924
+ if (readonly && !['f', 'F'].includes(event.key)) return;
925
+ const target = event.target as HTMLElement | null;
926
+ if (target?.closest('input, textarea, select, [contenteditable="true"]')) return;
927
+ const mod = event.metaKey || event.ctrlKey;
928
+ const key = event.key.toLowerCase();
929
+
930
+ if (key === 'f' && !mod) {
931
+ event.preventDefault();
932
+ zoomToSelection();
933
+ return;
934
+ }
935
+
936
+ if (readonly) return;
937
+
938
+ if (mod && key === 'a') {
939
+ event.preventDefault();
940
+ selectAll();
941
+ return;
942
+ }
943
+ if (mod && key === 'd') {
944
+ event.preventDefault();
945
+ duplicateSelected();
946
+ return;
947
+ }
948
+ if (mod && key === 'c') {
949
+ event.preventDefault();
950
+ void copySelected();
951
+ return;
952
+ }
953
+ if (mod && key === 'x') {
954
+ event.preventDefault();
955
+ void cutSelected();
956
+ return;
957
+ }
958
+ if (mod && key === 'v') {
959
+ event.preventDefault();
960
+ void pasteClipboard(placeAt ?? undefined);
961
+ return;
962
+ }
963
+ if (mod && key === 'z' && !event.shiftKey) {
964
+ event.preventDefault();
965
+ undo();
966
+ return;
967
+ }
968
+ if ((mod && key === 'y') || (mod && event.shiftKey && key === 'z')) {
969
+ event.preventDefault();
970
+ redo();
971
+ return;
972
+ }
973
+ if (key === 'c' && !mod && selectedNodeIds.length) {
974
+ event.preventDefault();
975
+ commentFromSelection();
976
+ return;
977
+ }
978
+ if (key === 'r' && !mod && selectedEdgeIds.length) {
979
+ event.preventDefault();
980
+ insertRerouteOnSelectedEdge();
981
+ return;
982
+ }
983
+ if (mod && key === 'g' && canCollapse) {
984
+ event.preventDefault();
985
+ collapseSelected();
986
+ return;
987
+ }
988
+ if (key === 'e' && !mod && selectedCollapsed) {
989
+ event.preventDefault();
990
+ expandSelected();
991
+ }
992
+ }
993
+ </script>
994
+
995
+ <div
996
+ class={['flex flex-col overflow-hidden border border-border bg-surface-elevated', className]}
997
+ data-node-graph
998
+ tabindex="0"
999
+ onkeydown={onGraphKeyDown}
1000
+ role="application"
1001
+ aria-label="Node graph"
1002
+ >
1003
+ {#if showToolbar}
1004
+ <div class="flex flex-wrap items-center gap-2 border-b border-border px-3 py-2">
1005
+ {#if !readonly}
1006
+ <div class="relative">
1007
+ <Button size="sm" onclick={() => (addOpen = !addOpen)}>Add node</Button>
1008
+ {#if addOpen}
1009
+ <div
1010
+ class="absolute left-0 z-30 mt-1 max-h-72 w-60 overflow-auto rounded-xl border border-border bg-surface-elevated py-1 shadow-lg"
1011
+ >
1012
+ {#each catalogTypes as nt (nt.type)}
1013
+ <button
1014
+ type="button"
1015
+ class="flex w-full items-start gap-2 px-3 py-2 text-left hover:bg-surface-overlay"
1016
+ onclick={() => placeNode(nt.type)}
1017
+ >
1018
+ <span
1019
+ class="mt-1 h-2.5 w-2.5 shrink-0 rounded-full"
1020
+ style={`background:${nt.color ?? '#64748b'}`}
1021
+ ></span>
1022
+ <span class="min-w-0">
1023
+ <span class="block text-sm font-medium text-primary">{nt.label}</span>
1024
+ <span class="block text-[11px] text-muted">{nt.category ?? nt.description}</span>
1025
+ </span>
1026
+ </button>
1027
+ {/each}
1028
+ </div>
1029
+ {/if}
1030
+ </div>
1031
+ <Button
1032
+ size="sm"
1033
+ variant="secondary"
1034
+ disabled={!selectedNodeIds.length}
1035
+ onclick={duplicateSelected}
1036
+ >
1037
+ Duplicate
1038
+ </Button>
1039
+ <Button size="sm" variant="secondary" disabled={!hasSelection} onclick={deleteSelected}>
1040
+ Delete
1041
+ </Button>
1042
+ <Button size="sm" variant="ghost" disabled={!canUndo} onclick={undo}>Undo</Button>
1043
+ <Button size="sm" variant="ghost" disabled={!canRedo} onclick={redo}>Redo</Button>
1044
+ <div class="relative">
1045
+ <Button
1046
+ size="sm"
1047
+ variant="secondary"
1048
+ disabled={!canAlign}
1049
+ onclick={() => (alignOpen = !alignOpen)}
1050
+ >
1051
+ Align
1052
+ </Button>
1053
+ {#if alignOpen && canAlign}
1054
+ <div
1055
+ class="absolute left-0 z-30 mt-1 w-44 overflow-hidden rounded-xl border border-border bg-surface-elevated py-1 shadow-lg"
1056
+ >
1057
+ {#each [
1058
+ ['left', 'Align left'],
1059
+ ['right', 'Align right'],
1060
+ ['top', 'Align top'],
1061
+ ['bottom', 'Align bottom'],
1062
+ ['centerX', 'Align center X'],
1063
+ ['centerY', 'Align center Y']
1064
+ ] as [id, label] (id)}
1065
+ <button
1066
+ type="button"
1067
+ class="block w-full px-3 py-1.5 text-left text-sm hover:bg-surface-overlay"
1068
+ onclick={() => alignSelected(id as AlignAxis)}
1069
+ >
1070
+ {label}
1071
+ </button>
1072
+ {/each}
1073
+ {#if canDistribute}
1074
+ <div class="my-1 h-px bg-border"></div>
1075
+ <button
1076
+ type="button"
1077
+ class="block w-full px-3 py-1.5 text-left text-sm hover:bg-surface-overlay"
1078
+ onclick={() => distributeSelected('horizontal')}
1079
+ >
1080
+ Distribute X
1081
+ </button>
1082
+ <button
1083
+ type="button"
1084
+ class="block w-full px-3 py-1.5 text-left text-sm hover:bg-surface-overlay"
1085
+ onclick={() => distributeSelected('vertical')}
1086
+ >
1087
+ Distribute Y
1088
+ </button>
1089
+ {/if}
1090
+ </div>
1091
+ {/if}
1092
+ </div>
1093
+ <Button
1094
+ size="sm"
1095
+ variant="secondary"
1096
+ disabled={!selectedNodeIds.length}
1097
+ onclick={commentFromSelection}
1098
+ >
1099
+ Comment
1100
+ </Button>
1101
+ {#if selectedCollapsed}
1102
+ <Button size="sm" variant="secondary" onclick={expandSelected}>Expand</Button>
1103
+ {:else}
1104
+ <Button
1105
+ size="sm"
1106
+ variant="secondary"
1107
+ disabled={!canCollapse}
1108
+ onclick={collapseSelected}
1109
+ >
1110
+ Collapse
1111
+ </Button>
1112
+ {/if}
1113
+ {#if selectedEdgeIds.length}
1114
+ <Button size="sm" variant="secondary" onclick={() => insertRerouteOnSelectedEdge()}>
1115
+ Reroute
1116
+ </Button>
1117
+ {/if}
1118
+ {/if}
1119
+ <Button size="sm" variant="ghost" onclick={zoomToSelection}>
1120
+ {selectedNodeIds.length ? 'Frame' : 'Fit'}
1121
+ </Button>
1122
+ <Button size="sm" variant="ghost" onclick={runValidate}>Validate</Button>
1123
+ <label class="flex items-center gap-1.5 text-xs text-muted">
1124
+ <input type="checkbox" bind:checked={showGrid} /> Grid
1125
+ </label>
1126
+ <label class="flex items-center gap-1.5 text-xs text-muted">
1127
+ <input type="checkbox" bind:checked={snapToGrid} disabled={readonly} /> Snap
1128
+ </label>
1129
+ </div>
1130
+ {/if}
1131
+
1132
+ {#if (selectedNode || selectedEdge) && !readonly && showToolbar}
1133
+ <div class="flex flex-wrap items-center gap-2 border-b border-border bg-surface-overlay/40 px-3 py-2">
1134
+ <span class="text-[11px] font-semibold uppercase tracking-wide text-muted">
1135
+ {selectedNode ? 'Node' : 'Edge'}
1136
+ </span>
1137
+ <Input
1138
+ bind:value={labelDraft}
1139
+ placeholder={selectedNode ? 'Label' : 'Edge label'}
1140
+ class="max-w-xs"
1141
+ size="sm"
1142
+ onkeydown={(e) => {
1143
+ if (e.key === 'Enter') applyLabel();
1144
+ }}
1145
+ />
1146
+ <Button size="sm" variant="secondary" onclick={applyLabel}>Save</Button>
1147
+ </div>
1148
+ {/if}
1149
+
1150
+ {#if issues.length}
1151
+ <div
1152
+ class="flex flex-wrap gap-2 border-b border-amber-500/30 bg-amber-50 px-3 py-1.5 text-[11px] text-amber-800 dark:bg-amber-950/30 dark:text-amber-200"
1153
+ >
1154
+ {#each issues.slice(0, 3) as issue (issue.id)}
1155
+ <span>{issue.message}</span>
1156
+ {/each}
1157
+ {#if issues.length > 3}
1158
+ <span class="text-muted">+{issues.length - 3} more</span>
1159
+ {/if}
1160
+ </div>
1161
+ {/if}
1162
+
1163
+ <div
1164
+ class="relative min-h-[320px] flex-1"
1165
+ ondragover={onCanvasDragOver}
1166
+ ondrop={onCanvasDrop}
1167
+ role="presentation"
1168
+ >
1169
+ <SvelteFlow
1170
+ bind:nodes
1171
+ bind:edges
1172
+ nodeTypes={flowNodeTypes}
1173
+ fitView
1174
+ fitViewOptions={{ padding: 0.2, maxZoom: 1, minZoom: 0.2 }}
1175
+ minZoom={0.25}
1176
+ maxZoom={1.25}
1177
+ nodesDraggable={!readonly}
1178
+ nodesConnectable={!readonly}
1179
+ elementsSelectable={!readonly}
1180
+ deleteKey={readonly ? null : ['Backspace', 'Delete']}
1181
+ multiSelectionKey={readonly ? null : 'Shift'}
1182
+ selectionKey={null}
1183
+ selectionOnDrag={!readonly}
1184
+ panOnDrag={[1]}
1185
+ selectionMode="partial"
1186
+ selectNodesOnDrag={false}
1187
+ snapGrid={snapToGrid ? ([gridSize, gridSize] as [number, number]) : undefined}
1188
+ isValidConnection={isValidConnection}
1189
+ onconnect={onConnect}
1190
+ onconnectstart={onConnectStart as never}
1191
+ onconnectend={onConnectEnd as never}
1192
+ onnodedragstart={onNodeDragStart as never}
1193
+ onnodedrag={onNodeDrag as never}
1194
+ onnodedragstop={onNodeDragStop}
1195
+ onselectionchange={onSelectionChange as never}
1196
+ ondelete={onDelete as never}
1197
+ onedgeclick={onEdgeClick as never}
1198
+ onpanecontextmenu={onPaneContextMenu}
1199
+ defaultEdgeOptions={{
1200
+ type: 'default',
1201
+ markerEnd: { type: MarkerType.ArrowClosed, width: 16, height: 16 }
1202
+ }}
1203
+ proOptions={{ hideAttribution: true }}
1204
+ colorMode="light"
1205
+ class="h-full w-full !bg-white"
1206
+ >
1207
+ {#if showGrid}
1208
+ <Background variant={BackgroundVariant.Dots} gap={gridSize} size={1} />
1209
+ {/if}
1210
+ <Controls showInteractive={!readonly} />
1211
+ {#if showMinimap}
1212
+ <MiniMap
1213
+ pannable
1214
+ zoomable
1215
+ class="!bg-surface-elevated !border-border"
1216
+ nodeColor={(n: BlueprintFlowNode) => {
1217
+ return n.data?.color ?? '#94a3b8';
1218
+ }}
1219
+ />
1220
+ {/if}
1221
+ <GraphFlowBridge onready={(api) => (flowApi = api)} />
1222
+ </SvelteFlow>
1223
+ </div>
1224
+ </div>
1225
+
1226
+ <div
1227
+ bind:this={placeMenuEl}
1228
+ popover="manual"
1229
+ role="menu"
1230
+ aria-label="Place node"
1231
+ data-place-menu
1232
+ ontoggle={onPlaceMenuToggle}
1233
+ style={`top:${placeMenuPos.y}px;left:${placeMenuPos.x}px;margin:0;`}
1234
+ class="node-place-menu flex max-h-[min(28rem,70vh)] w-64 flex-col overflow-hidden rounded-xl border border-border bg-surface-elevated shadow-xl outline-none"
1235
+ >
1236
+ <div class="border-b border-border p-2">
1237
+ <SearchInput
1238
+ bind:value={placeMenuQuery}
1239
+ placeholder={pendingVarChooser
1240
+ ? `Place ${pendingVarChooser.name}…`
1241
+ : menuSourcePort
1242
+ ? 'Search compatible nodes…'
1243
+ : 'Search nodes…'}
1244
+ size="sm"
1245
+ />
1246
+ </div>
1247
+ <div class="min-h-0 flex-1 overflow-auto p-1.5">
1248
+ {#if pendingVarChooser}
1249
+ <p
1250
+ class="px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted"
1251
+ role="presentation"
1252
+ >
1253
+ Variable
1254
+ </p>
1255
+ {#each (['get', 'set'] as const) as kind (kind)}
1256
+ <button
1257
+ type="button"
1258
+ role="menuitem"
1259
+ class="flex w-full items-start gap-2 rounded-lg px-2.5 py-2 text-left hover:bg-surface-overlay"
1260
+ onclick={() => placeVariableAccess(kind, pendingVarChooser!)}
1261
+ >
1262
+ <span
1263
+ class="mt-1 h-2.5 w-2.5 shrink-0 rounded-full"
1264
+ style={`background:${variablePinColor(pendingVarChooser.dataType)}`}
1265
+ ></span>
1266
+ <span class="min-w-0">
1267
+ <span class="block truncate text-sm font-medium text-primary">
1268
+ {kind === 'get' ? 'Get' : 'Set'}
1269
+ {pendingVarChooser.name}
1270
+ </span>
1271
+ <span class="block truncate text-[11px] text-muted">{pendingVarChooser.dataType}</span>
1272
+ </span>
1273
+ </button>
1274
+ {/each}
1275
+ {:else}
1276
+ {#if variableMenuItems.length}
1277
+ <p
1278
+ class="px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted"
1279
+ role="presentation"
1280
+ >
1281
+ Variables
1282
+ </p>
1283
+ {#each variableMenuItems as item (item.id)}
1284
+ <button
1285
+ type="button"
1286
+ role="menuitem"
1287
+ class="flex w-full items-start gap-2 rounded-lg px-2.5 py-2 text-left hover:bg-surface-overlay"
1288
+ onclick={() => placeVariableAccess(item.kind, item.variable)}
1289
+ >
1290
+ <span
1291
+ class="mt-1 h-2.5 w-2.5 shrink-0 rounded-full"
1292
+ style={`background:${item.color}`}
1293
+ ></span>
1294
+ <span class="min-w-0">
1295
+ <span class="block truncate text-sm font-medium text-primary">{item.label}</span>
1296
+ <span class="block truncate text-[11px] text-muted">{item.variable.dataType}</span>
1297
+ </span>
1298
+ </button>
1299
+ {/each}
1300
+ {/if}
1301
+
1302
+ {#each placeMenuGroups as group (group.category)}
1303
+ <p
1304
+ class="px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted"
1305
+ role="presentation"
1306
+ >
1307
+ {group.category}
1308
+ </p>
1309
+ {#each group.types as nt (nt.type)}
1310
+ <button
1311
+ type="button"
1312
+ role="menuitem"
1313
+ class="flex w-full items-start gap-2 rounded-lg px-2.5 py-2 text-left hover:bg-surface-overlay"
1314
+ onclick={() => onPlaceMenuSelect(nt.type)}
1315
+ >
1316
+ <span
1317
+ class="mt-1 h-2.5 w-2.5 shrink-0 rounded-full"
1318
+ style={`background:${nt.color ?? '#64748b'}`}
1319
+ ></span>
1320
+ <span class="min-w-0">
1321
+ <span class="block truncate text-sm font-medium text-primary">{nt.label}</span>
1322
+ {#if nt.description}
1323
+ <span class="block truncate text-[11px] text-muted">{nt.description}</span>
1324
+ {/if}
1325
+ </span>
1326
+ </button>
1327
+ {/each}
1328
+ {:else}
1329
+ {#if !variableMenuItems.length}
1330
+ <p class="px-3 py-6 text-center text-xs text-muted">No compatible nodes</p>
1331
+ {/if}
1332
+ {/each}
1333
+ {#if !menuSourcePort}
1334
+ <div class="my-1 h-px bg-border" role="separator"></div>
1335
+ <button
1336
+ type="button"
1337
+ role="menuitem"
1338
+ class="flex w-full items-center justify-between rounded-lg px-2.5 py-2 text-sm font-medium text-primary hover:bg-surface-overlay"
1339
+ onclick={() => {
1340
+ doFitView();
1341
+ closePlaceMenu();
1342
+ }}
1343
+ >
1344
+ Fit view
1345
+ <kbd
1346
+ class="rounded border border-border bg-surface-overlay px-1.5 py-0.5 font-mono text-[10px] text-secondary"
1347
+ >
1348
+ F
1349
+ </kbd>
1350
+ </button>
1351
+ {/if}
1352
+ {/if}
1353
+ </div>
1354
+ </div>
1355
+
1356
+ <style>
1357
+ /* Tailwind `flex` overrides UA popover display:none — hide until open */
1358
+ .node-place-menu {
1359
+ position: fixed;
1360
+ }
1361
+ .node-place-menu:not(:popover-open) {
1362
+ display: none !important;
1363
+ }
1364
+ .node-place-menu:popover-open {
1365
+ inset: unset;
1366
+ display: flex;
1367
+ }
1368
+
1369
+ :global(.svelte-flow) {
1370
+ --xy-node-border-default: transparent;
1371
+ --xy-node-background-color-default: transparent;
1372
+ }
1373
+ :global(.svelte-flow__node-blueprint) {
1374
+ padding: 0 !important;
1375
+ border: none !important;
1376
+ background: transparent !important;
1377
+ box-shadow: none !important;
1378
+ overflow: visible !important;
1379
+ }
1380
+ :global(.svelte-flow__handle) {
1381
+ display: flex !important;
1382
+ align-items: center;
1383
+ justify-content: center;
1384
+ }
1385
+ :global(.svelte-flow__handle-left) {
1386
+ left: 0 !important;
1387
+ }
1388
+ :global(.svelte-flow__handle-right) {
1389
+ right: 0 !important;
1390
+ left: auto !important;
1391
+ }
1392
+ :global(.svelte-flow__minimap) {
1393
+ border-radius: 0.5rem;
1394
+ overflow: hidden;
1395
+ }
1396
+ :global(.svelte-flow__selection) {
1397
+ background: rgba(14, 165, 233, 0.1);
1398
+ border: 1px solid rgba(14, 165, 233, 0.65);
1399
+ }
1400
+ :global(.svelte-flow__nodesselection-rect) {
1401
+ border: 1px solid rgba(14, 165, 233, 0.75);
1402
+ background: rgba(14, 165, 233, 0.04);
1403
+ }
1404
+ /* Keep comment boxes under nodes even when selected / elevated */
1405
+ :global(.svelte-flow__node.bp-flow-comment),
1406
+ :global(.svelte-flow__node-blueprint.bp-flow-comment) {
1407
+ z-index: -1 !important;
1408
+ }
1409
+ </style>