@flowdrop/flowdrop 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (388) hide show
  1. package/README.md +50 -50
  2. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  3. package/dist/adapters/WorkflowAdapter.js +25 -25
  4. package/dist/adapters/agentspec/AgentSpecAdapter.d.ts +2 -2
  5. package/dist/adapters/agentspec/AgentSpecAdapter.js +133 -122
  6. package/dist/adapters/agentspec/agentAdapter.d.ts +2 -2
  7. package/dist/adapters/agentspec/agentAdapter.js +10 -10
  8. package/dist/adapters/agentspec/autoLayout.d.ts +1 -1
  9. package/dist/adapters/agentspec/autoLayout.js +2 -2
  10. package/dist/adapters/agentspec/componentTypeDefaults.d.ts +1 -1
  11. package/dist/adapters/agentspec/componentTypeDefaults.js +120 -120
  12. package/dist/adapters/agentspec/defaultNodeTypes.d.ts +2 -2
  13. package/dist/adapters/agentspec/defaultNodeTypes.js +307 -307
  14. package/dist/adapters/agentspec/index.d.ts +10 -10
  15. package/dist/adapters/agentspec/index.js +6 -6
  16. package/dist/adapters/agentspec/validator.d.ts +2 -2
  17. package/dist/adapters/agentspec/validator.js +22 -20
  18. package/dist/api/enhanced-client.d.ts +3 -3
  19. package/dist/api/enhanced-client.js +73 -72
  20. package/dist/components/App.svelte +1081 -961
  21. package/dist/components/App.svelte.d.ts +9 -6
  22. package/dist/components/CanvasBanner.stories.svelte +23 -20
  23. package/dist/components/CanvasBanner.stories.svelte.d.ts +1 -1
  24. package/dist/components/CanvasBanner.svelte +46 -46
  25. package/dist/components/ConfigForm.svelte +1164 -1065
  26. package/dist/components/ConfigForm.svelte.d.ts +2 -2
  27. package/dist/components/ConfigModal.svelte +180 -180
  28. package/dist/components/ConfigModal.svelte.d.ts +1 -1
  29. package/dist/components/ConfigPanel.stories.svelte +35 -35
  30. package/dist/components/ConfigPanel.stories.svelte.d.ts +1 -1
  31. package/dist/components/ConfigPanel.svelte +178 -167
  32. package/dist/components/ConfigPanel.svelte.d.ts +1 -1
  33. package/dist/components/ConnectionLine.svelte +25 -25
  34. package/dist/components/EdgeRefresher.svelte +26 -26
  35. package/dist/components/FlowDropEdge.stories.svelte +197 -0
  36. package/dist/components/FlowDropEdge.stories.svelte.d.ts +26 -0
  37. package/dist/components/FlowDropEdge.svelte +168 -0
  38. package/dist/components/FlowDropEdge.svelte.d.ts +4 -0
  39. package/dist/components/FlowDropZone.svelte +63 -60
  40. package/dist/components/FlowDropZone.svelte.d.ts +1 -1
  41. package/dist/components/LoadingSpinner.stories.svelte +19 -19
  42. package/dist/components/LoadingSpinner.stories.svelte.d.ts +1 -1
  43. package/dist/components/LoadingSpinner.svelte +21 -21
  44. package/dist/components/LoadingSpinner.svelte.d.ts +1 -1
  45. package/dist/components/Logo.stories.svelte +13 -13
  46. package/dist/components/Logo.stories.svelte.d.ts +1 -1
  47. package/dist/components/Logo.svelte +101 -95
  48. package/dist/components/LogsSidebar.svelte +553 -546
  49. package/dist/components/LogsSidebar.svelte.d.ts +1 -1
  50. package/dist/components/MarkdownDisplay.stories.svelte +29 -23
  51. package/dist/components/MarkdownDisplay.stories.svelte.d.ts +1 -1
  52. package/dist/components/MarkdownDisplay.svelte +16 -14
  53. package/dist/components/Navbar.stories.svelte +43 -38
  54. package/dist/components/Navbar.stories.svelte.d.ts +1 -1
  55. package/dist/components/Navbar.svelte +760 -706
  56. package/dist/components/Navbar.svelte.d.ts +1 -1
  57. package/dist/components/NodeSidebar.svelte +900 -746
  58. package/dist/components/NodeSidebar.svelte.d.ts +3 -1
  59. package/dist/components/NodeStatusOverlay.stories.svelte +82 -70
  60. package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +1 -1
  61. package/dist/components/NodeStatusOverlay.svelte +295 -280
  62. package/dist/components/NodeStatusOverlay.svelte.d.ts +3 -3
  63. package/dist/components/PipelineStatus.svelte +326 -300
  64. package/dist/components/PipelineStatus.svelte.d.ts +4 -4
  65. package/dist/components/PortCoordinateTracker.svelte +49 -47
  66. package/dist/components/PortCoordinateTracker.svelte.d.ts +1 -1
  67. package/dist/components/ReadOnlyDetails.svelte +156 -156
  68. package/dist/components/SchemaForm.stories.svelte +106 -98
  69. package/dist/components/SchemaForm.stories.svelte.d.ts +1 -1
  70. package/dist/components/SchemaForm.svelte +490 -463
  71. package/dist/components/SchemaForm.svelte.d.ts +2 -2
  72. package/dist/components/SettingsModal.svelte +226 -223
  73. package/dist/components/SettingsModal.svelte.d.ts +1 -1
  74. package/dist/components/SettingsPanel.svelte +637 -601
  75. package/dist/components/SettingsPanel.svelte.d.ts +1 -1
  76. package/dist/components/StatusIcon.stories.svelte +62 -49
  77. package/dist/components/StatusIcon.stories.svelte.d.ts +1 -1
  78. package/dist/components/StatusIcon.svelte +87 -87
  79. package/dist/components/StatusIcon.svelte.d.ts +2 -2
  80. package/dist/components/StatusLabel.stories.svelte +12 -12
  81. package/dist/components/StatusLabel.stories.svelte.d.ts +1 -1
  82. package/dist/components/StatusLabel.svelte +19 -19
  83. package/dist/components/ThemeToggle.stories.svelte +16 -16
  84. package/dist/components/ThemeToggle.stories.svelte.d.ts +1 -1
  85. package/dist/components/ThemeToggle.svelte +180 -169
  86. package/dist/components/ThemeToggle.svelte.d.ts +1 -1
  87. package/dist/components/UniversalNode.svelte +150 -138
  88. package/dist/components/UniversalNode.svelte.d.ts +3 -3
  89. package/dist/components/WorkflowEditor.svelte +1069 -1007
  90. package/dist/components/WorkflowEditor.svelte.d.ts +4 -4
  91. package/dist/components/form/FormArray.svelte +1034 -973
  92. package/dist/components/form/FormArray.svelte.d.ts +1 -1
  93. package/dist/components/form/FormAutocomplete.svelte +1021 -978
  94. package/dist/components/form/FormAutocomplete.svelte.d.ts +1 -1
  95. package/dist/components/form/FormCheckboxGroup.stories.svelte +23 -20
  96. package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +1 -1
  97. package/dist/components/form/FormCheckboxGroup.svelte +136 -136
  98. package/dist/components/form/FormCodeEditor.svelte +452 -434
  99. package/dist/components/form/FormField.svelte +366 -355
  100. package/dist/components/form/FormField.svelte.d.ts +2 -2
  101. package/dist/components/form/FormFieldLight.svelte +400 -384
  102. package/dist/components/form/FormFieldLight.svelte.d.ts +1 -1
  103. package/dist/components/form/FormFieldWrapper.stories.svelte +42 -42
  104. package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +1 -1
  105. package/dist/components/form/FormFieldWrapper.svelte +100 -93
  106. package/dist/components/form/FormFieldWrapper.svelte.d.ts +1 -1
  107. package/dist/components/form/FormFieldset.svelte +108 -108
  108. package/dist/components/form/FormFieldset.svelte.d.ts +2 -2
  109. package/dist/components/form/FormMarkdownEditor.svelte +758 -725
  110. package/dist/components/form/FormNumberField.stories.svelte +25 -25
  111. package/dist/components/form/FormNumberField.stories.svelte.d.ts +1 -1
  112. package/dist/components/form/FormNumberField.svelte +88 -88
  113. package/dist/components/form/FormRangeField.stories.svelte +20 -20
  114. package/dist/components/form/FormRangeField.stories.svelte.d.ts +1 -1
  115. package/dist/components/form/FormRangeField.svelte +234 -226
  116. package/dist/components/form/FormSelect.stories.svelte +38 -38
  117. package/dist/components/form/FormSelect.stories.svelte.d.ts +1 -1
  118. package/dist/components/form/FormSelect.svelte +101 -101
  119. package/dist/components/form/FormSelect.svelte.d.ts +1 -1
  120. package/dist/components/form/FormTemplateEditor.svelte +847 -798
  121. package/dist/components/form/FormTemplateEditor.svelte.d.ts +1 -1
  122. package/dist/components/form/FormTextField.stories.svelte +29 -23
  123. package/dist/components/form/FormTextField.stories.svelte.d.ts +1 -1
  124. package/dist/components/form/FormTextField.svelte +68 -68
  125. package/dist/components/form/FormTextarea.stories.svelte +28 -25
  126. package/dist/components/form/FormTextarea.stories.svelte.d.ts +1 -1
  127. package/dist/components/form/FormTextarea.svelte +74 -74
  128. package/dist/components/form/FormToggle.stories.svelte +23 -20
  129. package/dist/components/form/FormToggle.stories.svelte.d.ts +1 -1
  130. package/dist/components/form/FormToggle.svelte +98 -98
  131. package/dist/components/form/FormUISchemaRenderer.svelte +120 -113
  132. package/dist/components/form/FormUISchemaRenderer.svelte.d.ts +3 -3
  133. package/dist/components/form/index.d.ts +19 -19
  134. package/dist/components/form/index.js +18 -18
  135. package/dist/components/form/templateAutocomplete.d.ts +2 -2
  136. package/dist/components/form/templateAutocomplete.js +64 -55
  137. package/dist/components/form/types.d.ts +6 -6
  138. package/dist/components/form/types.js +9 -4
  139. package/dist/components/icons/AlertCircleIcon.svelte +11 -0
  140. package/dist/components/icons/AlertCircleIcon.svelte.d.ts +26 -0
  141. package/dist/components/icons/CogIcon.svelte +11 -0
  142. package/dist/components/icons/CogIcon.svelte.d.ts +26 -0
  143. package/dist/components/interrupt/ChoicePrompt.stories.svelte +54 -38
  144. package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +1 -1
  145. package/dist/components/interrupt/ChoicePrompt.svelte +407 -383
  146. package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +1 -1
  147. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +48 -48
  148. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +1 -1
  149. package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -274
  150. package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +1 -1
  151. package/dist/components/interrupt/FormPrompt.svelte +223 -218
  152. package/dist/components/interrupt/FormPrompt.svelte.d.ts +1 -1
  153. package/dist/components/interrupt/InterruptBubble.svelte +617 -583
  154. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +2 -2
  155. package/dist/components/interrupt/ReviewPrompt.stories.svelte +66 -56
  156. package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +1 -1
  157. package/dist/components/interrupt/ReviewPrompt.svelte +861 -841
  158. package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +1 -1
  159. package/dist/components/interrupt/TextInputPrompt.stories.svelte +38 -33
  160. package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +1 -1
  161. package/dist/components/interrupt/TextInputPrompt.svelte +333 -328
  162. package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +1 -1
  163. package/dist/components/interrupt/index.d.ts +5 -5
  164. package/dist/components/interrupt/index.js +5 -5
  165. package/dist/components/layouts/MainLayout.svelte +724 -691
  166. package/dist/components/layouts/MainLayout.svelte.d.ts +6 -6
  167. package/dist/components/nodes/GatewayNode.stories.svelte +100 -99
  168. package/dist/components/nodes/GatewayNode.svelte +605 -571
  169. package/dist/components/nodes/GatewayNode.svelte.d.ts +3 -3
  170. package/dist/components/nodes/IdeaNode.stories.svelte +44 -43
  171. package/dist/components/nodes/IdeaNode.svelte +451 -437
  172. package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
  173. package/dist/components/nodes/NotesNode.stories.svelte +65 -64
  174. package/dist/components/nodes/NotesNode.svelte +380 -369
  175. package/dist/components/nodes/NotesNode.svelte.d.ts +1 -1
  176. package/dist/components/nodes/SimpleNode.stories.svelte +145 -144
  177. package/dist/components/nodes/SimpleNode.svelte +486 -424
  178. package/dist/components/nodes/SimpleNode.svelte.d.ts +1 -1
  179. package/dist/components/nodes/SquareNode.stories.svelte +73 -73
  180. package/dist/components/nodes/SquareNode.svelte +439 -380
  181. package/dist/components/nodes/SquareNode.svelte.d.ts +1 -1
  182. package/dist/components/nodes/TerminalNode.stories.svelte +13 -13
  183. package/dist/components/nodes/TerminalNode.svelte +709 -670
  184. package/dist/components/nodes/TerminalNode.svelte.d.ts +1 -1
  185. package/dist/components/nodes/ToolNode.stories.svelte +181 -180
  186. package/dist/components/nodes/ToolNode.svelte +505 -447
  187. package/dist/components/nodes/ToolNode.svelte.d.ts +1 -1
  188. package/dist/components/nodes/WorkflowNode.stories.svelte +70 -46
  189. package/dist/components/nodes/WorkflowNode.svelte +621 -551
  190. package/dist/components/nodes/WorkflowNode.svelte.d.ts +3 -3
  191. package/dist/components/playground/ChatPanel.svelte +945 -889
  192. package/dist/components/playground/ExecutionLogs.svelte +495 -472
  193. package/dist/components/playground/InputCollector.svelte +449 -428
  194. package/dist/components/playground/MessageBubble.stories.svelte +47 -47
  195. package/dist/components/playground/MessageBubble.stories.svelte.d.ts +1 -1
  196. package/dist/components/playground/MessageBubble.svelte +626 -610
  197. package/dist/components/playground/MessageBubble.svelte.d.ts +1 -1
  198. package/dist/components/playground/Playground.svelte +1088 -1057
  199. package/dist/components/playground/Playground.svelte.d.ts +3 -3
  200. package/dist/components/playground/PlaygroundModal.svelte +208 -204
  201. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
  202. package/dist/components/playground/SessionManager.svelte +527 -521
  203. package/dist/components/playground/SessionManager.svelte.d.ts +1 -1
  204. package/dist/config/agentSpecEndpoints.d.ts +1 -1
  205. package/dist/config/agentSpecEndpoints.js +20 -20
  206. package/dist/config/constants.d.ts +8 -0
  207. package/dist/config/constants.js +10 -2
  208. package/dist/config/defaultCategories.d.ts +1 -1
  209. package/dist/config/defaultCategories.js +86 -86
  210. package/dist/config/defaultPortConfig.d.ts +1 -1
  211. package/dist/config/defaultPortConfig.js +144 -144
  212. package/dist/config/endpoints.d.ts +4 -4
  213. package/dist/config/endpoints.js +65 -65
  214. package/dist/config/runtimeConfig.d.ts +2 -2
  215. package/dist/config/runtimeConfig.js +8 -8
  216. package/dist/core/index.d.ts +63 -59
  217. package/dist/core/index.js +35 -33
  218. package/dist/display/index.d.ts +2 -2
  219. package/dist/display/index.js +2 -2
  220. package/dist/editor/index.d.ts +62 -62
  221. package/dist/editor/index.js +53 -53
  222. package/dist/form/code.d.ts +5 -5
  223. package/dist/form/code.js +14 -14
  224. package/dist/form/fieldRegistry.d.ts +3 -3
  225. package/dist/form/fieldRegistry.js +11 -9
  226. package/dist/form/full.d.ts +8 -8
  227. package/dist/form/full.js +9 -9
  228. package/dist/form/index.d.ts +18 -18
  229. package/dist/form/index.js +16 -16
  230. package/dist/form/markdown.d.ts +4 -4
  231. package/dist/form/markdown.js +8 -8
  232. package/dist/helpers/proximityConnect.d.ts +3 -3
  233. package/dist/helpers/proximityConnect.js +34 -32
  234. package/dist/helpers/workflowEditorHelper.d.ts +5 -5
  235. package/dist/helpers/workflowEditorHelper.js +108 -96
  236. package/dist/index.d.ts +6 -6
  237. package/dist/index.js +6 -6
  238. package/dist/mocks/app-environment.js +2 -2
  239. package/dist/mocks/app-forms.js +9 -9
  240. package/dist/mocks/app-navigation.js +11 -11
  241. package/dist/mocks/app-stores.js +8 -8
  242. package/dist/playground/index.d.ts +19 -19
  243. package/dist/playground/index.js +16 -16
  244. package/dist/playground/mount.d.ts +3 -3
  245. package/dist/playground/mount.js +24 -24
  246. package/dist/registry/builtinFormats.js +13 -13
  247. package/dist/registry/builtinNodes.d.ts +2 -2
  248. package/dist/registry/builtinNodes.js +77 -77
  249. package/dist/registry/index.d.ts +4 -4
  250. package/dist/registry/index.js +4 -4
  251. package/dist/registry/nodeComponentRegistry.d.ts +8 -8
  252. package/dist/registry/nodeComponentRegistry.js +11 -9
  253. package/dist/registry/plugin.d.ts +2 -2
  254. package/dist/registry/plugin.js +11 -11
  255. package/dist/registry/workflowFormatRegistry.d.ts +3 -3
  256. package/dist/registry/workflowFormatRegistry.js +2 -2
  257. package/dist/schema/index.d.ts +1 -1
  258. package/dist/schema/index.js +2 -2
  259. package/dist/schemas/v1/workflow.schema.json +22 -107
  260. package/dist/services/agentSpecExecutionService.d.ts +3 -3
  261. package/dist/services/agentSpecExecutionService.js +59 -55
  262. package/dist/services/api.d.ts +18 -4
  263. package/dist/services/api.js +46 -43
  264. package/dist/services/apiVariableService.d.ts +1 -1
  265. package/dist/services/apiVariableService.js +41 -34
  266. package/dist/services/autoSaveService.js +8 -8
  267. package/dist/services/categoriesApi.d.ts +2 -2
  268. package/dist/services/categoriesApi.js +8 -8
  269. package/dist/services/draftStorage.d.ts +1 -1
  270. package/dist/services/draftStorage.js +11 -11
  271. package/dist/services/dynamicSchemaService.d.ts +1 -1
  272. package/dist/services/dynamicSchemaService.js +41 -39
  273. package/dist/services/globalSave.d.ts +2 -2
  274. package/dist/services/globalSave.js +53 -42
  275. package/dist/services/historyService.d.ts +1 -1
  276. package/dist/services/historyService.js +8 -8
  277. package/dist/services/interruptService.d.ts +1 -1
  278. package/dist/services/interruptService.js +35 -29
  279. package/dist/services/nodeExecutionService.d.ts +1 -1
  280. package/dist/services/nodeExecutionService.js +45 -44
  281. package/dist/services/playgroundService.d.ts +1 -1
  282. package/dist/services/playgroundService.js +29 -29
  283. package/dist/services/portConfigApi.d.ts +2 -2
  284. package/dist/services/portConfigApi.js +8 -8
  285. package/dist/services/settingsService.d.ts +2 -2
  286. package/dist/services/settingsService.js +25 -19
  287. package/dist/services/toastService.d.ts +4 -4
  288. package/dist/services/toastService.js +33 -33
  289. package/dist/services/variableService.d.ts +1 -1
  290. package/dist/services/variableService.js +36 -36
  291. package/dist/services/workflowStorage.d.ts +2 -2
  292. package/dist/services/workflowStorage.js +13 -13
  293. package/dist/settings/index.d.ts +7 -7
  294. package/dist/settings/index.js +6 -6
  295. package/dist/skins/default.d.ts +2 -0
  296. package/dist/skins/default.js +1 -0
  297. package/dist/skins/index.d.ts +13 -0
  298. package/dist/skins/index.js +30 -0
  299. package/dist/skins/slate.d.ts +2 -0
  300. package/dist/skins/slate.js +78 -0
  301. package/dist/stores/categoriesStore.svelte.d.ts +1 -1
  302. package/dist/stores/categoriesStore.svelte.js +5 -5
  303. package/dist/stores/editorStateMachine.svelte.d.ts +2 -2
  304. package/dist/stores/editorStateMachine.svelte.js +65 -33
  305. package/dist/stores/historyStore.svelte.d.ts +4 -4
  306. package/dist/stores/historyStore.svelte.js +4 -4
  307. package/dist/stores/interruptStore.svelte.d.ts +3 -3
  308. package/dist/stores/interruptStore.svelte.js +21 -21
  309. package/dist/stores/playgroundStore.svelte.d.ts +2 -2
  310. package/dist/stores/playgroundStore.svelte.js +25 -18
  311. package/dist/stores/portCoordinateStore.svelte.d.ts +2 -2
  312. package/dist/stores/portCoordinateStore.svelte.js +15 -8
  313. package/dist/stores/settingsStore.svelte.d.ts +2 -2
  314. package/dist/stores/settingsStore.svelte.js +62 -57
  315. package/dist/stores/workflowStore.svelte.d.ts +3 -3
  316. package/dist/stores/workflowStore.svelte.js +50 -47
  317. package/dist/stories/CanvasDecorator.svelte +35 -32
  318. package/dist/stories/CanvasDecorator.svelte.d.ts +2 -2
  319. package/dist/stories/EdgeDecorator.svelte +125 -0
  320. package/dist/stories/EdgeDecorator.svelte.d.ts +17 -0
  321. package/dist/stories/NodeDecorator.svelte +59 -53
  322. package/dist/stories/NodeDecorator.svelte.d.ts +1 -1
  323. package/dist/stories/utils.d.ts +2 -2
  324. package/dist/stories/utils.js +105 -67
  325. package/dist/styles/base.css +599 -595
  326. package/dist/styles/toast.css +14 -14
  327. package/dist/styles/tokens.css +409 -378
  328. package/dist/svelte-app.d.ts +9 -9
  329. package/dist/svelte-app.js +39 -39
  330. package/dist/themes/default.d.ts +2 -0
  331. package/dist/themes/default.js +9 -0
  332. package/dist/themes/index.d.ts +13 -0
  333. package/dist/themes/index.js +44 -0
  334. package/dist/themes/minimal.d.ts +2 -0
  335. package/dist/themes/minimal.js +11 -0
  336. package/dist/types/agentspec.d.ts +18 -18
  337. package/dist/types/agentspec.js +2 -2
  338. package/dist/types/auth.d.ts +1 -1
  339. package/dist/types/auth.js +6 -6
  340. package/dist/types/config.d.ts +6 -6
  341. package/dist/types/events.d.ts +2 -2
  342. package/dist/types/events.js +2 -2
  343. package/dist/types/index.d.ts +32 -32
  344. package/dist/types/index.js +6 -6
  345. package/dist/types/interrupt.d.ts +6 -6
  346. package/dist/types/interrupt.js +21 -21
  347. package/dist/types/interruptState.d.ts +12 -12
  348. package/dist/types/interruptState.js +66 -66
  349. package/dist/types/playground.d.ts +7 -7
  350. package/dist/types/playground.js +14 -14
  351. package/dist/types/settings.d.ts +5 -3
  352. package/dist/types/settings.js +25 -18
  353. package/dist/types/skin.d.ts +31 -0
  354. package/dist/types/skin.js +1 -0
  355. package/dist/types/theme.d.ts +35 -0
  356. package/dist/types/theme.js +1 -0
  357. package/dist/types/uischema.d.ts +4 -4
  358. package/dist/types/uischema.js +3 -3
  359. package/dist/utils/colors.d.ts +1 -1
  360. package/dist/utils/colors.js +97 -95
  361. package/dist/utils/config.d.ts +2 -2
  362. package/dist/utils/config.js +48 -48
  363. package/dist/utils/connections.d.ts +2 -2
  364. package/dist/utils/connections.js +15 -15
  365. package/dist/utils/errors.js +3 -3
  366. package/dist/utils/fetchWithAuth.d.ts +1 -1
  367. package/dist/utils/fetchWithAuth.js +2 -2
  368. package/dist/utils/handleIds.d.ts +2 -2
  369. package/dist/utils/handleIds.js +8 -8
  370. package/dist/utils/handlePositioning.d.ts +1 -1
  371. package/dist/utils/handlePositioning.js +2 -2
  372. package/dist/utils/icons.d.ts +1 -1
  373. package/dist/utils/icons.js +74 -74
  374. package/dist/utils/logger.d.ts +1 -1
  375. package/dist/utils/logger.js +7 -7
  376. package/dist/utils/nodeStatus.d.ts +1 -1
  377. package/dist/utils/nodeStatus.js +48 -48
  378. package/dist/utils/nodeTypes.d.ts +1 -1
  379. package/dist/utils/nodeTypes.js +21 -20
  380. package/dist/utils/nodeWrapper.d.ts +7 -7
  381. package/dist/utils/nodeWrapper.js +21 -19
  382. package/dist/utils/performanceUtils.d.ts +1 -1
  383. package/dist/utils/performanceUtils.js +2 -1
  384. package/dist/utils/sanitize.js +1 -1
  385. package/dist/utils/uischema.d.ts +2 -2
  386. package/dist/utils/uischema.js +8 -8
  387. package/dist/utils/validation.js +20 -8
  388. package/package.json +296 -291
