@flowdrop/flowdrop 1.0.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 (403) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/dist/adapters/WorkflowAdapter.d.ts +167 -0
  4. package/dist/adapters/WorkflowAdapter.js +368 -0
  5. package/dist/adapters/agentspec/AgentSpecAdapter.d.ts +96 -0
  6. package/dist/adapters/agentspec/AgentSpecAdapter.js +626 -0
  7. package/dist/adapters/agentspec/agentAdapter.d.ts +59 -0
  8. package/dist/adapters/agentspec/agentAdapter.js +91 -0
  9. package/dist/adapters/agentspec/autoLayout.d.ts +34 -0
  10. package/dist/adapters/agentspec/autoLayout.js +127 -0
  11. package/dist/adapters/agentspec/componentTypeDefaults.d.ts +73 -0
  12. package/dist/adapters/agentspec/componentTypeDefaults.js +238 -0
  13. package/dist/adapters/agentspec/defaultNodeTypes.d.ts +53 -0
  14. package/dist/adapters/agentspec/defaultNodeTypes.js +561 -0
  15. package/dist/adapters/agentspec/index.d.ts +37 -0
  16. package/dist/adapters/agentspec/index.js +39 -0
  17. package/dist/adapters/agentspec/validator.d.ts +34 -0
  18. package/dist/adapters/agentspec/validator.js +169 -0
  19. package/dist/api/enhanced-client.d.ts +183 -0
  20. package/dist/api/enhanced-client.js +430 -0
  21. package/dist/components/App.svelte +981 -0
  22. package/dist/components/App.svelte.d.ts +54 -0
  23. package/dist/components/CanvasBanner.stories.svelte +29 -0
  24. package/dist/components/CanvasBanner.stories.svelte.d.ts +27 -0
  25. package/dist/components/CanvasBanner.svelte +57 -0
  26. package/dist/components/CanvasBanner.svelte.d.ts +8 -0
  27. package/dist/components/ConfigForm.svelte +1138 -0
  28. package/dist/components/ConfigForm.svelte.d.ts +44 -0
  29. package/dist/components/ConfigModal.svelte +188 -0
  30. package/dist/components/ConfigModal.svelte.d.ts +13 -0
  31. package/dist/components/ConfigPanel.stories.svelte +47 -0
  32. package/dist/components/ConfigPanel.stories.svelte.d.ts +27 -0
  33. package/dist/components/ConfigPanel.svelte +182 -0
  34. package/dist/components/ConfigPanel.svelte.d.ts +32 -0
  35. package/dist/components/ConnectionLine.svelte +32 -0
  36. package/dist/components/ConnectionLine.svelte.d.ts +3 -0
  37. package/dist/components/EdgeRefresher.svelte +41 -0
  38. package/dist/components/EdgeRefresher.svelte.d.ts +9 -0
  39. package/dist/components/FlowDropZone.svelte +83 -0
  40. package/dist/components/FlowDropZone.svelte.d.ts +13 -0
  41. package/dist/components/LoadingSpinner.stories.svelte +30 -0
  42. package/dist/components/LoadingSpinner.stories.svelte.d.ts +27 -0
  43. package/dist/components/LoadingSpinner.svelte +36 -0
  44. package/dist/components/LoadingSpinner.svelte.d.ts +8 -0
  45. package/dist/components/Logo.stories.svelte +22 -0
  46. package/dist/components/Logo.stories.svelte.d.ts +27 -0
  47. package/dist/components/Logo.svelte +102 -0
  48. package/dist/components/Logo.svelte.d.ts +26 -0
  49. package/dist/components/LogsSidebar.svelte +563 -0
  50. package/dist/components/LogsSidebar.svelte.d.ts +17 -0
  51. package/dist/components/MarkdownDisplay.stories.svelte +36 -0
  52. package/dist/components/MarkdownDisplay.stories.svelte.d.ts +27 -0
  53. package/dist/components/MarkdownDisplay.svelte +29 -0
  54. package/dist/components/MarkdownDisplay.svelte.d.ts +7 -0
  55. package/dist/components/Navbar.stories.svelte +53 -0
  56. package/dist/components/Navbar.stories.svelte.d.ts +27 -0
  57. package/dist/components/Navbar.svelte +726 -0
  58. package/dist/components/Navbar.svelte.d.ts +29 -0
  59. package/dist/components/NodeSidebar.svelte +762 -0
  60. package/dist/components/NodeSidebar.svelte.d.ts +9 -0
  61. package/dist/components/NodeStatusOverlay.stories.svelte +85 -0
  62. package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +27 -0
  63. package/dist/components/NodeStatusOverlay.svelte +327 -0
  64. package/dist/components/NodeStatusOverlay.svelte.d.ts +11 -0
  65. package/dist/components/PipelineStatus.svelte +314 -0
  66. package/dist/components/PipelineStatus.svelte.d.ts +20 -0
  67. package/dist/components/PortCoordinateTracker.svelte +58 -0
  68. package/dist/components/PortCoordinateTracker.svelte.d.ts +12 -0
  69. package/dist/components/ReadOnlyDetails.svelte +170 -0
  70. package/dist/components/ReadOnlyDetails.svelte.d.ts +25 -0
  71. package/dist/components/SchemaForm.stories.svelte +116 -0
  72. package/dist/components/SchemaForm.stories.svelte.d.ts +27 -0
  73. package/dist/components/SchemaForm.svelte +536 -0
  74. package/dist/components/SchemaForm.svelte.d.ts +83 -0
  75. package/dist/components/SettingsModal.svelte +279 -0
  76. package/dist/components/SettingsModal.svelte.d.ts +23 -0
  77. package/dist/components/SettingsPanel.svelte +638 -0
  78. package/dist/components/SettingsPanel.svelte.d.ts +21 -0
  79. package/dist/components/StatusIcon.stories.svelte +60 -0
  80. package/dist/components/StatusIcon.stories.svelte.d.ts +27 -0
  81. package/dist/components/StatusIcon.svelte +119 -0
  82. package/dist/components/StatusIcon.svelte.d.ts +10 -0
  83. package/dist/components/StatusLabel.stories.svelte +17 -0
  84. package/dist/components/StatusLabel.stories.svelte.d.ts +27 -0
  85. package/dist/components/StatusLabel.svelte +33 -0
  86. package/dist/components/StatusLabel.svelte.d.ts +7 -0
  87. package/dist/components/ThemeToggle.stories.svelte +25 -0
  88. package/dist/components/ThemeToggle.stories.svelte.d.ts +27 -0
  89. package/dist/components/ThemeToggle.svelte +185 -0
  90. package/dist/components/ThemeToggle.svelte.d.ts +14 -0
  91. package/dist/components/UniversalNode.svelte +155 -0
  92. package/dist/components/UniversalNode.svelte.d.ts +15 -0
  93. package/dist/components/WorkflowEditor.svelte +1035 -0
  94. package/dist/components/WorkflowEditor.svelte.d.ts +23 -0
  95. package/dist/components/form/FormArray.svelte +1049 -0
  96. package/dist/components/form/FormArray.svelte.d.ts +22 -0
  97. package/dist/components/form/FormAutocomplete.svelte +1009 -0
  98. package/dist/components/form/FormAutocomplete.svelte.d.ts +25 -0
  99. package/dist/components/form/FormCheckboxGroup.stories.svelte +28 -0
  100. package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +27 -0
  101. package/dist/components/form/FormCheckboxGroup.svelte +155 -0
  102. package/dist/components/form/FormCheckboxGroup.svelte.d.ts +17 -0
  103. package/dist/components/form/FormCodeEditor.svelte +458 -0
  104. package/dist/components/form/FormCodeEditor.svelte.d.ts +25 -0
  105. package/dist/components/form/FormField.svelte +417 -0
  106. package/dist/components/form/FormField.svelte.d.ts +29 -0
  107. package/dist/components/form/FormFieldLight.svelte +425 -0
  108. package/dist/components/form/FormFieldLight.svelte.d.ts +18 -0
  109. package/dist/components/form/FormFieldWrapper.stories.svelte +53 -0
  110. package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +27 -0
  111. package/dist/components/form/FormFieldWrapper.svelte +125 -0
  112. package/dist/components/form/FormFieldWrapper.svelte.d.ts +18 -0
  113. package/dist/components/form/FormFieldset.svelte +142 -0
  114. package/dist/components/form/FormFieldset.svelte.d.ts +11 -0
  115. package/dist/components/form/FormMarkdownEditor.svelte +752 -0
  116. package/dist/components/form/FormMarkdownEditor.svelte.d.ts +33 -0
  117. package/dist/components/form/FormNumberField.stories.svelte +36 -0
  118. package/dist/components/form/FormNumberField.stories.svelte.d.ts +27 -0
  119. package/dist/components/form/FormNumberField.svelte +112 -0
  120. package/dist/components/form/FormNumberField.svelte.d.ts +25 -0
  121. package/dist/components/form/FormRangeField.stories.svelte +31 -0
  122. package/dist/components/form/FormRangeField.stories.svelte.d.ts +27 -0
  123. package/dist/components/form/FormRangeField.svelte +246 -0
  124. package/dist/components/form/FormRangeField.svelte.d.ts +23 -0
  125. package/dist/components/form/FormSelect.stories.svelte +50 -0
  126. package/dist/components/form/FormSelect.stories.svelte.d.ts +27 -0
  127. package/dist/components/form/FormSelect.svelte +129 -0
  128. package/dist/components/form/FormSelect.svelte.d.ts +20 -0
  129. package/dist/components/form/FormTemplateEditor.svelte +825 -0
  130. package/dist/components/form/FormTemplateEditor.svelte.d.ts +41 -0
  131. package/dist/components/form/FormTextField.stories.svelte +30 -0
  132. package/dist/components/form/FormTextField.stories.svelte.d.ts +27 -0
  133. package/dist/components/form/FormTextField.svelte +91 -0
  134. package/dist/components/form/FormTextField.svelte.d.ts +19 -0
  135. package/dist/components/form/FormTextarea.stories.svelte +34 -0
  136. package/dist/components/form/FormTextarea.stories.svelte.d.ts +27 -0
  137. package/dist/components/form/FormTextarea.svelte +97 -0
  138. package/dist/components/form/FormTextarea.svelte.d.ts +21 -0
  139. package/dist/components/form/FormToggle.stories.svelte +30 -0
  140. package/dist/components/form/FormToggle.stories.svelte.d.ts +27 -0
  141. package/dist/components/form/FormToggle.svelte +126 -0
  142. package/dist/components/form/FormToggle.svelte.d.ts +19 -0
  143. package/dist/components/form/FormUISchemaRenderer.svelte +136 -0
  144. package/dist/components/form/FormUISchemaRenderer.svelte.d.ts +32 -0
  145. package/dist/components/form/index.d.ts +50 -0
  146. package/dist/components/form/index.js +54 -0
  147. package/dist/components/form/templateAutocomplete.d.ts +29 -0
  148. package/dist/components/form/templateAutocomplete.js +254 -0
  149. package/dist/components/form/types.d.ts +485 -0
  150. package/dist/components/form/types.js +73 -0
  151. package/dist/components/interrupt/ChoicePrompt.stories.svelte +52 -0
  152. package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +27 -0
  153. package/dist/components/interrupt/ChoicePrompt.svelte +401 -0
  154. package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +23 -0
  155. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +71 -0
  156. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +27 -0
  157. package/dist/components/interrupt/ConfirmationPrompt.svelte +292 -0
  158. package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +25 -0
  159. package/dist/components/interrupt/FormPrompt.svelte +236 -0
  160. package/dist/components/interrupt/FormPrompt.svelte.d.ts +23 -0
  161. package/dist/components/interrupt/InterruptBubble.svelte +601 -0
  162. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +16 -0
  163. package/dist/components/interrupt/ReviewPrompt.stories.svelte +67 -0
  164. package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +27 -0
  165. package/dist/components/interrupt/ReviewPrompt.svelte +861 -0
  166. package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +23 -0
  167. package/dist/components/interrupt/TextInputPrompt.stories.svelte +47 -0
  168. package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +27 -0
  169. package/dist/components/interrupt/TextInputPrompt.svelte +346 -0
  170. package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +23 -0
  171. package/dist/components/interrupt/index.d.ts +13 -0
  172. package/dist/components/interrupt/index.js +15 -0
  173. package/dist/components/layouts/MainLayout.svelte +718 -0
  174. package/dist/components/layouts/MainLayout.svelte.d.ts +62 -0
  175. package/dist/components/nodes/GatewayNode.stories.svelte +108 -0
  176. package/dist/components/nodes/GatewayNode.stories.svelte.d.ts +26 -0
  177. package/dist/components/nodes/GatewayNode.svelte +591 -0
  178. package/dist/components/nodes/GatewayNode.svelte.d.ts +15 -0
  179. package/dist/components/nodes/IdeaNode.stories.svelte +52 -0
  180. package/dist/components/nodes/IdeaNode.stories.svelte.d.ts +26 -0
  181. package/dist/components/nodes/IdeaNode.svelte +455 -0
  182. package/dist/components/nodes/IdeaNode.svelte.d.ts +24 -0
  183. package/dist/components/nodes/NotesNode.stories.svelte +76 -0
  184. package/dist/components/nodes/NotesNode.stories.svelte.d.ts +26 -0
  185. package/dist/components/nodes/NotesNode.svelte +378 -0
  186. package/dist/components/nodes/NotesNode.svelte.d.ts +24 -0
  187. package/dist/components/nodes/SimpleNode.stories.svelte +159 -0
  188. package/dist/components/nodes/SimpleNode.stories.svelte.d.ts +26 -0
  189. package/dist/components/nodes/SimpleNode.svelte +451 -0
  190. package/dist/components/nodes/SimpleNode.svelte.d.ts +25 -0
  191. package/dist/components/nodes/SquareNode.stories.svelte +82 -0
  192. package/dist/components/nodes/SquareNode.stories.svelte.d.ts +26 -0
  193. package/dist/components/nodes/SquareNode.svelte +407 -0
  194. package/dist/components/nodes/SquareNode.svelte.d.ts +25 -0
  195. package/dist/components/nodes/TerminalNode.stories.svelte +25 -0
  196. package/dist/components/nodes/TerminalNode.stories.svelte.d.ts +26 -0
  197. package/dist/components/nodes/TerminalNode.svelte +690 -0
  198. package/dist/components/nodes/TerminalNode.svelte.d.ts +25 -0
  199. package/dist/components/nodes/ToolNode.stories.svelte +189 -0
  200. package/dist/components/nodes/ToolNode.stories.svelte.d.ts +26 -0
  201. package/dist/components/nodes/ToolNode.svelte +471 -0
  202. package/dist/components/nodes/ToolNode.svelte.d.ts +36 -0
  203. package/dist/components/nodes/WorkflowNode.stories.svelte +55 -0
  204. package/dist/components/nodes/WorkflowNode.stories.svelte.d.ts +26 -0
  205. package/dist/components/nodes/WorkflowNode.svelte +571 -0
  206. package/dist/components/nodes/WorkflowNode.svelte.d.ts +15 -0
  207. package/dist/components/playground/ChatPanel.svelte +905 -0
  208. package/dist/components/playground/ChatPanel.svelte.d.ts +46 -0
  209. package/dist/components/playground/ExecutionLogs.svelte +488 -0
  210. package/dist/components/playground/ExecutionLogs.svelte.d.ts +14 -0
  211. package/dist/components/playground/InputCollector.svelte +444 -0
  212. package/dist/components/playground/InputCollector.svelte.d.ts +16 -0
  213. package/dist/components/playground/MessageBubble.stories.svelte +62 -0
  214. package/dist/components/playground/MessageBubble.stories.svelte.d.ts +27 -0
  215. package/dist/components/playground/MessageBubble.svelte +633 -0
  216. package/dist/components/playground/MessageBubble.svelte.d.ts +24 -0
  217. package/dist/components/playground/Playground.svelte +1075 -0
  218. package/dist/components/playground/Playground.svelte.d.ts +25 -0
  219. package/dist/components/playground/PlaygroundModal.svelte +220 -0
  220. package/dist/components/playground/PlaygroundModal.svelte.d.ts +25 -0
  221. package/dist/components/playground/SessionManager.svelte +538 -0
  222. package/dist/components/playground/SessionManager.svelte.d.ts +20 -0
  223. package/dist/config/agentSpecEndpoints.d.ts +70 -0
  224. package/dist/config/agentSpecEndpoints.js +65 -0
  225. package/dist/config/constants.d.ts +43 -0
  226. package/dist/config/constants.js +31 -0
  227. package/dist/config/defaultCategories.d.ts +7 -0
  228. package/dist/config/defaultCategories.js +126 -0
  229. package/dist/config/defaultPortConfig.d.ts +6 -0
  230. package/dist/config/defaultPortConfig.js +201 -0
  231. package/dist/config/endpoints.d.ts +160 -0
  232. package/dist/config/endpoints.js +146 -0
  233. package/dist/config/runtimeConfig.d.ts +47 -0
  234. package/dist/config/runtimeConfig.js +80 -0
  235. package/dist/core/index.d.ts +75 -0
  236. package/dist/core/index.js +92 -0
  237. package/dist/display/index.d.ts +29 -0
  238. package/dist/display/index.js +36 -0
  239. package/dist/editor/index.d.ts +95 -0
  240. package/dist/editor/index.js +138 -0
  241. package/dist/form/code.d.ts +101 -0
  242. package/dist/form/code.js +168 -0
  243. package/dist/form/fieldRegistry.d.ts +169 -0
  244. package/dist/form/fieldRegistry.js +152 -0
  245. package/dist/form/full.d.ts +56 -0
  246. package/dist/form/full.js +80 -0
  247. package/dist/form/index.d.ts +77 -0
  248. package/dist/form/index.js +91 -0
  249. package/dist/form/markdown.d.ts +69 -0
  250. package/dist/form/markdown.js +103 -0
  251. package/dist/helpers/nodeLayoutHelper.d.ts +14 -0
  252. package/dist/helpers/nodeLayoutHelper.js +19 -0
  253. package/dist/helpers/proximityConnect.d.ts +94 -0
  254. package/dist/helpers/proximityConnect.js +314 -0
  255. package/dist/helpers/workflowEditorHelper.d.ts +183 -0
  256. package/dist/helpers/workflowEditorHelper.js +595 -0
  257. package/dist/index.d.ts +37 -0
  258. package/dist/index.js +64 -0
  259. package/dist/mocks/app-environment.d.ts +8 -0
  260. package/dist/mocks/app-environment.js +16 -0
  261. package/dist/mocks/app-forms.d.ts +2 -0
  262. package/dist/mocks/app-forms.js +22 -0
  263. package/dist/mocks/app-navigation.d.ts +5 -0
  264. package/dist/mocks/app-navigation.js +36 -0
  265. package/dist/mocks/app-stores.d.ts +14 -0
  266. package/dist/mocks/app-stores.js +26 -0
  267. package/dist/playground/index.d.ts +131 -0
  268. package/dist/playground/index.js +172 -0
  269. package/dist/playground/mount.d.ts +203 -0
  270. package/dist/playground/mount.js +235 -0
  271. package/dist/registry/BaseRegistry.d.ts +92 -0
  272. package/dist/registry/BaseRegistry.js +124 -0
  273. package/dist/registry/builtinFormats.d.ts +23 -0
  274. package/dist/registry/builtinFormats.js +70 -0
  275. package/dist/registry/builtinNodes.d.ts +77 -0
  276. package/dist/registry/builtinNodes.js +211 -0
  277. package/dist/registry/index.d.ts +8 -0
  278. package/dist/registry/index.js +12 -0
  279. package/dist/registry/nodeComponentRegistry.d.ts +276 -0
  280. package/dist/registry/nodeComponentRegistry.js +262 -0
  281. package/dist/registry/plugin.d.ts +215 -0
  282. package/dist/registry/plugin.js +249 -0
  283. package/dist/registry/workflowFormatRegistry.d.ts +122 -0
  284. package/dist/registry/workflowFormatRegistry.js +96 -0
  285. package/dist/schema/index.d.ts +23 -0
  286. package/dist/schema/index.js +23 -0
  287. package/dist/schemas/v1/workflow.schema.json +1078 -0
  288. package/dist/services/agentSpecExecutionService.d.ts +106 -0
  289. package/dist/services/agentSpecExecutionService.js +334 -0
  290. package/dist/services/api.d.ts +115 -0
  291. package/dist/services/api.js +214 -0
  292. package/dist/services/apiVariableService.d.ts +114 -0
  293. package/dist/services/apiVariableService.js +338 -0
  294. package/dist/services/autoSaveService.d.ts +112 -0
  295. package/dist/services/autoSaveService.js +227 -0
  296. package/dist/services/categoriesApi.d.ts +14 -0
  297. package/dist/services/categoriesApi.js +49 -0
  298. package/dist/services/draftStorage.d.ts +171 -0
  299. package/dist/services/draftStorage.js +299 -0
  300. package/dist/services/dynamicSchemaService.d.ts +108 -0
  301. package/dist/services/dynamicSchemaService.js +444 -0
  302. package/dist/services/globalSave.d.ts +69 -0
  303. package/dist/services/globalSave.js +248 -0
  304. package/dist/services/historyService.d.ts +208 -0
  305. package/dist/services/historyService.js +321 -0
  306. package/dist/services/interruptService.d.ts +133 -0
  307. package/dist/services/interruptService.js +280 -0
  308. package/dist/services/nodeExecutionService.d.ts +63 -0
  309. package/dist/services/nodeExecutionService.js +266 -0
  310. package/dist/services/playgroundService.d.ts +130 -0
  311. package/dist/services/playgroundService.js +321 -0
  312. package/dist/services/portConfigApi.d.ts +14 -0
  313. package/dist/services/portConfigApi.js +54 -0
  314. package/dist/services/settingsService.d.ts +92 -0
  315. package/dist/services/settingsService.js +196 -0
  316. package/dist/services/toastService.d.ts +156 -0
  317. package/dist/services/toastService.js +265 -0
  318. package/dist/services/variableService.d.ts +141 -0
  319. package/dist/services/variableService.js +463 -0
  320. package/dist/services/workflowStorage.d.ts +37 -0
  321. package/dist/services/workflowStorage.js +116 -0
  322. package/dist/settings/index.d.ts +25 -0
  323. package/dist/settings/index.js +33 -0
  324. package/dist/stores/categoriesStore.svelte.d.ts +32 -0
  325. package/dist/stores/categoriesStore.svelte.js +77 -0
  326. package/dist/stores/editorStateMachine.svelte.d.ts +42 -0
  327. package/dist/stores/editorStateMachine.svelte.js +132 -0
  328. package/dist/stores/historyStore.svelte.d.ts +136 -0
  329. package/dist/stores/historyStore.svelte.js +207 -0
  330. package/dist/stores/interruptStore.svelte.d.ts +179 -0
  331. package/dist/stores/interruptStore.svelte.js +346 -0
  332. package/dist/stores/playgroundStore.svelte.d.ts +230 -0
  333. package/dist/stores/playgroundStore.svelte.js +515 -0
  334. package/dist/stores/portCoordinateStore.svelte.d.ts +66 -0
  335. package/dist/stores/portCoordinateStore.svelte.js +186 -0
  336. package/dist/stores/settingsStore.svelte.d.ts +158 -0
  337. package/dist/stores/settingsStore.svelte.js +544 -0
  338. package/dist/stores/workflowStore.svelte.d.ts +260 -0
  339. package/dist/stores/workflowStore.svelte.js +649 -0
  340. package/dist/stories/CanvasDecorator.svelte +49 -0
  341. package/dist/stories/CanvasDecorator.svelte.d.ts +8 -0
  342. package/dist/stories/NodeDecorator.svelte +73 -0
  343. package/dist/stories/NodeDecorator.svelte.d.ts +8 -0
  344. package/dist/stories/utils.d.ts +93 -0
  345. package/dist/stories/utils.js +122 -0
  346. package/dist/styles/base.css +1300 -0
  347. package/dist/styles/toast.css +35 -0
  348. package/dist/styles/tokens.css +475 -0
  349. package/dist/svelte-app.d.ts +150 -0
  350. package/dist/svelte-app.js +295 -0
  351. package/dist/types/agentspec.d.ts +318 -0
  352. package/dist/types/agentspec.js +48 -0
  353. package/dist/types/auth.d.ts +263 -0
  354. package/dist/types/auth.js +229 -0
  355. package/dist/types/config.d.ts +151 -0
  356. package/dist/types/config.js +7 -0
  357. package/dist/types/events.d.ts +190 -0
  358. package/dist/types/events.js +30 -0
  359. package/dist/types/index.d.ts +1234 -0
  360. package/dist/types/index.js +27 -0
  361. package/dist/types/interrupt.d.ts +390 -0
  362. package/dist/types/interrupt.js +145 -0
  363. package/dist/types/interruptState.d.ts +211 -0
  364. package/dist/types/interruptState.js +308 -0
  365. package/dist/types/playground.d.ts +351 -0
  366. package/dist/types/playground.js +95 -0
  367. package/dist/types/settings.d.ts +189 -0
  368. package/dist/types/settings.js +97 -0
  369. package/dist/types/uischema.d.ts +144 -0
  370. package/dist/types/uischema.js +51 -0
  371. package/dist/utils/colors.d.ts +288 -0
  372. package/dist/utils/colors.js +548 -0
  373. package/dist/utils/config.d.ts +37 -0
  374. package/dist/utils/config.js +226 -0
  375. package/dist/utils/connections.d.ts +125 -0
  376. package/dist/utils/connections.js +414 -0
  377. package/dist/utils/errors.d.ts +28 -0
  378. package/dist/utils/errors.js +44 -0
  379. package/dist/utils/fetchWithAuth.d.ts +25 -0
  380. package/dist/utils/fetchWithAuth.js +34 -0
  381. package/dist/utils/handleIds.d.ts +35 -0
  382. package/dist/utils/handleIds.js +58 -0
  383. package/dist/utils/handlePositioning.d.ts +31 -0
  384. package/dist/utils/handlePositioning.js +35 -0
  385. package/dist/utils/icons.d.ts +106 -0
  386. package/dist/utils/icons.js +157 -0
  387. package/dist/utils/logger.d.ts +47 -0
  388. package/dist/utils/logger.js +72 -0
  389. package/dist/utils/nodeStatus.d.ts +53 -0
  390. package/dist/utils/nodeStatus.js +183 -0
  391. package/dist/utils/nodeTypes.d.ts +117 -0
  392. package/dist/utils/nodeTypes.js +244 -0
  393. package/dist/utils/nodeWrapper.d.ts +39 -0
  394. package/dist/utils/nodeWrapper.js +62 -0
  395. package/dist/utils/performanceUtils.d.ts +30 -0
  396. package/dist/utils/performanceUtils.js +108 -0
  397. package/dist/utils/sanitize.d.ts +19 -0
  398. package/dist/utils/sanitize.js +31 -0
  399. package/dist/utils/uischema.d.ts +52 -0
  400. package/dist/utils/uischema.js +88 -0
  401. package/dist/utils/validation.d.ts +29 -0
  402. package/dist/utils/validation.js +39 -0
  403. package/package.json +292 -0
