@flowdrop/flowdrop 1.0.0 → 1.1.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 (388) hide show
  1. package/README.md +50 -50
  2. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  3. package/dist/adapters/WorkflowAdapter.js +25 -25
  4. package/dist/adapters/agentspec/AgentSpecAdapter.d.ts +2 -2
  5. package/dist/adapters/agentspec/AgentSpecAdapter.js +133 -122
  6. package/dist/adapters/agentspec/agentAdapter.d.ts +2 -2
  7. package/dist/adapters/agentspec/agentAdapter.js +10 -10
  8. package/dist/adapters/agentspec/autoLayout.d.ts +1 -1
  9. package/dist/adapters/agentspec/autoLayout.js +2 -2
  10. package/dist/adapters/agentspec/componentTypeDefaults.d.ts +1 -1
  11. package/dist/adapters/agentspec/componentTypeDefaults.js +120 -120
  12. package/dist/adapters/agentspec/defaultNodeTypes.d.ts +2 -2
  13. package/dist/adapters/agentspec/defaultNodeTypes.js +307 -307
  14. package/dist/adapters/agentspec/index.d.ts +10 -10
  15. package/dist/adapters/agentspec/index.js +6 -6
  16. package/dist/adapters/agentspec/validator.d.ts +2 -2
  17. package/dist/adapters/agentspec/validator.js +22 -20
  18. package/dist/api/enhanced-client.d.ts +3 -3
  19. package/dist/api/enhanced-client.js +73 -72
  20. package/dist/components/App.svelte +1081 -961
  21. package/dist/components/App.svelte.d.ts +9 -6
  22. package/dist/components/CanvasBanner.stories.svelte +23 -20
  23. package/dist/components/CanvasBanner.stories.svelte.d.ts +1 -1
  24. package/dist/components/CanvasBanner.svelte +46 -46
  25. package/dist/components/ConfigForm.svelte +1164 -1065
  26. package/dist/components/ConfigForm.svelte.d.ts +2 -2
  27. package/dist/components/ConfigModal.svelte +180 -180
  28. package/dist/components/ConfigModal.svelte.d.ts +1 -1
  29. package/dist/components/ConfigPanel.stories.svelte +35 -35
  30. package/dist/components/ConfigPanel.stories.svelte.d.ts +1 -1
  31. package/dist/components/ConfigPanel.svelte +178 -167
  32. package/dist/components/ConfigPanel.svelte.d.ts +1 -1
  33. package/dist/components/ConnectionLine.svelte +25 -25
  34. package/dist/components/EdgeRefresher.svelte +26 -26
  35. package/dist/components/FlowDropEdge.stories.svelte +197 -0
  36. package/dist/components/FlowDropEdge.stories.svelte.d.ts +26 -0
  37. package/dist/components/FlowDropEdge.svelte +168 -0
  38. package/dist/components/FlowDropEdge.svelte.d.ts +4 -0
  39. package/dist/components/FlowDropZone.svelte +63 -60
  40. package/dist/components/FlowDropZone.svelte.d.ts +1 -1
  41. package/dist/components/LoadingSpinner.stories.svelte +19 -19
  42. package/dist/components/LoadingSpinner.stories.svelte.d.ts +1 -1
  43. package/dist/components/LoadingSpinner.svelte +21 -21
  44. package/dist/components/LoadingSpinner.svelte.d.ts +1 -1
  45. package/dist/components/Logo.stories.svelte +13 -13
  46. package/dist/components/Logo.stories.svelte.d.ts +1 -1
  47. package/dist/components/Logo.svelte +101 -95
  48. package/dist/components/LogsSidebar.svelte +553 -546
  49. package/dist/components/LogsSidebar.svelte.d.ts +1 -1
  50. package/dist/components/MarkdownDisplay.stories.svelte +29 -23
  51. package/dist/components/MarkdownDisplay.stories.svelte.d.ts +1 -1
  52. package/dist/components/MarkdownDisplay.svelte +16 -14
  53. package/dist/components/Navbar.stories.svelte +43 -38
  54. package/dist/components/Navbar.stories.svelte.d.ts +1 -1
  55. package/dist/components/Navbar.svelte +760 -706
  56. package/dist/components/Navbar.svelte.d.ts +1 -1
  57. package/dist/components/NodeSidebar.svelte +900 -746
  58. package/dist/components/NodeSidebar.svelte.d.ts +3 -1
  59. package/dist/components/NodeStatusOverlay.stories.svelte +82 -70
  60. package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +1 -1
  61. package/dist/components/NodeStatusOverlay.svelte +295 -280
  62. package/dist/components/NodeStatusOverlay.svelte.d.ts +3 -3
  63. package/dist/components/PipelineStatus.svelte +326 -300
  64. package/dist/components/PipelineStatus.svelte.d.ts +4 -4
  65. package/dist/components/PortCoordinateTracker.svelte +49 -47
  66. package/dist/components/PortCoordinateTracker.svelte.d.ts +1 -1
  67. package/dist/components/ReadOnlyDetails.svelte +156 -156
  68. package/dist/components/SchemaForm.stories.svelte +106 -98
  69. package/dist/components/SchemaForm.stories.svelte.d.ts +1 -1
  70. package/dist/components/SchemaForm.svelte +490 -463
  71. package/dist/components/SchemaForm.svelte.d.ts +2 -2
  72. package/dist/components/SettingsModal.svelte +226 -223
  73. package/dist/components/SettingsModal.svelte.d.ts +1 -1
  74. package/dist/components/SettingsPanel.svelte +637 -601
  75. package/dist/components/SettingsPanel.svelte.d.ts +1 -1
  76. package/dist/components/StatusIcon.stories.svelte +62 -49
  77. package/dist/components/StatusIcon.stories.svelte.d.ts +1 -1
  78. package/dist/components/StatusIcon.svelte +87 -87
  79. package/dist/components/StatusIcon.svelte.d.ts +2 -2
  80. package/dist/components/StatusLabel.stories.svelte +12 -12
  81. package/dist/components/StatusLabel.stories.svelte.d.ts +1 -1
  82. package/dist/components/StatusLabel.svelte +19 -19
  83. package/dist/components/ThemeToggle.stories.svelte +16 -16
  84. package/dist/components/ThemeToggle.stories.svelte.d.ts +1 -1
  85. package/dist/components/ThemeToggle.svelte +180 -169
  86. package/dist/components/ThemeToggle.svelte.d.ts +1 -1
  87. package/dist/components/UniversalNode.svelte +150 -138
  88. package/dist/components/UniversalNode.svelte.d.ts +3 -3
  89. package/dist/components/WorkflowEditor.svelte +1069 -1007
  90. package/dist/components/WorkflowEditor.svelte.d.ts +4 -4
  91. package/dist/components/form/FormArray.svelte +1034 -973
  92. package/dist/components/form/FormArray.svelte.d.ts +1 -1
  93. package/dist/components/form/FormAutocomplete.svelte +1021 -978
  94. package/dist/components/form/FormAutocomplete.svelte.d.ts +1 -1
  95. package/dist/components/form/FormCheckboxGroup.stories.svelte +23 -20
  96. package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +1 -1
  97. package/dist/components/form/FormCheckboxGroup.svelte +136 -136
  98. package/dist/components/form/FormCodeEditor.svelte +452 -434
  99. package/dist/components/form/FormField.svelte +366 -355
  100. package/dist/components/form/FormField.svelte.d.ts +2 -2
  101. package/dist/components/form/FormFieldLight.svelte +400 -384
  102. package/dist/components/form/FormFieldLight.svelte.d.ts +1 -1
  103. package/dist/components/form/FormFieldWrapper.stories.svelte +42 -42
  104. package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +1 -1
  105. package/dist/components/form/FormFieldWrapper.svelte +100 -93
  106. package/dist/components/form/FormFieldWrapper.svelte.d.ts +1 -1
  107. package/dist/components/form/FormFieldset.svelte +108 -108
  108. package/dist/components/form/FormFieldset.svelte.d.ts +2 -2
  109. package/dist/components/form/FormMarkdownEditor.svelte +758 -725
  110. package/dist/components/form/FormNumberField.stories.svelte +25 -25
  111. package/dist/components/form/FormNumberField.stories.svelte.d.ts +1 -1
  112. package/dist/components/form/FormNumberField.svelte +88 -88
  113. package/dist/components/form/FormRangeField.stories.svelte +20 -20
  114. package/dist/components/form/FormRangeField.stories.svelte.d.ts +1 -1
  115. package/dist/components/form/FormRangeField.svelte +234 -226
  116. package/dist/components/form/FormSelect.stories.svelte +38 -38
  117. package/dist/components/form/FormSelect.stories.svelte.d.ts +1 -1
  118. package/dist/components/form/FormSelect.svelte +101 -101
  119. package/dist/components/form/FormSelect.svelte.d.ts +1 -1
  120. package/dist/components/form/FormTemplateEditor.svelte +847 -798
  121. package/dist/components/form/FormTemplateEditor.svelte.d.ts +1 -1
  122. package/dist/components/form/FormTextField.stories.svelte +29 -23
  123. package/dist/components/form/FormTextField.stories.svelte.d.ts +1 -1
  124. package/dist/components/form/FormTextField.svelte +68 -68
  125. package/dist/components/form/FormTextarea.stories.svelte +28 -25
  126. package/dist/components/form/FormTextarea.stories.svelte.d.ts +1 -1
  127. package/dist/components/form/FormTextarea.svelte +74 -74
  128. package/dist/components/form/FormToggle.stories.svelte +23 -20
  129. package/dist/components/form/FormToggle.stories.svelte.d.ts +1 -1
  130. package/dist/components/form/FormToggle.svelte +98 -98
  131. package/dist/components/form/FormUISchemaRenderer.svelte +120 -113
  132. package/dist/components/form/FormUISchemaRenderer.svelte.d.ts +3 -3
  133. package/dist/components/form/index.d.ts +19 -19
  134. package/dist/components/form/index.js +18 -18
  135. package/dist/components/form/templateAutocomplete.d.ts +2 -2
  136. package/dist/components/form/templateAutocomplete.js +64 -55
  137. package/dist/components/form/types.d.ts +6 -6
  138. package/dist/components/form/types.js +9 -4
  139. package/dist/components/icons/AlertCircleIcon.svelte +11 -0
  140. package/dist/components/icons/AlertCircleIcon.svelte.d.ts +26 -0
  141. package/dist/components/icons/CogIcon.svelte +11 -0
  142. package/dist/components/icons/CogIcon.svelte.d.ts +26 -0
  143. package/dist/components/interrupt/ChoicePrompt.stories.svelte +54 -38
  144. package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +1 -1
  145. package/dist/components/interrupt/ChoicePrompt.svelte +407 -383
  146. package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +1 -1
  147. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +48 -48
  148. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +1 -1
  149. package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -274
  150. package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +1 -1
  151. package/dist/components/interrupt/FormPrompt.svelte +223 -218
  152. package/dist/components/interrupt/FormPrompt.svelte.d.ts +1 -1
  153. package/dist/components/interrupt/InterruptBubble.svelte +617 -583
  154. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +2 -2
  155. package/dist/components/interrupt/ReviewPrompt.stories.svelte +66 -56
  156. package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +1 -1
  157. package/dist/components/interrupt/ReviewPrompt.svelte +861 -841
  158. package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +1 -1
  159. package/dist/components/interrupt/TextInputPrompt.stories.svelte +38 -33
  160. package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +1 -1
  161. package/dist/components/interrupt/TextInputPrompt.svelte +333 -328
  162. package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +1 -1
  163. package/dist/components/interrupt/index.d.ts +5 -5
  164. package/dist/components/interrupt/index.js +5 -5
  165. package/dist/components/layouts/MainLayout.svelte +724 -691
  166. package/dist/components/layouts/MainLayout.svelte.d.ts +6 -6
  167. package/dist/components/nodes/GatewayNode.stories.svelte +100 -99
  168. package/dist/components/nodes/GatewayNode.svelte +605 -571
  169. package/dist/components/nodes/GatewayNode.svelte.d.ts +3 -3
  170. package/dist/components/nodes/IdeaNode.stories.svelte +44 -43
  171. package/dist/components/nodes/IdeaNode.svelte +451 -437
  172. package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
  173. package/dist/components/nodes/NotesNode.stories.svelte +65 -64
  174. package/dist/components/nodes/NotesNode.svelte +380 -369
  175. package/dist/components/nodes/NotesNode.svelte.d.ts +1 -1
  176. package/dist/components/nodes/SimpleNode.stories.svelte +145 -144
  177. package/dist/components/nodes/SimpleNode.svelte +486 -424
  178. package/dist/components/nodes/SimpleNode.svelte.d.ts +1 -1
  179. package/dist/components/nodes/SquareNode.stories.svelte +73 -73
  180. package/dist/components/nodes/SquareNode.svelte +439 -380
  181. package/dist/components/nodes/SquareNode.svelte.d.ts +1 -1
  182. package/dist/components/nodes/TerminalNode.stories.svelte +13 -13
  183. package/dist/components/nodes/TerminalNode.svelte +709 -670
  184. package/dist/components/nodes/TerminalNode.svelte.d.ts +1 -1
  185. package/dist/components/nodes/ToolNode.stories.svelte +181 -180
  186. package/dist/components/nodes/ToolNode.svelte +505 -447
  187. package/dist/components/nodes/ToolNode.svelte.d.ts +1 -1
  188. package/dist/components/nodes/WorkflowNode.stories.svelte +70 -46
  189. package/dist/components/nodes/WorkflowNode.svelte +621 -551
  190. package/dist/components/nodes/WorkflowNode.svelte.d.ts +3 -3
  191. package/dist/components/playground/ChatPanel.svelte +945 -889
  192. package/dist/components/playground/ExecutionLogs.svelte +495 -472
  193. package/dist/components/playground/InputCollector.svelte +449 -428
  194. package/dist/components/playground/MessageBubble.stories.svelte +47 -47
  195. package/dist/components/playground/MessageBubble.stories.svelte.d.ts +1 -1
  196. package/dist/components/playground/MessageBubble.svelte +626 -610
  197. package/dist/components/playground/MessageBubble.svelte.d.ts +1 -1
  198. package/dist/components/playground/Playground.svelte +1088 -1057
  199. package/dist/components/playground/Playground.svelte.d.ts +3 -3
  200. package/dist/components/playground/PlaygroundModal.svelte +208 -204
  201. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
  202. package/dist/components/playground/SessionManager.svelte +527 -521
  203. package/dist/components/playground/SessionManager.svelte.d.ts +1 -1
  204. package/dist/config/agentSpecEndpoints.d.ts +1 -1
  205. package/dist/config/agentSpecEndpoints.js +20 -20
  206. package/dist/config/constants.d.ts +8 -0
  207. package/dist/config/constants.js +10 -2
  208. package/dist/config/defaultCategories.d.ts +1 -1
  209. package/dist/config/defaultCategories.js +86 -86
  210. package/dist/config/defaultPortConfig.d.ts +1 -1
  211. package/dist/config/defaultPortConfig.js +144 -144
  212. package/dist/config/endpoints.d.ts +4 -4
  213. package/dist/config/endpoints.js +65 -65
  214. package/dist/config/runtimeConfig.d.ts +2 -2
  215. package/dist/config/runtimeConfig.js +8 -8
  216. package/dist/core/index.d.ts +63 -59
  217. package/dist/core/index.js +35 -33
  218. package/dist/display/index.d.ts +2 -2
  219. package/dist/display/index.js +2 -2
  220. package/dist/editor/index.d.ts +62 -62
  221. package/dist/editor/index.js +53 -53
  222. package/dist/form/code.d.ts +5 -5
  223. package/dist/form/code.js +14 -14
  224. package/dist/form/fieldRegistry.d.ts +3 -3
  225. package/dist/form/fieldRegistry.js +11 -9
  226. package/dist/form/full.d.ts +8 -8
  227. package/dist/form/full.js +9 -9
  228. package/dist/form/index.d.ts +18 -18
  229. package/dist/form/index.js +16 -16
  230. package/dist/form/markdown.d.ts +4 -4
  231. package/dist/form/markdown.js +8 -8
  232. package/dist/helpers/proximityConnect.d.ts +3 -3
  233. package/dist/helpers/proximityConnect.js +34 -32
  234. package/dist/helpers/workflowEditorHelper.d.ts +5 -5
  235. package/dist/helpers/workflowEditorHelper.js +108 -96
  236. package/dist/index.d.ts +6 -6
  237. package/dist/index.js +6 -6
  238. package/dist/mocks/app-environment.js +2 -2
  239. package/dist/mocks/app-forms.js +9 -9
  240. package/dist/mocks/app-navigation.js +11 -11
  241. package/dist/mocks/app-stores.js +8 -8
  242. package/dist/playground/index.d.ts +19 -19
  243. package/dist/playground/index.js +16 -16
  244. package/dist/playground/mount.d.ts +3 -3
  245. package/dist/playground/mount.js +24 -24
  246. package/dist/registry/builtinFormats.js +13 -13
  247. package/dist/registry/builtinNodes.d.ts +2 -2
  248. package/dist/registry/builtinNodes.js +77 -77
  249. package/dist/registry/index.d.ts +4 -4
  250. package/dist/registry/index.js +4 -4
  251. package/dist/registry/nodeComponentRegistry.d.ts +8 -8
  252. package/dist/registry/nodeComponentRegistry.js +11 -9
  253. package/dist/registry/plugin.d.ts +2 -2
  254. package/dist/registry/plugin.js +11 -11
  255. package/dist/registry/workflowFormatRegistry.d.ts +3 -3
  256. package/dist/registry/workflowFormatRegistry.js +2 -2
  257. package/dist/schema/index.d.ts +1 -1
  258. package/dist/schema/index.js +2 -2
  259. package/dist/schemas/v1/workflow.schema.json +22 -107
  260. package/dist/services/agentSpecExecutionService.d.ts +3 -3
  261. package/dist/services/agentSpecExecutionService.js +59 -55
  262. package/dist/services/api.d.ts +18 -4
  263. package/dist/services/api.js +46 -43
  264. package/dist/services/apiVariableService.d.ts +1 -1
  265. package/dist/services/apiVariableService.js +41 -34
  266. package/dist/services/autoSaveService.js +8 -8
  267. package/dist/services/categoriesApi.d.ts +2 -2
  268. package/dist/services/categoriesApi.js +8 -8
  269. package/dist/services/draftStorage.d.ts +1 -1
  270. package/dist/services/draftStorage.js +11 -11
  271. package/dist/services/dynamicSchemaService.d.ts +1 -1
  272. package/dist/services/dynamicSchemaService.js +41 -39
  273. package/dist/services/globalSave.d.ts +2 -2
  274. package/dist/services/globalSave.js +53 -42
  275. package/dist/services/historyService.d.ts +1 -1
  276. package/dist/services/historyService.js +8 -8
  277. package/dist/services/interruptService.d.ts +1 -1
  278. package/dist/services/interruptService.js +35 -29
  279. package/dist/services/nodeExecutionService.d.ts +1 -1
  280. package/dist/services/nodeExecutionService.js +45 -44
  281. package/dist/services/playgroundService.d.ts +1 -1
  282. package/dist/services/playgroundService.js +29 -29
  283. package/dist/services/portConfigApi.d.ts +2 -2
  284. package/dist/services/portConfigApi.js +8 -8
  285. package/dist/services/settingsService.d.ts +2 -2
  286. package/dist/services/settingsService.js +25 -19
  287. package/dist/services/toastService.d.ts +4 -4
  288. package/dist/services/toastService.js +33 -33
  289. package/dist/services/variableService.d.ts +1 -1
  290. package/dist/services/variableService.js +36 -36
  291. package/dist/services/workflowStorage.d.ts +2 -2
  292. package/dist/services/workflowStorage.js +13 -13
  293. package/dist/settings/index.d.ts +7 -7
  294. package/dist/settings/index.js +6 -6
  295. package/dist/skins/default.d.ts +2 -0
  296. package/dist/skins/default.js +1 -0
  297. package/dist/skins/index.d.ts +13 -0
  298. package/dist/skins/index.js +30 -0
  299. package/dist/skins/slate.d.ts +2 -0
  300. package/dist/skins/slate.js +78 -0
  301. package/dist/stores/categoriesStore.svelte.d.ts +1 -1
  302. package/dist/stores/categoriesStore.svelte.js +5 -5
  303. package/dist/stores/editorStateMachine.svelte.d.ts +2 -2
  304. package/dist/stores/editorStateMachine.svelte.js +65 -33
  305. package/dist/stores/historyStore.svelte.d.ts +4 -4
  306. package/dist/stores/historyStore.svelte.js +4 -4
  307. package/dist/stores/interruptStore.svelte.d.ts +3 -3
  308. package/dist/stores/interruptStore.svelte.js +21 -21
  309. package/dist/stores/playgroundStore.svelte.d.ts +2 -2
  310. package/dist/stores/playgroundStore.svelte.js +25 -18
  311. package/dist/stores/portCoordinateStore.svelte.d.ts +2 -2
  312. package/dist/stores/portCoordinateStore.svelte.js +15 -8
  313. package/dist/stores/settingsStore.svelte.d.ts +2 -2
  314. package/dist/stores/settingsStore.svelte.js +62 -57
  315. package/dist/stores/workflowStore.svelte.d.ts +3 -3
  316. package/dist/stores/workflowStore.svelte.js +50 -47
  317. package/dist/stories/CanvasDecorator.svelte +35 -32
  318. package/dist/stories/CanvasDecorator.svelte.d.ts +2 -2
  319. package/dist/stories/EdgeDecorator.svelte +125 -0
  320. package/dist/stories/EdgeDecorator.svelte.d.ts +17 -0
  321. package/dist/stories/NodeDecorator.svelte +59 -53
  322. package/dist/stories/NodeDecorator.svelte.d.ts +1 -1
  323. package/dist/stories/utils.d.ts +2 -2
  324. package/dist/stories/utils.js +105 -67
  325. package/dist/styles/base.css +599 -595
  326. package/dist/styles/toast.css +14 -14
  327. package/dist/styles/tokens.css +409 -378
  328. package/dist/svelte-app.d.ts +9 -9
  329. package/dist/svelte-app.js +39 -39
  330. package/dist/themes/default.d.ts +2 -0
  331. package/dist/themes/default.js +9 -0
  332. package/dist/themes/index.d.ts +13 -0
  333. package/dist/themes/index.js +44 -0
  334. package/dist/themes/minimal.d.ts +2 -0
  335. package/dist/themes/minimal.js +11 -0
  336. package/dist/types/agentspec.d.ts +18 -18
  337. package/dist/types/agentspec.js +2 -2
  338. package/dist/types/auth.d.ts +1 -1
  339. package/dist/types/auth.js +6 -6
  340. package/dist/types/config.d.ts +6 -6
  341. package/dist/types/events.d.ts +2 -2
  342. package/dist/types/events.js +2 -2
  343. package/dist/types/index.d.ts +32 -32
  344. package/dist/types/index.js +6 -6
  345. package/dist/types/interrupt.d.ts +6 -6
  346. package/dist/types/interrupt.js +21 -21
  347. package/dist/types/interruptState.d.ts +12 -12
  348. package/dist/types/interruptState.js +66 -66
  349. package/dist/types/playground.d.ts +7 -7
  350. package/dist/types/playground.js +14 -14
  351. package/dist/types/settings.d.ts +5 -3
  352. package/dist/types/settings.js +25 -18
  353. package/dist/types/skin.d.ts +31 -0
  354. package/dist/types/skin.js +1 -0
  355. package/dist/types/theme.d.ts +35 -0
  356. package/dist/types/theme.js +1 -0
  357. package/dist/types/uischema.d.ts +4 -4
  358. package/dist/types/uischema.js +3 -3
  359. package/dist/utils/colors.d.ts +1 -1
  360. package/dist/utils/colors.js +97 -95
  361. package/dist/utils/config.d.ts +2 -2
  362. package/dist/utils/config.js +48 -48
  363. package/dist/utils/connections.d.ts +2 -2
  364. package/dist/utils/connections.js +15 -15
  365. package/dist/utils/errors.js +3 -3
  366. package/dist/utils/fetchWithAuth.d.ts +1 -1
  367. package/dist/utils/fetchWithAuth.js +2 -2
  368. package/dist/utils/handleIds.d.ts +2 -2
  369. package/dist/utils/handleIds.js +8 -8
  370. package/dist/utils/handlePositioning.d.ts +1 -1
  371. package/dist/utils/handlePositioning.js +2 -2
  372. package/dist/utils/icons.d.ts +1 -1
  373. package/dist/utils/icons.js +74 -74
  374. package/dist/utils/logger.d.ts +1 -1
  375. package/dist/utils/logger.js +7 -7
  376. package/dist/utils/nodeStatus.d.ts +1 -1
  377. package/dist/utils/nodeStatus.js +48 -48
  378. package/dist/utils/nodeTypes.d.ts +1 -1
  379. package/dist/utils/nodeTypes.js +21 -20
  380. package/dist/utils/nodeWrapper.d.ts +7 -7
  381. package/dist/utils/nodeWrapper.js +21 -19
  382. package/dist/utils/performanceUtils.d.ts +1 -1
  383. package/dist/utils/performanceUtils.js +2 -1
  384. package/dist/utils/sanitize.js +1 -1
  385. package/dist/utils/uischema.d.ts +2 -2
  386. package/dist/utils/uischema.js +8 -8
  387. package/dist/utils/validation.js +20 -8
  388. package/package.json +296 -291
