@flowdrop/flowdrop 1.0.1 → 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 (386) 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 +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 +900 -746
  57. package/dist/components/NodeSidebar.svelte.d.ts +3 -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/schemas/v1/workflow.schema.json +22 -107
  258. package/dist/services/agentSpecExecutionService.d.ts +3 -3
  259. package/dist/services/agentSpecExecutionService.js +59 -55
  260. package/dist/services/api.d.ts +2 -2
  261. package/dist/services/api.js +37 -37
  262. package/dist/services/apiVariableService.d.ts +1 -1
  263. package/dist/services/apiVariableService.js +41 -34
  264. package/dist/services/autoSaveService.js +8 -8
  265. package/dist/services/categoriesApi.d.ts +2 -2
  266. package/dist/services/categoriesApi.js +8 -8
  267. package/dist/services/draftStorage.d.ts +1 -1
  268. package/dist/services/draftStorage.js +11 -11
  269. package/dist/services/dynamicSchemaService.d.ts +1 -1
  270. package/dist/services/dynamicSchemaService.js +41 -39
  271. package/dist/services/globalSave.d.ts +2 -2
  272. package/dist/services/globalSave.js +41 -38
  273. package/dist/services/historyService.d.ts +1 -1
  274. package/dist/services/historyService.js +8 -8
  275. package/dist/services/interruptService.d.ts +1 -1
  276. package/dist/services/interruptService.js +35 -29
  277. package/dist/services/nodeExecutionService.d.ts +1 -1
  278. package/dist/services/nodeExecutionService.js +45 -44
  279. package/dist/services/playgroundService.d.ts +1 -1
  280. package/dist/services/playgroundService.js +29 -29
  281. package/dist/services/portConfigApi.d.ts +2 -2
  282. package/dist/services/portConfigApi.js +8 -8
  283. package/dist/services/settingsService.d.ts +2 -2
  284. package/dist/services/settingsService.js +25 -19
  285. package/dist/services/toastService.d.ts +4 -4
  286. package/dist/services/toastService.js +33 -33
  287. package/dist/services/variableService.d.ts +1 -1
  288. package/dist/services/variableService.js +36 -36
  289. package/dist/services/workflowStorage.d.ts +2 -2
  290. package/dist/services/workflowStorage.js +13 -13
  291. package/dist/settings/index.d.ts +7 -7
  292. package/dist/settings/index.js +6 -6
  293. package/dist/skins/default.d.ts +2 -0
  294. package/dist/skins/default.js +1 -0
  295. package/dist/skins/index.d.ts +13 -0
  296. package/dist/skins/index.js +30 -0
  297. package/dist/skins/slate.d.ts +2 -0
  298. package/dist/skins/slate.js +78 -0
  299. package/dist/stores/categoriesStore.svelte.d.ts +1 -1
  300. package/dist/stores/categoriesStore.svelte.js +5 -5
  301. package/dist/stores/editorStateMachine.svelte.d.ts +2 -2
  302. package/dist/stores/editorStateMachine.svelte.js +65 -33
  303. package/dist/stores/historyStore.svelte.d.ts +4 -4
  304. package/dist/stores/historyStore.svelte.js +4 -4
  305. package/dist/stores/interruptStore.svelte.d.ts +3 -3
  306. package/dist/stores/interruptStore.svelte.js +21 -21
  307. package/dist/stores/playgroundStore.svelte.d.ts +2 -2
  308. package/dist/stores/playgroundStore.svelte.js +25 -18
  309. package/dist/stores/portCoordinateStore.svelte.d.ts +2 -2
  310. package/dist/stores/portCoordinateStore.svelte.js +15 -8
  311. package/dist/stores/settingsStore.svelte.d.ts +2 -2
  312. package/dist/stores/settingsStore.svelte.js +62 -57
  313. package/dist/stores/workflowStore.svelte.d.ts +3 -3
  314. package/dist/stores/workflowStore.svelte.js +50 -47
  315. package/dist/stories/CanvasDecorator.svelte +35 -32
  316. package/dist/stories/CanvasDecorator.svelte.d.ts +2 -2
  317. package/dist/stories/EdgeDecorator.svelte +102 -99
  318. package/dist/stories/EdgeDecorator.svelte.d.ts +1 -1
  319. package/dist/stories/NodeDecorator.svelte +59 -53
  320. package/dist/stories/NodeDecorator.svelte.d.ts +1 -1
  321. package/dist/stories/utils.d.ts +2 -2
  322. package/dist/stories/utils.js +105 -67
  323. package/dist/styles/base.css +599 -595
  324. package/dist/styles/toast.css +14 -14
  325. package/dist/styles/tokens.css +409 -378
  326. package/dist/svelte-app.d.ts +9 -9
  327. package/dist/svelte-app.js +39 -39
  328. package/dist/themes/default.d.ts +2 -0
  329. package/dist/themes/default.js +9 -0
  330. package/dist/themes/index.d.ts +13 -0
  331. package/dist/themes/index.js +44 -0
  332. package/dist/themes/minimal.d.ts +2 -0
  333. package/dist/themes/minimal.js +11 -0
  334. package/dist/types/agentspec.d.ts +18 -18
  335. package/dist/types/agentspec.js +2 -2
  336. package/dist/types/auth.d.ts +1 -1
  337. package/dist/types/auth.js +6 -6
  338. package/dist/types/config.d.ts +6 -6
  339. package/dist/types/events.d.ts +2 -2
  340. package/dist/types/events.js +2 -2
  341. package/dist/types/index.d.ts +32 -32
  342. package/dist/types/index.js +6 -6
  343. package/dist/types/interrupt.d.ts +6 -6
  344. package/dist/types/interrupt.js +21 -21
  345. package/dist/types/interruptState.d.ts +12 -12
  346. package/dist/types/interruptState.js +66 -66
  347. package/dist/types/playground.d.ts +7 -7
  348. package/dist/types/playground.js +14 -14
  349. package/dist/types/settings.d.ts +5 -3
  350. package/dist/types/settings.js +25 -18
  351. package/dist/types/skin.d.ts +31 -0
  352. package/dist/types/skin.js +1 -0
  353. package/dist/types/theme.d.ts +35 -0
  354. package/dist/types/theme.js +1 -0
  355. package/dist/types/uischema.d.ts +4 -4
  356. package/dist/types/uischema.js +3 -3
  357. package/dist/utils/colors.d.ts +1 -1
  358. package/dist/utils/colors.js +97 -95
  359. package/dist/utils/config.d.ts +2 -2
  360. package/dist/utils/config.js +48 -48
  361. package/dist/utils/connections.d.ts +2 -2
  362. package/dist/utils/connections.js +15 -15
  363. package/dist/utils/errors.js +3 -3
  364. package/dist/utils/fetchWithAuth.d.ts +1 -1
  365. package/dist/utils/fetchWithAuth.js +2 -2
  366. package/dist/utils/handleIds.d.ts +2 -2
  367. package/dist/utils/handleIds.js +8 -8
  368. package/dist/utils/handlePositioning.d.ts +1 -1
  369. package/dist/utils/handlePositioning.js +2 -2
  370. package/dist/utils/icons.d.ts +1 -1
  371. package/dist/utils/icons.js +74 -74
  372. package/dist/utils/logger.d.ts +1 -1
  373. package/dist/utils/logger.js +7 -7
  374. package/dist/utils/nodeStatus.d.ts +1 -1
  375. package/dist/utils/nodeStatus.js +48 -48
  376. package/dist/utils/nodeTypes.d.ts +1 -1
  377. package/dist/utils/nodeTypes.js +21 -20
  378. package/dist/utils/nodeWrapper.d.ts +7 -7
  379. package/dist/utils/nodeWrapper.js +21 -19
  380. package/dist/utils/performanceUtils.d.ts +1 -1
  381. package/dist/utils/performanceUtils.js +2 -1
  382. package/dist/utils/sanitize.js +1 -1
  383. package/dist/utils/uischema.d.ts +2 -2
  384. package/dist/utils/uischema.js +8 -8
  385. package/dist/utils/validation.js +20 -8
  386. package/package.json +1 -1
@@ -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>