@flowdrop/flowdrop 1.0.1 → 1.2.0

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