@@ -9,682 +9,721 @@
9
9
  -->
10
10
 
11
11
  <script lang="ts">
12
- import { Position, Handle } from '@xyflow/svelte';
13
- import type { ConfigValues, NodeMetadata, NodeExtensions, NodePort } from '../../types/index.js';
14
- import Icon from '@iconify/svelte';
15
- import { getDataTypeColor, getCategoryColorToken } from '../../utils/colors.js';
16
- import { getNodeIcon } from '../../utils/icons.js';
17
- import { getCircleHandlePosition } from '../../utils/handlePositioning.js';
18
- import { getConnectedHandles } from '../../stores/workflowStore.svelte.js';
19
-
20
- /**
21
- * Terminal node variant types
22
- */
23
- type TerminalVariant = 'start' | 'end' | 'exit';
24
-
25
- /**
26
- * Configuration for each terminal variant
27
- */
28
- interface VariantConfig {
29
- /** Default icon for this variant */
30
- icon: string;
31
- /** Default color for this variant */
32
- color: string;
33
- /** Default label for this variant */
34
- label: string;
35
- /** Whether this variant has input handles */
36
- hasInputs: boolean;
37
- /** Whether this variant has output handles */
38
- hasOutputs: boolean;
39
- }
40
-
41
- /**
42
- * Variant configurations mapping
43
- */
44
- const VARIANT_CONFIGS: Record<TerminalVariant, VariantConfig> = {
45
- start: {
46
- icon: 'mdi:play-circle',
47
- color: 'var(--fd-node-emerald)',
48
- label: 'Start',
49
- hasInputs: false,
50
- hasOutputs: true
51
- },
52
- end: {
53
- icon: 'mdi:stop-circle',
54
- color: 'var(--fd-node-slate)',
55
- label: 'End',
56
- hasInputs: true,
57
- hasOutputs: false
58
- },
59
- exit: {
60
- icon: 'mdi:close-circle',
61
- color: 'var(--fd-node-red)',
62
- label: 'Exit',
63
- hasInputs: true,
64
- hasOutputs: false
65
- }
66
- };
67
-
68
- const props = $props<{
69
- data: {
70
- label: string;
71
- config: ConfigValues;
72
- metadata: NodeMetadata;
73
- nodeId?: string;
74
- extensions?: NodeExtensions;
75
- onConfigOpen?: (node: {
76
- id: string;
77
- type: string;
78
- data: { label: string; config: ConfigValues; metadata: NodeMetadata };
79
- }) => void;
80
- };
81
- selected?: boolean;
82
- isProcessing?: boolean;
83
- isError?: boolean;
84
- }>();
85
-
86
- /**
87
- * Determine terminal variant from config or metadata
88
- * Priority: config.variant > metadata tag detection > default to "start"
89
- */
90
- function getVariant(): TerminalVariant {
91
- // Check config first
92
- const configVariant = props.data.config?.variant as string | undefined;
93
- if (configVariant && configVariant in VARIANT_CONFIGS) {
94
- return configVariant as TerminalVariant;
95
- }
96
-
97
- // Check metadata tags for variant hints
98
- const tags = props.data.metadata?.tags || [];
99
- if (tags.includes('start') || tags.includes('entry')) {
100
- return 'start';
101
- }
102
- if (tags.includes('exit') || tags.includes('abort') || tags.includes('error')) {
103
- return 'exit';
104
- }
105
- if (tags.includes('end') || tags.includes('finish') || tags.includes('complete')) {
106
- return 'end';
107
- }
108
-
109
- // Check metadata id/name for hints
110
- const idLower = (props.data.metadata?.id || '').toLowerCase();
111
- const nameLower = (props.data.metadata?.name || '').toLowerCase();
112
- if (idLower.includes('start') || nameLower.includes('start')) {
113
- return 'start';
114
- }
115
- if (
116
- idLower.includes('exit') ||
117
- idLower.includes('abort') ||
118
- nameLower.includes('exit') ||
119
- nameLower.includes('abort')
120
- ) {
121
- return 'exit';
122
- }
123
- if (idLower.includes('end') || nameLower.includes('end')) {
124
- return 'end';
125
- }
126
-
127
- // Default to start
128
- return 'start';
129
- }
130
-
131
- let variant = $derived(getVariant());
132
-
133
- /**
134
- * Get current variant configuration
135
- */
136
- let variantConfig = $derived(VARIANT_CONFIGS[variant]);
137
-
138
- /**
139
- * Get the hideUnconnectedHandles setting from extensions
140
- * Merges node type defaults with instance overrides
141
- */
142
- const hideUnconnectedHandles = $derived(() => {
143
- const typeDefault = props.data.metadata?.extensions?.ui?.hideUnconnectedHandles ?? false;
144
- const instanceOverride = props.data.extensions?.ui?.hideUnconnectedHandles;
145
- return instanceOverride ?? typeDefault;
146
- });
147
-
148
- /**
149
- * Check if a port should be visible based on connection state and settings
150
- */
151
- function isPortVisible(port: NodePort, type: 'input' | 'output'): boolean {
152
- if (!hideUnconnectedHandles()) {
153
- return true;
154
- }
155
- if (port.required) {
156
- return true;
157
- }
158
- const handleId = `${props.data.nodeId}-${type}-${port.id}`;
159
- return getConnectedHandles().has(handleId);
160
- }
161
-
162
- /**
163
- * Get icon using the same resolution as WorkflowNode
164
- * Uses getNodeIcon utility with category fallback, or variant default
165
- */
166
- let terminalIcon = $derived(
167
- props.data.metadata?.icon
168
- ? getNodeIcon(props.data.metadata.icon, props.data.metadata?.category)
169
- : variantConfig.icon
170
- );
171
-
172
- /**
173
- * Get color using category-based color tokens for consistency
174
- * Falls back to variant default color if category not available
175
- */
176
- let terminalColor = $derived(
177
- props.data.metadata?.category
178
- ? getCategoryColorToken(props.data.metadata.category)
179
- : variantConfig.color
180
- );
181
-
182
- /**
183
- * Instance-specific title override from config.
184
- * Falls back to the original label if not set.
185
- * This allows users to customize the node title per-instance via config.
186
- */
187
- const displayTitle = $derived(
188
- (props.data.config?.instanceTitle as string) ||
189
- props.data.label ||
190
- props.data.metadata?.name ||
191
- variantConfig.label
192
- );
193
-
194
- /**
195
- * Instance-specific description override from config.
196
- * Falls back to the metadata description if not set.
197
- * This allows users to customize the node description per-instance via config.
198
- */
199
- const displayDescription = $derived(
200
- (props.data.config?.instanceDescription as string) || props.data.metadata?.description || ''
201
- );
202
-
203
- /**
204
- * Check if metadata explicitly defines inputs (including empty array)
205
- * This allows API to control ports:
206
- * - undefined: use variant default
207
- * - []: explicitly no inputs
208
- * - [{...}]: use these inputs
209
- */
210
- let hasExplicitInputs = $derived(Array.isArray(props.data.metadata?.inputs));
211
-
212
- /**
213
- * Check if metadata explicitly defines outputs (including empty array)
214
- */
215
- let hasExplicitOutputs = $derived(Array.isArray(props.data.metadata?.outputs));
216
-
217
- /**
218
- * Default trigger input port for end/exit nodes
219
- */
220
- const DEFAULT_INPUT_PORT = {
221
- id: 'trigger',
222
- name: 'Trigger',
223
- type: 'input' as const,
224
- dataType: 'trigger',
225
- description: 'Workflow trigger input'
226
- };
227
-
228
- /**
229
- * Default trigger output port for start nodes
230
- */
231
- const DEFAULT_OUTPUT_PORT = {
232
- id: 'trigger',
233
- name: 'Trigger',
234
- type: 'output' as const,
235
- dataType: 'trigger',
236
- description: 'Workflow trigger output'
237
- };
238
-
239
- /**
240
- * Get input ports from metadata or create default trigger input
241
- * Priority:
242
- * 1. If metadata.inputs is defined (even empty array), use it exactly
243
- * 2. Otherwise, use variant default (trigger port for end/exit)
244
- */
245
- let inputPorts = $derived(
246
- hasExplicitInputs
247
- ? props.data.metadata.inputs
248
- : variantConfig.hasInputs
249
- ? [DEFAULT_INPUT_PORT]
250
- : []
251
- );
252
-
253
- /**
254
- * Get output ports from metadata or create default trigger output
255
- * Priority:
256
- * 1. If metadata.outputs is defined (even empty array), use it exactly
257
- * 2. Otherwise, use variant default (trigger port for start)
258
- */
259
- let outputPorts = $derived(
260
- hasExplicitOutputs
261
- ? props.data.metadata.outputs
262
- : variantConfig.hasOutputs
263
- ? [DEFAULT_OUTPUT_PORT]
264
- : []
265
- );
266
-
267
- /**
268
- * Visible input ports filtered by hideUnconnectedHandles setting
269
- */
270
- let visibleInputPorts = $derived(
271
- inputPorts.filter((port: NodePort) => isPortVisible(port, 'input'))
272
- );
273
-
274
- /**
275
- * Visible output ports filtered by hideUnconnectedHandles setting
276
- */
277
- let visibleOutputPorts = $derived(
278
- outputPorts.filter((port: NodePort) => isPortVisible(port, 'output'))
279
- );
280
-
281
- /**
282
- * Determine if we should show inputs based on visible ports
283
- */
284
- let showInputs = $derived(visibleInputPorts.length > 0);
285
-
286
- /**
287
- * Determine if we should show outputs based on visible ports
288
- */
289
- let showOutputs = $derived(visibleOutputPorts.length > 0);
290
-
291
- /**
292
- * Handle configuration sidebar - using global ConfigSidebar
293
- */
294
- function openConfigSidebar(): void {
295
- if (props.data.onConfigOpen) {
296
- const nodeForConfig = {
297
- id: props.data.nodeId || 'unknown',
298
- type: 'terminal',
299
- data: props.data
300
- };
301
- props.data.onConfigOpen(nodeForConfig);
302
- }
303
- }
304
-
305
- /**
306
- * Handle double-click to open config
307
- */
308
- function handleDoubleClick(): void {
309
- openConfigSidebar();
310
- }
311
-
312
- /**
313
- * Handle single click - only handle selection
314
- */
315
- function handleClick(): void {
316
- // Node selection is handled by Svelte Flow
317
- }
318
-
319
- /**
320
- * Handle keyboard events for accessibility
321
- */
322
- function handleKeydown(event: KeyboardEvent): void {
323
- if (event.key === 'Enter' || event.key === ' ') {
324
- event.preventDefault();
325
- handleDoubleClick();
326
- }
327
- }
12
+ import { Position, Handle } from "@xyflow/svelte";
13
+ import type {
14
+ ConfigValues,
15
+ NodeMetadata,
16
+ NodeExtensions,
17
+ NodePort,
18
+ } from "../../types/index.js";
19
+ import Icon from "@iconify/svelte";
20
+ import {
21
+ getDataTypeColor,
22
+ getCategoryColorToken,
23
+ } from "../../utils/colors.js";
24
+ import { getNodeIcon } from "../../utils/icons.js";
25
+ import { getCircleHandlePosition } from "../../utils/handlePositioning.js";
26
+ import { getConnectedHandles } from "../../stores/workflowStore.svelte.js";
27
+
28
+ /**
29
+ * Terminal node variant types
30
+ */
31
+ type TerminalVariant = "start" | "end" | "exit";
32
+
33
+ /**
34
+ * Configuration for each terminal variant
35
+ */
36
+ interface VariantConfig {
37
+ /** Default icon for this variant */
38
+ icon: string;
39
+ /** Default color for this variant */
40
+ color: string;
41
+ /** Default label for this variant */
42
+ label: string;
43
+ /** Whether this variant has input handles */
44
+ hasInputs: boolean;
45
+ /** Whether this variant has output handles */
46
+ hasOutputs: boolean;
47
+ }
48
+
49
+ /**
50
+ * Variant configurations mapping
51
+ */
52
+ const VARIANT_CONFIGS: Record<TerminalVariant, VariantConfig> = {
53
+ start: {
54
+ icon: "mdi:play-circle",
55
+ color: "var(--fd-node-emerald)",
56
+ label: "Start",
57
+ hasInputs: false,
58
+ hasOutputs: true,
59
+ },
60
+ end: {
61
+ icon: "mdi:stop-circle",
62
+ color: "var(--fd-node-slate)",
63
+ label: "End",
64
+ hasInputs: true,
65
+ hasOutputs: false,
66
+ },
67
+ exit: {
68
+ icon: "mdi:close-circle",
69
+ color: "var(--fd-node-red)",
70
+ label: "Exit",
71
+ hasInputs: true,
72
+ hasOutputs: false,
73
+ },
74
+ };
75
+
76
+ const props = $props<{
77
+ data: {
78
+ label: string;
79
+ config: ConfigValues;
80
+ metadata: NodeMetadata;
81
+ nodeId?: string;
82
+ extensions?: NodeExtensions;
83
+ onConfigOpen?: (node: {
84
+ id: string;
85
+ type: string;
86
+ data: { label: string; config: ConfigValues; metadata: NodeMetadata };
87
+ }) => void;
88
+ };
89
+ selected?: boolean;
90
+ isProcessing?: boolean;
91
+ isError?: boolean;
92
+ }>();
93
+
94
+ /**
95
+ * Determine terminal variant from config or metadata
96
+ * Priority: config.variant > metadata tag detection > default to "start"
97
+ */
98
+ function getVariant(): TerminalVariant {
99
+ // Check config first
100
+ const configVariant = props.data.config?.variant as string | undefined;
101
+ if (configVariant && configVariant in VARIANT_CONFIGS) {
102
+ return configVariant as TerminalVariant;
103
+ }
104
+
105
+ // Check metadata tags for variant hints
106
+ const tags = props.data.metadata?.tags || [];
107
+ if (tags.includes("start") || tags.includes("entry")) {
108
+ return "start";
109
+ }
110
+ if (
111
+ tags.includes("exit") ||
112
+ tags.includes("abort") ||
113
+ tags.includes("error")
114
+ ) {
115
+ return "exit";
116
+ }
117
+ if (
118
+ tags.includes("end") ||
119
+ tags.includes("finish") ||
120
+ tags.includes("complete")
121
+ ) {
122
+ return "end";
123
+ }
124
+
125
+ // Check metadata id/name for hints
126
+ const idLower = (props.data.metadata?.id || "").toLowerCase();
127
+ const nameLower = (props.data.metadata?.name || "").toLowerCase();
128
+ if (idLower.includes("start") || nameLower.includes("start")) {
129
+ return "start";
130
+ }
131
+ if (
132
+ idLower.includes("exit") ||
133
+ idLower.includes("abort") ||
134
+ nameLower.includes("exit") ||
135
+ nameLower.includes("abort")
136
+ ) {
137
+ return "exit";
138
+ }
139
+ if (idLower.includes("end") || nameLower.includes("end")) {
140
+ return "end";
141
+ }
142
+
143
+ // Default to start
144
+ return "start";
145
+ }
146
+
147
+ let variant = $derived(getVariant());
148
+
149
+ /**
150
+ * Get current variant configuration
151
+ */
152
+ let variantConfig = $derived(VARIANT_CONFIGS[variant]);
153
+
154
+ /**
155
+ * Get the hideUnconnectedHandles setting from extensions
156
+ * Merges node type defaults with instance overrides
157
+ */
158
+ const hideUnconnectedHandles = $derived(() => {
159
+ const typeDefault =
160
+ props.data.metadata?.extensions?.ui?.hideUnconnectedHandles ?? false;
161
+ const instanceOverride = props.data.extensions?.ui?.hideUnconnectedHandles;
162
+ return instanceOverride ?? typeDefault;
163
+ });
164
+
165
+ /**
166
+ * Check if a port should be visible based on connection state and settings
167
+ */
168
+ function isPortVisible(port: NodePort, type: "input" | "output"): boolean {
169
+ if (!hideUnconnectedHandles()) {
170
+ return true;
171
+ }
172
+ if (port.required) {
173
+ return true;
174
+ }
175
+ const handleId = `${props.data.nodeId}-${type}-${port.id}`;
176
+ return getConnectedHandles().has(handleId);
177
+ }
178
+
179
+ /**
180
+ * Get icon using the same resolution as WorkflowNode
181
+ * Uses getNodeIcon utility with category fallback, or variant default
182
+ */
183
+ let terminalIcon = $derived(
184
+ props.data.metadata?.icon
185
+ ? getNodeIcon(props.data.metadata.icon, props.data.metadata?.category)
186
+ : variantConfig.icon,
187
+ );
188
+
189
+ /**
190
+ * Get color using category-based color tokens for consistency
191
+ * Falls back to variant default color if category not available
192
+ */
193
+ let terminalColor = $derived(
194
+ props.data.metadata?.category
195
+ ? getCategoryColorToken(props.data.metadata.category)
196
+ : variantConfig.color,
197
+ );
198
+
199
+ /**
200
+ * Instance-specific title override from config.
201
+ * Falls back to the original label if not set.
202
+ * This allows users to customize the node title per-instance via config.
203
+ */
204
+ const displayTitle = $derived(
205
+ (props.data.config?.instanceTitle as string) ||
206
+ props.data.label ||
207
+ props.data.metadata?.name ||
208
+ variantConfig.label,
209
+ );
210
+
211
+ /**
212
+ * Instance-specific description override from config.
213
+ * Falls back to the metadata description if not set.
214
+ * This allows users to customize the node description per-instance via config.
215
+ */
216
+ const displayDescription = $derived(
217
+ (props.data.config?.instanceDescription as string) ||
218
+ props.data.metadata?.description ||
219
+ "",
220
+ );
221
+
222
+ /**
223
+ * Check if metadata explicitly defines inputs (including empty array)
224
+ * This allows API to control ports:
225
+ * - undefined: use variant default
226
+ * - []: explicitly no inputs
227
+ * - [{...}]: use these inputs
228
+ */
229
+ let hasExplicitInputs = $derived(Array.isArray(props.data.metadata?.inputs));
230
+
231
+ /**
232
+ * Check if metadata explicitly defines outputs (including empty array)
233
+ */
234
+ let hasExplicitOutputs = $derived(
235
+ Array.isArray(props.data.metadata?.outputs),
236
+ );
237
+
238
+ /**
239
+ * Default trigger input port for end/exit nodes
240
+ */
241
+ const DEFAULT_INPUT_PORT = {
242
+ id: "trigger",
243
+ name: "Trigger",
244
+ type: "input" as const,
245
+ dataType: "trigger",
246
+ description: "Workflow trigger input",
247
+ };
248
+
249
+ /**
250
+ * Default trigger output port for start nodes
251
+ */
252
+ const DEFAULT_OUTPUT_PORT = {
253
+ id: "trigger",
254
+ name: "Trigger",
255
+ type: "output" as const,
256
+ dataType: "trigger",
257
+ description: "Workflow trigger output",
258
+ };
259
+
260
+ /**
261
+ * Get input ports from metadata or create default trigger input
262
+ * Priority:
263
+ * 1. If metadata.inputs is defined (even empty array), use it exactly
264
+ * 2. Otherwise, use variant default (trigger port for end/exit)
265
+ */
266
+ let inputPorts = $derived(
267
+ hasExplicitInputs
268
+ ? props.data.metadata.inputs
269
+ : variantConfig.hasInputs
270
+ ? [DEFAULT_INPUT_PORT]
271
+ : [],
272
+ );
273
+
274
+ /**
275
+ * Get output ports from metadata or create default trigger output
276
+ * Priority:
277
+ * 1. If metadata.outputs is defined (even empty array), use it exactly
278
+ * 2. Otherwise, use variant default (trigger port for start)
279
+ */
280
+ let outputPorts = $derived(
281
+ hasExplicitOutputs
282
+ ? props.data.metadata.outputs
283
+ : variantConfig.hasOutputs
284
+ ? [DEFAULT_OUTPUT_PORT]
285
+ : [],
286
+ );
287
+
288
+ /**
289
+ * Visible input ports filtered by hideUnconnectedHandles setting
290
+ */
291
+ let visibleInputPorts = $derived(
292
+ inputPorts.filter((port: NodePort) => isPortVisible(port, "input")),
293
+ );
294
+
295
+ /**
296
+ * Visible output ports filtered by hideUnconnectedHandles setting
297
+ */
298
+ let visibleOutputPorts = $derived(
299
+ outputPorts.filter((port: NodePort) => isPortVisible(port, "output")),
300
+ );
301
+
302
+ /**
303
+ * Determine if we should show inputs based on visible ports
304
+ */
305
+ let showInputs = $derived(visibleInputPorts.length > 0);
306
+
307
+ /**
308
+ * Determine if we should show outputs based on visible ports
309
+ */
310
+ let showOutputs = $derived(visibleOutputPorts.length > 0);
311
+
312
+ /**
313
+ * Handle configuration sidebar - using global ConfigSidebar
314
+ */
315
+ function openConfigSidebar(): void {
316
+ if (props.data.onConfigOpen) {
317
+ const nodeForConfig = {
318
+ id: props.data.nodeId || "unknown",
319
+ type: "terminal",
320
+ data: props.data,
321
+ };
322
+ props.data.onConfigOpen(nodeForConfig);
323
+ }
324
+ }
325
+
326
+ /**
327
+ * Handle double-click to open config
328
+ */
329
+ function handleDoubleClick(): void {
330
+ openConfigSidebar();
331
+ }
332
+
333
+ /**
334
+ * Handle single click - only handle selection
335
+ */
336
+ function handleClick(): void {
337
+ // Node selection is handled by Svelte Flow
338
+ }
339
+
340
+ /**
341
+ * Handle keyboard events for accessibility
342
+ */
343
+ function handleKeydown(event: KeyboardEvent): void {
344
+ if (event.key === "Enter" || event.key === " ") {
345
+ event.preventDefault();
346
+ handleDoubleClick();
347
+ }
348
+ }
328
349
  </script>
