@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
@@ -5,758 +5,912 @@
5
5
  -->
6
6
 
7
7
  <script lang="ts">
8
- import type { NodeMetadata, NodeCategory, WorkflowFormat } from '../types/index.js';
9
- import LoadingSpinner from './LoadingSpinner.svelte';
10
- import Icon from '@iconify/svelte';
11
- import { getNodeIcon, getCategoryIcon } from '../utils/icons.js';
12
- import { getCategoryColorToken } from '../utils/colors.js';
13
- import { getCategoryLabel } from '../stores/categoriesStore.svelte.js';
14
- import { SvelteSet } from 'svelte/reactivity';
15
- import { getUiSettings, updateSettings } from '../stores/settingsStore.svelte.js';
16
-
17
- interface Props {
18
- nodes: NodeMetadata[];
19
- selectedCategory?: NodeCategory;
20
- activeFormat?: WorkflowFormat;
21
- }
22
-
23
- let props: Props = $props();
24
- let searchInput = $state('');
25
- // svelte-ignore state_referenced_locally — initial default, user selects interactively
26
- let selectedCategory = $state(props.selectedCategory || 'all');
27
-
28
- /**
29
- * Toggle the sidebar collapsed state
30
- * Persists the new state to settings
31
- */
32
- function toggleSidebar(): void {
33
- updateSettings({ ui: { sidebarCollapsed: !getUiSettings().sidebarCollapsed } });
34
- }
35
-
36
- /**
37
- * Check if a node is compatible with the active workflow format.
38
- * Nodes without formats are universal (compatible with all formats).
39
- */
40
- function isNodeCompatibleWithFormat(node: NodeMetadata): boolean {
41
- if (!props.activeFormat) return true;
42
- if (!node.formats || node.formats.length === 0) return true;
43
- return node.formats.includes(props.activeFormat);
44
- }
45
-
46
- /** Nodes filtered by format compatibility */
47
- let formatCompatibleNodes = $derived((props.nodes || []).filter(isNodeCompatibleWithFormat));
48
-
49
- let filteredNodes = $derived(getFilteredNodes());
50
- let categories = $derived(getCategories());
51
-
52
- /**
53
- * Get all unique categories from node types, preserving API order
54
- * Categories appear in the order their first node appears in the API response
55
- */
56
- function getCategories(): NodeCategory[] {
57
- if (formatCompatibleNodes.length === 0) return [];
58
- // Use a Set to track uniqueness while preserving insertion order
59
- const seen = new SvelteSet<NodeCategory>();
60
- const orderedCategories: NodeCategory[] = [];
61
- for (const node of formatCompatibleNodes) {
62
- if (!seen.has(node.category)) {
63
- seen.add(node.category);
64
- orderedCategories.push(node.category);
65
- }
66
- }
67
- return orderedCategories;
68
- }
69
-
70
- /**
71
- * Filter node types based on search query and selected category
72
- * Preserves the API order - no client-side sorting applied
73
- */
74
- function getFilteredNodes(): NodeMetadata[] {
75
- // Start with format-compatible nodes
76
- let filtered = formatCompatibleNodes;
77
-
78
- // Filter by category
79
- if (selectedCategory !== 'all') {
80
- filtered = filtered.filter((node) => node.category === selectedCategory);
81
- }
82
-
83
- // Filter by search query
84
- if (searchInput.trim()) {
85
- const query = searchInput.toLowerCase();
86
- filtered = filtered.filter(
87
- (node) =>
88
- node.name.toLowerCase().includes(query) ||
89
- node.description.toLowerCase().includes(query) ||
90
- node.tags?.some((tag) => tag.toLowerCase().includes(query))
91
- );
92
- }
93
-
94
- // Return filtered results preserving API order
95
- return filtered;
96
- }
97
-
98
- /**
99
- * Handle node type drag start - creates a new node instance
100
- */
101
- function handleNodeDragStart(event: DragEvent, nodeType: NodeMetadata): void {
102
- if (!event.dataTransfer) return;
103
-
104
- // Extract initial config from configSchema with proper null checks
105
- let initialConfig: Record<string, unknown> = {};
106
- if (
107
- nodeType.configSchema &&
108
- typeof nodeType.configSchema === 'object' &&
109
- nodeType.configSchema.properties &&
110
- typeof nodeType.configSchema.properties === 'object'
111
- ) {
112
- // JSON Schema format - extract defaults
113
- Object.entries(nodeType.configSchema.properties).forEach(([key, prop]) => {
114
- if (prop && typeof prop === 'object' && 'default' in prop) {
115
- initialConfig[key] = prop.default;
116
- }
117
- });
118
- }
119
-
120
- // Create a new node instance from the node type
121
- const newNodeData = {
122
- type: 'node',
123
- nodeType: nodeType.id,
124
- nodeData: {
125
- label: nodeType.name,
126
- config: initialConfig,
127
- metadata: nodeType
128
- }
129
- };
130
-
131
- const jsonData = JSON.stringify(newNodeData);
132
-
133
- // Set the data that SvelteFlow will receive
134
- event.dataTransfer.setData('application/json', jsonData);
135
- event.dataTransfer.setData('text/plain', nodeType.name);
136
- event.dataTransfer.effectAllowed = 'copy';
137
-
138
- // Set drag image
139
- if (event.target) {
140
- const rect = (event.target as HTMLElement).getBoundingClientRect();
141
- event.dataTransfer.setDragImage(event.target as HTMLElement, rect.width / 2, rect.height / 2);
142
- }
143
- }
144
-
145
- /**
146
- * Handle search input change
147
- */
148
- function handleSearchChange(): void {
149
- // Search is handled reactively through the derived filteredNodes
150
- }
151
-
152
- /**
153
- * Handle node click
154
- */
155
- function handleNodeClick(nodeType: NodeMetadata): void {
156
- // Handle node click - could be used for preview or configuration
157
- }
158
-
159
- /**
160
- * Get category display name from the categories store.
161
- * Falls back to auto-capitalizing the category machine name.
162
- */
163
- function getCategoryDisplayName(category: NodeCategory): string {
164
- return getCategoryLabel(category);
165
- }
166
-
167
- /**
168
- * Get node types for category
169
- * Preserves the API order - no client-side sorting applied
170
- */
171
- function getNodesForCategory(category: NodeCategory): NodeMetadata[] {
172
- return formatCompatibleNodes.filter((node) => node.category === category);
173
- }
174
-
175
- /**
176
- * Get filtered nodes for category
177
- */
178
- function getFilteredNodesForCategory(category: NodeCategory): NodeMetadata[] {
179
- let nodes = getNodesForCategory(category);
180
-
181
- // Filter by search query
182
- if (searchInput.trim()) {
183
- const query = searchInput.toLowerCase();
184
- nodes = nodes.filter(
185
- (node) =>
186
- node.name.toLowerCase().includes(query) ||
187
- node.description.toLowerCase().includes(query) ||
188
- node.tags?.some((tag) => tag.toLowerCase().includes(query))
189
- );
190
- }
191
-
192
- return nodes;
193
- }
8
+ import type {
9
+ NodeMetadata,
10
+ NodeCategory,
11
+ WorkflowFormat,
12
+ } from "../types/index.js";
13
+ import LoadingSpinner from "./LoadingSpinner.svelte";
14
+ import Icon from "@iconify/svelte";
15
+ import { getNodeIcon, getCategoryIcon } from "../utils/icons.js";
16
+ import { getCategoryColorToken } from "../utils/colors.js";
17
+ import { getCategoryLabel } from "../stores/categoriesStore.svelte.js";
18
+ import { SvelteSet } from "svelte/reactivity";
19
+ import {
20
+ getUiSettings,
21
+ updateSettings,
22
+ } from "../stores/settingsStore.svelte.js";
23
+
24
+ interface Props {
25
+ nodes: NodeMetadata[];
26
+ selectedCategory?: NodeCategory;
27
+ activeFormat?: WorkflowFormat;
28
+ /** Whether category <details> accordions start open (card mode). Default: false */
29
+ categoriesDefaultOpen?: boolean;
30
+ }
31
+
32
+ let props: Props = $props();
33
+ let searchInput = $state("");
34
+ // svelte-ignore state_referenced_locally — initial default, user selects interactively
35
+ let selectedCategory = $state(props.selectedCategory || "all");
36
+
37
+ /**
38
+ * Toggle the sidebar collapsed state
39
+ * Persists the new state to settings
40
+ */
41
+ function toggleSidebar(): void {
42
+ updateSettings({
43
+ ui: { sidebarCollapsed: !getUiSettings().sidebarCollapsed },
44
+ });
45
+ }
46
+
47
+ /**
48
+ * Check if a node is compatible with the active workflow format.
49
+ * Nodes without formats are universal (compatible with all formats).
50
+ */
51
+ function isNodeCompatibleWithFormat(node: NodeMetadata): boolean {
52
+ if (!props.activeFormat) return true;
53
+ if (!node.formats || node.formats.length === 0) return true;
54
+ return node.formats.includes(props.activeFormat);
55
+ }
56
+
57
+ /** Effective collapsed state driven by persisted UI setting */
58
+ let isCollapsed = $derived(getUiSettings().sidebarCollapsed);
59
+
60
+ /** Nodes filtered by format compatibility */
61
+ let formatCompatibleNodes = $derived(
62
+ (props.nodes || []).filter(isNodeCompatibleWithFormat),
63
+ );
64
+
65
+ let filteredNodes = $derived(getFilteredNodes());
66
+ let categories = $derived(getCategories());
67
+
68
+ /**
69
+ * Get all unique categories from node types, preserving API order
70
+ * Categories appear in the order their first node appears in the API response
71
+ */
72
+ function getCategories(): NodeCategory[] {
73
+ if (formatCompatibleNodes.length === 0) return [];
74
+ // Use a Set to track uniqueness while preserving insertion order
75
+ const seen = new SvelteSet<NodeCategory>();
76
+ const orderedCategories: NodeCategory[] = [];
77
+ for (const node of formatCompatibleNodes) {
78
+ if (!seen.has(node.category)) {
79
+ seen.add(node.category);
80
+ orderedCategories.push(node.category);
81
+ }
82
+ }
83
+ return orderedCategories;
84
+ }
85
+
86
+ /**
87
+ * Filter node types based on search query and selected category
88
+ * Preserves the API order - no client-side sorting applied
89
+ */
90
+ function getFilteredNodes(): NodeMetadata[] {
91
+ // Start with format-compatible nodes
92
+ let filtered = formatCompatibleNodes;
93
+
94
+ // Filter by category
95
+ if (selectedCategory !== "all") {
96
+ filtered = filtered.filter((node) => node.category === selectedCategory);
97
+ }
98
+
99
+ // Filter by search query
100
+ if (searchInput.trim()) {
101
+ const query = searchInput.toLowerCase();
102
+ filtered = filtered.filter(
103
+ (node) =>
104
+ node.name.toLowerCase().includes(query) ||
105
+ node.description.toLowerCase().includes(query) ||
106
+ node.tags?.some((tag) => tag.toLowerCase().includes(query)),
107
+ );
108
+ }
109
+
110
+ // Return filtered results preserving API order
111
+ return filtered;
112
+ }
113
+
114
+ /**
115
+ * Handle node type drag start - creates a new node instance
116
+ */
117
+ function handleNodeDragStart(event: DragEvent, nodeType: NodeMetadata): void {
118
+ if (!event.dataTransfer) return;
119
+
120
+ // Extract initial config from configSchema with proper null checks
121
+ let initialConfig: Record<string, unknown> = {};
122
+ if (
123
+ nodeType.configSchema &&
124
+ typeof nodeType.configSchema === "object" &&
125
+ nodeType.configSchema.properties &&
126
+ typeof nodeType.configSchema.properties === "object"
127
+ ) {
128
+ // JSON Schema format - extract defaults
129
+ Object.entries(nodeType.configSchema.properties).forEach(
130
+ ([key, prop]) => {
131
+ if (prop && typeof prop === "object" && "default" in prop) {
132
+ initialConfig[key] = prop.default;
133
+ }
134
+ },
135
+ );
136
+ }
137
+
138
+ // Create a new node instance from the node type
139
+ const newNodeData = {
140
+ type: "node",
141
+ nodeType: nodeType.id,
142
+ nodeData: {
143
+ label: nodeType.name,
144
+ config: initialConfig,
145
+ metadata: nodeType,
146
+ },
147
+ };
148
+
149
+ const jsonData = JSON.stringify(newNodeData);
150
+
151
+ // Set the data that SvelteFlow will receive
152
+ event.dataTransfer.setData("application/json", jsonData);
153
+ event.dataTransfer.setData("text/plain", nodeType.name);
154
+ event.dataTransfer.effectAllowed = "copy";
155
+
156
+ // Set drag image
157
+ if (event.target) {
158
+ const rect = (event.target as HTMLElement).getBoundingClientRect();
159
+ event.dataTransfer.setDragImage(
160
+ event.target as HTMLElement,
161
+ rect.width / 2,
162
+ rect.height / 2,
163
+ );
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Handle search input change
169
+ */
170
+ function handleSearchChange(): void {
171
+ // Search is handled reactively through the derived filteredNodes
172
+ }
173
+
174
+ /**
175
+ * Handle node click
176
+ */
177
+ function handleNodeClick(nodeType: NodeMetadata): void {
178
+ // Handle node click - could be used for preview or configuration
179
+ }
180
+
181
+ /**
182
+ * Get category display name from the categories store.
183
+ * Falls back to auto-capitalizing the category machine name.
184
+ */
185
+ function getCategoryDisplayName(category: NodeCategory): string {
186
+ return getCategoryLabel(category);
187
+ }
188
+
189
+ /**
190
+ * Get node types for category
191
+ * Preserves the API order - no client-side sorting applied
192
+ */
193
+ function getNodesForCategory(category: NodeCategory): NodeMetadata[] {
194
+ return formatCompatibleNodes.filter((node) => node.category === category);
195
+ }
196
+
197
+ /**
198
+ * Get filtered nodes for category
199
+ */
200
+ function getFilteredNodesForCategory(category: NodeCategory): NodeMetadata[] {
201
+ let nodes = getNodesForCategory(category);
202
+
203
+ // Filter by search query
204
+ if (searchInput.trim()) {
205
+ const query = searchInput.toLowerCase();
206
+ nodes = nodes.filter(
207
+ (node) =>
208
+ node.name.toLowerCase().includes(query) ||
209
+ node.description.toLowerCase().includes(query) ||
210
+ node.tags?.some((tag) => tag.toLowerCase().includes(query)),
211
+ );
212
+ }
213
+
214
+ return nodes;
215
+ }
194
216
  </script>
195
217
 
196
218
  <!-- Components Sidebar - Always Visible -->
197
219
  <aside
198
- class="flowdrop-sidebar flowdrop-sidebar--container"
199
- class:flowdrop-sidebar--collapsed={getUiSettings().sidebarCollapsed}
200
- class:flowdrop-sidebar--compact={getUiSettings().compactMode}
201
- style:width="{getUiSettings().sidebarCollapsed ? 48 : getUiSettings().sidebarWidth}px"
202
- aria-label="Components sidebar"
220
+ class="flowdrop-sidebar flowdrop-sidebar--container"
221
+ class:flowdrop-sidebar--collapsed={isCollapsed}
222
+ class:flowdrop-sidebar--compact={getUiSettings().compactMode}
223
+ style:width="{isCollapsed ? 48 : getUiSettings().sidebarWidth}px"
224
+ aria-label="Components sidebar"
203
225
  >
204
- <!-- Header -->
205
- <div class="flowdrop-sidebar__header">
206
- <button
207
- class="flowdrop-sidebar__toggle"
208
- onclick={toggleSidebar}
209
- aria-label={getUiSettings().sidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
210
- title={getUiSettings().sidebarCollapsed ? 'Expand sidebar' : 'Collapse sidebar'}
211
- >
212
- <Icon icon={getUiSettings().sidebarCollapsed ? 'mdi:chevron-right' : 'mdi:chevron-left'} />
213
- </button>
214
- {#if !getUiSettings().sidebarCollapsed}
215
- <div class="flowdrop-sidebar__title">
216
- <h2 class="flowdrop-text--lg flowdrop-font--bold">Components</h2>
217
- </div>
218
- {/if}
219
- </div>
220
-
221
- {#if !getUiSettings().sidebarCollapsed}
222
- <!-- Search Section -->
223
- <div class="flowdrop-sidebar__search">
224
- <div class="flowdrop-join flowdrop-w--full">
225
- <div class="flowdrop-join__item flowdrop-flex--1">
226
- <input
227
- type="text"
228
- placeholder="Search components..."
229
- class="flowdrop-input flowdrop-join__item flowdrop-w--full"
230
- bind:value={searchInput}
231
- oninput={handleSearchChange}
232
- />
233
- </div>
234
- <button class="flowdrop-btn flowdrop-join__item" aria-label="Search components">
235
- <Icon icon="mdi:magnify" class="flowdrop-icon" />
236
- </button>
237
- </div>
238
- </div>
239
-
240
- <!-- Node Types List -->
241
- <div class="flowdrop-sidebar__content">
242
- {#if props.nodes?.length === 0}
243
- <!-- No node types available -->
244
- <div class="flowdrop-hero">
245
- <div class="flowdrop-hero__content">
246
- <div class="flowdrop-hero__icon">📦</div>
247
- <h3 class="flowdrop-hero__title">No node types available</h3>
248
- <p class="flowdrop-hero__description">Node type definitions will appear here</p>
249
- <div class="flowdrop-mb--4">
250
- <LoadingSpinner size="md" text="Loading from server..." />
251
- </div>
252
- </div>
253
- </div>
254
- {:else if searchInput.trim()}
255
- <!-- Search Results -->
256
- <div class="flowdrop-p--4">
257
- <div class="flowdrop-divider">
258
- <h3 class="flowdrop-divider__text">Search Results</h3>
259
- </div>
260
- {#if filteredNodes.length === 0}
261
- <div class="flowdrop-hero">
262
- <div class="flowdrop-hero__content">
263
- <div class="flowdrop-hero__icon">🔍</div>
264
- <h3 class="flowdrop-hero__title">No components found</h3>
265
- <p class="flowdrop-hero__description">Try adjusting your search</p>
266
- {#if props.nodes?.length === 0}
267
- <div class="flowdrop-mb--4">
268
- <LoadingSpinner size="sm" text="Loading components..." />
269
- </div>
270
- {/if}
271
- </div>
272
- </div>
273
- {:else}
274
- <div class="flowdrop-node-list">
275
- {#each filteredNodes as nodeType (nodeType.id)}
276
- <div
277
- class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
278
- draggable="true"
279
- ondragstart={(e) => handleNodeDragStart(e, nodeType)}
280
- role="button"
281
- tabindex="0"
282
- >
283
- <div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
284
- <div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
285
- <!-- Node Type Icon with Squircle Background -->
286
- <span
287
- class="flowdrop-node-icon"
288
- style="--_icon-color: {getCategoryColorToken(nodeType.category)}"
289
- >
290
- <Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
291
- </span>
292
-
293
- <!-- Node Type Info - Icon and Title only -->
294
- <h4
295
- class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1"
296
- >
297
- {nodeType.name}
298
- </h4>
299
- </div>
300
- <p
301
- class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0"
302
- >
303
- {nodeType.description}
304
- </p>
305
- </div>
306
- </div>
307
- {/each}
308
- </div>
309
- {/if}
310
- </div>
311
- {:else}
312
- <!-- Show categories with details when no search is active -->
313
- <div class="flowdrop-p--4">
314
- <!-- Category-specific details -->
315
- <div class="flowdrop-category-list">
316
- {#each categories as category (category)}
317
- {@const categoryNodes = getFilteredNodesForCategory(category)}
318
- {#if categoryNodes.length > 0}
319
- <details class="flowdrop-details">
320
- <summary class="flowdrop-details__summary">
321
- <div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
322
- <span
323
- class="flowdrop-node-icon"
324
- style="--_icon-color: {getCategoryColorToken(category)}"
325
- >
326
- <Icon icon={getCategoryIcon(category)} />
327
- </span>
328
- <span>{getCategoryDisplayName(category)}</span>
329
- </div>
330
- <div class="flowdrop-badge flowdrop-badge--secondary">
331
- {categoryNodes.length}
332
- </div>
333
- </summary>
334
- <div class="flowdrop-details__content">
335
- <div class="flowdrop-node-list">
336
- {#each categoryNodes as nodeType (nodeType.id)}
337
- <div
338
- class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
339
- draggable="true"
340
- ondragstart={(e) => handleNodeDragStart(e, nodeType)}
341
- onclick={() => handleNodeClick(nodeType)}
342
- role="button"
343
- tabindex="0"
344
- onkeydown={(e) => {
345
- if (e.key === 'Enter' || e.key === ' ') {
346
- e.preventDefault();
347
- handleNodeClick(nodeType);
348
- }
349
- }}
350
- >
351
- <div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
352
- <div class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center">
353
- <!-- Node Type Icon with Squircle Background -->
354
- <span
355
- class="flowdrop-node-icon"
356
- style="--_icon-color: {getCategoryColorToken(nodeType.category)}"
357
- >
358
- <Icon icon={getNodeIcon(nodeType.icon, nodeType.category)} />
359
- </span>
360
-
361
- <!-- Node Type Info - Icon and Title only -->
362
- <h4
363
- class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1"
364
- >
365
- {nodeType.name}
366
- </h4>
367
- </div>
368
- <p
369
- class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0"
370
- >
371
- {nodeType.description}
372
- </p>
373
- </div>
374
- </div>
375
- {/each}
376
- </div>
377
- </div>
378
- </details>
379
- {/if}
380
- {/each}
381
- </div>
382
- </div>
383
- {/if}
384
- </div>
385
-
386
- <!-- Footer -->
387
- <div class="flowdrop-sidebar__footer">
388
- <div class="flowdrop-flex flowdrop-gap--4">
389
- <div class="flowdrop-flex flowdrop-gap--4">
390
- {#if props.nodes?.length === 0}
391
- <span class="flowdrop-text--xs flowdrop-text--gray">Loading components...</span>
392
- {:else}
393
- <span class="flowdrop-text--xs flowdrop-text--gray"
394
- >Total: {props.nodes?.length || 0} components</span
395
- >
396
- <span class="flowdrop-text--xs flowdrop-text--gray"
397
- >Showing: {filteredNodes.length}</span
398
- >
399
- {/if}
400
- </div>
401
- </div>
402
- </div>
403
- {/if}
226
+ <!-- Header — visibility controlled by --fd-sidebar-header-display -->
227
+ <div class="flowdrop-sidebar__header">
228
+ <button
229
+ class="flowdrop-sidebar__toggle"
230
+ onclick={toggleSidebar}
231
+ aria-label={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
232
+ title={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
233
+ >
234
+ <Icon icon={isCollapsed ? "mdi:chevron-right" : "mdi:chevron-left"} />
235
+ </button>
236
+ {#if !isCollapsed}
237
+ <div class="flowdrop-sidebar__title">
238
+ <h2 class="flowdrop-text--lg flowdrop-font--bold">Components</h2>
239
+ </div>
240
+ {/if}
241
+ </div>
242
+
243
+ {#if !isCollapsed}
244
+ <!-- Search Section — visibility controlled by --fd-sidebar-search-display -->
245
+ <div class="flowdrop-sidebar__search">
246
+ <div class="flowdrop-join flowdrop-w--full">
247
+ <div class="flowdrop-join__item flowdrop-flex--1">
248
+ <input
249
+ type="text"
250
+ placeholder="Search components..."
251
+ class="flowdrop-input flowdrop-join__item flowdrop-w--full"
252
+ bind:value={searchInput}
253
+ oninput={handleSearchChange}
254
+ />
255
+ </div>
256
+ <button
257
+ class="flowdrop-btn flowdrop-join__item"
258
+ aria-label="Search components"
259
+ >
260
+ <Icon icon="mdi:magnify" class="flowdrop-icon" />
261
+ </button>
262
+ </div>
263
+ </div>
264
+
265
+ <!-- Node Types List -->
266
+ <div class="flowdrop-sidebar__content">
267
+ {#if props.nodes?.length === 0}
268
+ <!-- No node types available -->
269
+ <div class="flowdrop-hero">
270
+ <div class="flowdrop-hero__content">
271
+ <div class="flowdrop-hero__icon">📦</div>
272
+ <h3 class="flowdrop-hero__title">No node types available</h3>
273
+ <p class="flowdrop-hero__description">
274
+ Node type definitions will appear here
275
+ </p>
276
+ <div class="flowdrop-mb--4">
277
+ <LoadingSpinner size="md" text="Loading from server..." />
278
+ </div>
279
+ </div>
280
+ </div>
281
+ {:else if searchInput.trim()}
282
+ <!-- Search Results -->
283
+ <div class="flowdrop-p--4">
284
+ <div class="flowdrop-divider">
285
+ <h3 class="flowdrop-divider__text">Search Results</h3>
286
+ </div>
287
+ {#if filteredNodes.length === 0}
288
+ <div class="flowdrop-hero">
289
+ <div class="flowdrop-hero__content">
290
+ <div class="flowdrop-hero__icon">🔍</div>
291
+ <h3 class="flowdrop-hero__title">No components found</h3>
292
+ <p class="flowdrop-hero__description">
293
+ Try adjusting your search
294
+ </p>
295
+ {#if props.nodes?.length === 0}
296
+ <div class="flowdrop-mb--4">
297
+ <LoadingSpinner size="sm" text="Loading components..." />
298
+ </div>
299
+ {/if}
300
+ </div>
301
+ </div>
302
+ {:else}
303
+ <div class="flowdrop-node-list">
304
+ {#each filteredNodes as nodeType (nodeType.id)}
305
+ <div
306
+ class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
307
+ draggable="true"
308
+ ondragstart={(e) => handleNodeDragStart(e, nodeType)}
309
+ role="button"
310
+ tabindex="0"
311
+ >
312
+ <div class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1">
313
+ <div
314
+ class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center"
315
+ >
316
+ <!-- Node Type Icon with Squircle Background -->
317
+ <span
318
+ class="flowdrop-node-icon"
319
+ style="--_icon-color: {getCategoryColorToken(
320
+ nodeType.category,
321
+ )}"
322
+ >
323
+ <Icon
324
+ icon={getNodeIcon(nodeType.icon, nodeType.category)}
325
+ />
326
+ </span>
327
+
328
+ <!-- Node Type Info - Icon and Title only -->
329
+ <h4
330
+ class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1"
331
+ >
332
+ {nodeType.name}
333
+ </h4>
334
+ </div>
335
+ <p
336
+ class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0"
337
+ >
338
+ {nodeType.description}
339
+ </p>
340
+ </div>
341
+ </div>
342
+ {/each}
343
+ </div>
344
+ {/if}
345
+ </div>
346
+ {:else}
347
+ <!-- Show categories with details when no search is active -->
348
+ <div class="flowdrop-p--4">
349
+ <!-- Category-specific details -->
350
+ <div class="flowdrop-category-list">
351
+ {#each categories as category (category)}
352
+ {@const categoryNodes = getFilteredNodesForCategory(category)}
353
+ {#if categoryNodes.length > 0}
354
+ <!-- Flat style: label + dot+name rows (shown/hidden via CSS token) -->
355
+ <div class="fd-sidebar-flat-section">
356
+ <div class="fd-sidebar-flat-category">
357
+ {getCategoryDisplayName(category).toUpperCase()}
358
+ </div>
359
+ <div class="fd-sidebar-flat-list">
360
+ {#each categoryNodes as nodeType (nodeType.id)}
361
+ <div
362
+ class="fd-sidebar-flat-item"
363
+ draggable="true"
364
+ ondragstart={(e) => handleNodeDragStart(e, nodeType)}
365
+ onclick={() => handleNodeClick(nodeType)}
366
+ role="button"
367
+ tabindex="0"
368
+ onkeydown={(e) => {
369
+ if (e.key === "Enter" || e.key === " ") {
370
+ e.preventDefault();
371
+ handleNodeClick(nodeType);
372
+ }
373
+ }}
374
+ >
375
+ <span
376
+ class="fd-sidebar-flat-dot"
377
+ style="background: {getCategoryColorToken(
378
+ nodeType.category,
379
+ )}"
380
+ ></span>
381
+ <span class="fd-sidebar-flat-name">{nodeType.name}</span
382
+ >
383
+ </div>
384
+ {/each}
385
+ </div>
386
+ </div>
387
+ <!-- Card style: <details> accordion (shown/hidden via CSS token) -->
388
+ <details
389
+ class="flowdrop-details fd-sidebar-card-section"
390
+ open={props.categoriesDefaultOpen || undefined}
391
+ >
392
+ <summary class="flowdrop-details__summary">
393
+ <div
394
+ class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center"
395
+ >
396
+ <span
397
+ class="flowdrop-node-icon"
398
+ style="--_icon-color: {getCategoryColorToken(category)}"
399
+ >
400
+ <Icon icon={getCategoryIcon(category)} />
401
+ </span>
402
+ <span>{getCategoryDisplayName(category)}</span>
403
+ </div>
404
+ <div class="flowdrop-badge flowdrop-badge--secondary">
405
+ {categoryNodes.length}
406
+ </div>
407
+ </summary>
408
+ <div class="flowdrop-details__content">
409
+ <div class="flowdrop-node-list">
410
+ {#each categoryNodes as nodeType (nodeType.id)}
411
+ <div
412
+ class="flowdrop-card flowdrop-card--compact flowdrop-node-item"
413
+ draggable="true"
414
+ ondragstart={(e) => handleNodeDragStart(e, nodeType)}
415
+ onclick={() => handleNodeClick(nodeType)}
416
+ role="button"
417
+ tabindex="0"
418
+ onkeydown={(e) => {
419
+ if (e.key === "Enter" || e.key === " ") {
420
+ e.preventDefault();
421
+ handleNodeClick(nodeType);
422
+ }
423
+ }}
424
+ >
425
+ <div
426
+ class="flowdrop-card__body flowdrop-p--1 flowdrop-py--1"
427
+ >
428
+ <div
429
+ class="flowdrop-flex flowdrop-gap--2 flowdrop-items--center"
430
+ >
431
+ <!-- Node Type Icon with Squircle Background -->
432
+ <span
433
+ class="flowdrop-node-icon"
434
+ style="--_icon-color: {getCategoryColorToken(
435
+ nodeType.category,
436
+ )}"
437
+ >
438
+ <Icon
439
+ icon={getNodeIcon(
440
+ nodeType.icon,
441
+ nodeType.category,
442
+ )}
443
+ />
444
+ </span>
445
+
446
+ <!-- Node Type Info - Icon and Title only -->
447
+ <h4
448
+ class="flowdrop-text--sm flowdrop-font--medium flowdrop-truncate flowdrop-flex--1"
449
+ >
450
+ {nodeType.name}
451
+ </h4>
452
+ </div>
453
+ <p
454
+ class="flowdrop-text--xs flowdrop-text--gray flowdrop-truncate flowdrop-mt--1 flowdrop-ml--0"
455
+ >
456
+ {nodeType.description}
457
+ </p>
458
+ </div>
459
+ </div>
460
+ {/each}
461
+ </div>
462
+ </div>
463
+ </details>
464
+ {/if}
465
+ {/each}
466
+ </div>
467
+ </div>
468
+ {/if}
469
+ </div>
470
+
471
+ <!-- Footer -->
472
+ <div class="flowdrop-sidebar__footer">
473
+ <div class="flowdrop-flex flowdrop-gap--4">
474
+ <div class="flowdrop-flex flowdrop-gap--4">
475
+ {#if props.nodes?.length === 0}
476
+ <span class="flowdrop-text--xs flowdrop-text--gray"
477
+ >Loading components...</span
478
+ >
479
+ {:else}
480
+ <span class="flowdrop-text--xs flowdrop-text--gray"
481
+ >Total: {props.nodes?.length || 0} components</span
482
+ >
483
+ <span class="flowdrop-text--xs flowdrop-text--gray"
484
+ >Showing: {filteredNodes.length}</span
485
+ >
486
+ {/if}
487
+ </div>
488
+ </div>
489
+ </div>
490
+ {/if}
404
491
  </aside>
405
492
 
406
493
  <style>
407
- /* Components Sidebar - Always Visible */
408
- .flowdrop-sidebar {
409
- height: calc(100vh - var(--fd-navbar-height)); /* Account for navbar height */
410
- background-color: var(--fd-background);
411
- border-right: 1px solid var(--fd-border);
412
- display: flex;
413
- flex-direction: column;
414
- box-shadow: var(--fd-shadow-md);
415
- flex-shrink: 0;
416
- transition: width 0.2s ease;
417
- }
418
-
419
- .flowdrop-sidebar--container {
420
- display: flex;
421
- flex-direction: column;
422
- height: 100%;
423
- }
424
-
425
- /* Collapsed state */
426
- .flowdrop-sidebar--collapsed {
427
- overflow: hidden;
428
- }
429
-
430
- .flowdrop-sidebar--collapsed .flowdrop-sidebar__header {
431
- justify-content: center;
432
- padding: 0.75rem 0.5rem;
433
- }
434
-
435
- /* Compact mode styles */
436
- .flowdrop-sidebar--compact .flowdrop-sidebar__header {
437
- padding: 0.5rem 0.75rem;
438
- }
439
-
440
- .flowdrop-sidebar--compact .flowdrop-sidebar__search {
441
- padding: 0.5rem 0.75rem;
442
- }
443
-
444
- .flowdrop-sidebar--compact .flowdrop-sidebar__content {
445
- padding-bottom: 2rem;
446
- }
447
-
448
- .flowdrop-sidebar--compact .flowdrop-sidebar__footer {
449
- padding: 0.375rem 0.5rem;
450
- height: 32px;
451
- }
452
-
453
- .flowdrop-sidebar--compact .flowdrop-node-icon {
454
- width: 1.5rem;
455
- height: 1.5rem;
456
- font-size: var(--fd-text-xs);
457
- }
458
-
459
- .flowdrop-sidebar--compact .flowdrop-node-list {
460
- gap: 0.25rem;
461
- }
462
-
463
- .flowdrop-sidebar--compact .flowdrop-category-list {
464
- gap: 0.25rem;
465
- }
466
-
467
- /* Toggle button */
468
- .flowdrop-sidebar__toggle {
469
- display: flex;
470
- align-items: center;
471
- justify-content: center;
472
- width: 2rem;
473
- height: 2rem;
474
- border: none;
475
- background: transparent;
476
- color: var(--fd-muted-foreground);
477
- border-radius: var(--fd-radius-md);
478
- cursor: pointer;
479
- transition:
480
- color var(--fd-transition-fast),
481
- background-color var(--fd-transition-fast);
482
- flex-shrink: 0;
483
- }
484
-
485
- .flowdrop-sidebar__toggle:hover {
486
- color: var(--fd-foreground);
487
- background-color: var(--fd-subtle);
488
- }
489
-
490
- .flowdrop-sidebar__toggle:focus {
491
- outline: none;
492
- box-shadow: 0 0 0 2px var(--fd-ring);
493
- }
494
-
495
- .flowdrop-sidebar__header {
496
- background: var(--fd-header);
497
- border-bottom: 1px solid var(--fd-border);
498
- padding: 0.75rem 1rem;
499
- display: flex;
500
- align-items: center;
501
- justify-content: space-between;
502
- flex-shrink: 0;
503
- }
504
-
505
- .flowdrop-sidebar__title {
506
- display: flex;
507
- align-items: center;
508
- }
509
-
510
- .flowdrop-sidebar__title h2 {
511
- font-size: 1rem;
512
- font-weight: 600;
513
- margin: 0;
514
- color: var(--fd-foreground);
515
- }
516
-
517
- .flowdrop-sidebar__search {
518
- padding: 0.75rem 1rem;
519
- background-color: var(--fd-background);
520
- border-bottom: 1px solid var(--fd-border);
521
- }
522
-
523
- .flowdrop-sidebar__content {
524
- flex: 1;
525
- overflow-y: scroll; /* Changed from auto to scroll to always show scrollbar */
526
- scrollbar-width: thin;
527
- scrollbar-color: var(--fd-scrollbar-thumb) var(--fd-scrollbar-track);
528
- padding-bottom: 4rem; /* Add padding to ensure content is scrollable above footer */
529
- min-height: 0; /* Allow flex item to shrink below content size */
530
- }
531
-
532
- .flowdrop-sidebar__content::-webkit-scrollbar {
533
- width: 8px;
534
- display: block; /* Ensure scrollbar is always visible */
535
- }
536
-
537
- .flowdrop-sidebar__content::-webkit-scrollbar-track {
538
- background: var(--fd-scrollbar-track);
539
- }
540
-
541
- .flowdrop-sidebar__content::-webkit-scrollbar-thumb {
542
- background: var(--fd-scrollbar-thumb);
543
- border-radius: 4px;
544
- min-height: 20px; /* Ensure thumb has minimum height for visibility */
545
- }
546
-
547
- .flowdrop-sidebar__content::-webkit-scrollbar-thumb:hover {
548
- background: var(--fd-scrollbar-thumb-hover);
549
- }
550
-
551
- .flowdrop-sidebar__footer {
552
- background-color: var(--fd-backdrop);
553
- backdrop-filter: var(--fd-backdrop-blur);
554
- border-top: 1px solid var(--fd-border);
555
- padding: 0.5rem 0.75rem;
556
- flex-shrink: 0; /* Prevent footer from shrinking */
557
- position: relative;
558
- z-index: 10; /* Ensure footer stays on top */
559
- height: 40px;
560
- display: flex;
561
- align-items: center;
562
- }
563
-
564
- .flowdrop-node-list {
565
- display: flex;
566
- flex-direction: column;
567
- gap: 0.375rem;
568
- }
569
-
570
- .flowdrop-node-item {
571
- cursor: grab;
572
- transition: all var(--fd-transition-fast);
573
- border-radius: var(--fd-radius-md);
574
- border: 1px solid transparent;
575
- background-color: var(--fd-card);
576
- }
577
-
578
- .flowdrop-node-item:hover {
579
- border-color: var(--fd-border);
580
- background-color: var(--fd-muted);
581
- box-shadow: var(--fd-shadow-sm);
582
- transform: translateY(-1px);
583
- }
584
-
585
- .flowdrop-node-item:active {
586
- cursor: grabbing;
587
- transform: translateY(0);
588
- box-shadow: none;
589
- }
590
-
591
- /* Squircle icon wrapper - Apple-style rounded square background */
592
- .flowdrop-node-icon {
593
- width: 2rem;
594
- height: 2rem;
595
- border-radius: 0.5rem;
596
- background: color-mix(in srgb, var(--_icon-color) var(--fd-node-icon-bg-opacity), transparent);
597
- color: var(--fd-node-icon);
598
- font-size: var(--fd-text-sm);
599
- display: flex;
600
- align-items: center;
601
- justify-content: center;
602
- flex-shrink: 0;
603
- transition: all var(--fd-transition-normal);
604
- }
605
-
606
- .flowdrop-node-item:hover .flowdrop-node-icon,
607
- .flowdrop-details__summary:hover .flowdrop-node-icon {
608
- background: color-mix(
609
- in srgb,
610
- var(--_icon-color) var(--fd-node-icon-bg-opacity-hover),
611
- transparent
612
- );
613
- transform: scale(1.05);
614
- }
615
-
616
- .flowdrop-category-list {
617
- display: flex;
618
- flex-direction: column;
619
- gap: 0.375rem;
620
- }
621
-
622
- .flowdrop-items--center {
623
- align-items: center;
624
- }
625
-
626
- .flowdrop-truncate {
627
- overflow: hidden;
628
- text-overflow: ellipsis;
629
- white-space: nowrap;
630
- }
631
-
632
- .flowdrop-card__body h4 {
633
- margin: 0;
634
- line-height: 1;
635
- color: var(--fd-foreground);
636
- }
637
-
638
- .flowdrop-p--4 {
639
- padding: 1rem;
640
- }
641
-
642
- .flowdrop-py--1 {
643
- padding-top: 0.25rem;
644
- padding-bottom: 0.25rem;
645
- }
646
-
647
- .flowdrop-ml--0 {
648
- margin-left: 0;
649
- }
650
-
651
- /* Form Elements - Matching App Design System */
652
- .flowdrop-input {
653
- padding: 0.625rem 0.75rem;
654
- border: 1px solid var(--fd-border-strong);
655
- border-radius: var(--fd-radius-md);
656
- font-size: var(--fd-text-sm);
657
- color: var(--fd-foreground);
658
- background-color: var(--fd-background);
659
- transition:
660
- border-color var(--fd-transition-normal),
661
- box-shadow var(--fd-transition-normal);
662
- width: 100%;
663
- height: 2.5rem;
664
- box-sizing: border-box;
665
- }
666
-
667
- .flowdrop-input:focus {
668
- outline: none;
669
- border-color: var(--fd-ring);
670
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
671
- }
672
-
673
- .flowdrop-input::placeholder {
674
- color: var(--fd-muted-foreground);
675
- }
676
-
677
- .flowdrop-btn {
678
- padding: 0.625rem 0.75rem;
679
- border-radius: var(--fd-radius-md);
680
- font-size: var(--fd-text-sm);
681
- font-weight: 500;
682
- cursor: pointer;
683
- transition: all var(--fd-transition-normal);
684
- border: 1px solid var(--fd-border-strong);
685
- display: flex;
686
- align-items: center;
687
- justify-content: center;
688
- gap: 0.5rem;
689
- background-color: var(--fd-muted);
690
- color: var(--fd-foreground);
691
- height: 2.5rem;
692
- min-width: 2.5rem;
693
- box-sizing: border-box;
694
- }
695
-
696
- .flowdrop-btn:hover {
697
- background-color: var(--fd-subtle);
698
- border-color: var(--fd-border-strong);
699
- }
700
-
701
- .flowdrop-btn:active {
702
- background-color: var(--fd-border);
703
- border-color: var(--fd-muted-foreground);
704
- }
705
-
706
- .flowdrop-btn:focus {
707
- outline: none;
708
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
709
- }
710
-
711
- .flowdrop-btn:disabled {
712
- background-color: var(--fd-muted-foreground);
713
- border-color: var(--fd-muted-foreground);
714
- cursor: not-allowed;
715
- opacity: 0.6;
716
- }
717
-
718
- /* Join component styles - Seamless integration */
719
- .flowdrop-join {
720
- display: flex;
721
- width: 100%;
722
- border-radius: var(--fd-radius-md);
723
- overflow: hidden;
724
- border: 1px solid var(--fd-border-strong);
725
- background-color: var(--fd-background);
726
- }
727
-
728
- .flowdrop-join__item {
729
- border: none;
730
- border-radius: 0;
731
- background-color: transparent;
732
- }
733
-
734
- .flowdrop-join__item:first-child {
735
- border-right: 1px solid var(--fd-border-strong);
736
- flex: 1;
737
- }
738
-
739
- .flowdrop-join__item:last-child {
740
- border-left: none;
741
- background-color: var(--fd-muted);
742
- color: var(--fd-foreground);
743
- }
744
-
745
- .flowdrop-join__item:last-child:hover {
746
- background-color: var(--fd-subtle);
747
- }
748
-
749
- .flowdrop-join:focus-within {
750
- border-color: var(--fd-ring);
751
- box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
752
- }
753
-
754
- /* Utility classes */
755
- .flowdrop-w--full {
756
- width: 100%;
757
- }
758
-
759
- .flowdrop-flex--1 {
760
- flex: 1;
761
- }
494
+ /* Components Sidebar - Always Visible */
495
+ .flowdrop-sidebar {
496
+ height: calc(
497
+ 100vh - var(--fd-navbar-height)
498
+ ); /* Account for navbar height */
499
+ background-color: var(--fd-background);
500
+ border-right: 1px solid var(--fd-border);
501
+ display: flex;
502
+ flex-direction: column;
503
+ box-shadow: var(--fd-shadow-md);
504
+ flex-shrink: 0;
505
+ transition: width 0.2s ease;
506
+ }
507
+
508
+ .flowdrop-sidebar--container {
509
+ display: flex;
510
+ flex-direction: column;
511
+ height: 100%;
512
+ }
513
+
514
+ /* Collapsed state */
515
+ .flowdrop-sidebar--collapsed {
516
+ overflow: hidden;
517
+ }
518
+
519
+ .flowdrop-sidebar--collapsed .flowdrop-sidebar__header {
520
+ justify-content: center;
521
+ padding: 0.75rem 0.5rem;
522
+ }
523
+
524
+ /* Compact mode styles */
525
+ .flowdrop-sidebar--compact .flowdrop-sidebar__header {
526
+ padding: 0.5rem 0.75rem;
527
+ }
528
+
529
+ .flowdrop-sidebar--compact .flowdrop-sidebar__search {
530
+ padding: 0.5rem 0.75rem;
531
+ }
532
+
533
+ .flowdrop-sidebar--compact .flowdrop-sidebar__content {
534
+ padding-bottom: 2rem;
535
+ }
536
+
537
+ .flowdrop-sidebar--compact .flowdrop-sidebar__footer {
538
+ padding: 0.375rem 0.5rem;
539
+ height: 32px;
540
+ }
541
+
542
+ .flowdrop-sidebar--compact .flowdrop-node-icon {
543
+ width: 1.5rem;
544
+ height: 1.5rem;
545
+ font-size: var(--fd-text-xs);
546
+ }
547
+
548
+ .flowdrop-sidebar--compact .flowdrop-node-list {
549
+ gap: 0.25rem;
550
+ }
551
+
552
+ .flowdrop-sidebar--compact .flowdrop-category-list {
553
+ gap: 0.25rem;
554
+ }
555
+
556
+ /* Toggle button */
557
+ .flowdrop-sidebar__toggle {
558
+ display: flex;
559
+ align-items: center;
560
+ justify-content: center;
561
+ width: 2rem;
562
+ height: 2rem;
563
+ border: none;
564
+ background: transparent;
565
+ color: var(--fd-muted-foreground);
566
+ border-radius: var(--fd-radius-md);
567
+ cursor: pointer;
568
+ transition:
569
+ color var(--fd-transition-fast),
570
+ background-color var(--fd-transition-fast);
571
+ flex-shrink: 0;
572
+ }
573
+
574
+ .flowdrop-sidebar__toggle:hover {
575
+ color: var(--fd-foreground);
576
+ background-color: var(--fd-subtle);
577
+ }
578
+
579
+ .flowdrop-sidebar__toggle:focus {
580
+ outline: none;
581
+ box-shadow: 0 0 0 2px var(--fd-ring);
582
+ }
583
+
584
+ .flowdrop-sidebar__header {
585
+ background: var(--fd-header);
586
+ border-bottom: 1px solid var(--fd-border);
587
+ padding: 0.75rem 1rem;
588
+ display: var(--fd-sidebar-header-display, flex);
589
+ align-items: center;
590
+ justify-content: space-between;
591
+ flex-shrink: 0;
592
+ }
593
+
594
+ .flowdrop-sidebar__title {
595
+ display: flex;
596
+ align-items: center;
597
+ }
598
+
599
+ .flowdrop-sidebar__title h2 {
600
+ font-size: 1rem;
601
+ font-weight: 600;
602
+ margin: 0;
603
+ color: var(--fd-foreground);
604
+ }
605
+
606
+ .flowdrop-sidebar__search {
607
+ padding: 0.75rem 1rem;
608
+ background-color: var(--fd-background);
609
+ border-bottom: 1px solid var(--fd-border);
610
+ display: var(--fd-sidebar-search-display, block);
611
+ }
612
+
613
+ .flowdrop-sidebar__content {
614
+ flex: 1;
615
+ overflow-y: scroll; /* Changed from auto to scroll to always show scrollbar */
616
+ scrollbar-width: thin;
617
+ scrollbar-color: var(--fd-scrollbar-thumb) var(--fd-scrollbar-track);
618
+ padding-bottom: 4rem; /* Add padding to ensure content is scrollable above footer */
619
+ min-height: 0; /* Allow flex item to shrink below content size */
620
+ }
621
+
622
+ .flowdrop-sidebar__content::-webkit-scrollbar {
623
+ width: 8px;
624
+ display: block; /* Ensure scrollbar is always visible */
625
+ }
626
+
627
+ .flowdrop-sidebar__content::-webkit-scrollbar-track {
628
+ background: var(--fd-scrollbar-track);
629
+ }
630
+
631
+ .flowdrop-sidebar__content::-webkit-scrollbar-thumb {
632
+ background: var(--fd-scrollbar-thumb);
633
+ border-radius: 4px;
634
+ min-height: 20px; /* Ensure thumb has minimum height for visibility */
635
+ }
636
+
637
+ .flowdrop-sidebar__content::-webkit-scrollbar-thumb:hover {
638
+ background: var(--fd-scrollbar-thumb-hover);
639
+ }
640
+
641
+ .flowdrop-sidebar__footer {
642
+ background-color: var(--fd-backdrop);
643
+ backdrop-filter: var(--fd-backdrop-blur);
644
+ border-top: 1px solid var(--fd-border);
645
+ padding: 0.5rem 0.75rem;
646
+ flex-shrink: 0; /* Prevent footer from shrinking */
647
+ position: relative;
648
+ z-index: 10; /* Ensure footer stays on top */
649
+ height: 40px;
650
+ display: flex;
651
+ align-items: center;
652
+ }
653
+
654
+ .flowdrop-node-list {
655
+ display: flex;
656
+ flex-direction: column;
657
+ gap: 0.375rem;
658
+ }
659
+
660
+ .flowdrop-node-item {
661
+ cursor: grab;
662
+ transition: all var(--fd-transition-fast);
663
+ border-radius: var(--fd-radius-md);
664
+ border: 1px solid transparent;
665
+ background-color: var(--fd-card);
666
+ }
667
+
668
+ .flowdrop-node-item:hover {
669
+ border-color: var(--fd-border);
670
+ background-color: var(--fd-muted);
671
+ box-shadow: var(--fd-shadow-sm);
672
+ transform: translateY(-1px);
673
+ }
674
+
675
+ .flowdrop-node-item:active {
676
+ cursor: grabbing;
677
+ transform: translateY(0);
678
+ box-shadow: none;
679
+ }
680
+
681
+ /* Squircle icon wrapper - Apple-style rounded square background */
682
+ .flowdrop-node-icon {
683
+ width: 2rem;
684
+ height: 2rem;
685
+ border-radius: 0.5rem;
686
+ background: color-mix(
687
+ in srgb,
688
+ var(--_icon-color) var(--fd-node-icon-bg-opacity),
689
+ transparent
690
+ );
691
+ color: var(--fd-node-icon);
692
+ font-size: var(--fd-text-sm);
693
+ display: flex;
694
+ align-items: center;
695
+ justify-content: center;
696
+ flex-shrink: 0;
697
+ transition: all var(--fd-transition-normal);
698
+ }
699
+
700
+ .flowdrop-node-item:hover .flowdrop-node-icon,
701
+ .flowdrop-details__summary:hover .flowdrop-node-icon {
702
+ background: color-mix(
703
+ in srgb,
704
+ var(--_icon-color) var(--fd-node-icon-bg-opacity-hover),
705
+ transparent
706
+ );
707
+ transform: scale(1.05);
708
+ }
709
+
710
+ .flowdrop-category-list {
711
+ display: flex;
712
+ flex-direction: column;
713
+ gap: 0.375rem;
714
+ }
715
+
716
+ .flowdrop-items--center {
717
+ align-items: center;
718
+ }
719
+
720
+ .flowdrop-truncate {
721
+ overflow: hidden;
722
+ text-overflow: ellipsis;
723
+ white-space: nowrap;
724
+ }
725
+
726
+ .flowdrop-card__body h4 {
727
+ margin: 0;
728
+ line-height: 1;
729
+ color: var(--fd-foreground);
730
+ }
731
+
732
+ .flowdrop-p--4 {
733
+ padding: 1rem;
734
+ }
735
+
736
+ .flowdrop-py--1 {
737
+ padding-top: 0.25rem;
738
+ padding-bottom: 0.25rem;
739
+ }
740
+
741
+ .flowdrop-ml--0 {
742
+ margin-left: 0;
743
+ }
744
+
745
+ /* Form Elements - Matching App Design System */
746
+ .flowdrop-input {
747
+ padding: 0.625rem 0.75rem;
748
+ border: 1px solid var(--fd-border-strong);
749
+ border-radius: var(--fd-radius-md);
750
+ font-size: var(--fd-text-sm);
751
+ color: var(--fd-foreground);
752
+ background-color: var(--fd-background);
753
+ transition:
754
+ border-color var(--fd-transition-normal),
755
+ box-shadow var(--fd-transition-normal);
756
+ width: 100%;
757
+ height: 2.5rem;
758
+ box-sizing: border-box;
759
+ }
760
+
761
+ .flowdrop-input:focus {
762
+ outline: none;
763
+ border-color: var(--fd-ring);
764
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
765
+ }
766
+
767
+ .flowdrop-input::placeholder {
768
+ color: var(--fd-muted-foreground);
769
+ }
770
+
771
+ .flowdrop-btn {
772
+ padding: 0.625rem 0.75rem;
773
+ border-radius: var(--fd-radius-md);
774
+ font-size: var(--fd-text-sm);
775
+ font-weight: 500;
776
+ cursor: pointer;
777
+ transition: all var(--fd-transition-normal);
778
+ border: 1px solid var(--fd-border-strong);
779
+ display: flex;
780
+ align-items: center;
781
+ justify-content: center;
782
+ gap: 0.5rem;
783
+ background-color: var(--fd-muted);
784
+ color: var(--fd-foreground);
785
+ height: 2.5rem;
786
+ min-width: 2.5rem;
787
+ box-sizing: border-box;
788
+ }
789
+
790
+ .flowdrop-btn:hover {
791
+ background-color: var(--fd-subtle);
792
+ border-color: var(--fd-border-strong);
793
+ }
794
+
795
+ .flowdrop-btn:active {
796
+ background-color: var(--fd-border);
797
+ border-color: var(--fd-muted-foreground);
798
+ }
799
+
800
+ .flowdrop-btn:focus {
801
+ outline: none;
802
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
803
+ }
804
+
805
+ .flowdrop-btn:disabled {
806
+ background-color: var(--fd-muted-foreground);
807
+ border-color: var(--fd-muted-foreground);
808
+ cursor: not-allowed;
809
+ opacity: 0.6;
810
+ }
811
+
812
+ /* Join component styles - Seamless integration */
813
+ .flowdrop-join {
814
+ display: flex;
815
+ width: 100%;
816
+ border-radius: var(--fd-radius-md);
817
+ overflow: hidden;
818
+ border: 1px solid var(--fd-border-strong);
819
+ background-color: var(--fd-background);
820
+ }
821
+
822
+ .flowdrop-join__item {
823
+ border: none;
824
+ border-radius: 0;
825
+ background-color: transparent;
826
+ }
827
+
828
+ .flowdrop-join__item:first-child {
829
+ border-right: 1px solid var(--fd-border-strong);
830
+ flex: 1;
831
+ }
832
+
833
+ .flowdrop-join__item:last-child {
834
+ border-left: none;
835
+ background-color: var(--fd-muted);
836
+ color: var(--fd-foreground);
837
+ }
838
+
839
+ .flowdrop-join__item:last-child:hover {
840
+ background-color: var(--fd-subtle);
841
+ }
842
+
843
+ .flowdrop-join:focus-within {
844
+ border-color: var(--fd-ring);
845
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--fd-ring) 20%, transparent);
846
+ }
847
+
848
+ /* Utility classes */
849
+ .flowdrop-w--full {
850
+ width: 100%;
851
+ }
852
+
853
+ .flowdrop-flex--1 {
854
+ flex: 1;
855
+ }
856
+
857
+ /* Display-control token wiring for skin variants */
858
+ .fd-sidebar-flat-section {
859
+ display: var(--fd-sidebar-flat-display, none);
860
+ }
861
+
862
+ .fd-sidebar-card-section {
863
+ display: var(--fd-sidebar-card-display, block);
864
+ }
865
+
866
+ /* Flat sidebar node style (minimal skin) */
867
+ .fd-sidebar-flat-category {
868
+ font-size: var(--fd-text-xs);
869
+ font-weight: 600;
870
+ color: var(--fd-sidebar-category-color, var(--fd-muted-foreground));
871
+ letter-spacing: 0.08em;
872
+ padding: 1.25rem 0.75rem 0.375rem 0.75rem;
873
+ text-transform: uppercase;
874
+ }
875
+
876
+ .fd-sidebar-flat-list {
877
+ display: flex;
878
+ flex-direction: column;
879
+ gap: 1px;
880
+ margin-bottom: 0.25rem;
881
+ }
882
+
883
+ .fd-sidebar-flat-item {
884
+ display: flex;
885
+ align-items: center;
886
+ gap: 0.625rem;
887
+ padding: 0.5rem 0.75rem;
888
+ border-radius: var(--fd-radius-sm);
889
+ cursor: grab;
890
+ transition: background-color var(--fd-transition-fast);
891
+ }
892
+
893
+ .fd-sidebar-flat-item:hover {
894
+ background-color: var(--fd-muted);
895
+ }
896
+
897
+ .fd-sidebar-flat-item:active {
898
+ cursor: grabbing;
899
+ }
900
+
901
+ .fd-sidebar-flat-dot {
902
+ width: 8px;
903
+ height: 8px;
904
+ border-radius: 50%;
905
+ flex-shrink: 0;
906
+ display: inline-block;
907
+ }
908
+
909
+ .fd-sidebar-flat-name {
910
+ font-size: var(--fd-text-sm);
911
+ color: var(--fd-sidebar-flat-item-color, var(--fd-foreground));
912
+ white-space: nowrap;
913
+ overflow: hidden;
914
+ text-overflow: ellipsis;
915
+ }
762
916
  </style>