@@ -8,1068 +8,1099 @@
8
8
  -->
9
9
 
10
10
  <script lang="ts">
11
- import { onMount, onDestroy } from 'svelte';
12
- import Icon from '@iconify/svelte';
13
- import ChatPanel from './ChatPanel.svelte';
14
- import type { Workflow } from '../../types/index.js';
15
- import type { EndpointConfig } from '../../config/endpoints.js';
16
- import type { PlaygroundMode, PlaygroundConfig } from '../../types/playground.js';
17
- import { playgroundService } from '../../services/playgroundService.js';
18
- import { interruptService } from '../../services/interruptService.js';
19
- import { setEndpointConfig } from '../../services/api.js';
20
- import {
21
- getCurrentSession,
22
- getSessions,
23
- getIsExecuting,
24
- getIsLoading,
25
- getError,
26
- playgroundActions,
27
- getInputFields,
28
- createPollingCallback
29
- } from '../../stores/playgroundStore.svelte.js';
30
- import { interruptActions } from '../../stores/interruptStore.svelte.js';
31
- import { logger } from '../../utils/logger.js';
32
-
33
- /**
34
- * Component props
35
- */
36
- interface Props {
37
- /** Target workflow ID */
38
- workflowId: string;
39
- /** Pre-loaded workflow (optional, will be fetched if not provided) */
40
- workflow?: Workflow;
41
- /** Display mode: embedded (panel) or standalone (page) */
42
- mode?: PlaygroundMode;
43
- /** Resume a specific session */
44
- initialSessionId?: string;
45
- /** API endpoint configuration */
46
- endpointConfig?: EndpointConfig;
47
- /** Playground configuration options */
48
- config?: PlaygroundConfig;
49
- /** Callback when playground is closed (for embedded mode) */
50
- onClose?: () => void;
51
- }
52
-
53
- let {
54
- workflowId,
55
- workflow,
56
- mode = 'standalone',
57
- initialSessionId,
58
- endpointConfig,
59
- config = {},
60
- onClose
61
- }: Props = $props();
62
-
63
- /** Current input values from InputCollector */
64
- let inputValues = $state<Record<string, unknown>>({});
65
-
66
- /** Track session being edited for rename */
67
- let editingSessionId = $state<string | null>(null);
68
-
69
- /** Track which session's dropdown menu is open */
70
- let openMenuId = $state<string | null>(null);
71
-
72
- /** Track if initial session has been loaded to prevent duplicate loads */
73
- let initialSessionLoaded = $state(false);
74
-
75
- /** Track the session ID that was loaded to detect prop changes */
76
- let loadedInitialSessionId = $state<string | undefined>(undefined);
77
-
78
- /** Track if auto-run has already been triggered to prevent duplicate executions */
79
- let autoRunTriggered = $state(false);
80
-
81
- /**
82
- * Initialize the playground on mount
83
- */
84
- onMount(() => {
85
- // Set endpoint config if provided
86
- if (endpointConfig) {
87
- setEndpointConfig(endpointConfig);
88
- }
89
-
90
- // Set workflow in store
91
- if (workflow) {
92
- playgroundActions.setWorkflow(workflow);
93
- }
94
-
95
- // Async initialization
96
- const initializePlayground = async (): Promise<void> => {
97
- try {
98
- // Load sessions
99
- await loadSessions();
100
-
101
- // Resume initial session if provided
102
- if (initialSessionId) {
103
- await loadInitialSession(initialSessionId);
104
- }
105
-
106
- // Handle auto-run after initialization is complete
107
- if (config.autoRun && !autoRunTriggered) {
108
- autoRunTriggered = true;
109
- const predefinedMessage = config.predefinedMessage ?? 'Run workflow';
110
- logger.debug('[Playground] Auto-run triggered with message:', predefinedMessage);
111
- await handleSendMessage(predefinedMessage);
112
- }
113
- } catch (err) {
114
- logger.error('[Playground] Initialization error:', err);
115
- }
116
- };
117
-
118
- // Execute initialization
119
- void initializePlayground();
120
- });
121
-
122
- /**
123
- * Handle reactive changes to initialSessionId prop
124
- * This allows the initial session to be set after mount
125
- */
126
- $effect(() => {
127
- // Skip if no initialSessionId provided
128
- if (!initialSessionId) {
129
- return;
130
- }
131
-
132
- // Skip if this session was already loaded
133
- if (initialSessionLoaded && loadedInitialSessionId === initialSessionId) {
134
- return;
135
- }
136
-
137
- // Skip if sessions haven't been loaded yet (will be handled by onMount)
138
- const sessionList = getSessions();
139
- if (sessionList.length === 0 && getIsLoading()) {
140
- return;
141
- }
142
-
143
- // Load the initial session if sessions are available
144
- if (sessionList.length > 0 && !initialSessionLoaded) {
145
- void loadInitialSession(initialSessionId);
146
- }
147
- });
148
-
149
- /**
150
- * Load the initial session with validation and error handling
151
- *
152
- * @param sessionId - The session ID to load
153
- */
154
- async function loadInitialSession(sessionId: string): Promise<void> {
155
- // Validate session exists in loaded sessions
156
- const sessionList = getSessions();
157
- const sessionExists = sessionList.some((s) => s.id === sessionId);
158
-
159
- if (!sessionExists) {
160
- logger.warn(
161
- `[Playground] Initial session "${sessionId}" not found in available sessions. ` +
162
- `Available sessions: ${sessionList.map((s) => s.id).join(', ') || 'none'}`
163
- );
164
- // Don't set error - just log warning and let user pick a session
165
- initialSessionLoaded = true;
166
- loadedInitialSessionId = sessionId;
167
- return;
168
- }
169
-
170
- try {
171
- await loadSession(sessionId);
172
- initialSessionLoaded = true;
173
- loadedInitialSessionId = sessionId;
174
- } catch (err) {
175
- logger.error('[Playground] Failed to load initial session:', err);
176
- // Mark as attempted to prevent retry loops
177
- initialSessionLoaded = true;
178
- loadedInitialSessionId = sessionId;
179
- }
180
- }
181
-
182
- /**
183
- * Cleanup on destroy
184
- */
185
- onDestroy(() => {
186
- playgroundService.stopPolling();
187
- interruptService.stopPolling();
188
- playgroundActions.reset();
189
- interruptActions.reset();
190
- });
191
-
192
- /**
193
- * Close dropdown menu when clicking outside
194
- */
195
- $effect(() => {
196
- if (!openMenuId) return;
197
-
198
- function onDocumentClick() {
199
- openMenuId = null;
200
- }
201
-
202
- document.addEventListener('click', onDocumentClick);
203
- return () => document.removeEventListener('click', onDocumentClick);
204
- });
205
-
206
- /**
207
- * Load sessions for the workflow
208
- */
209
- async function loadSessions(): Promise<void> {
210
- playgroundActions.setLoading(true);
211
- playgroundActions.setError(null);
212
-
213
- try {
214
- const sessionList = await playgroundService.listSessions(workflowId);
215
- playgroundActions.setSessions(sessionList);
216
- } catch (err) {
217
- const errorMessage = err instanceof Error ? err.message : 'Failed to load sessions';
218
- playgroundActions.setError(errorMessage);
219
- logger.error('Failed to load sessions:', err);
220
- } finally {
221
- playgroundActions.setLoading(false);
222
- }
223
- }
224
-
225
- /**
226
- * Load a specific session and its messages
227
- */
228
- async function loadSession(sessionId: string): Promise<void> {
229
- playgroundActions.setLoading(true);
230
- playgroundActions.setError(null);
231
-
232
- try {
233
- // Get session details
234
- const session = await playgroundService.getSession(sessionId);
235
- playgroundActions.setCurrentSession(session);
236
-
237
- // Get messages
238
- const response = await playgroundService.getMessages(sessionId);
239
- playgroundActions.setMessages(response.data ?? []);
240
-
241
- // Start polling if session is running
242
- if (session.status === 'running') {
243
- startPolling(sessionId);
244
- }
245
- } catch (err) {
246
- const errorMessage = err instanceof Error ? err.message : 'Failed to load session';
247
- playgroundActions.setError(errorMessage);
248
- logger.error('Failed to load session:', err);
249
- } finally {
250
- playgroundActions.setLoading(false);
251
- }
252
- }
253
-
254
- /**
255
- * Create a new session
256
- */
257
- async function handleCreateSession(): Promise<void> {
258
- playgroundActions.setLoading(true);
259
- playgroundActions.setError(null);
260
-
261
- try {
262
- const sessionName = `Session ${getSessions().length + 1}`;
263
- const session = await playgroundService.createSession(workflowId, sessionName);
264
- playgroundActions.addSession(session);
265
- playgroundActions.setCurrentSession(session);
266
- playgroundActions.clearMessages();
267
- } catch (err) {
268
- const errorMessage = err instanceof Error ? err.message : 'Failed to create session';
269
- playgroundActions.setError(errorMessage);
270
- logger.error('Failed to create session:', err);
271
- } finally {
272
- playgroundActions.setLoading(false);
273
- }
274
- }
275
-
276
- /**
277
- * Select a session
278
- */
279
- async function handleSelectSession(sessionId: string): Promise<void> {
280
- const currentSessionId = getCurrentSession()?.id;
281
- if (currentSessionId === sessionId) {
282
- return;
283
- }
284
-
285
- // Stop polling for current session
286
- playgroundService.stopPolling();
287
-
288
- await loadSession(sessionId);
289
- }
290
-
291
- /**
292
- * Delete a session
293
- */
294
- async function handleDeleteSession(sessionId: string): Promise<void> {
295
- try {
296
- await playgroundService.deleteSession(sessionId);
297
- playgroundActions.removeSession(sessionId);
298
-
299
- // If we deleted the current session, clear it
300
- if (getCurrentSession()?.id === sessionId) {
301
- playgroundService.stopPolling();
302
- }
303
- } catch (err) {
304
- const errorMessage = err instanceof Error ? err.message : 'Failed to delete session';
305
- playgroundActions.setError(errorMessage);
306
- logger.error('Failed to delete session:', err);
307
- }
308
- }
309
-
310
- /**
311
- * Toggle session dropdown menu
312
- */
313
- function handleMenuToggle(event: Event, sessionId: string): void {
314
- event.stopPropagation();
315
- openMenuId = openMenuId === sessionId ? null : sessionId;
316
- }
317
-
318
- /**
319
- * Handle delete from dropdown menu
320
- */
321
- function handleMenuDelete(event: Event, sessionId: string): void {
322
- event.stopPropagation();
323
- openMenuId = null;
324
- void handleDeleteSession(sessionId);
325
- }
326
-
327
- /**
328
- * Close current session (go back to welcome)
329
- */
330
- function handleCloseSession(): void {
331
- playgroundService.stopPolling();
332
- interruptService.stopPolling();
333
- playgroundActions.setCurrentSession(null);
334
- playgroundActions.clearMessages();
335
- // Clear interrupts for this session
336
- const sessionId = getCurrentSession()?.id;
337
- if (sessionId) {
338
- interruptActions.clearSessionInterrupts(sessionId);
339
- }
340
- }
341
-
342
- /**
343
- * Send a message
344
- */
345
- async function handleSendMessage(content: string): Promise<void> {
346
- const session = getCurrentSession();
347
- if (!session) {
348
- // Create a session first if none exists
349
- await handleCreateSession();
350
- const newSession = getCurrentSession();
351
- if (!newSession) {
352
- return;
353
- }
354
- }
355
-
356
- const sessionId = getCurrentSession()?.id;
357
- if (!sessionId) {
358
- return;
359
- }
360
-
361
- playgroundActions.setExecuting(true);
362
- playgroundActions.setError(null);
363
-
364
- try {
365
- // Prepare inputs from the input collector
366
- const inputs: Record<string, unknown> = {};
367
- const fields = getInputFields();
368
-
369
- fields.forEach((field) => {
370
- const key = `${field.nodeId}:${field.fieldId}`;
371
- if (inputValues[key] !== undefined) {
372
- // Map to node ID and field ID for the backend
373
- if (!inputs[field.nodeId]) {
374
- inputs[field.nodeId] = {};
375
- }
376
- (inputs[field.nodeId] as Record<string, unknown>)[field.fieldId] = inputValues[key];
377
- }
378
- });
379
-
380
- // Send message
381
- const message = await playgroundService.sendMessage(sessionId, content, inputs);
382
- playgroundActions.addMessage(message);
383
-
384
- // Update session status
385
- playgroundActions.updateSessionStatus('running');
386
-
387
- // Start polling for responses
388
- startPolling(sessionId);
389
- } catch (err) {
390
- const errorMessage = err instanceof Error ? err.message : 'Failed to send message';
391
- playgroundActions.setError(errorMessage);
392
- playgroundActions.setExecuting(false);
393
- logger.error('Failed to send message:', err);
394
- }
395
- }
396
-
397
- /**
398
- * Stop execution
399
- */
400
- async function handleStopExecution(): Promise<void> {
401
- const sessionId = getCurrentSession()?.id;
402
- if (!sessionId) {
403
- return;
404
- }
405
-
406
- try {
407
- await playgroundService.stopExecution(sessionId);
408
- playgroundService.stopPolling();
409
- playgroundActions.setExecuting(false);
410
- playgroundActions.updateSessionStatus('idle');
411
- } catch (err) {
412
- const errorMessage = err instanceof Error ? err.message : 'Failed to stop execution';
413
- playgroundActions.setError(errorMessage);
414
- logger.error('Failed to stop execution:', err);
415
- }
416
- }
417
-
418
- /** Shared polling callback created from config lifecycle hooks */
419
- // svelte-ignore state_referenced_locally config is static
420
- const pollingCallback = createPollingCallback(config.isTerminalStatus);
421
-
422
- /**
423
- * Start polling for messages
424
- */
425
- function startPolling(sessionId: string): void {
426
- const pollingInterval = config.pollingInterval ?? 1500;
427
-
428
- playgroundService.startPolling(
429
- sessionId,
430
- pollingCallback,
431
- pollingInterval,
432
- config.shouldStopPolling
433
- );
434
- }
435
-
436
- /**
437
- * Refresh messages for the current session
438
- * Called after interrupt resolution when polling has stopped
439
- */
440
- async function handleInterruptResolved(): Promise<void> {
441
- const sessionId = getCurrentSession()?.id;
442
- if (!sessionId) return;
443
-
444
- try {
445
- const response = await playgroundService.getMessages(sessionId);
446
- pollingCallback(response);
447
- } catch (err) {
448
- logger.error('[Playground] Failed to refresh messages after interrupt:', err);
449
- }
450
- }
451
-
452
- /**
453
- * Format date for display
454
- */
455
- function formatDate(dateString: string): string {
456
- const date = new Date(dateString);
457
- const now = new Date();
458
- const diffMs = now.getTime() - date.getTime();
459
- const diffMins = Math.floor(diffMs / 60000);
460
- const diffHours = Math.floor(diffMs / 3600000);
461
- const diffDays = Math.floor(diffMs / 86400000);
462
-
463
- if (diffMins < 1) {
464
- return 'Just now';
465
- }
466
- if (diffMins < 60) {
467
- return `${diffMins}m ago`;
468
- }
469
- if (diffHours < 24) {
470
- return `${diffHours}h ago`;
471
- }
472
- if (diffDays < 7) {
473
- return `${diffDays}d ago`;
474
- }
475
- return date.toLocaleDateString('en-US', {
476
- month: 'short',
477
- day: 'numeric'
478
- });
479
- }
11
+ import { onMount, onDestroy } from "svelte";
12
+ import Icon from "@iconify/svelte";
13
+ import ChatPanel from "./ChatPanel.svelte";
14
+ import type { Workflow } from "../../types/index.js";
15
+ import type { EndpointConfig } from "../../config/endpoints.js";
16
+ import type {
17
+ PlaygroundMode,
18
+ PlaygroundConfig,
19
+ } from "../../types/playground.js";
20
+ import { playgroundService } from "../../services/playgroundService.js";
21
+ import { interruptService } from "../../services/interruptService.js";
22
+ import { setEndpointConfig } from "../../services/api.js";
23
+ import {
24
+ getCurrentSession,
25
+ getSessions,
26
+ getIsExecuting,
27
+ getIsLoading,
28
+ getError,
29
+ playgroundActions,
30
+ getInputFields,
31
+ createPollingCallback,
32
+ } from "../../stores/playgroundStore.svelte.js";
33
+ import { interruptActions } from "../../stores/interruptStore.svelte.js";
34
+ import { logger } from "../../utils/logger.js";
35
+
36
+ /**
37
+ * Component props
38
+ */
39
+ interface Props {
40
+ /** Target workflow ID */
41
+ workflowId: string;
42
+ /** Pre-loaded workflow (optional, will be fetched if not provided) */
43
+ workflow?: Workflow;
44
+ /** Display mode: embedded (panel) or standalone (page) */
45
+ mode?: PlaygroundMode;
46
+ /** Resume a specific session */
47
+ initialSessionId?: string;
48
+ /** API endpoint configuration */
49
+ endpointConfig?: EndpointConfig;
50
+ /** Playground configuration options */
51
+ config?: PlaygroundConfig;
52
+ /** Callback when playground is closed (for embedded mode) */
53
+ onClose?: () => void;
54
+ }
55
+
56
+ let {
57
+ workflowId,
58
+ workflow,
59
+ mode = "standalone",
60
+ initialSessionId,
61
+ endpointConfig,
62
+ config = {},
63
+ onClose,
64
+ }: Props = $props();
65
+
66
+ /** Current input values from InputCollector */
67
+ let inputValues = $state<Record<string, unknown>>({});
68
+
69
+ /** Track session being edited for rename */
70
+ let editingSessionId = $state<string | null>(null);
71
+
72
+ /** Track which session's dropdown menu is open */
73
+ let openMenuId = $state<string | null>(null);
74
+
75
+ /** Track if initial session has been loaded to prevent duplicate loads */
76
+ let initialSessionLoaded = $state(false);
77
+
78
+ /** Track the session ID that was loaded to detect prop changes */
79
+ let loadedInitialSessionId = $state<string | undefined>(undefined);
80
+
81
+ /** Track if auto-run has already been triggered to prevent duplicate executions */
82
+ let autoRunTriggered = $state(false);
83
+
84
+ /**
85
+ * Initialize the playground on mount
86
+ */
87
+ onMount(() => {
88
+ // Set endpoint config if provided
89
+ if (endpointConfig) {
90
+ setEndpointConfig(endpointConfig);
91
+ }
92
+
93
+ // Set workflow in store
94
+ if (workflow) {
95
+ playgroundActions.setWorkflow(workflow);
96
+ }
97
+
98
+ // Async initialization
99
+ const initializePlayground = async (): Promise<void> => {
100
+ try {
101
+ // Load sessions
102
+ await loadSessions();
103
+
104
+ // Resume initial session if provided
105
+ if (initialSessionId) {
106
+ await loadInitialSession(initialSessionId);
107
+ }
108
+
109
+ // Handle auto-run after initialization is complete
110
+ if (config.autoRun && !autoRunTriggered) {
111
+ autoRunTriggered = true;
112
+ const predefinedMessage = config.predefinedMessage ?? "Run workflow";
113
+ logger.debug(
114
+ "[Playground] Auto-run triggered with message:",
115
+ predefinedMessage,
116
+ );
117
+ await handleSendMessage(predefinedMessage);
118
+ }
119
+ } catch (err) {
120
+ logger.error("[Playground] Initialization error:", err);
121
+ }
122
+ };
123
+
124
+ // Execute initialization
125
+ void initializePlayground();
126
+ });
127
+
128
+ /**
129
+ * Handle reactive changes to initialSessionId prop
130
+ * This allows the initial session to be set after mount
131
+ */
132
+ $effect(() => {
133
+ // Skip if no initialSessionId provided
134
+ if (!initialSessionId) {
135
+ return;
136
+ }
137
+
138
+ // Skip if this session was already loaded
139
+ if (initialSessionLoaded && loadedInitialSessionId === initialSessionId) {
140
+ return;
141
+ }
142
+
143
+ // Skip if sessions haven't been loaded yet (will be handled by onMount)
144
+ const sessionList = getSessions();
145
+ if (sessionList.length === 0 && getIsLoading()) {
146
+ return;
147
+ }
148
+
149
+ // Load the initial session if sessions are available
150
+ if (sessionList.length > 0 && !initialSessionLoaded) {
151
+ void loadInitialSession(initialSessionId);
152
+ }
153
+ });
154
+
155
+ /**
156
+ * Load the initial session with validation and error handling
157
+ *
158
+ * @param sessionId - The session ID to load
159
+ */
160
+ async function loadInitialSession(sessionId: string): Promise<void> {
161
+ // Validate session exists in loaded sessions
162
+ const sessionList = getSessions();
163
+ const sessionExists = sessionList.some((s) => s.id === sessionId);
164
+
165
+ if (!sessionExists) {
166
+ logger.warn(
167
+ `[Playground] Initial session "${sessionId}" not found in available sessions. ` +
168
+ `Available sessions: ${sessionList.map((s) => s.id).join(", ") || "none"}`,
169
+ );
170
+ // Don't set error - just log warning and let user pick a session
171
+ initialSessionLoaded = true;
172
+ loadedInitialSessionId = sessionId;
173
+ return;
174
+ }
175
+
176
+ try {
177
+ await loadSession(sessionId);
178
+ initialSessionLoaded = true;
179
+ loadedInitialSessionId = sessionId;
180
+ } catch (err) {
181
+ logger.error("[Playground] Failed to load initial session:", err);
182
+ // Mark as attempted to prevent retry loops
183
+ initialSessionLoaded = true;
184
+ loadedInitialSessionId = sessionId;
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Cleanup on destroy
190
+ */
191
+ onDestroy(() => {
192
+ playgroundService.stopPolling();
193
+ interruptService.stopPolling();
194
+ playgroundActions.reset();
195
+ interruptActions.reset();
196
+ });
197
+
198
+ /**
199
+ * Close dropdown menu when clicking outside
200
+ */
201
+ $effect(() => {
202
+ if (!openMenuId) return;
203
+
204
+ function onDocumentClick() {
205
+ openMenuId = null;
206
+ }
207
+
208
+ document.addEventListener("click", onDocumentClick);
209
+ return () => document.removeEventListener("click", onDocumentClick);
210
+ });
211
+
212
+ /**
213
+ * Load sessions for the workflow
214
+ */
215
+ async function loadSessions(): Promise<void> {
216
+ playgroundActions.setLoading(true);
217
+ playgroundActions.setError(null);
218
+
219
+ try {
220
+ const sessionList = await playgroundService.listSessions(workflowId);
221
+ playgroundActions.setSessions(sessionList);
222
+ } catch (err) {
223
+ const errorMessage =
224
+ err instanceof Error ? err.message : "Failed to load sessions";
225
+ playgroundActions.setError(errorMessage);
226
+ logger.error("Failed to load sessions:", err);
227
+ } finally {
228
+ playgroundActions.setLoading(false);
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Load a specific session and its messages
234
+ */
235
+ async function loadSession(sessionId: string): Promise<void> {
236
+ playgroundActions.setLoading(true);
237
+ playgroundActions.setError(null);
238
+
239
+ try {
240
+ // Get session details
241
+ const session = await playgroundService.getSession(sessionId);
242
+ playgroundActions.setCurrentSession(session);
243
+
244
+ // Get messages
245
+ const response = await playgroundService.getMessages(sessionId);
246
+ playgroundActions.setMessages(response.data ?? []);
247
+
248
+ // Start polling if session is running
249
+ if (session.status === "running") {
250
+ startPolling(sessionId);
251
+ }
252
+ } catch (err) {
253
+ const errorMessage =
254
+ err instanceof Error ? err.message : "Failed to load session";
255
+ playgroundActions.setError(errorMessage);
256
+ logger.error("Failed to load session:", err);
257
+ } finally {
258
+ playgroundActions.setLoading(false);
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Create a new session
264
+ */
265
+ async function handleCreateSession(): Promise<void> {
266
+ playgroundActions.setLoading(true);
267
+ playgroundActions.setError(null);
268
+
269
+ try {
270
+ const sessionName = `Session ${getSessions().length + 1}`;
271
+ const session = await playgroundService.createSession(
272
+ workflowId,
273
+ sessionName,
274
+ );
275
+ playgroundActions.addSession(session);
276
+ playgroundActions.setCurrentSession(session);
277
+ playgroundActions.clearMessages();
278
+ } catch (err) {
279
+ const errorMessage =
280
+ err instanceof Error ? err.message : "Failed to create session";
281
+ playgroundActions.setError(errorMessage);
282
+ logger.error("Failed to create session:", err);
283
+ } finally {
284
+ playgroundActions.setLoading(false);
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Select a session
290
+ */
291
+ async function handleSelectSession(sessionId: string): Promise<void> {
292
+ const currentSessionId = getCurrentSession()?.id;
293
+ if (currentSessionId === sessionId) {
294
+ return;
295
+ }
296
+
297
+ // Stop polling for current session
298
+ playgroundService.stopPolling();
299
+
300
+ await loadSession(sessionId);
301
+ }
302
+
303
+ /**
304
+ * Delete a session
305
+ */
306
+ async function handleDeleteSession(sessionId: string): Promise<void> {
307
+ try {
308
+ await playgroundService.deleteSession(sessionId);
309
+ playgroundActions.removeSession(sessionId);
310
+
311
+ // If we deleted the current session, clear it
312
+ if (getCurrentSession()?.id === sessionId) {
313
+ playgroundService.stopPolling();
314
+ }
315
+ } catch (err) {
316
+ const errorMessage =
317
+ err instanceof Error ? err.message : "Failed to delete session";
318
+ playgroundActions.setError(errorMessage);
319
+ logger.error("Failed to delete session:", err);
320
+ }
321
+ }
322
+
323
+ /**
324
+ * Toggle session dropdown menu
325
+ */
326
+ function handleMenuToggle(event: Event, sessionId: string): void {
327
+ event.stopPropagation();
328
+ openMenuId = openMenuId === sessionId ? null : sessionId;
329
+ }
330
+
331
+ /**
332
+ * Handle delete from dropdown menu
333
+ */
334
+ function handleMenuDelete(event: Event, sessionId: string): void {
335
+ event.stopPropagation();
336
+ openMenuId = null;
337
+ void handleDeleteSession(sessionId);
338
+ }
339
+
340
+ /**
341
+ * Close current session (go back to welcome)
342
+ */
343
+ function handleCloseSession(): void {
344
+ playgroundService.stopPolling();
345
+ interruptService.stopPolling();
346
+ playgroundActions.setCurrentSession(null);
347
+ playgroundActions.clearMessages();
348
+ // Clear interrupts for this session
349
+ const sessionId = getCurrentSession()?.id;
350
+ if (sessionId) {
351
+ interruptActions.clearSessionInterrupts(sessionId);
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Send a message
357
+ */
358
+ async function handleSendMessage(content: string): Promise<void> {
359
+ const session = getCurrentSession();
360
+ if (!session) {
361
+ // Create a session first if none exists
362
+ await handleCreateSession();
363
+ const newSession = getCurrentSession();
364
+ if (!newSession) {
365
+ return;
366
+ }
367
+ }
368
+
369
+ const sessionId = getCurrentSession()?.id;
370
+ if (!sessionId) {
371
+ return;
372
+ }
373
+
374
+ playgroundActions.setExecuting(true);
375
+ playgroundActions.setError(null);
376
+
377
+ try {
378
+ // Prepare inputs from the input collector
379
+ const inputs: Record<string, unknown> = {};
380
+ const fields = getInputFields();
381
+
382
+ fields.forEach((field) => {
383
+ const key = `${field.nodeId}:${field.fieldId}`;
384
+ if (inputValues[key] !== undefined) {
385
+ // Map to node ID and field ID for the backend
386
+ if (!inputs[field.nodeId]) {
387
+ inputs[field.nodeId] = {};
388
+ }
389
+ (inputs[field.nodeId] as Record<string, unknown>)[field.fieldId] =
390
+ inputValues[key];
391
+ }
392
+ });
393
+
394
+ // Send message
395
+ const message = await playgroundService.sendMessage(
396
+ sessionId,
397
+ content,
398
+ inputs,
399
+ );
400
+ playgroundActions.addMessage(message);
401
+
402
+ // Update session status
403
+ playgroundActions.updateSessionStatus("running");
404
+
405
+ // Start polling for responses
406
+ startPolling(sessionId);
407
+ } catch (err) {
408
+ const errorMessage =
409
+ err instanceof Error ? err.message : "Failed to send message";
410
+ playgroundActions.setError(errorMessage);
411
+ playgroundActions.setExecuting(false);
412
+ logger.error("Failed to send message:", err);
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Stop execution
418
+ */
419
+ async function handleStopExecution(): Promise<void> {
420
+ const sessionId = getCurrentSession()?.id;
421
+ if (!sessionId) {
422
+ return;
423
+ }
424
+
425
+ try {
426
+ await playgroundService.stopExecution(sessionId);
427
+ playgroundService.stopPolling();
428
+ playgroundActions.setExecuting(false);
429
+ playgroundActions.updateSessionStatus("idle");
430
+ } catch (err) {
431
+ const errorMessage =
432
+ err instanceof Error ? err.message : "Failed to stop execution";
433
+ playgroundActions.setError(errorMessage);
434
+ logger.error("Failed to stop execution:", err);
435
+ }
436
+ }
437
+
438
+ /** Shared polling callback created from config lifecycle hooks */
439
+ // svelte-ignore state_referenced_locally — config is static
440
+ const pollingCallback = createPollingCallback(config.isTerminalStatus);
441
+
442
+ /**
443
+ * Start polling for messages
444
+ */
445
+ function startPolling(sessionId: string): void {
446
+ const pollingInterval = config.pollingInterval ?? 1500;
447
+
448
+ playgroundService.startPolling(
449
+ sessionId,
450
+ pollingCallback,
451
+ pollingInterval,
452
+ config.shouldStopPolling,
453
+ );
454
+ }
455
+
456
+ /**
457
+ * Refresh messages for the current session
458
+ * Called after interrupt resolution when polling has stopped
459
+ */
460
+ async function handleInterruptResolved(): Promise<void> {
461
+ const sessionId = getCurrentSession()?.id;
462
+ if (!sessionId) return;
463
+
464
+ try {
465
+ const response = await playgroundService.getMessages(sessionId);
466
+ pollingCallback(response);
467
+ } catch (err) {
468
+ logger.error(
469
+ "[Playground] Failed to refresh messages after interrupt:",
470
+ err,
471
+ );
472
+ }
473
+ }
474
+
475
+ /**
476
+ * Format date for display
477
+ */
478
+ function formatDate(dateString: string): string {
479
+ const date = new Date(dateString);
480
+ const now = new Date();
481
+ const diffMs = now.getTime() - date.getTime();
482
+ const diffMins = Math.floor(diffMs / 60000);
483
+ const diffHours = Math.floor(diffMs / 3600000);
484
+ const diffDays = Math.floor(diffMs / 86400000);
485
+
486
+ if (diffMins < 1) {
487
+ return "Just now";
488
+ }
489
+ if (diffMins < 60) {
490
+ return `${diffMins}m ago`;
491
+ }
492
+ if (diffHours < 24) {
493
+ return `${diffHours}h ago`;
494
+ }
495
+ if (diffDays < 7) {
496
+ return `${diffDays}d ago`;
497
+ }
498
+ return date.toLocaleDateString("en-US", {
499
+ month: "short",
500
+ day: "numeric",
501
+ });
502
+ }
480
503
  </script>
481
504
 
482
505
  <div
483
- class="playground"
484
- class:playground--embedded={mode === 'embedded'}
485
- class:playground--standalone={mode === 'standalone'}
486
- class:playground--modal={mode === 'modal'}
487
- class:playground--no-sidebar={config.showSidebar === false}
506
+ class="playground"
507
+ class:playground--embedded={mode === "embedded"}
508
+ class:playground--standalone={mode === "standalone"}
509
+ class:playground--modal={mode === "modal"}
510
+ class:playground--no-sidebar={config.showSidebar === false}
488
511
  >
489
- <div class="playground__container">
490
- <!-- Sidebar (conditionally rendered based on config.showSidebar) -->
491
- {#if config.showSidebar !== false}
492
- <aside
493
- class="playground__sidebar"
494
- style={config.sidebarWidth ? `--fd-playground-sidebar-width: ${config.sidebarWidth}` : ''}
495
- >
496
- <!-- Sidebar Header -->
497
- <div class="playground__sidebar-header">
498
- <div class="playground__sidebar-title">
499
- <span>Playground</span>
500
- </div>
501
- {#if (mode === 'embedded' || mode === 'modal') && onClose}
502
- <button
503
- type="button"
504
- class="playground__sidebar-close"
505
- onclick={onClose}
506
- title="Close playground"
507
- >
508
- {#if mode === 'modal'}
509
- <Icon icon="mdi:close" />
510
- {:else}
511
- <Icon icon="mdi:dock-right" />
512
- {/if}
513
- </button>
514
- {/if}
515
- </div>
516
-
517
- <!-- New Session Section -->
518
- <div class="playground__section">
519
- <button
520
- type="button"
521
- class="playground__new-session-btn"
522
- onclick={handleCreateSession}
523
- disabled={getIsLoading()}
524
- title="Start a new session"
525
- >
526
- <Icon icon="mdi:plus" />
527
- <span>New Session</span>
528
- </button>
529
-
530
- <!-- Sessions List - click a session to load it -->
531
- <div class="playground__sessions-wrap">
532
- {#if getSessions().length > 0}
533
- <p class="playground__sessions-hint">Click a session to load it</p>
534
- {/if}
535
- <div class="playground__sessions">
536
- {#if getSessions().length === 0 && !getIsLoading()}
537
- <div class="playground__sessions-empty">
538
- <span>No sessions yet</span>
539
- </div>
540
- {:else}
541
- {#each getSessions() as session (session.id)}
542
- <div
543
- class="playground__session"
544
- class:playground__session--active={getCurrentSession()?.id === session.id}
545
- role="button"
546
- tabindex="0"
547
- title="Click to load this session"
548
- aria-label="Load session: {session.name}"
549
- onclick={() => handleSelectSession(session.id)}
550
- onkeydown={(e) => e.key === 'Enter' && handleSelectSession(session.id)}
551
- >
552
- <span class="playground__session-name" title={session.name}>
553
- {session.name}
554
- </span>
555
- <div class="playground__session-actions">
556
- <button
557
- type="button"
558
- class="playground__session-menu"
559
- class:playground__session-menu--open={openMenuId === session.id}
560
- onclick={(e) => handleMenuToggle(e, session.id)}
561
- title="Session options"
562
- >
563
- <Icon icon="mdi:dots-vertical" />
564
- </button>
565
- {#if openMenuId === session.id}
566
- <div class="playground__session-dropdown">
567
- <button
568
- type="button"
569
- class="playground__session-dropdown-item playground__session-dropdown-item--danger"
570
- onclick={(e) => handleMenuDelete(e, session.id)}
571
- >
572
- <Icon icon="mdi:delete-outline" />
573
- <span>Delete</span>
574
- </button>
575
- </div>
576
- {/if}
577
- </div>
578
- </div>
579
- {/each}
580
- {/if}
581
- </div>
582
- </div>
583
- </div>
584
- </aside>
585
- {/if}
586
-
587
- <!-- Main Content -->
588
- <main class="playground__main">
589
- <!-- Session Header (conditionally rendered based on config.showSessionHeader) -->
590
- {#if getCurrentSession() && config.showSessionHeader !== false}
591
- <header class="playground__header">
592
- <h2 class="playground__header-title">{getCurrentSession()?.name}</h2>
593
- <button
594
- type="button"
595
- class="playground__header-close"
596
- onclick={handleCloseSession}
597
- title="Close session"
598
- >
599
- <Icon icon="mdi:close" />
600
- </button>
601
- </header>
602
- {/if}
603
-
604
- <!-- Error Banner -->
605
- {#if getError()}
606
- <div class="playground__error">
607
- <Icon icon="mdi:alert-circle" />
608
- <span>{getError()}</span>
609
- <button
610
- type="button"
611
- class="playground__error-dismiss"
612
- onclick={() => playgroundActions.setError(null)}
613
- >
614
- <Icon icon="mdi:close" />
615
- </button>
616
- </div>
617
- {/if}
618
-
619
- <!-- Chat Content -->
620
- <div class="playground__content">
621
- {#if getIsLoading() && !getCurrentSession()}
622
- <div class="playground__loading">
623
- <Icon icon="mdi:loading" class="playground__loading-icon" />
624
- <span>Loading...</span>
625
- </div>
626
- {:else}
627
- <ChatPanel
628
- showTimestamps={config.showTimestamps ?? true}
629
- autoScroll={config.autoScroll ?? true}
630
- showLogsInline={config.logDisplayMode === 'inline'}
631
- enableMarkdown={config.enableMarkdown ?? true}
632
- showChatInput={config.showChatInput ?? true}
633
- showRunButton={config.showRunButton ?? true}
634
- predefinedMessage={config.predefinedMessage ?? 'Run workflow'}
635
- onSendMessage={handleSendMessage}
636
- onStopExecution={handleStopExecution}
637
- onInterruptResolved={handleInterruptResolved}
638
- />
639
- {/if}
640
- </div>
641
- </main>
642
- </div>
512
+ <div class="playground__container">
513
+ <!-- Sidebar (conditionally rendered based on config.showSidebar) -->
514
+ {#if config.showSidebar !== false}
515
+ <aside
516
+ class="playground__sidebar"
517
+ style={config.sidebarWidth
518
+ ? `--fd-playground-sidebar-width: ${config.sidebarWidth}`
519
+ : ""}
520
+ >
521
+ <!-- Sidebar Header -->
522
+ <div class="playground__sidebar-header">
523
+ <div class="playground__sidebar-title">
524
+ <span>Playground</span>
525
+ </div>
526
+ {#if (mode === "embedded" || mode === "modal") && onClose}
527
+ <button
528
+ type="button"
529
+ class="playground__sidebar-close"
530
+ onclick={onClose}
531
+ title="Close playground"
532
+ >
533
+ {#if mode === "modal"}
534
+ <Icon icon="mdi:close" />
535
+ {:else}
536
+ <Icon icon="mdi:dock-right" />
537
+ {/if}
538
+ </button>
539
+ {/if}
540
+ </div>
541
+
542
+ <!-- New Session Section -->
543
+ <div class="playground__section">
544
+ <button
545
+ type="button"
546
+ class="playground__new-session-btn"
547
+ onclick={handleCreateSession}
548
+ disabled={getIsLoading()}
549
+ title="Start a new session"
550
+ >
551
+ <Icon icon="mdi:plus" />
552
+ <span>New Session</span>
553
+ </button>
554
+
555
+ <!-- Sessions List - click a session to load it -->
556
+ <div class="playground__sessions-wrap">
557
+ {#if getSessions().length > 0}
558
+ <p class="playground__sessions-hint">
559
+ Click a session to load it
560
+ </p>
561
+ {/if}
562
+ <div class="playground__sessions">
563
+ {#if getSessions().length === 0 && !getIsLoading()}
564
+ <div class="playground__sessions-empty">
565
+ <span>No sessions yet</span>
566
+ </div>
567
+ {:else}
568
+ {#each getSessions() as session (session.id)}
569
+ <div
570
+ class="playground__session"
571
+ class:playground__session--active={getCurrentSession()
572
+ ?.id === session.id}
573
+ role="button"
574
+ tabindex="0"
575
+ title="Click to load this session"
576
+ aria-label="Load session: {session.name}"
577
+ onclick={() => handleSelectSession(session.id)}
578
+ onkeydown={(e) =>
579
+ e.key === "Enter" && handleSelectSession(session.id)}
580
+ >
581
+ <span class="playground__session-name" title={session.name}>
582
+ {session.name}
583
+ </span>
584
+ <div class="playground__session-actions">
585
+ <button
586
+ type="button"
587
+ class="playground__session-menu"
588
+ class:playground__session-menu--open={openMenuId ===
589
+ session.id}
590
+ onclick={(e) => handleMenuToggle(e, session.id)}
591
+ title="Session options"
592
+ >
593
+ <Icon icon="mdi:dots-vertical" />
594
+ </button>
595
+ {#if openMenuId === session.id}
596
+ <div class="playground__session-dropdown">
597
+ <button
598
+ type="button"
599
+ class="playground__session-dropdown-item playground__session-dropdown-item--danger"
600
+ onclick={(e) => handleMenuDelete(e, session.id)}
601
+ >
602
+ <Icon icon="mdi:delete-outline" />
603
+ <span>Delete</span>
604
+ </button>
605
+ </div>
606
+ {/if}
607
+ </div>
608
+ </div>
609
+ {/each}
610
+ {/if}
611
+ </div>
612
+ </div>
613
+ </div>
614
+ </aside>
615
+ {/if}
616
+
617
+ <!-- Main Content -->
618
+ <main class="playground__main">
619
+ <!-- Session Header (conditionally rendered based on config.showSessionHeader) -->
620
+ {#if getCurrentSession() && config.showSessionHeader !== false}
621
+ <header class="playground__header">
622
+ <h2 class="playground__header-title">{getCurrentSession()?.name}</h2>
623
+ <button
624
+ type="button"
625
+ class="playground__header-close"
626
+ onclick={handleCloseSession}
627
+ title="Close session"
628
+ >
629
+ <Icon icon="mdi:close" />
630
+ </button>
631
+ </header>
632
+ {/if}
633
+
634
+ <!-- Error Banner -->
635
+ {#if getError()}
636
+ <div class="playground__error">
637
+ <Icon icon="mdi:alert-circle" />
638
+ <span>{getError()}</span>
639
+ <button
640
+ type="button"
641
+ class="playground__error-dismiss"
642
+ onclick={() => playgroundActions.setError(null)}
643
+ >
644
+ <Icon icon="mdi:close" />
645
+ </button>
646
+ </div>
647
+ {/if}
648
+
649
+ <!-- Chat Content -->
650
+ <div class="playground__content">
651
+ {#if getIsLoading() && !getCurrentSession()}
652
+ <div class="playground__loading">
653
+ <Icon icon="mdi:loading" class="playground__loading-icon" />
654
+ <span>Loading...</span>
655
+ </div>
656
+ {:else}
657
+ <ChatPanel
658
+ showTimestamps={config.showTimestamps ?? true}
659
+ autoScroll={config.autoScroll ?? true}
660
+ showLogsInline={config.logDisplayMode === "inline"}
661
+ enableMarkdown={config.enableMarkdown ?? true}
662
+ showChatInput={config.showChatInput ?? true}
663
+ showRunButton={config.showRunButton ?? true}
664
+ predefinedMessage={config.predefinedMessage ?? "Run workflow"}
665
+ onSendMessage={handleSendMessage}
666
+ onStopExecution={handleStopExecution}
667
+ onInterruptResolved={handleInterruptResolved}
668
+ />
669
+ {/if}
670
+ </div>
671
+ </main>
672
+ </div>
643
673
  </div>
644
674
 
645
675
  <style>
646
- .playground {
647
- display: flex;
648
- flex-direction: column;
649
- height: 100%;
650
- overflow: hidden; /* Prevent playground-level scrolling */
651
- background-color: var(--fd-muted);
652
- font-family:
653
- -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
654
- }
655
-
656
- .playground--embedded {
657
- border-left: 1px solid var(--fd-border);
658
- box-shadow: -4px 0 20px rgba(0, 0, 0, 0.08);
659
- }
660
-
661
- .playground--standalone {
662
- height: 100vh;
663
- background: var(--fd-layout-background, var(--fd-muted));
664
- }
665
-
666
- /* Dark mode override for standalone */
667
- :global([data-theme='dark']) .playground--standalone {
668
- background: linear-gradient(135deg, #141418 0%, #1a1a2e 50%, #16162a 100%);
669
- }
670
-
671
- .playground--modal {
672
- height: 100%;
673
- width: 100%;
674
- }
675
-
676
- /* No sidebar mode - minimal chat widget experience */
677
- .playground--no-sidebar .playground__main {
678
- border-left: none;
679
- }
680
-
681
- /* Container */
682
- .playground__container {
683
- display: flex;
684
- flex: 1;
685
- min-height: 0;
686
- }
687
-
688
- /* Sidebar */
689
- .playground__sidebar {
690
- width: var(--fd-playground-sidebar-width);
691
- background-color: var(--fd-background);
692
- border-right: 1px solid var(--fd-border);
693
- display: flex;
694
- flex-direction: column;
695
- }
696
-
697
- /* Fixed height so sidebar and main session header align on same horizontal line */
698
- .playground__sidebar-header {
699
- display: flex;
700
- align-items: center;
701
- justify-content: space-between;
702
- height: var(--fd-playground-header-height);
703
- padding: 0 var(--fd-space-xl);
704
- border-bottom: 1px solid var(--fd-border);
705
- box-sizing: border-box;
706
- flex-shrink: 0;
707
- }
708
-
709
- .playground__sidebar-title {
710
- display: flex;
711
- align-items: center;
712
- gap: var(--fd-space-xs);
713
- font-size: var(--fd-text-md);
714
- font-weight: 600;
715
- line-height: 1.25;
716
- color: var(--fd-foreground);
717
- }
718
-
719
- .playground__sidebar-close {
720
- display: flex;
721
- align-items: center;
722
- justify-content: center;
723
- width: var(--fd-playground-icon-btn-size);
724
- height: var(--fd-playground-icon-btn-size);
725
- border: none;
726
- border-radius: var(--fd-radius-md);
727
- background: transparent;
728
- color: var(--fd-muted-foreground);
729
- cursor: pointer;
730
- transition: all var(--fd-transition-fast);
731
- }
732
-
733
- .playground__sidebar-close:hover {
734
- background-color: var(--fd-muted);
735
- color: var(--fd-foreground);
736
- }
737
-
738
- /* Section */
739
- .playground__section {
740
- flex: 1;
741
- display: flex;
742
- flex-direction: column;
743
- min-height: 0;
744
- padding: var(--fd-space-md) var(--fd-space-xs) 0;
745
- }
746
-
747
- /* New Session – neutral full-width button with icon */
748
- .playground__new-session-btn {
749
- display: flex;
750
- align-items: center;
751
- justify-content: center;
752
- gap: var(--fd-space-xs);
753
- width: 100%;
754
- padding: var(--fd-space-sm) var(--fd-space-xl);
755
- border: 1px solid var(--fd-border);
756
- border-radius: var(--fd-radius-md);
757
- background-color: var(--fd-background);
758
- color: var(--fd-foreground);
759
- font-size: var(--fd-text-sm);
760
- font-weight: 500;
761
- cursor: pointer;
762
- transition:
763
- background-color var(--fd-transition-fast),
764
- border-color var(--fd-transition-fast),
765
- transform 0.1s ease;
766
- box-sizing: border-box;
767
- }
768
-
769
- .playground__new-session-btn:hover:not(:disabled) {
770
- background-color: var(--fd-muted);
771
- border-color: var(--fd-border);
772
- transform: translateY(-1px);
773
- }
774
-
775
- .playground__new-session-btn:focus {
776
- outline: none;
777
- box-shadow: 0 0 0 2px var(--fd-ring);
778
- }
779
-
780
- .playground__new-session-btn:disabled {
781
- opacity: 0.5;
782
- cursor: not-allowed;
783
- transform: none;
784
- }
785
-
786
- .playground__new-session-btn :global(svg) {
787
- width: 1.125rem;
788
- height: 1.125rem;
789
- }
790
-
791
- /* Sessions */
792
- .playground__sessions-wrap {
793
- flex: 1;
794
- display: flex;
795
- flex-direction: column;
796
- min-height: 0;
797
- }
798
-
799
- .playground__sessions-hint {
800
- font-size: var(--fd-text-2xs);
801
- color: var(--fd-muted-foreground);
802
- margin: var(--fd-space-md) 0 var(--fd-space-2xs) var(--fd-space-md);
803
- line-height: 1.3;
804
- }
805
-
806
- .playground__sessions {
807
- flex: 1;
808
- overflow-y: auto;
809
- padding: 0 var(--fd-space-xs) var(--fd-space-xl);
810
- min-height: 0;
811
- }
812
-
813
- .playground__sessions-empty {
814
- padding: var(--fd-space-xl);
815
- text-align: center;
816
- font-size: var(--fd-text-xsm);
817
- color: var(--fd-muted-foreground);
818
- }
819
-
820
- /* Session row - clickable to load session; clear hover/active affordance */
821
- .playground__session {
822
- display: flex;
823
- align-items: center;
824
- justify-content: space-between;
825
- padding: var(--fd-space-sm) var(--fd-space-md);
826
- margin-bottom: var(--fd-space-3xs);
827
- border-radius: var(--fd-radius-md);
828
- border-left: 3px solid transparent;
829
- cursor: pointer;
830
- transition:
831
- background-color var(--fd-transition-fast),
832
- border-left-color var(--fd-transition-fast);
833
- }
834
-
835
- .playground__session:hover {
836
- background-color: var(--fd-muted);
837
- border-left-color: var(--fd-border);
838
- }
839
-
840
- .playground__session--active {
841
- background-color: var(--fd-primary-muted);
842
- border-left-color: var(--fd-primary);
843
- }
844
-
845
- .playground__session--active:hover {
846
- background-color: var(--fd-primary-muted);
847
- border-left-color: var(--fd-primary);
848
- }
849
-
850
- .playground__session-name {
851
- flex: 1;
852
- font-size: var(--fd-text-sm);
853
- color: var(--fd-foreground);
854
- white-space: nowrap;
855
- overflow: hidden;
856
- text-overflow: ellipsis;
857
- }
858
-
859
- .playground__session--active .playground__session-name {
860
- color: var(--fd-primary);
861
- font-weight: 500;
862
- }
863
-
864
- .playground__session-menu {
865
- display: flex;
866
- align-items: center;
867
- justify-content: center;
868
- width: var(--fd-space-3xl);
869
- height: var(--fd-space-3xl);
870
- border: none;
871
- border-radius: var(--fd-radius-sm);
872
- background: transparent;
873
- color: var(--fd-muted-foreground);
874
- cursor: pointer;
875
- opacity: 0;
876
- transition: all var(--fd-transition-fast);
877
- }
878
-
879
- .playground__session:hover .playground__session-menu {
880
- opacity: 1;
881
- }
882
-
883
- .playground__session-menu:hover {
884
- background-color: var(--fd-muted);
885
- color: var(--fd-foreground);
886
- }
887
-
888
- .playground__session-menu--open {
889
- opacity: 1;
890
- background-color: var(--fd-muted);
891
- color: var(--fd-foreground);
892
- }
893
-
894
- .playground__session-actions {
895
- position: relative;
896
- display: flex;
897
- align-items: center;
898
- flex-shrink: 0;
899
- }
900
-
901
- .playground__session-dropdown {
902
- position: absolute;
903
- top: 100%;
904
- right: 0;
905
- z-index: 50;
906
- min-width: 140px;
907
- padding: var(--fd-space-xs);
908
- background-color: var(--fd-background);
909
- border: 1px solid var(--fd-border);
910
- border-radius: var(--fd-radius-md);
911
- box-shadow: var(--fd-shadow-lg);
912
- }
913
-
914
- .playground__session-dropdown-item {
915
- display: flex;
916
- align-items: center;
917
- gap: var(--fd-space-sm);
918
- width: 100%;
919
- padding: var(--fd-space-sm) var(--fd-space-md);
920
- border: none;
921
- border-radius: var(--fd-radius-sm);
922
- background: transparent;
923
- color: var(--fd-foreground);
924
- font-size: var(--fd-text-sm);
925
- cursor: pointer;
926
- transition: all var(--fd-transition-fast);
927
- white-space: nowrap;
928
- }
929
-
930
- .playground__session-dropdown-item:hover {
931
- background-color: var(--fd-muted);
932
- }
933
-
934
- .playground__session-dropdown-item--danger {
935
- color: var(--fd-error);
936
- }
937
-
938
- .playground__session-dropdown-item--danger:hover {
939
- background-color: var(--fd-error-muted);
940
- color: var(--fd-error);
941
- }
942
-
943
- /* Main Content */
944
- .playground__main {
945
- flex: 1;
946
- display: flex;
947
- flex-direction: column;
948
- min-width: 0;
949
- min-height: 0; /* Allow proper flex shrinking */
950
- overflow: hidden; /* Prevent scrolling - ChatPanel handles it */
951
- background-color: var(--fd-background);
952
- }
953
-
954
- /* Header - exact same height as playground__sidebar-header for alignment */
955
- .playground__header {
956
- display: flex;
957
- align-items: center;
958
- justify-content: space-between;
959
- height: var(--fd-playground-header-height);
960
- padding: 0 var(--fd-space-2xl);
961
- border-bottom: 1px solid var(--fd-border);
962
- background-color: var(--fd-background);
963
- box-sizing: border-box;
964
- flex-shrink: 0;
965
- }
966
-
967
- .playground__header-title {
968
- font-size: var(--fd-text-md);
969
- font-weight: 600;
970
- line-height: 1.25;
971
- color: var(--fd-foreground);
972
- margin: 0;
973
- }
974
-
975
- .playground__header-close {
976
- display: flex;
977
- align-items: center;
978
- justify-content: center;
979
- width: var(--fd-playground-icon-btn-size);
980
- height: var(--fd-playground-icon-btn-size);
981
- border: none;
982
- border-radius: var(--fd-radius-md);
983
- background: transparent;
984
- color: var(--fd-muted-foreground);
985
- cursor: pointer;
986
- transition: all var(--fd-transition-fast);
987
- }
988
-
989
- .playground__header-close:hover {
990
- background-color: var(--fd-muted);
991
- color: var(--fd-foreground);
992
- }
993
-
994
- /* Error */
995
- .playground__error {
996
- display: flex;
997
- align-items: center;
998
- gap: var(--fd-space-xs);
999
- padding: var(--fd-space-md) var(--fd-space-xl);
1000
- background-color: var(--fd-error-muted);
1001
- border-bottom: 1px solid var(--fd-error);
1002
- color: var(--fd-error);
1003
- font-size: var(--fd-text-sm);
1004
- }
1005
-
1006
- .playground__error-dismiss {
1007
- margin-left: auto;
1008
- display: flex;
1009
- align-items: center;
1010
- justify-content: center;
1011
- width: var(--fd-space-3xl);
1012
- height: var(--fd-space-3xl);
1013
- border: none;
1014
- border-radius: var(--fd-radius-sm);
1015
- background: transparent;
1016
- color: var(--fd-error);
1017
- cursor: pointer;
1018
- transition: background-color var(--fd-transition-fast);
1019
- }
1020
-
1021
- .playground__error-dismiss:hover {
1022
- background-color: var(--fd-error-muted);
1023
- }
1024
-
1025
- /* Content */
1026
- .playground__content {
1027
- flex: 1;
1028
- min-height: 0;
1029
- display: flex;
1030
- flex-direction: column;
1031
- }
1032
-
1033
- /* Loading */
1034
- .playground__loading {
1035
- display: flex;
1036
- flex-direction: column;
1037
- align-items: center;
1038
- justify-content: center;
1039
- flex: 1;
1040
- gap: var(--fd-space-xl);
1041
- color: var(--fd-muted-foreground);
1042
- }
1043
-
1044
- :global(.playground__loading-icon) {
1045
- font-size: var(--fd-text-2xl);
1046
- animation: spin 1s linear infinite;
1047
- }
1048
-
1049
- @keyframes spin {
1050
- from {
1051
- transform: rotate(0deg);
1052
- }
1053
- to {
1054
- transform: rotate(360deg);
1055
- }
1056
- }
1057
-
1058
- /* Responsive */
1059
- @media (max-width: 768px) {
1060
- .playground__sidebar {
1061
- width: 180px;
1062
- }
1063
- }
1064
-
1065
- @media (max-width: 640px) {
1066
- .playground__sidebar {
1067
- position: absolute;
1068
- left: 0;
1069
- top: 0;
1070
- bottom: 0;
1071
- z-index: 20;
1072
- box-shadow: 4px 0 20px rgba(0, 0, 0, 0.1);
1073
- }
1074
- }
676
+ .playground {
677
+ display: flex;
678
+ flex-direction: column;
679
+ height: 100%;
680
+ overflow: hidden; /* Prevent playground-level scrolling */
681
+ background-color: var(--fd-muted);
682
+ font-family:
683
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue",
684
+ Arial, sans-serif;
685
+ }
686
+
687
+ .playground--embedded {
688
+ border-left: 1px solid var(--fd-border);
689
+ box-shadow: -4px 0 20px rgba(0, 0, 0, 0.08);
690
+ }
691
+
692
+ .playground--standalone {
693
+ height: 100vh;
694
+ background: var(--fd-layout-background, var(--fd-muted));
695
+ }
696
+
697
+ /* Dark mode override for standalone */
698
+ :global([data-theme="dark"]) .playground--standalone {
699
+ background: linear-gradient(135deg, #141418 0%, #1a1a2e 50%, #16162a 100%);
700
+ }
701
+
702
+ .playground--modal {
703
+ height: 100%;
704
+ width: 100%;
705
+ }
706
+
707
+ /* No sidebar mode - minimal chat widget experience */
708
+ .playground--no-sidebar .playground__main {
709
+ border-left: none;
710
+ }
711
+
712
+ /* Container */
713
+ .playground__container {
714
+ display: flex;
715
+ flex: 1;
716
+ min-height: 0;
717
+ }
718
+
719
+ /* Sidebar */
720
+ .playground__sidebar {
721
+ width: var(--fd-playground-sidebar-width);
722
+ background-color: var(--fd-background);
723
+ border-right: 1px solid var(--fd-border);
724
+ display: flex;
725
+ flex-direction: column;
726
+ }
727
+
728
+ /* Fixed height so sidebar and main session header align on same horizontal line */
729
+ .playground__sidebar-header {
730
+ display: flex;
731
+ align-items: center;
732
+ justify-content: space-between;
733
+ height: var(--fd-playground-header-height);
734
+ padding: 0 var(--fd-space-xl);
735
+ border-bottom: 1px solid var(--fd-border);
736
+ box-sizing: border-box;
737
+ flex-shrink: 0;
738
+ }
739
+
740
+ .playground__sidebar-title {
741
+ display: flex;
742
+ align-items: center;
743
+ gap: var(--fd-space-xs);
744
+ font-size: var(--fd-text-md);
745
+ font-weight: 600;
746
+ line-height: 1.25;
747
+ color: var(--fd-foreground);
748
+ }
749
+
750
+ .playground__sidebar-close {
751
+ display: flex;
752
+ align-items: center;
753
+ justify-content: center;
754
+ width: var(--fd-playground-icon-btn-size);
755
+ height: var(--fd-playground-icon-btn-size);
756
+ border: none;
757
+ border-radius: var(--fd-radius-md);
758
+ background: transparent;
759
+ color: var(--fd-muted-foreground);
760
+ cursor: pointer;
761
+ transition: all var(--fd-transition-fast);
762
+ }
763
+
764
+ .playground__sidebar-close:hover {
765
+ background-color: var(--fd-muted);
766
+ color: var(--fd-foreground);
767
+ }
768
+
769
+ /* Section */
770
+ .playground__section {
771
+ flex: 1;
772
+ display: flex;
773
+ flex-direction: column;
774
+ min-height: 0;
775
+ padding: var(--fd-space-md) var(--fd-space-xs) 0;
776
+ }
777
+
778
+ /* New Session – neutral full-width button with icon */
779
+ .playground__new-session-btn {
780
+ display: flex;
781
+ align-items: center;
782
+ justify-content: center;
783
+ gap: var(--fd-space-xs);
784
+ width: 100%;
785
+ padding: var(--fd-space-sm) var(--fd-space-xl);
786
+ border: 1px solid var(--fd-border);
787
+ border-radius: var(--fd-radius-md);
788
+ background-color: var(--fd-background);
789
+ color: var(--fd-foreground);
790
+ font-size: var(--fd-text-sm);
791
+ font-weight: 500;
792
+ cursor: pointer;
793
+ transition:
794
+ background-color var(--fd-transition-fast),
795
+ border-color var(--fd-transition-fast),
796
+ transform 0.1s ease;
797
+ box-sizing: border-box;
798
+ }
799
+
800
+ .playground__new-session-btn:hover:not(:disabled) {
801
+ background-color: var(--fd-muted);
802
+ border-color: var(--fd-border);
803
+ transform: translateY(-1px);
804
+ }
805
+
806
+ .playground__new-session-btn:focus {
807
+ outline: none;
808
+ box-shadow: 0 0 0 2px var(--fd-ring);
809
+ }
810
+
811
+ .playground__new-session-btn:disabled {
812
+ opacity: 0.5;
813
+ cursor: not-allowed;
814
+ transform: none;
815
+ }
816
+
817
+ .playground__new-session-btn :global(svg) {
818
+ width: 1.125rem;
819
+ height: 1.125rem;
820
+ }
821
+
822
+ /* Sessions */
823
+ .playground__sessions-wrap {
824
+ flex: 1;
825
+ display: flex;
826
+ flex-direction: column;
827
+ min-height: 0;
828
+ }
829
+
830
+ .playground__sessions-hint {
831
+ font-size: var(--fd-text-2xs);
832
+ color: var(--fd-muted-foreground);
833
+ margin: var(--fd-space-md) 0 var(--fd-space-2xs) var(--fd-space-md);
834
+ line-height: 1.3;
835
+ }
836
+
837
+ .playground__sessions {
838
+ flex: 1;
839
+ overflow-y: auto;
840
+ padding: 0 var(--fd-space-xs) var(--fd-space-xl);
841
+ min-height: 0;
842
+ }
843
+
844
+ .playground__sessions-empty {
845
+ padding: var(--fd-space-xl);
846
+ text-align: center;
847
+ font-size: var(--fd-text-xsm);
848
+ color: var(--fd-muted-foreground);
849
+ }
850
+
851
+ /* Session row - clickable to load session; clear hover/active affordance */
852
+ .playground__session {
853
+ display: flex;
854
+ align-items: center;
855
+ justify-content: space-between;
856
+ padding: var(--fd-space-sm) var(--fd-space-md);
857
+ margin-bottom: var(--fd-space-3xs);
858
+ border-radius: var(--fd-radius-md);
859
+ border-left: 3px solid transparent;
860
+ cursor: pointer;
861
+ transition:
862
+ background-color var(--fd-transition-fast),
863
+ border-left-color var(--fd-transition-fast);
864
+ }
865
+
866
+ .playground__session:hover {
867
+ background-color: var(--fd-muted);
868
+ border-left-color: var(--fd-border);
869
+ }
870
+
871
+ .playground__session--active {
872
+ background-color: var(--fd-primary-muted);
873
+ border-left-color: var(--fd-primary);
874
+ }
875
+
876
+ .playground__session--active:hover {
877
+ background-color: var(--fd-primary-muted);
878
+ border-left-color: var(--fd-primary);
879
+ }
880
+
881
+ .playground__session-name {
882
+ flex: 1;
883
+ font-size: var(--fd-text-sm);
884
+ color: var(--fd-foreground);
885
+ white-space: nowrap;
886
+ overflow: hidden;
887
+ text-overflow: ellipsis;
888
+ }
889
+
890
+ .playground__session--active .playground__session-name {
891
+ color: var(--fd-primary);
892
+ font-weight: 500;
893
+ }
894
+
895
+ .playground__session-menu {
896
+ display: flex;
897
+ align-items: center;
898
+ justify-content: center;
899
+ width: var(--fd-space-3xl);
900
+ height: var(--fd-space-3xl);
901
+ border: none;
902
+ border-radius: var(--fd-radius-sm);
903
+ background: transparent;
904
+ color: var(--fd-muted-foreground);
905
+ cursor: pointer;
906
+ opacity: 0;
907
+ transition: all var(--fd-transition-fast);
908
+ }
909
+
910
+ .playground__session:hover .playground__session-menu {
911
+ opacity: 1;
912
+ }
913
+
914
+ .playground__session-menu:hover {
915
+ background-color: var(--fd-muted);
916
+ color: var(--fd-foreground);
917
+ }
918
+
919
+ .playground__session-menu--open {
920
+ opacity: 1;
921
+ background-color: var(--fd-muted);
922
+ color: var(--fd-foreground);
923
+ }
924
+
925
+ .playground__session-actions {
926
+ position: relative;
927
+ display: flex;
928
+ align-items: center;
929
+ flex-shrink: 0;
930
+ }
931
+
932
+ .playground__session-dropdown {
933
+ position: absolute;
934
+ top: 100%;
935
+ right: 0;
936
+ z-index: 50;
937
+ min-width: 140px;
938
+ padding: var(--fd-space-xs);
939
+ background-color: var(--fd-background);
940
+ border: 1px solid var(--fd-border);
941
+ border-radius: var(--fd-radius-md);
942
+ box-shadow: var(--fd-shadow-lg);
943
+ }
944
+
945
+ .playground__session-dropdown-item {
946
+ display: flex;
947
+ align-items: center;
948
+ gap: var(--fd-space-sm);
949
+ width: 100%;
950
+ padding: var(--fd-space-sm) var(--fd-space-md);
951
+ border: none;
952
+ border-radius: var(--fd-radius-sm);
953
+ background: transparent;
954
+ color: var(--fd-foreground);
955
+ font-size: var(--fd-text-sm);
956
+ cursor: pointer;
957
+ transition: all var(--fd-transition-fast);
958
+ white-space: nowrap;
959
+ }
960
+
961
+ .playground__session-dropdown-item:hover {
962
+ background-color: var(--fd-muted);
963
+ }
964
+
965
+ .playground__session-dropdown-item--danger {
966
+ color: var(--fd-error);
967
+ }
968
+
969
+ .playground__session-dropdown-item--danger:hover {
970
+ background-color: var(--fd-error-muted);
971
+ color: var(--fd-error);
972
+ }
973
+
974
+ /* Main Content */
975
+ .playground__main {
976
+ flex: 1;
977
+ display: flex;
978
+ flex-direction: column;
979
+ min-width: 0;
980
+ min-height: 0; /* Allow proper flex shrinking */
981
+ overflow: hidden; /* Prevent scrolling - ChatPanel handles it */
982
+ background-color: var(--fd-background);
983
+ }
984
+
985
+ /* Header - exact same height as playground__sidebar-header for alignment */
986
+ .playground__header {
987
+ display: flex;
988
+ align-items: center;
989
+ justify-content: space-between;
990
+ height: var(--fd-playground-header-height);
991
+ padding: 0 var(--fd-space-2xl);
992
+ border-bottom: 1px solid var(--fd-border);
993
+ background-color: var(--fd-background);
994
+ box-sizing: border-box;
995
+ flex-shrink: 0;
996
+ }
997
+
998
+ .playground__header-title {
999
+ font-size: var(--fd-text-md);
1000
+ font-weight: 600;
1001
+ line-height: 1.25;
1002
+ color: var(--fd-foreground);
1003
+ margin: 0;
1004
+ }
1005
+
1006
+ .playground__header-close {
1007
+ display: flex;
1008
+ align-items: center;
1009
+ justify-content: center;
1010
+ width: var(--fd-playground-icon-btn-size);
1011
+ height: var(--fd-playground-icon-btn-size);
1012
+ border: none;
1013
+ border-radius: var(--fd-radius-md);
1014
+ background: transparent;
1015
+ color: var(--fd-muted-foreground);
1016
+ cursor: pointer;
1017
+ transition: all var(--fd-transition-fast);
1018
+ }
1019
+
1020
+ .playground__header-close:hover {
1021
+ background-color: var(--fd-muted);
1022
+ color: var(--fd-foreground);
1023
+ }
1024
+
1025
+ /* Error */
1026
+ .playground__error {
1027
+ display: flex;
1028
+ align-items: center;
1029
+ gap: var(--fd-space-xs);
1030
+ padding: var(--fd-space-md) var(--fd-space-xl);
1031
+ background-color: var(--fd-error-muted);
1032
+ border-bottom: 1px solid var(--fd-error);
1033
+ color: var(--fd-error);
1034
+ font-size: var(--fd-text-sm);
1035
+ }
1036
+
1037
+ .playground__error-dismiss {
1038
+ margin-left: auto;
1039
+ display: flex;
1040
+ align-items: center;
1041
+ justify-content: center;
1042
+ width: var(--fd-space-3xl);
1043
+ height: var(--fd-space-3xl);
1044
+ border: none;
1045
+ border-radius: var(--fd-radius-sm);
1046
+ background: transparent;
1047
+ color: var(--fd-error);
1048
+ cursor: pointer;
1049
+ transition: background-color var(--fd-transition-fast);
1050
+ }
1051
+
1052
+ .playground__error-dismiss:hover {
1053
+ background-color: var(--fd-error-muted);
1054
+ }
1055
+
1056
+ /* Content */
1057
+ .playground__content {
1058
+ flex: 1;
1059
+ min-height: 0;
1060
+ display: flex;
1061
+ flex-direction: column;
1062
+ }
1063
+
1064
+ /* Loading */
1065
+ .playground__loading {
1066
+ display: flex;
1067
+ flex-direction: column;
1068
+ align-items: center;
1069
+ justify-content: center;
1070
+ flex: 1;
1071
+ gap: var(--fd-space-xl);
1072
+ color: var(--fd-muted-foreground);
1073
+ }
1074
+
1075
+ :global(.playground__loading-icon) {
1076
+ font-size: var(--fd-text-2xl);
1077
+ animation: spin 1s linear infinite;
1078
+ }
1079
+
1080
+ @keyframes spin {
1081
+ from {
1082
+ transform: rotate(0deg);
1083
+ }
1084
+ to {
1085
+ transform: rotate(360deg);
1086
+ }
1087
+ }
1088
+
1089
+ /* Responsive */
1090
+ @media (max-width: 768px) {
1091
+ .playground__sidebar {
1092
+ width: 180px;
1093
+ }
1094
+ }
1095
+
1096
+ @media (max-width: 640px) {
1097
+ .playground__sidebar {
1098
+ position: absolute;
1099
+ left: 0;
1100
+ top: 0;
1101
+ bottom: 0;
1102
+ z-index: 20;
1103
+ box-shadow: 4px 0 20px rgba(0, 0, 0, 0.1);
1104
+ }
1105
+ }
1075
1106
  </style>