@@ -0,0 +1,1009 @@
1
+ <!--
2
+ FormAutocomplete Component
3
+ Text input with autocomplete suggestions fetched from a callback URL
4
+
5
+ Features:
6
+ - Single or multiple selection support
7
+ - Selected values displayed as removable tags with light blue background
8
+ - Debounced fetching on input
9
+ - Optional fetch on focus
10
+ - Loading state indicator
11
+ - Keyboard navigation (arrow keys, enter, escape, backspace to remove)
12
+ - Click-away to close dropdown
13
+ - Response mapping with labelField/valueField
14
+ - Error handling with retry
15
+ - Full accessibility support (ARIA combobox pattern)
16
+
17
+ Usage:
18
+ - For single selection: autocomplete.multiple = false (default)
19
+ - For multiple selection: autocomplete.multiple = true
20
+ - Selected values appear as tags with "x" button to remove
21
+ -->
22
+
23
+ <script lang="ts">
24
+ import { getContext, onMount } from 'svelte';
25
+ import Icon from '@iconify/svelte';
26
+ import type { AutocompleteConfig, AuthProvider } from '../../types/index.js';
27
+ import type { FieldOption } from './types.js';
28
+ import { buildFetchHeaders } from '../../utils/fetchWithAuth.js';
29
+ import { logger } from '../../utils/logger.js';
30
+
31
+ /**
32
+ * Props interface for FormAutocomplete component
33
+ */
34
+ interface Props {
35
+ /** Field identifier */
36
+ id: string;
37
+ /** Current selected value (string for single, string[] for multiple) */
38
+ value: string | string[];
39
+ /** Autocomplete configuration */
40
+ autocomplete: AutocompleteConfig;
41
+ /** Whether the field is required */
42
+ required?: boolean;
43
+ /** Placeholder text */
44
+ placeholder?: string;
45
+ /** Whether the field is disabled */
46
+ disabled?: boolean;
47
+ /** ARIA description ID */
48
+ ariaDescribedBy?: string;
49
+ /** Callback when value changes */
50
+ onChange: (value: string | string[]) => void;
51
+ }
52
+
53
+ let {
54
+ id,
55
+ value = '',
56
+ autocomplete,
57
+ required = false,
58
+ placeholder = '',
59
+ disabled = false,
60
+ ariaDescribedBy,
61
+ onChange
62
+ }: Props = $props();
63
+
64
+ // Get AuthProvider and baseUrl from context via getter functions
65
+ // This pattern ensures we always get the current value, even if props change after mount
66
+ const getAuthProvider = getContext<(() => AuthProvider | undefined) | undefined>(
67
+ 'flowdrop:getAuthProvider'
68
+ );
69
+ const getBaseUrl = getContext<(() => string) | undefined>('flowdrop:getBaseUrl');
70
+
71
+ // Configuration with defaults
72
+ const queryParam = $derived(autocomplete.queryParam ?? 'q');
73
+ const minChars = $derived(autocomplete.minChars ?? 0);
74
+ const debounceMs = $derived(autocomplete.debounceMs ?? 300);
75
+ const fetchOnFocus = $derived(autocomplete.fetchOnFocus ?? false);
76
+ const labelField = $derived(autocomplete.labelField ?? 'label');
77
+ const valueField = $derived(autocomplete.valueField ?? 'value');
78
+ const allowFreeText = $derived(autocomplete.allowFreeText ?? false);
79
+ const multiple = $derived(autocomplete.multiple ?? false);
80
+
81
+ // Component state
82
+ let inputElement: HTMLInputElement | undefined = $state(undefined);
83
+ let containerElement: HTMLDivElement | undefined = $state(undefined);
84
+ let popoverElement: HTMLDivElement | undefined = $state(undefined);
85
+ let inputValue = $state('');
86
+ let suggestions = $state<FieldOption[]>([]);
87
+ let isOpen = $state(false);
88
+ let isLoading = $state(false);
89
+ let error = $state<string | null>(null);
90
+ let highlightedIndex = $state(-1);
91
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null;
92
+ let abortController: AbortController | null = null;
93
+
94
+ // Popover positioning style
95
+ let popoverStyle = $state('');
96
+
97
+ // Cache of value-to-label mappings for selected items
98
+ let labelCache = $state<Map<string, string>>(new Map());
99
+
100
+ // Generate unique IDs for accessibility
101
+ // svelte-ignore state_referenced_locally — id prop never changes
102
+ const listboxId = `${id}-listbox`;
103
+ // svelte-ignore state_referenced_locally
104
+ const getOptionId = (index: number): string => `${id}-option-${index}`;
105
+
106
+ /**
107
+ * Get the selected values as an array (normalizes single/multiple)
108
+ */
109
+ const selectedValues = $derived<string[]>(
110
+ multiple
111
+ ? Array.isArray(value)
112
+ ? value
113
+ : value
114
+ ? [String(value)]
115
+ : []
116
+ : value
117
+ ? [String(value)]
118
+ : []
119
+ );
120
+
121
+ /**
122
+ * Check if a value is selected
123
+ */
124
+ function isSelected(optionValue: string): boolean {
125
+ return selectedValues.includes(optionValue);
126
+ }
127
+
128
+ /**
129
+ * Get display label for a selected value
130
+ */
131
+ function getDisplayLabel(val: string): string {
132
+ // Check cache first
133
+ if (labelCache.has(val)) {
134
+ return labelCache.get(val) ?? val;
135
+ }
136
+ // Check current suggestions
137
+ const match = suggestions.find((s) => s.value === val);
138
+ if (match) {
139
+ labelCache.set(val, match.label);
140
+ return match.label;
141
+ }
142
+ // Return value as fallback
143
+ return val;
144
+ }
145
+
146
+ /**
147
+ * Build the full URL for fetching suggestions
148
+ * @param query - The search query
149
+ * @returns Full URL with query parameter
150
+ */
151
+ function buildUrl(query: string): string {
152
+ const baseUrl = getBaseUrl?.() ?? '';
153
+ const url = autocomplete.url.startsWith('http')
154
+ ? autocomplete.url
155
+ : `${baseUrl}${autocomplete.url}`;
156
+
157
+ const separator = url.includes('?') ? '&' : '?';
158
+ return `${url}${separator}${encodeURIComponent(queryParam)}=${encodeURIComponent(query)}`;
159
+ }
160
+
161
+ /**
162
+ * Map API response to FieldOption array
163
+ * @param data - Response data from API
164
+ * @returns Array of FieldOption objects
165
+ */
166
+ function mapResponse(data: unknown): FieldOption[] {
167
+ if (!Array.isArray(data)) {
168
+ logger.warn('[FormAutocomplete] Response is not an array:', data);
169
+ return [];
170
+ }
171
+
172
+ return data.map((item: Record<string, unknown>) => ({
173
+ label: String(item[labelField] ?? item[valueField] ?? ''),
174
+ value: String(item[valueField] ?? '')
175
+ }));
176
+ }
177
+
178
+ /**
179
+ * Fetch suggestions from the callback URL
180
+ * @param query - The search query
181
+ */
182
+ async function fetchSuggestions(query: string): Promise<void> {
183
+ // Cancel any pending request
184
+ if (abortController) {
185
+ abortController.abort();
186
+ }
187
+
188
+ // Check minimum characters requirement
189
+ if (query.length < minChars && query.length > 0) {
190
+ suggestions = [];
191
+ return;
192
+ }
193
+
194
+ isLoading = true;
195
+ error = null;
196
+ abortController = new AbortController();
197
+
198
+ try {
199
+ // Build headers with authentication (call getter to get current value)
200
+ const headers = await buildFetchHeaders(getAuthProvider?.());
201
+
202
+ // Fetch with timeout
203
+ const timeoutId = setTimeout(() => {
204
+ abortController?.abort();
205
+ }, 5000);
206
+
207
+ const response = await fetch(buildUrl(query), {
208
+ method: 'GET',
209
+ headers,
210
+ signal: abortController.signal
211
+ });
212
+
213
+ clearTimeout(timeoutId);
214
+
215
+ if (!response.ok) {
216
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
217
+ }
218
+
219
+ const data = await response.json();
220
+ const mapped = mapResponse(data);
221
+
222
+ // Update label cache with fetched suggestions
223
+ mapped.forEach((opt) => {
224
+ labelCache.set(opt.value, opt.label);
225
+ });
226
+
227
+ suggestions = mapped;
228
+ highlightedIndex = -1;
229
+ } catch (err) {
230
+ if (err instanceof Error && err.name === 'AbortError') {
231
+ // Request was cancelled, ignore
232
+ return;
233
+ }
234
+ logger.error('[FormAutocomplete] Fetch error:', err);
235
+ error = err instanceof Error ? err.message : 'Failed to fetch suggestions';
236
+ suggestions = [];
237
+ } finally {
238
+ isLoading = false;
239
+ abortController = null;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Debounced fetch for input changes
245
+ * @param query - The search query
246
+ */
247
+ function debouncedFetch(query: string): void {
248
+ if (debounceTimer) {
249
+ clearTimeout(debounceTimer);
250
+ }
251
+
252
+ debounceTimer = setTimeout(() => {
253
+ fetchSuggestions(query);
254
+ }, debounceMs);
255
+ }
256
+
257
+ /**
258
+ * Handle input value changes
259
+ * @param event - Input event
260
+ */
261
+ function handleInput(event: Event): void {
262
+ const target = event.currentTarget as HTMLInputElement;
263
+ inputValue = target.value;
264
+
265
+ // Open dropdown
266
+ showDropdown();
267
+
268
+ // If allowFreeText and single mode, update the value immediately
269
+ if (allowFreeText && !multiple) {
270
+ onChange(inputValue);
271
+ }
272
+
273
+ // Fetch suggestions with debounce
274
+ debouncedFetch(inputValue);
275
+ }
276
+
277
+ /**
278
+ * Handle input focus
279
+ */
280
+ function handleFocus(): void {
281
+ if (fetchOnFocus && suggestions.length === 0 && !isLoading) {
282
+ fetchSuggestions(inputValue);
283
+ }
284
+ showDropdown();
285
+ }
286
+
287
+ /**
288
+ * Handle input blur
289
+ * Delayed to allow click events on options to fire first
290
+ */
291
+ function handleBlur(): void {
292
+ setTimeout(() => {
293
+ hideDropdown();
294
+
295
+ // If not allowFreeText and single mode, validate the input
296
+ if (!allowFreeText && !multiple && inputValue !== '') {
297
+ const currentVal = selectedValues;
298
+ const matchingSuggestion = suggestions.find(
299
+ (s) => s.value === currentVal[0] || s.label.toLowerCase() === inputValue.toLowerCase()
300
+ );
301
+ if (!matchingSuggestion && currentVal.length === 0) {
302
+ inputValue = '';
303
+ }
304
+ }
305
+ }, 200);
306
+ }
307
+
308
+ /**
309
+ * Handle option selection
310
+ * @param option - Selected option
311
+ */
312
+ function selectOption(option: FieldOption): void {
313
+ // Update label cache
314
+ labelCache.set(option.value, option.label);
315
+
316
+ if (multiple) {
317
+ const current = selectedValues;
318
+ if (current.includes(option.value)) {
319
+ // Remove if already selected
320
+ const newValues = current.filter((v) => v !== option.value);
321
+ onChange(newValues);
322
+ } else {
323
+ // Add to selection
324
+ const newValues = [...current, option.value];
325
+ onChange(newValues);
326
+ }
327
+ // Clear input and keep dropdown open for more selections
328
+ inputValue = '';
329
+ inputElement?.focus();
330
+ } else {
331
+ // Single selection mode
332
+ inputValue = '';
333
+ onChange(option.value);
334
+ hideDropdown();
335
+ }
336
+ highlightedIndex = -1;
337
+ }
338
+
339
+ /**
340
+ * Remove a selected tag
341
+ * @param valueToRemove - The value to remove
342
+ */
343
+ function removeTag(valueToRemove: string): void {
344
+ if (disabled) return;
345
+
346
+ if (multiple) {
347
+ const current = selectedValues;
348
+ const newValues = current.filter((v) => v !== valueToRemove);
349
+ onChange(newValues);
350
+ } else {
351
+ onChange('');
352
+ }
353
+ inputElement?.focus();
354
+ }
355
+
356
+ /**
357
+ * Handle keyboard navigation
358
+ * @param event - Keyboard event
359
+ */
360
+ function handleKeydown(event: KeyboardEvent): void {
361
+ // Handle backspace to remove last tag in multiple mode
362
+ if (event.key === 'Backspace' && inputValue === '' && selectedValues.length > 0) {
363
+ event.preventDefault();
364
+ const current = selectedValues;
365
+ if (multiple) {
366
+ const newValues = current.slice(0, -1);
367
+ onChange(newValues);
368
+ } else {
369
+ onChange('');
370
+ }
371
+ return;
372
+ }
373
+
374
+ if (!isOpen && event.key !== 'ArrowDown' && event.key !== 'Enter') {
375
+ return;
376
+ }
377
+
378
+ switch (event.key) {
379
+ case 'ArrowDown':
380
+ event.preventDefault();
381
+ if (!isOpen) {
382
+ showDropdown();
383
+ if (fetchOnFocus && suggestions.length === 0) {
384
+ fetchSuggestions(inputValue);
385
+ }
386
+ } else {
387
+ highlightedIndex = Math.min(highlightedIndex + 1, suggestions.length - 1);
388
+ }
389
+ break;
390
+
391
+ case 'ArrowUp':
392
+ event.preventDefault();
393
+ highlightedIndex = Math.max(highlightedIndex - 1, -1);
394
+ break;
395
+
396
+ case 'Enter':
397
+ event.preventDefault();
398
+ if (highlightedIndex >= 0 && highlightedIndex < suggestions.length) {
399
+ selectOption(suggestions[highlightedIndex]);
400
+ } else if (allowFreeText && inputValue !== '') {
401
+ if (multiple) {
402
+ const current = selectedValues;
403
+ if (!current.includes(inputValue)) {
404
+ onChange([...current, inputValue]);
405
+ }
406
+ inputValue = '';
407
+ } else {
408
+ onChange(inputValue);
409
+ hideDropdown();
410
+ }
411
+ }
412
+ break;
413
+
414
+ case 'Escape':
415
+ event.preventDefault();
416
+ hideDropdown();
417
+ highlightedIndex = -1;
418
+ break;
419
+
420
+ case 'Tab':
421
+ // Allow tab to close dropdown naturally
422
+ hideDropdown();
423
+ break;
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Retry failed fetch
429
+ */
430
+ function handleRetry(): void {
431
+ error = null;
432
+ fetchSuggestions(inputValue);
433
+ }
434
+
435
+ /**
436
+ * Clear all selections
437
+ */
438
+ function handleClearAll(): void {
439
+ inputValue = '';
440
+ onChange(multiple ? [] : '');
441
+ suggestions = [];
442
+ inputElement?.focus();
443
+ }
444
+
445
+ /**
446
+ * Sync label cache when value prop changes externally
447
+ */
448
+ $effect(() => {
449
+ // When value changes, try to find labels from suggestions
450
+ const vals = selectedValues;
451
+ vals.forEach((val) => {
452
+ if (!labelCache.has(val)) {
453
+ const match = suggestions.find((s) => s.value === val);
454
+ if (match) {
455
+ labelCache.set(val, match.label);
456
+ }
457
+ }
458
+ });
459
+ });
460
+
461
+ /**
462
+ * Calculate popover position relative to viewport
463
+ * Popover API renders in top layer, bypassing all stacking contexts
464
+ */
465
+ function updatePopoverPosition(): void {
466
+ if (!containerElement) return;
467
+
468
+ const rect = containerElement.getBoundingClientRect();
469
+ const viewportHeight = window.innerHeight;
470
+ const maxDropdownHeight = 240; // 15rem in pixels approximately
471
+ const spaceBelow = viewportHeight - rect.bottom;
472
+ const spaceAbove = rect.top;
473
+
474
+ const left = rect.left;
475
+ const width = rect.width;
476
+
477
+ if (spaceBelow < maxDropdownHeight && spaceAbove > spaceBelow) {
478
+ // Position above the input
479
+ const bottom = viewportHeight - rect.top + 4;
480
+ const maxHeight = Math.min(spaceAbove - 8, maxDropdownHeight);
481
+ popoverStyle = `bottom: ${bottom}px; left: ${left}px; width: ${width}px; max-height: ${maxHeight}px;`;
482
+ } else {
483
+ // Position below the input (default)
484
+ const top = rect.bottom + 4;
485
+ const maxHeight = Math.min(spaceBelow - 8, maxDropdownHeight);
486
+ popoverStyle = `top: ${top}px; left: ${left}px; width: ${width}px; max-height: ${maxHeight}px;`;
487
+ }
488
+ }
489
+
490
+ /**
491
+ * Show the popover dropdown
492
+ */
493
+ function showDropdown(): void {
494
+ if (!popoverElement || disabled) return;
495
+
496
+ updatePopoverPosition();
497
+
498
+ try {
499
+ popoverElement.showPopover();
500
+ isOpen = true;
501
+ } catch {
502
+ // Fallback for browsers without popover support - just set isOpen
503
+ isOpen = true;
504
+ }
505
+ }
506
+
507
+ /**
508
+ * Hide the popover dropdown
509
+ */
510
+ function hideDropdown(): void {
511
+ if (!popoverElement) return;
512
+
513
+ try {
514
+ popoverElement.hidePopover();
515
+ } catch {
516
+ // Fallback for browsers without popover support
517
+ }
518
+ isOpen = false;
519
+ }
520
+
521
+ /**
522
+ * Effect to update popover position on scroll/resize when open
523
+ */
524
+ $effect(() => {
525
+ if (isOpen && containerElement) {
526
+ const handlePositionUpdate = (): void => {
527
+ updatePopoverPosition();
528
+ };
529
+
530
+ window.addEventListener('scroll', handlePositionUpdate, true);
531
+ window.addEventListener('resize', handlePositionUpdate);
532
+
533
+ return () => {
534
+ window.removeEventListener('scroll', handlePositionUpdate, true);
535
+ window.removeEventListener('resize', handlePositionUpdate);
536
+ };
537
+ }
538
+ });
539
+
540
+ /**
541
+ * Cleanup on unmount
542
+ */
543
+ onMount(() => {
544
+ return () => {
545
+ // Cleanup debounce timer
546
+ if (debounceTimer) {
547
+ clearTimeout(debounceTimer);
548
+ }
549
+ // Cleanup abort controller
550
+ if (abortController) {
551
+ abortController.abort();
552
+ }
553
+ };
554
+ });
555
+ </script>
556
+
557
+ <div
558
+ bind:this={containerElement}
559
+ class="form-autocomplete"
560
+ class:form-autocomplete--disabled={disabled}
561
+ class:form-autocomplete--multiple={multiple}
562
+ class:form-autocomplete--has-value={selectedValues.length > 0}
563
+ >
564
+ <!-- Main input container styled like a textfield/textarea -->
565
+ <div
566
+ class="form-autocomplete__field"
567
+ class:form-autocomplete__field--focused={isOpen}
568
+ onclick={() => inputElement?.focus()}
569
+ onkeydown={() => {}}
570
+ role="presentation"
571
+ >
572
+ <!-- Selected tags -->
573
+ {#if selectedValues.length > 0}
574
+ <div class="form-autocomplete__tags">
575
+ {#each selectedValues as selectedVal (selectedVal)}
576
+ <span class="form-autocomplete__tag">
577
+ <span class="form-autocomplete__tag-label">{getDisplayLabel(selectedVal)}</span>
578
+ {#if !disabled}
579
+ <button
580
+ type="button"
581
+ class="form-autocomplete__tag-remove"
582
+ aria-label={`Remove ${getDisplayLabel(selectedVal)}`}
583
+ onclick={(e) => {
584
+ e.stopPropagation();
585
+ removeTag(selectedVal);
586
+ }}
587
+ tabindex={-1}
588
+ >
589
+ <Icon icon="heroicons:x-mark" />
590
+ </button>
591
+ {/if}
592
+ </span>
593
+ {/each}
594
+ </div>
595
+ {/if}
596
+
597
+ <!-- Input area -->
598
+ <div class="form-autocomplete__input-area">
599
+ <input
600
+ bind:this={inputElement}
601
+ type="text"
602
+ {id}
603
+ class="form-autocomplete__input"
604
+ value={inputValue}
605
+ placeholder={selectedValues.length === 0 ? placeholder : ''}
606
+ {disabled}
607
+ aria-required={required}
608
+ aria-describedby={ariaDescribedBy}
609
+ role="combobox"
610
+ aria-expanded={isOpen}
611
+ aria-controls={listboxId}
612
+ aria-activedescendant={highlightedIndex >= 0 ? getOptionId(highlightedIndex) : undefined}
613
+ aria-autocomplete="list"
614
+ autocomplete="off"
615
+ oninput={handleInput}
616
+ onfocus={handleFocus}
617
+ onblur={handleBlur}
618
+ onkeydown={handleKeydown}
619
+ />
620
+ </div>
621
+
622
+ <!-- Status icons -->
623
+ <div class="form-autocomplete__icons">
624
+ {#if isLoading}
625
+ <span class="form-autocomplete__spinner" aria-label="Loading suggestions">
626
+ <Icon icon="heroicons:arrow-path" />
627
+ </span>
628
+ {:else if selectedValues.length > 0 && !disabled}
629
+ <button
630
+ type="button"
631
+ class="form-autocomplete__clear"
632
+ aria-label="Clear all selections"
633
+ onclick={(e) => {
634
+ e.stopPropagation();
635
+ handleClearAll();
636
+ }}
637
+ tabindex={-1}
638
+ >
639
+ <Icon icon="heroicons:x-mark" />
640
+ </button>
641
+ {:else}
642
+ <span class="form-autocomplete__chevron" aria-hidden="true">
643
+ <Icon icon="heroicons:chevron-down" />
644
+ </span>
645
+ {/if}
646
+ </div>
647
+ </div>
648
+
649
+ <!-- Dropdown popover (uses Popover API to render in top layer, bypassing stacking contexts) -->
650
+ <!-- svelte-ignore a11y_no_static_element_interactions — role="presentation" container; onmousedown prevents focus loss from input -->
651
+ <div
652
+ bind:this={popoverElement}
653
+ id={listboxId}
654
+ class="form-autocomplete__popover"
655
+ popover="manual"
656
+ role="presentation"
657
+ style={popoverStyle}
658
+ onmousedown={(e) => e.preventDefault()}
659
+ >
660
+ <ul class="form-autocomplete__listbox" role="listbox" aria-label="Suggestions">
661
+ {#if isLoading}
662
+ <li class="form-autocomplete__status form-autocomplete__status--loading">
663
+ <Icon icon="heroicons:arrow-path" class="form-autocomplete__status-icon" />
664
+ <span>Loading suggestions...</span>
665
+ </li>
666
+ {:else if error}
667
+ <li class="form-autocomplete__status form-autocomplete__status--error">
668
+ <Icon icon="heroicons:exclamation-triangle" class="form-autocomplete__status-icon" />
669
+ <span>{error}</span>
670
+ <button type="button" class="form-autocomplete__retry" onclick={handleRetry}>
671
+ Retry
672
+ </button>
673
+ </li>
674
+ {:else if suggestions.length === 0}
675
+ <li class="form-autocomplete__status form-autocomplete__status--empty">
676
+ <Icon icon="heroicons:magnifying-glass" class="form-autocomplete__status-icon" />
677
+ <span>No results found</span>
678
+ </li>
679
+ {:else}
680
+ {#each suggestions as option, index (option.value)}
681
+ <!-- svelte-ignore a11y_click_events_have_key_events — WAI-ARIA combobox: keyboard nav handled on input, not individual options -->
682
+ <li
683
+ id={getOptionId(index)}
684
+ class="form-autocomplete__option"
685
+ class:form-autocomplete__option--highlighted={index === highlightedIndex}
686
+ class:form-autocomplete__option--selected={isSelected(option.value)}
687
+ role="option"
688
+ aria-selected={isSelected(option.value)}
689
+ onmouseenter={() => (highlightedIndex = index)}
690
+ onclick={() => selectOption(option)}
691
+ >
692
+ <span class="form-autocomplete__option-label">{option.label}</span>
693
+ {#if isSelected(option.value)}
694
+ <Icon icon="heroicons:check" class="form-autocomplete__option-check" />
695
+ {/if}
696
+ </li>
697
+ {/each}
698
+ {/if}
699
+ </ul>
700
+ </div>
701
+ </div>
702
+
703
+ <style>
704
+ .form-autocomplete {
705
+ position: relative;
706
+ width: 100%;
707
+ }
708
+
709
+ .form-autocomplete--disabled {
710
+ opacity: 0.6;
711
+ pointer-events: none;
712
+ }
713
+
714
+ /* Field container styled like a textfield */
715
+ .form-autocomplete__field {
716
+ display: flex;
717
+ flex-wrap: wrap;
718
+ align-items: flex-start;
719
+ gap: var(--fd-space-3xs);
720
+ min-height: 2.625rem;
721
+ padding: var(--fd-space-3xs) 2.5rem var(--fd-space-3xs) var(--fd-space-md);
722
+ border: 1px solid var(--fd-border);
723
+ border-radius: var(--fd-radius-lg);
724
+ font-size: var(--fd-text-sm);
725
+ font-family: inherit;
726
+ color: var(--fd-foreground);
727
+ background-color: var(--fd-muted);
728
+ transition: all var(--fd-transition-normal);
729
+ box-shadow: var(--fd-shadow-sm);
730
+ cursor: text;
731
+ position: relative;
732
+ }
733
+
734
+ .form-autocomplete__field:hover {
735
+ border-color: var(--fd-border-strong);
736
+ background-color: var(--fd-background);
737
+ }
738
+
739
+ .form-autocomplete__field--focused {
740
+ border-color: var(--fd-primary);
741
+ background-color: var(--fd-background);
742
+ box-shadow:
743
+ 0 0 0 3px rgba(59, 130, 246, 0.12),
744
+ var(--fd-shadow-sm);
745
+ }
746
+
747
+ /* Multiple mode - textarea-like styling */
748
+ .form-autocomplete--multiple .form-autocomplete__field {
749
+ min-height: 3rem;
750
+ align-content: flex-start;
751
+ }
752
+
753
+ /* Tags container */
754
+ .form-autocomplete__tags {
755
+ display: flex;
756
+ flex-wrap: wrap;
757
+ gap: var(--fd-space-3xs);
758
+ align-items: center;
759
+ }
760
+
761
+ /* Individual tag - selected item chip */
762
+ .form-autocomplete__tag {
763
+ display: inline-flex;
764
+ align-items: center;
765
+ gap: var(--fd-space-3xs);
766
+ padding: var(--fd-space-3xs) var(--fd-space-3xs) var(--fd-space-3xs) var(--fd-space-xs);
767
+ background-color: var(--fd-primary-muted);
768
+ border: 1px solid var(--fd-primary-muted);
769
+ border-radius: var(--fd-radius-md);
770
+ font-size: 0.8125rem;
771
+ color: var(--fd-primary-hover);
772
+ line-height: 1.2;
773
+ max-width: 100%;
774
+ }
775
+
776
+ .form-autocomplete__tag-label {
777
+ overflow: hidden;
778
+ text-overflow: ellipsis;
779
+ white-space: nowrap;
780
+ max-width: 12rem;
781
+ }
782
+
783
+ .form-autocomplete__tag-remove {
784
+ display: flex;
785
+ align-items: center;
786
+ justify-content: center;
787
+ padding: 0.125rem;
788
+ margin-left: 0.125rem;
789
+ border: none;
790
+ border-radius: var(--fd-radius-sm);
791
+ background: transparent;
792
+ color: var(--fd-primary);
793
+ cursor: pointer;
794
+ transition: all var(--fd-transition-fast);
795
+ flex-shrink: 0;
796
+ }
797
+
798
+ .form-autocomplete__tag-remove:hover {
799
+ background-color: var(--fd-primary-muted);
800
+ color: var(--fd-primary-hover);
801
+ }
802
+
803
+ .form-autocomplete__tag-remove :global(svg) {
804
+ width: 0.875rem;
805
+ height: 0.875rem;
806
+ }
807
+
808
+ /* Input area */
809
+ .form-autocomplete__input-area {
810
+ flex: 1;
811
+ min-width: 4rem;
812
+ display: flex;
813
+ align-items: center;
814
+ }
815
+
816
+ .form-autocomplete__input {
817
+ width: 100%;
818
+ padding: var(--fd-space-3xs) 0;
819
+ border: none;
820
+ outline: none;
821
+ font-size: var(--fd-text-sm);
822
+ font-family: inherit;
823
+ color: var(--fd-foreground);
824
+ background-color: transparent;
825
+ }
826
+
827
+ .form-autocomplete__input::placeholder {
828
+ color: var(--fd-muted-foreground);
829
+ }
830
+
831
+ /* Status icons */
832
+ .form-autocomplete__icons {
833
+ position: absolute;
834
+ right: var(--fd-space-xs);
835
+ top: 0.625rem;
836
+ display: flex;
837
+ align-items: center;
838
+ gap: var(--fd-space-3xs);
839
+ }
840
+
841
+ .form-autocomplete__chevron,
842
+ .form-autocomplete__spinner {
843
+ display: flex;
844
+ align-items: center;
845
+ justify-content: center;
846
+ color: var(--fd-muted-foreground);
847
+ pointer-events: none;
848
+ }
849
+
850
+ .form-autocomplete__chevron :global(svg),
851
+ .form-autocomplete__spinner :global(svg) {
852
+ width: 1rem;
853
+ height: 1rem;
854
+ }
855
+
856
+ .form-autocomplete__spinner {
857
+ animation: autocomplete-spin 1s linear infinite;
858
+ }
859
+
860
+ @keyframes autocomplete-spin {
861
+ from {
862
+ transform: rotate(0deg);
863
+ }
864
+ to {
865
+ transform: rotate(360deg);
866
+ }
867
+ }
868
+
869
+ .form-autocomplete__clear {
870
+ display: flex;
871
+ align-items: center;
872
+ justify-content: center;
873
+ padding: var(--fd-space-3xs);
874
+ border: none;
875
+ border-radius: var(--fd-radius-sm);
876
+ background: transparent;
877
+ color: var(--fd-muted-foreground);
878
+ cursor: pointer;
879
+ transition: all var(--fd-transition-fast);
880
+ }
881
+
882
+ .form-autocomplete__clear:hover {
883
+ background-color: var(--fd-muted);
884
+ color: var(--fd-foreground);
885
+ }
886
+
887
+ .form-autocomplete__clear :global(svg) {
888
+ width: 1rem;
889
+ height: 1rem;
890
+ }
891
+
892
+ /* Popover container - renders in top layer via Popover API */
893
+ .form-autocomplete__popover {
894
+ position: fixed;
895
+ margin: 0;
896
+ padding: 0;
897
+ border: none;
898
+ background: transparent;
899
+ overflow: visible;
900
+ }
901
+
902
+ /* Reset default popover styles */
903
+ .form-autocomplete__popover:popover-open {
904
+ display: block;
905
+ }
906
+
907
+ /* Dropdown listbox inside popover */
908
+ .form-autocomplete__listbox {
909
+ margin: 0;
910
+ padding: var(--fd-space-3xs);
911
+ list-style: none;
912
+ background-color: var(--fd-background);
913
+ border: 1px solid var(--fd-border);
914
+ border-radius: var(--fd-radius-lg);
915
+ box-shadow: var(--fd-shadow-lg);
916
+ overflow-y: auto;
917
+ max-height: inherit;
918
+ }
919
+
920
+ .form-autocomplete__option {
921
+ display: flex;
922
+ align-items: center;
923
+ justify-content: space-between;
924
+ padding: var(--fd-space-xs) var(--fd-space-md);
925
+ border-radius: var(--fd-radius-md);
926
+ cursor: pointer;
927
+ transition: background-color var(--fd-transition-fast);
928
+ }
929
+
930
+ .form-autocomplete__option:hover,
931
+ .form-autocomplete__option--highlighted {
932
+ background-color: var(--fd-muted);
933
+ }
934
+
935
+ .form-autocomplete__option--selected {
936
+ background-color: var(--fd-primary-muted);
937
+ }
938
+
939
+ .form-autocomplete__option--highlighted.form-autocomplete__option--selected {
940
+ background-color: var(--fd-primary-muted);
941
+ }
942
+
943
+ .form-autocomplete__option-label {
944
+ font-size: var(--fd-text-sm);
945
+ color: var(--fd-foreground);
946
+ flex: 1;
947
+ overflow: hidden;
948
+ text-overflow: ellipsis;
949
+ white-space: nowrap;
950
+ }
951
+
952
+ .form-autocomplete__option :global(.form-autocomplete__option-check) {
953
+ width: 1rem;
954
+ height: 1rem;
955
+ color: var(--fd-primary);
956
+ flex-shrink: 0;
957
+ margin-left: var(--fd-space-xs);
958
+ }
959
+
960
+ /* Status messages */
961
+ .form-autocomplete__status {
962
+ display: flex;
963
+ align-items: center;
964
+ gap: var(--fd-space-xs);
965
+ padding: var(--fd-space-md);
966
+ font-size: var(--fd-text-sm);
967
+ color: var(--fd-muted-foreground);
968
+ }
969
+
970
+ .form-autocomplete__status--loading {
971
+ color: var(--fd-primary);
972
+ }
973
+
974
+ .form-autocomplete__status--error {
975
+ color: var(--fd-error);
976
+ flex-wrap: wrap;
977
+ }
978
+
979
+ .form-autocomplete__status--empty {
980
+ color: var(--fd-muted-foreground);
981
+ }
982
+
983
+ .form-autocomplete__status :global(.form-autocomplete__status-icon) {
984
+ width: 1rem;
985
+ height: 1rem;
986
+ flex-shrink: 0;
987
+ }
988
+
989
+ .form-autocomplete__status--loading :global(.form-autocomplete__status-icon) {
990
+ animation: autocomplete-spin 1s linear infinite;
991
+ }
992
+
993
+ .form-autocomplete__retry {
994
+ margin-left: auto;
995
+ padding: var(--fd-space-3xs) var(--fd-space-xs);
996
+ border: 1px solid var(--fd-error);
997
+ border-radius: var(--fd-radius-sm);
998
+ background-color: transparent;
999
+ color: var(--fd-error);
1000
+ font-size: var(--fd-text-xs);
1001
+ font-weight: 500;
1002
+ cursor: pointer;
1003
+ transition: all var(--fd-transition-fast);
1004
+ }
1005
+
1006
+ .form-autocomplete__retry:hover {
1007
+ background-color: var(--fd-error-muted);
1008
+ }
1009
+ </style>