329
350
 
330
351
  <!-- Terminal Node -->
331
352
  <div
332
- class="flowdrop-terminal-node"
333
- class:flowdrop-terminal-node--selected={props.selected}
334
- class:flowdrop-terminal-node--processing={props.isProcessing}
335
- class:flowdrop-terminal-node--error={props.isError}
336
- class:flowdrop-terminal-node--start={variant === 'start'}
337
- class:flowdrop-terminal-node--end={variant === 'end'}
338
- class:flowdrop-terminal-node--exit={variant === 'exit'}
339
- style="--terminal-color: {terminalColor};"
340
- onclick={handleClick}
341
- ondblclick={handleDoubleClick}
342
- onkeydown={handleKeydown}
343
- role="button"
344
- tabindex="0"
345
- aria-label="{variant} node: {displayTitle}"
353
+ class="flowdrop-terminal-node"
354
+ class:flowdrop-terminal-node--selected={props.selected}
355
+ class:flowdrop-terminal-node--processing={props.isProcessing}
356
+ class:flowdrop-terminal-node--error={props.isError}
357
+ class:flowdrop-terminal-node--start={variant === "start"}
358
+ class:flowdrop-terminal-node--end={variant === "end"}
359
+ class:flowdrop-terminal-node--exit={variant === "exit"}
360
+ style="--terminal-color: {terminalColor};"
361
+ onclick={handleClick}
362
+ ondblclick={handleDoubleClick}
363
+ onkeydown={handleKeydown}
364
+ role="button"
365
+ tabindex="0"
366
+ aria-label="{variant} node: {displayTitle}"
346
367
  >
347
- <!-- Config button at top -->
348
- <button
349
- class="flowdrop-terminal-node__config-btn"
350
- onclick={openConfigSidebar}
351
- title="Configure node"
352
- >
353
- <Icon icon="mdi:cog" />
354
- </button>
355
-
356
- <!-- Circle wrapper for proper handle positioning -->
357
- <div class="flowdrop-terminal-node__circle-wrapper">
358
- <!-- Input Handles (for end/exit variants) -->
359
- {#if showInputs}
360
- {#each visibleInputPorts as port, index (`${port.id}-${visibleInputPorts.length}`)}
361
- {@const pos = getCircleHandlePosition(index, visibleInputPorts.length, 'left')}
362
- <Handle
363
- type="target"
364
- position={Position.Left}
365
- style="--fd-handle-fill: {getDataTypeColor(
366
- port.dataType
367
- )}; --fd-handle-border-color: var(--fd-handle-border); left: {pos.left}px; top: {pos.top}px; transform: translate(-50%, -50%); z-index: 30;"
368
- id={`${props.data.nodeId}-input-${port.id}`}
369
- />
370
- {/each}
371
- {/if}
372
-
373
- <!-- Circular content with icon in squircle wrapper -->
374
- <div class="flowdrop-terminal-node__content">
375
- <div class="flowdrop-terminal-node__icon-wrapper" style="--_icon-color: {terminalColor}">
376
- <Icon icon={terminalIcon} class="flowdrop-terminal-node__icon" />
377
- </div>
378
- </div>
379
-
380
- <!-- Output Handles (for start variant) -->
381
- {#if showOutputs}
382
- {#each visibleOutputPorts as port, index (`${port.id}-${visibleOutputPorts.length}`)}
383
- {@const pos = getCircleHandlePosition(index, visibleOutputPorts.length, 'right')}
384
- <Handle
385
- type="source"
386
- position={Position.Right}
387
- id={`${props.data.nodeId}-output-${port.id}`}
388
- style="--fd-handle-fill: {getDataTypeColor(
389
- port.dataType
390
- )}; --fd-handle-border-color: var(--fd-handle-border); left: {pos.left}px; top: {pos.top}px; transform: translate(-50%, -50%); z-index: 30;"
391
- />
392
- {/each}
393
- {/if}
394
- </div>
395
-
396
- <!-- Label and description below the circle -->
397
- <div class="flowdrop-terminal-node__label-container">
398
- <div class="flowdrop-terminal-node__label">
399
- {displayTitle}
400
- </div>
401
- {#if displayDescription}
402
- <div class="flowdrop-terminal-node__description">
403
- {displayDescription}
404
- </div>
405
- {/if}
406
- </div>
407
-
408
- <!-- Processing indicator -->
409
- {#if props.isProcessing}
410
- <div class="flowdrop-terminal-node__processing">
411
- <div class="flowdrop-terminal-node__spinner"></div>
412
- </div>
413
- {/if}
414
-
415
- <!-- Error indicator -->
416
- {#if props.isError}
417
- <div class="flowdrop-terminal-node__error">
418
- <Icon icon="mdi:alert-circle" class="flowdrop-terminal-node__error-icon" />
419
- </div>
420
- {/if}
368
+ <!-- Config button at top -->
369
+ <button
370
+ class="flowdrop-terminal-node__config-btn"
371
+ onclick={openConfigSidebar}
372
+ title="Configure node"
373
+ >
374
+ <Icon icon="mdi:cog" />
375
+ </button>
376
+
377
+ <!-- Circle wrapper for proper handle positioning -->
378
+ <div class="flowdrop-terminal-node__circle-wrapper">
379
+ <!-- Input Handles (for end/exit variants) -->
380
+ {#if showInputs}
381
+ {#each visibleInputPorts as port, index (`${port.id}-${visibleInputPorts.length}`)}
382
+ {@const pos = getCircleHandlePosition(
383
+ index,
384
+ visibleInputPorts.length,
385
+ "left",
386
+ )}
387
+ <Handle
388
+ type="target"
389
+ position={Position.Left}
390
+ style="--fd-handle-fill: {getDataTypeColor(
391
+ port.dataType,
392
+ )}; --fd-handle-border-color: var(--fd-handle-border); left: {pos.left}px; top: {pos.top}px; transform: translate(-50%, -50%); z-index: 30;"
393
+ id={`${props.data.nodeId}-input-${port.id}`}
394
+ />
395
+ {/each}
396
+ {/if}
397
+
398
+ <!-- Circular content with icon in squircle wrapper -->
399
+ <div class="flowdrop-terminal-node__content">
400
+ <div
401
+ class="flowdrop-terminal-node__icon-wrapper"
402
+ style="--_icon-color: {terminalColor}"
403
+ >
404
+ <Icon icon={terminalIcon} class="flowdrop-terminal-node__icon" />
405
+ </div>
406
+ </div>
407
+
408
+ <!-- Output Handles (for start variant) -->
409
+ {#if showOutputs}
410
+ {#each visibleOutputPorts as port, index (`${port.id}-${visibleOutputPorts.length}`)}
411
+ {@const pos = getCircleHandlePosition(
412
+ index,
413
+ visibleOutputPorts.length,
414
+ "right",
415
+ )}
416
+ <Handle
417
+ type="source"
418
+ position={Position.Right}
419
+ id={`${props.data.nodeId}-output-${port.id}`}
420
+ style="--fd-handle-fill: {getDataTypeColor(
421
+ port.dataType,
422
+ )}; --fd-handle-border-color: var(--fd-handle-border); left: {pos.left}px; top: {pos.top}px; transform: translate(-50%, -50%); z-index: 30;"
423
+ />
424
+ {/each}
425
+ {/if}
426
+ </div>
427
+
428
+ <!-- Label and description below the circle -->
429
+ <div class="flowdrop-terminal-node__label-container">
430
+ <div class="flowdrop-terminal-node__label">
431
+ {displayTitle}
432
+ </div>
433
+ {#if displayDescription}
434
+ <div class="flowdrop-terminal-node__description">
435
+ {displayDescription}
436
+ </div>
437
+ {/if}
438
+ </div>
439
+
440
+ <!-- Processing indicator -->
441
+ {#if props.isProcessing}
442
+ <div class="flowdrop-terminal-node__processing">
443
+ <div class="flowdrop-terminal-node__spinner"></div>
444
+ </div>
445
+ {/if}
446
+
447
+ <!-- Error indicator -->
448
+ {#if props.isError}
449
+ <div class="flowdrop-terminal-node__error">
450
+ <Icon
451
+ icon="mdi:alert-circle"
452
+ class="flowdrop-terminal-node__error-icon"
453
+ />
454
+ </div>
455
+ {/if}
421
456
  </div>
422
457
 
423
458
  <style>
424
- .flowdrop-terminal-node {
425
- position: relative;
426
- display: flex;
427
- flex-direction: column;
428
- align-items: center;
429
- gap: var(--fd-space-xs);
430
- cursor: pointer;
431
- transition: all var(--fd-transition-normal);
432
- z-index: 10;
433
- color: var(--fd-foreground);
434
- }
435
-
436
- /* Wrapper for circle and handles - ensures handles are vertically centered to circle */
437
- .flowdrop-terminal-node__circle-wrapper {
438
- position: relative;
439
- display: flex;
440
- align-items: center;
441
- justify-content: center;
442
- }
443
-
444
- .flowdrop-terminal-node__content {
445
- width: var(--fd-node-terminal-size);
446
- height: var(--fd-node-terminal-size);
447
- background-color: var(--fd-background);
448
- border: 3px solid var(--terminal-color, var(--fd-muted-foreground));
449
- border-radius: 50%;
450
- display: flex;
451
- align-items: center;
452
- justify-content: center;
453
- box-shadow: var(--fd-shadow-md);
454
- transition: all var(--fd-transition-normal);
455
- }
456
-
457
- .flowdrop-terminal-node:hover .flowdrop-terminal-node__content {
458
- box-shadow: var(--fd-shadow-lg);
459
- transform: scale(1.05);
460
- }
461
-
462
- .flowdrop-terminal-node--selected .flowdrop-terminal-node__content {
463
- box-shadow:
464
- var(--fd-shadow-lg),
465
- 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
466
- border-color: var(--fd-primary);
467
- }
468
-
469
- .flowdrop-terminal-node--selected:hover .flowdrop-terminal-node__content {
470
- box-shadow:
471
- var(--fd-shadow-lg),
472
- 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
473
- border-color: var(--fd-primary);
474
- transform: scale(1.05);
475
- }
476
-
477
- .flowdrop-terminal-node:focus-visible .flowdrop-terminal-node__content {
478
- outline: 2px solid var(--fd-ring);
479
- outline-offset: 2px;
480
- }
481
-
482
- .flowdrop-terminal-node--processing .flowdrop-terminal-node__content {
483
- opacity: 0.7;
484
- }
485
-
486
- .flowdrop-terminal-node--error .flowdrop-terminal-node__content {
487
- border-color: var(--fd-error) !important;
488
- background-color: var(--fd-error-muted) !important;
489
- }
490
-
491
- /* Variant-specific glow effects */
492
- .flowdrop-terminal-node--start .flowdrop-terminal-node__content {
493
- box-shadow:
494
- 0 4px 6px -1px color-mix(in srgb, var(--fd-success) 20%, transparent),
495
- 0 2px 4px -1px color-mix(in srgb, var(--fd-success) 10%, transparent);
496
- }
497
-
498
- .flowdrop-terminal-node--start:hover .flowdrop-terminal-node__content {
499
- box-shadow:
500
- 0 10px 15px -3px color-mix(in srgb, var(--fd-success) 30%, transparent),
501
- 0 4px 6px -2px color-mix(in srgb, var(--fd-success) 15%, transparent);
502
- }
503
-
504
- .flowdrop-terminal-node--start.flowdrop-terminal-node--selected:hover
505
- .flowdrop-terminal-node__content {
506
- box-shadow:
507
- 0 10px 15px -3px color-mix(in srgb, var(--fd-success) 30%, transparent),
508
- 0 4px 6px -2px color-mix(in srgb, var(--fd-success) 15%, transparent),
509
- 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
510
- }
511
-
512
- .flowdrop-terminal-node--exit .flowdrop-terminal-node__content {
513
- box-shadow:
514
- 0 4px 6px -1px color-mix(in srgb, var(--fd-error) 20%, transparent),
515
- 0 2px 4px -1px color-mix(in srgb, var(--fd-error) 10%, transparent);
516
- }
517
-
518
- .flowdrop-terminal-node--exit:hover .flowdrop-terminal-node__content {
519
- box-shadow:
520
- 0 10px 15px -3px color-mix(in srgb, var(--fd-error) 30%, transparent),
521
- 0 4px 6px -2px color-mix(in srgb, var(--fd-error) 15%, transparent);
522
- }
523
-
524
- .flowdrop-terminal-node--exit.flowdrop-terminal-node--selected:hover
525
- .flowdrop-terminal-node__content {
526
- box-shadow:
527
- 0 10px 15px -3px color-mix(in srgb, var(--fd-error) 30%, transparent),
528
- 0 4px 6px -2px color-mix(in srgb, var(--fd-error) 15%, transparent),
529
- 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
530
- }
531
-
532
- /* Squircle icon wrapper - matching WorkflowNode style */
533
- .flowdrop-terminal-node__icon-wrapper {
534
- display: flex;
535
- align-items: center;
536
- justify-content: center;
537
- width: 2.75rem;
538
- height: 2.75rem;
539
- border-radius: 0.625rem;
540
- background: color-mix(in srgb, var(--_icon-color) var(--fd-node-icon-bg-opacity), transparent);
541
- flex-shrink: 0;
542
- transition: all var(--fd-transition-normal);
543
- }
544
-
545
- .flowdrop-terminal-node:hover .flowdrop-terminal-node__icon-wrapper {
546
- background: color-mix(
547
- in srgb,
548
- var(--_icon-color) var(--fd-node-icon-bg-opacity-hover),
549
- transparent
550
- );
551
- }
552
-
553
- .flowdrop-terminal-node__icon-wrapper :global(.flowdrop-terminal-node__icon) {
554
- width: 1.5rem;
555
- height: 1.5rem;
556
- color: var(--fd-node-icon);
557
- }
558
-
559
- .flowdrop-terminal-node__label-container {
560
- display: flex;
561
- flex-direction: column;
562
- align-items: center;
563
- gap: 0.125rem;
564
- background-color: var(--fd-backdrop);
565
- padding: var(--fd-space-3xs) var(--fd-space-xs);
566
- border-radius: var(--fd-radius-sm);
567
- box-shadow: var(--fd-shadow-sm);
568
- max-width: 140px;
569
- }
570
-
571
- .flowdrop-terminal-node__label {
572
- font-size: var(--fd-text-xs);
573
- font-weight: 500;
574
- color: var(--fd-foreground);
575
- text-align: center;
576
- overflow: hidden;
577
- text-overflow: ellipsis;
578
- white-space: nowrap;
579
- max-width: 100%;
580
- }
581
-
582
- .flowdrop-terminal-node__description {
583
- font-size: 0.625rem;
584
- color: var(--fd-muted-foreground);
585
- text-align: center;
586
- overflow: hidden;
587
- text-overflow: ellipsis;
588
- white-space: nowrap;
589
- max-width: 100%;
590
- }
591
-
592
- .flowdrop-terminal-node__processing {
593
- position: absolute;
594
- top: 1.5rem;
595
- right: 0;
596
- }
597
-
598
- .flowdrop-terminal-node__spinner {
599
- width: 14px;
600
- height: 14px;
601
- border: 2px solid color-mix(in srgb, var(--fd-foreground) 30%, transparent);
602
- border-top: 2px solid var(--terminal-color, var(--fd-muted-foreground));
603
- border-radius: 50%;
604
- animation: spin 1s linear infinite;
605
- }
606
-
607
- .flowdrop-terminal-node__error {
608
- position: absolute;
609
- top: 1.5rem;
610
- right: 0;
611
- color: var(--fd-error);
612
- }
613
-
614
- :global(.flowdrop-terminal-node__error-icon) {
615
- width: 14px;
616
- height: 14px;
617
- }
618
-
619
- /* Config button positioned at top center */
620
- .flowdrop-terminal-node__config-btn {
621
- position: absolute;
622
- top: -1.5rem;
623
- left: 50%;
624
- transform: translateX(-50%);
625
- width: 1.5rem;
626
- height: 1.5rem;
627
- background-color: var(--fd-backdrop);
628
- border: 1px solid var(--fd-border);
629
- border-radius: 50%;
630
- color: var(--fd-muted-foreground);
631
- cursor: pointer;
632
- display: flex;
633
- align-items: center;
634
- justify-content: center;
635
- opacity: 0;
636
- transition: all var(--fd-transition-normal);
637
- backdrop-filter: var(--fd-backdrop-blur);
638
- z-index: 15;
639
- font-size: var(--fd-text-xs);
640
- box-shadow: var(--fd-shadow-sm);
641
- }
642
-
643
- .flowdrop-terminal-node:hover .flowdrop-terminal-node__config-btn {
644
- opacity: 1;
645
- }
646
-
647
- .flowdrop-terminal-node__config-btn:hover {
648
- background-color: var(--fd-muted);
649
- border-color: var(--fd-border-strong);
650
- color: var(--fd-foreground);
651
- transform: translateX(-50%) scale(1.1);
652
- }
653
-
654
- @keyframes spin {
655
- to {
656
- transform: rotate(360deg);
657
- }
658
- }
659
-
660
- /* Handle styles - positioned along circle arc using cos/sin */
661
- :global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle) {
662
- width: 16px !important;
663
- height: 16px !important;
664
- border-radius: 50% !important;
665
- border: none !important;
666
- transition: all var(--fd-transition-normal) !important;
667
- cursor: pointer !important;
668
- z-index: 20 !important;
669
- pointer-events: auto !important;
670
- }
671
-
672
- :global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle:hover) {
673
- transform: translate(-50%, -50%) scale(1.2) !important;
674
- }
675
-
676
- :global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle:focus) {
677
- outline: 2px solid var(--fd-ring) !important;
678
- outline-offset: 2px !important;
679
- }
680
-
681
- /* Also keep node-level handle styles for fallback */
682
- :global(.svelte-flow__node-terminal .svelte-flow__handle) {
683
- z-index: 20 !important;
684
- pointer-events: auto !important;
685
- }
686
-
687
- :global(.svelte-flow__node-terminal .svelte-flow__handle:hover) {
688
- transform: translate(-50%, -50%) scale(1.2) !important;
689
- }
459
+ .flowdrop-terminal-node {
460
+ position: relative;
461
+ display: flex;
462
+ flex-direction: column;
463
+ align-items: center;
464
+ gap: var(--fd-space-xs);
465
+ cursor: pointer;
466
+ transition: all var(--fd-transition-normal);
467
+ z-index: 10;
468
+ color: var(--fd-foreground);
469
+ }
470
+
471
+ /* Wrapper for circle and handles - ensures handles are vertically centered to circle */
472
+ .flowdrop-terminal-node__circle-wrapper {
473
+ position: relative;
474
+ display: flex;
475
+ align-items: center;
476
+ justify-content: center;
477
+ }
478
+
479
+ .flowdrop-terminal-node__content {
480
+ width: var(--fd-node-terminal-size);
481
+ height: var(--fd-node-terminal-size);
482
+ background-color: var(--fd-background);
483
+ border: 3px solid var(--terminal-color, var(--fd-muted-foreground));
484
+ border-radius: 50%;
485
+ display: flex;
486
+ align-items: center;
487
+ justify-content: center;
488
+ box-shadow: var(--fd-shadow-md);
489
+ transition: all var(--fd-transition-normal);
490
+ }
491
+
492
+ .flowdrop-terminal-node:hover .flowdrop-terminal-node__content {
493
+ box-shadow: var(--fd-shadow-lg);
494
+ transform: scale(1.05);
495
+ }
496
+
497
+ .flowdrop-terminal-node--selected .flowdrop-terminal-node__content {
498
+ box-shadow:
499
+ var(--fd-shadow-lg),
500
+ 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
501
+ border-color: var(--fd-primary);
502
+ }
503
+
504
+ .flowdrop-terminal-node--selected:hover .flowdrop-terminal-node__content {
505
+ box-shadow:
506
+ var(--fd-shadow-lg),
507
+ 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
508
+ border-color: var(--fd-primary);
509
+ transform: scale(1.05);
510
+ }
511
+
512
+ .flowdrop-terminal-node:focus-visible .flowdrop-terminal-node__content {
513
+ outline: 2px solid var(--fd-ring);
514
+ outline-offset: 2px;
515
+ }
516
+
517
+ .flowdrop-terminal-node--processing .flowdrop-terminal-node__content {
518
+ opacity: 0.7;
519
+ }
520
+
521
+ .flowdrop-terminal-node--error .flowdrop-terminal-node__content {
522
+ border-color: var(--fd-error) !important;
523
+ background-color: var(--fd-error-muted) !important;
524
+ }
525
+
526
+ /* Variant-specific glow effects */
527
+ .flowdrop-terminal-node--start .flowdrop-terminal-node__content {
528
+ box-shadow:
529
+ 0 4px 6px -1px color-mix(in srgb, var(--fd-success) 20%, transparent),
530
+ 0 2px 4px -1px color-mix(in srgb, var(--fd-success) 10%, transparent);
531
+ }
532
+
533
+ .flowdrop-terminal-node--start:hover .flowdrop-terminal-node__content {
534
+ box-shadow:
535
+ 0 10px 15px -3px color-mix(in srgb, var(--fd-success) 30%, transparent),
536
+ 0 4px 6px -2px color-mix(in srgb, var(--fd-success) 15%, transparent);
537
+ }
538
+
539
+ .flowdrop-terminal-node--start.flowdrop-terminal-node--selected:hover
540
+ .flowdrop-terminal-node__content {
541
+ box-shadow:
542
+ 0 10px 15px -3px color-mix(in srgb, var(--fd-success) 30%, transparent),
543
+ 0 4px 6px -2px color-mix(in srgb, var(--fd-success) 15%, transparent),
544
+ 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
545
+ }
546
+
547
+ .flowdrop-terminal-node--exit .flowdrop-terminal-node__content {
548
+ box-shadow:
549
+ 0 4px 6px -1px color-mix(in srgb, var(--fd-error) 20%, transparent),
550
+ 0 2px 4px -1px color-mix(in srgb, var(--fd-error) 10%, transparent);
551
+ }
552
+
553
+ .flowdrop-terminal-node--exit:hover .flowdrop-terminal-node__content {
554
+ box-shadow:
555
+ 0 10px 15px -3px color-mix(in srgb, var(--fd-error) 30%, transparent),
556
+ 0 4px 6px -2px color-mix(in srgb, var(--fd-error) 15%, transparent);
557
+ }
558
+
559
+ .flowdrop-terminal-node--exit.flowdrop-terminal-node--selected:hover
560
+ .flowdrop-terminal-node__content {
561
+ box-shadow:
562
+ 0 10px 15px -3px color-mix(in srgb, var(--fd-error) 30%, transparent),
563
+ 0 4px 6px -2px color-mix(in srgb, var(--fd-error) 15%, transparent),
564
+ 0 0 0 3px color-mix(in srgb, var(--fd-primary) 50%, transparent);
565
+ }
566
+
567
+ /* Squircle icon wrapper - matching WorkflowNode style */
568
+ .flowdrop-terminal-node__icon-wrapper {
569
+ display: flex;
570
+ align-items: center;
571
+ justify-content: center;
572
+ width: 2.75rem;
573
+ height: 2.75rem;
574
+ border-radius: 0.625rem;
575
+ background: color-mix(
576
+ in srgb,
577
+ var(--_icon-color) var(--fd-node-icon-bg-opacity),
578
+ transparent
579
+ );
580
+ flex-shrink: 0;
581
+ transition: all var(--fd-transition-normal);
582
+ }
583
+
584
+ .flowdrop-terminal-node:hover .flowdrop-terminal-node__icon-wrapper {
585
+ background: color-mix(
586
+ in srgb,
587
+ var(--_icon-color) var(--fd-node-icon-bg-opacity-hover),
588
+ transparent
589
+ );
590
+ }
591
+
592
+ .flowdrop-terminal-node__icon-wrapper :global(.flowdrop-terminal-node__icon) {
593
+ width: 1.5rem;
594
+ height: 1.5rem;
595
+ color: var(--fd-node-icon);
596
+ }
597
+
598
+ .flowdrop-terminal-node__label-container {
599
+ display: flex;
600
+ flex-direction: column;
601
+ align-items: center;
602
+ gap: 0.125rem;
603
+ background-color: var(--fd-backdrop);
604
+ padding: var(--fd-space-3xs) var(--fd-space-xs);
605
+ border-radius: var(--fd-radius-sm);
606
+ box-shadow: var(--fd-shadow-sm);
607
+ max-width: 140px;
608
+ }
609
+
610
+ .flowdrop-terminal-node__label {
611
+ font-size: var(--fd-text-xs);
612
+ font-weight: 500;
613
+ color: var(--fd-foreground);
614
+ text-align: center;
615
+ overflow: hidden;
616
+ text-overflow: ellipsis;
617
+ white-space: nowrap;
618
+ max-width: 100%;
619
+ }
620
+
621
+ .flowdrop-terminal-node__description {
622
+ font-size: 0.625rem;
623
+ color: var(--fd-muted-foreground);
624
+ text-align: center;
625
+ overflow: hidden;
626
+ text-overflow: ellipsis;
627
+ white-space: nowrap;
628
+ max-width: 100%;
629
+ }
630
+
631
+ .flowdrop-terminal-node__processing {
632
+ position: absolute;
633
+ top: 1.5rem;
634
+ right: 0;
635
+ }
636
+
637
+ .flowdrop-terminal-node__spinner {
638
+ width: 14px;
639
+ height: 14px;
640
+ border: 2px solid color-mix(in srgb, var(--fd-foreground) 30%, transparent);
641
+ border-top: 2px solid var(--terminal-color, var(--fd-muted-foreground));
642
+ border-radius: 50%;
643
+ animation: spin 1s linear infinite;
644
+ }
645
+
646
+ .flowdrop-terminal-node__error {
647
+ position: absolute;
648
+ top: 1.5rem;
649
+ right: 0;
650
+ color: var(--fd-error);
651
+ }
652
+
653
+ :global(.flowdrop-terminal-node__error-icon) {
654
+ width: 14px;
655
+ height: 14px;
656
+ }
657
+
658
+ /* Config button positioned at top center */
659
+ .flowdrop-terminal-node__config-btn {
660
+ position: absolute;
661
+ top: -1.5rem;
662
+ left: 50%;
663
+ transform: translateX(-50%);
664
+ width: 1.5rem;
665
+ height: 1.5rem;
666
+ background-color: var(--fd-backdrop);
667
+ border: 1px solid var(--fd-border);
668
+ border-radius: 50%;
669
+ color: var(--fd-muted-foreground);
670
+ cursor: pointer;
671
+ display: flex;
672
+ align-items: center;
673
+ justify-content: center;
674
+ opacity: 0;
675
+ transition: all var(--fd-transition-normal);
676
+ backdrop-filter: var(--fd-backdrop-blur);
677
+ z-index: 15;
678
+ font-size: var(--fd-text-xs);
679
+ box-shadow: var(--fd-shadow-sm);
680
+ }
681
+
682
+ .flowdrop-terminal-node:hover .flowdrop-terminal-node__config-btn {
683
+ opacity: 1;
684
+ }
685
+
686
+ .flowdrop-terminal-node__config-btn:hover {
687
+ background-color: var(--fd-muted);
688
+ border-color: var(--fd-border-strong);
689
+ color: var(--fd-foreground);
690
+ transform: translateX(-50%) scale(1.1);
691
+ }
692
+
693
+ @keyframes spin {
694
+ to {
695
+ transform: rotate(360deg);
696
+ }
697
+ }
698
+
699
+ /* Handle styles - positioned along circle arc using cos/sin */
700
+ :global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle) {
701
+ width: 16px !important;
702
+ height: 16px !important;
703
+ border-radius: 50% !important;
704
+ border: none !important;
705
+ transition: all var(--fd-transition-normal) !important;
706
+ cursor: pointer !important;
707
+ z-index: 20 !important;
708
+ pointer-events: auto !important;
709
+ }
710
+
711
+ :global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle:hover) {
712
+ transform: translate(-50%, -50%) scale(1.2) !important;
713
+ }
714
+
715
+ :global(.flowdrop-terminal-node__circle-wrapper .svelte-flow__handle:focus) {
716
+ outline: 2px solid var(--fd-ring) !important;
717
+ outline-offset: 2px !important;
718
+ }
719
+
720
+ /* Also keep node-level handle styles for fallback */
721
+ :global(.svelte-flow__node-terminal .svelte-flow__handle) {
722
+ z-index: 20 !important;
723
+ pointer-events: auto !important;
724
+ }
725
+
726
+ :global(.svelte-flow__node-terminal .svelte-flow__handle:hover) {
727
+ transform: translate(-50%, -50%) scale(1.2) !important;
728
+ }
690
729
  </style>