@flowdrop/flowdrop 1.0.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/README.md +50 -50
  2. package/dist/adapters/WorkflowAdapter.d.ts +1 -1
  3. package/dist/adapters/WorkflowAdapter.js +25 -25
  4. package/dist/adapters/agentspec/AgentSpecAdapter.d.ts +2 -2
  5. package/dist/adapters/agentspec/AgentSpecAdapter.js +133 -122
  6. package/dist/adapters/agentspec/agentAdapter.d.ts +2 -2
  7. package/dist/adapters/agentspec/agentAdapter.js +10 -10
  8. package/dist/adapters/agentspec/autoLayout.d.ts +1 -1
  9. package/dist/adapters/agentspec/autoLayout.js +2 -2
  10. package/dist/adapters/agentspec/componentTypeDefaults.d.ts +1 -1
  11. package/dist/adapters/agentspec/componentTypeDefaults.js +120 -120
  12. package/dist/adapters/agentspec/defaultNodeTypes.d.ts +2 -2
  13. package/dist/adapters/agentspec/defaultNodeTypes.js +307 -307
  14. package/dist/adapters/agentspec/index.d.ts +10 -10
  15. package/dist/adapters/agentspec/index.js +6 -6
  16. package/dist/adapters/agentspec/validator.d.ts +2 -2
  17. package/dist/adapters/agentspec/validator.js +22 -20
  18. package/dist/api/enhanced-client.d.ts +3 -3
  19. package/dist/api/enhanced-client.js +73 -72
  20. package/dist/components/App.svelte +1090 -961
  21. package/dist/components/App.svelte.d.ts +9 -6
  22. package/dist/components/CanvasBanner.stories.svelte +23 -20
  23. package/dist/components/CanvasBanner.stories.svelte.d.ts +1 -1
  24. package/dist/components/CanvasBanner.svelte +52 -46
  25. package/dist/components/ConfigForm.svelte +1164 -1065
  26. package/dist/components/ConfigForm.svelte.d.ts +2 -2
  27. package/dist/components/ConfigModal.svelte +180 -180
  28. package/dist/components/ConfigModal.svelte.d.ts +1 -1
  29. package/dist/components/ConfigPanel.stories.svelte +35 -35
  30. package/dist/components/ConfigPanel.stories.svelte.d.ts +1 -1
  31. package/dist/components/ConfigPanel.svelte +178 -167
  32. package/dist/components/ConfigPanel.svelte.d.ts +1 -1
  33. package/dist/components/ConnectionLine.svelte +25 -25
  34. package/dist/components/EdgeRefresher.svelte +26 -26
  35. package/dist/components/FlowDropEdge.stories.svelte +179 -143
  36. package/dist/components/FlowDropEdge.svelte +147 -147
  37. package/dist/components/FlowDropEdge.svelte.d.ts +1 -1
  38. package/dist/components/FlowDropZone.svelte +63 -60
  39. package/dist/components/FlowDropZone.svelte.d.ts +1 -1
  40. package/dist/components/LoadingSpinner.stories.svelte +19 -19
  41. package/dist/components/LoadingSpinner.stories.svelte.d.ts +1 -1
  42. package/dist/components/LoadingSpinner.svelte +21 -21
  43. package/dist/components/LoadingSpinner.svelte.d.ts +1 -1
  44. package/dist/components/Logo.stories.svelte +13 -13
  45. package/dist/components/Logo.stories.svelte.d.ts +1 -1
  46. package/dist/components/Logo.svelte +101 -95
  47. package/dist/components/LogsSidebar.svelte +553 -546
  48. package/dist/components/LogsSidebar.svelte.d.ts +1 -1
  49. package/dist/components/MarkdownDisplay.stories.svelte +29 -23
  50. package/dist/components/MarkdownDisplay.stories.svelte.d.ts +1 -1
  51. package/dist/components/MarkdownDisplay.svelte +16 -14
  52. package/dist/components/Navbar.stories.svelte +43 -38
  53. package/dist/components/Navbar.stories.svelte.d.ts +1 -1
  54. package/dist/components/Navbar.svelte +760 -706
  55. package/dist/components/Navbar.svelte.d.ts +1 -1
  56. package/dist/components/NodeSidebar.svelte +905 -746
  57. package/dist/components/NodeSidebar.svelte.d.ts +5 -1
  58. package/dist/components/NodeStatusOverlay.stories.svelte +82 -70
  59. package/dist/components/NodeStatusOverlay.stories.svelte.d.ts +1 -1
  60. package/dist/components/NodeStatusOverlay.svelte +295 -280
  61. package/dist/components/NodeStatusOverlay.svelte.d.ts +3 -3
  62. package/dist/components/PipelineStatus.svelte +326 -300
  63. package/dist/components/PipelineStatus.svelte.d.ts +4 -4
  64. package/dist/components/PortCoordinateTracker.svelte +49 -47
  65. package/dist/components/PortCoordinateTracker.svelte.d.ts +1 -1
  66. package/dist/components/ReadOnlyDetails.svelte +156 -156
  67. package/dist/components/SchemaForm.stories.svelte +106 -98
  68. package/dist/components/SchemaForm.stories.svelte.d.ts +1 -1
  69. package/dist/components/SchemaForm.svelte +490 -463
  70. package/dist/components/SchemaForm.svelte.d.ts +2 -2
  71. package/dist/components/SettingsModal.svelte +226 -223
  72. package/dist/components/SettingsModal.svelte.d.ts +1 -1
  73. package/dist/components/SettingsPanel.svelte +637 -601
  74. package/dist/components/SettingsPanel.svelte.d.ts +1 -1
  75. package/dist/components/StatusIcon.stories.svelte +62 -49
  76. package/dist/components/StatusIcon.stories.svelte.d.ts +1 -1
  77. package/dist/components/StatusIcon.svelte +87 -87
  78. package/dist/components/StatusIcon.svelte.d.ts +2 -2
  79. package/dist/components/StatusLabel.stories.svelte +12 -12
  80. package/dist/components/StatusLabel.stories.svelte.d.ts +1 -1
  81. package/dist/components/StatusLabel.svelte +19 -19
  82. package/dist/components/ThemeToggle.stories.svelte +16 -16
  83. package/dist/components/ThemeToggle.stories.svelte.d.ts +1 -1
  84. package/dist/components/ThemeToggle.svelte +180 -169
  85. package/dist/components/ThemeToggle.svelte.d.ts +1 -1
  86. package/dist/components/UniversalNode.svelte +150 -138
  87. package/dist/components/UniversalNode.svelte.d.ts +3 -3
  88. package/dist/components/WorkflowEditor.svelte +1069 -1014
  89. package/dist/components/WorkflowEditor.svelte.d.ts +4 -4
  90. package/dist/components/form/FormArray.svelte +1034 -973
  91. package/dist/components/form/FormArray.svelte.d.ts +1 -1
  92. package/dist/components/form/FormAutocomplete.svelte +1021 -978
  93. package/dist/components/form/FormAutocomplete.svelte.d.ts +1 -1
  94. package/dist/components/form/FormCheckboxGroup.stories.svelte +23 -20
  95. package/dist/components/form/FormCheckboxGroup.stories.svelte.d.ts +1 -1
  96. package/dist/components/form/FormCheckboxGroup.svelte +136 -136
  97. package/dist/components/form/FormCodeEditor.svelte +452 -434
  98. package/dist/components/form/FormField.svelte +366 -355
  99. package/dist/components/form/FormField.svelte.d.ts +2 -2
  100. package/dist/components/form/FormFieldLight.svelte +400 -384
  101. package/dist/components/form/FormFieldLight.svelte.d.ts +1 -1
  102. package/dist/components/form/FormFieldWrapper.stories.svelte +42 -42
  103. package/dist/components/form/FormFieldWrapper.stories.svelte.d.ts +1 -1
  104. package/dist/components/form/FormFieldWrapper.svelte +100 -93
  105. package/dist/components/form/FormFieldWrapper.svelte.d.ts +1 -1
  106. package/dist/components/form/FormFieldset.svelte +108 -108
  107. package/dist/components/form/FormFieldset.svelte.d.ts +2 -2
  108. package/dist/components/form/FormMarkdownEditor.svelte +758 -725
  109. package/dist/components/form/FormNumberField.stories.svelte +25 -25
  110. package/dist/components/form/FormNumberField.stories.svelte.d.ts +1 -1
  111. package/dist/components/form/FormNumberField.svelte +88 -88
  112. package/dist/components/form/FormRangeField.stories.svelte +20 -20
  113. package/dist/components/form/FormRangeField.stories.svelte.d.ts +1 -1
  114. package/dist/components/form/FormRangeField.svelte +234 -226
  115. package/dist/components/form/FormSelect.stories.svelte +38 -38
  116. package/dist/components/form/FormSelect.stories.svelte.d.ts +1 -1
  117. package/dist/components/form/FormSelect.svelte +101 -101
  118. package/dist/components/form/FormSelect.svelte.d.ts +1 -1
  119. package/dist/components/form/FormTemplateEditor.svelte +847 -798
  120. package/dist/components/form/FormTemplateEditor.svelte.d.ts +1 -1
  121. package/dist/components/form/FormTextField.stories.svelte +29 -23
  122. package/dist/components/form/FormTextField.stories.svelte.d.ts +1 -1
  123. package/dist/components/form/FormTextField.svelte +68 -68
  124. package/dist/components/form/FormTextarea.stories.svelte +28 -25
  125. package/dist/components/form/FormTextarea.stories.svelte.d.ts +1 -1
  126. package/dist/components/form/FormTextarea.svelte +74 -74
  127. package/dist/components/form/FormToggle.stories.svelte +23 -20
  128. package/dist/components/form/FormToggle.stories.svelte.d.ts +1 -1
  129. package/dist/components/form/FormToggle.svelte +98 -98
  130. package/dist/components/form/FormUISchemaRenderer.svelte +120 -113
  131. package/dist/components/form/FormUISchemaRenderer.svelte.d.ts +3 -3
  132. package/dist/components/form/index.d.ts +19 -19
  133. package/dist/components/form/index.js +18 -18
  134. package/dist/components/form/templateAutocomplete.d.ts +2 -2
  135. package/dist/components/form/templateAutocomplete.js +64 -55
  136. package/dist/components/form/types.d.ts +6 -6
  137. package/dist/components/form/types.js +9 -4
  138. package/dist/components/icons/AlertCircleIcon.svelte +11 -0
  139. package/dist/components/icons/AlertCircleIcon.svelte.d.ts +26 -0
  140. package/dist/components/icons/CogIcon.svelte +11 -0
  141. package/dist/components/icons/CogIcon.svelte.d.ts +26 -0
  142. package/dist/components/interrupt/ChoicePrompt.stories.svelte +54 -38
  143. package/dist/components/interrupt/ChoicePrompt.stories.svelte.d.ts +1 -1
  144. package/dist/components/interrupt/ChoicePrompt.svelte +407 -383
  145. package/dist/components/interrupt/ChoicePrompt.svelte.d.ts +1 -1
  146. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte +48 -48
  147. package/dist/components/interrupt/ConfirmationPrompt.stories.svelte.d.ts +1 -1
  148. package/dist/components/interrupt/ConfirmationPrompt.svelte +280 -274
  149. package/dist/components/interrupt/ConfirmationPrompt.svelte.d.ts +1 -1
  150. package/dist/components/interrupt/FormPrompt.svelte +223 -218
  151. package/dist/components/interrupt/FormPrompt.svelte.d.ts +1 -1
  152. package/dist/components/interrupt/InterruptBubble.svelte +617 -583
  153. package/dist/components/interrupt/InterruptBubble.svelte.d.ts +2 -2
  154. package/dist/components/interrupt/ReviewPrompt.stories.svelte +66 -56
  155. package/dist/components/interrupt/ReviewPrompt.stories.svelte.d.ts +1 -1
  156. package/dist/components/interrupt/ReviewPrompt.svelte +861 -841
  157. package/dist/components/interrupt/ReviewPrompt.svelte.d.ts +1 -1
  158. package/dist/components/interrupt/TextInputPrompt.stories.svelte +38 -33
  159. package/dist/components/interrupt/TextInputPrompt.stories.svelte.d.ts +1 -1
  160. package/dist/components/interrupt/TextInputPrompt.svelte +333 -328
  161. package/dist/components/interrupt/TextInputPrompt.svelte.d.ts +1 -1
  162. package/dist/components/interrupt/index.d.ts +5 -5
  163. package/dist/components/interrupt/index.js +5 -5
  164. package/dist/components/layouts/MainLayout.svelte +724 -691
  165. package/dist/components/layouts/MainLayout.svelte.d.ts +6 -6
  166. package/dist/components/nodes/GatewayNode.stories.svelte +100 -99
  167. package/dist/components/nodes/GatewayNode.svelte +605 -571
  168. package/dist/components/nodes/GatewayNode.svelte.d.ts +3 -3
  169. package/dist/components/nodes/IdeaNode.stories.svelte +44 -43
  170. package/dist/components/nodes/IdeaNode.svelte +451 -437
  171. package/dist/components/nodes/IdeaNode.svelte.d.ts +1 -1
  172. package/dist/components/nodes/NotesNode.stories.svelte +65 -64
  173. package/dist/components/nodes/NotesNode.svelte +380 -369
  174. package/dist/components/nodes/NotesNode.svelte.d.ts +1 -1
  175. package/dist/components/nodes/SimpleNode.stories.svelte +145 -144
  176. package/dist/components/nodes/SimpleNode.svelte +486 -424
  177. package/dist/components/nodes/SimpleNode.svelte.d.ts +1 -1
  178. package/dist/components/nodes/SquareNode.stories.svelte +73 -73
  179. package/dist/components/nodes/SquareNode.svelte +439 -380
  180. package/dist/components/nodes/SquareNode.svelte.d.ts +1 -1
  181. package/dist/components/nodes/TerminalNode.stories.svelte +13 -13
  182. package/dist/components/nodes/TerminalNode.svelte +709 -670
  183. package/dist/components/nodes/TerminalNode.svelte.d.ts +1 -1
  184. package/dist/components/nodes/ToolNode.stories.svelte +181 -180
  185. package/dist/components/nodes/ToolNode.svelte +505 -447
  186. package/dist/components/nodes/ToolNode.svelte.d.ts +1 -1
  187. package/dist/components/nodes/WorkflowNode.stories.svelte +70 -46
  188. package/dist/components/nodes/WorkflowNode.svelte +621 -551
  189. package/dist/components/nodes/WorkflowNode.svelte.d.ts +3 -3
  190. package/dist/components/playground/ChatPanel.svelte +945 -889
  191. package/dist/components/playground/ExecutionLogs.svelte +495 -472
  192. package/dist/components/playground/InputCollector.svelte +449 -428
  193. package/dist/components/playground/MessageBubble.stories.svelte +47 -47
  194. package/dist/components/playground/MessageBubble.stories.svelte.d.ts +1 -1
  195. package/dist/components/playground/MessageBubble.svelte +626 -610
  196. package/dist/components/playground/MessageBubble.svelte.d.ts +1 -1
  197. package/dist/components/playground/Playground.svelte +1088 -1057
  198. package/dist/components/playground/Playground.svelte.d.ts +3 -3
  199. package/dist/components/playground/PlaygroundModal.svelte +208 -204
  200. package/dist/components/playground/PlaygroundModal.svelte.d.ts +3 -3
  201. package/dist/components/playground/SessionManager.svelte +527 -521
  202. package/dist/components/playground/SessionManager.svelte.d.ts +1 -1
  203. package/dist/config/agentSpecEndpoints.d.ts +1 -1
  204. package/dist/config/agentSpecEndpoints.js +20 -20
  205. package/dist/config/constants.js +2 -2
  206. package/dist/config/defaultCategories.d.ts +1 -1
  207. package/dist/config/defaultCategories.js +86 -86
  208. package/dist/config/defaultPortConfig.d.ts +1 -1
  209. package/dist/config/defaultPortConfig.js +144 -144
  210. package/dist/config/endpoints.d.ts +4 -4
  211. package/dist/config/endpoints.js +65 -65
  212. package/dist/config/runtimeConfig.d.ts +2 -2
  213. package/dist/config/runtimeConfig.js +8 -8
  214. package/dist/core/index.d.ts +63 -59
  215. package/dist/core/index.js +35 -33
  216. package/dist/display/index.d.ts +2 -2
  217. package/dist/display/index.js +2 -2
  218. package/dist/editor/index.d.ts +62 -62
  219. package/dist/editor/index.js +53 -53
  220. package/dist/form/code.d.ts +5 -5
  221. package/dist/form/code.js +14 -14
  222. package/dist/form/fieldRegistry.d.ts +3 -3
  223. package/dist/form/fieldRegistry.js +11 -9
  224. package/dist/form/full.d.ts +8 -8
  225. package/dist/form/full.js +9 -9
  226. package/dist/form/index.d.ts +18 -18
  227. package/dist/form/index.js +16 -16
  228. package/dist/form/markdown.d.ts +4 -4
  229. package/dist/form/markdown.js +8 -8
  230. package/dist/helpers/proximityConnect.d.ts +3 -3
  231. package/dist/helpers/proximityConnect.js +34 -32
  232. package/dist/helpers/workflowEditorHelper.d.ts +5 -5
  233. package/dist/helpers/workflowEditorHelper.js +108 -96
  234. package/dist/index.d.ts +6 -6
  235. package/dist/index.js +6 -6
  236. package/dist/mocks/app-environment.js +2 -2
  237. package/dist/mocks/app-forms.js +9 -9
  238. package/dist/mocks/app-navigation.js +11 -11
  239. package/dist/mocks/app-stores.js +8 -8
  240. package/dist/playground/index.d.ts +19 -19
  241. package/dist/playground/index.js +16 -16
  242. package/dist/playground/mount.d.ts +3 -3
  243. package/dist/playground/mount.js +24 -24
  244. package/dist/registry/builtinFormats.js +13 -13
  245. package/dist/registry/builtinNodes.d.ts +2 -2
  246. package/dist/registry/builtinNodes.js +77 -77
  247. package/dist/registry/index.d.ts +4 -4
  248. package/dist/registry/index.js +4 -4
  249. package/dist/registry/nodeComponentRegistry.d.ts +8 -8
  250. package/dist/registry/nodeComponentRegistry.js +11 -9
  251. package/dist/registry/plugin.d.ts +2 -2
  252. package/dist/registry/plugin.js +11 -11
  253. package/dist/registry/workflowFormatRegistry.d.ts +3 -3
  254. package/dist/registry/workflowFormatRegistry.js +2 -2
  255. package/dist/schema/index.d.ts +1 -1
  256. package/dist/schema/index.js +2 -2
  257. package/dist/services/agentSpecExecutionService.d.ts +3 -3
  258. package/dist/services/agentSpecExecutionService.js +59 -55
  259. package/dist/services/api.d.ts +2 -2
  260. package/dist/services/api.js +37 -37
  261. package/dist/services/apiVariableService.d.ts +1 -1
  262. package/dist/services/apiVariableService.js +41 -34
  263. package/dist/services/autoSaveService.js +8 -8
  264. package/dist/services/categoriesApi.d.ts +2 -2
  265. package/dist/services/categoriesApi.js +8 -8
  266. package/dist/services/draftStorage.d.ts +1 -1
  267. package/dist/services/draftStorage.js +11 -11
  268. package/dist/services/dynamicSchemaService.d.ts +1 -1
  269. package/dist/services/dynamicSchemaService.js +41 -39
  270. package/dist/services/globalSave.d.ts +2 -2
  271. package/dist/services/globalSave.js +41 -38
  272. package/dist/services/historyService.d.ts +1 -1
  273. package/dist/services/historyService.js +8 -8
  274. package/dist/services/interruptService.d.ts +1 -1
  275. package/dist/services/interruptService.js +35 -29
  276. package/dist/services/nodeExecutionService.d.ts +1 -1
  277. package/dist/services/nodeExecutionService.js +45 -44
  278. package/dist/services/playgroundService.d.ts +1 -1
  279. package/dist/services/playgroundService.js +29 -29
  280. package/dist/services/portConfigApi.d.ts +2 -2
  281. package/dist/services/portConfigApi.js +8 -8
  282. package/dist/services/settingsService.d.ts +2 -2
  283. package/dist/services/settingsService.js +25 -19
  284. package/dist/services/toastService.d.ts +4 -4
  285. package/dist/services/toastService.js +33 -33
  286. package/dist/services/variableService.d.ts +1 -1
  287. package/dist/services/variableService.js +36 -36
  288. package/dist/services/workflowStorage.d.ts +2 -2
  289. package/dist/services/workflowStorage.js +13 -13
  290. package/dist/settings/index.d.ts +7 -7
  291. package/dist/settings/index.js +6 -6
  292. package/dist/skins/default.d.ts +2 -0
  293. package/dist/skins/default.js +1 -0
  294. package/dist/skins/index.d.ts +13 -0
  295. package/dist/skins/index.js +30 -0
  296. package/dist/skins/slate.d.ts +2 -0
  297. package/dist/skins/slate.js +78 -0
  298. package/dist/stores/categoriesStore.svelte.d.ts +1 -1
  299. package/dist/stores/categoriesStore.svelte.js +5 -5
  300. package/dist/stores/editorStateMachine.svelte.d.ts +2 -2
  301. package/dist/stores/editorStateMachine.svelte.js +65 -33
  302. package/dist/stores/historyStore.svelte.d.ts +4 -4
  303. package/dist/stores/historyStore.svelte.js +4 -4
  304. package/dist/stores/interruptStore.svelte.d.ts +3 -3
  305. package/dist/stores/interruptStore.svelte.js +21 -21
  306. package/dist/stores/playgroundStore.svelte.d.ts +2 -2
  307. package/dist/stores/playgroundStore.svelte.js +25 -18
  308. package/dist/stores/portCoordinateStore.svelte.d.ts +2 -2
  309. package/dist/stores/portCoordinateStore.svelte.js +15 -8
  310. package/dist/stores/settingsStore.svelte.d.ts +2 -2
  311. package/dist/stores/settingsStore.svelte.js +62 -57
  312. package/dist/stores/workflowStore.svelte.d.ts +3 -3
  313. package/dist/stores/workflowStore.svelte.js +50 -47
  314. package/dist/stories/CanvasDecorator.svelte +35 -32
  315. package/dist/stories/CanvasDecorator.svelte.d.ts +2 -2
  316. package/dist/stories/EdgeDecorator.svelte +102 -99
  317. package/dist/stories/EdgeDecorator.svelte.d.ts +1 -1
  318. package/dist/stories/NodeDecorator.svelte +59 -53
  319. package/dist/stories/NodeDecorator.svelte.d.ts +1 -1
  320. package/dist/stories/utils.d.ts +2 -2
  321. package/dist/stories/utils.js +105 -67
  322. package/dist/styles/base.css +599 -595
  323. package/dist/styles/toast.css +14 -14
  324. package/dist/styles/tokens.css +409 -378
  325. package/dist/svelte-app.d.ts +12 -9
  326. package/dist/svelte-app.js +40 -39
  327. package/dist/themes/default.d.ts +2 -0
  328. package/dist/themes/default.js +9 -0
  329. package/dist/themes/index.d.ts +13 -0
  330. package/dist/themes/index.js +44 -0
  331. package/dist/themes/minimal.d.ts +2 -0
  332. package/dist/themes/minimal.js +11 -0
  333. package/dist/types/agentspec.d.ts +18 -18
  334. package/dist/types/agentspec.js +2 -2
  335. package/dist/types/auth.d.ts +1 -1
  336. package/dist/types/auth.js +6 -6
  337. package/dist/types/config.d.ts +6 -6
  338. package/dist/types/events.d.ts +2 -2
  339. package/dist/types/events.js +2 -2
  340. package/dist/types/index.d.ts +32 -32
  341. package/dist/types/index.js +6 -6
  342. package/dist/types/interrupt.d.ts +6 -6
  343. package/dist/types/interrupt.js +21 -21
  344. package/dist/types/interruptState.d.ts +12 -12
  345. package/dist/types/interruptState.js +66 -66
  346. package/dist/types/playground.d.ts +7 -7
  347. package/dist/types/playground.js +14 -14
  348. package/dist/types/settings.d.ts +5 -3
  349. package/dist/types/settings.js +25 -18
  350. package/dist/types/skin.d.ts +31 -0
  351. package/dist/types/skin.js +1 -0
  352. package/dist/types/theme.d.ts +35 -0
  353. package/dist/types/theme.js +1 -0
  354. package/dist/types/uischema.d.ts +4 -4
  355. package/dist/types/uischema.js +3 -3
  356. package/dist/utils/colors.d.ts +1 -1
  357. package/dist/utils/colors.js +97 -95
  358. package/dist/utils/config.d.ts +2 -2
  359. package/dist/utils/config.js +48 -48
  360. package/dist/utils/connections.d.ts +2 -2
  361. package/dist/utils/connections.js +15 -15
  362. package/dist/utils/errors.js +3 -3
  363. package/dist/utils/fetchWithAuth.d.ts +1 -1
  364. package/dist/utils/fetchWithAuth.js +2 -2
  365. package/dist/utils/handleIds.d.ts +2 -2
  366. package/dist/utils/handleIds.js +8 -8
  367. package/dist/utils/handlePositioning.d.ts +1 -1
  368. package/dist/utils/handlePositioning.js +2 -2
  369. package/dist/utils/icons.d.ts +1 -1
  370. package/dist/utils/icons.js +74 -74
  371. package/dist/utils/logger.d.ts +1 -1
  372. package/dist/utils/logger.js +7 -7
  373. package/dist/utils/nodeStatus.d.ts +1 -1
  374. package/dist/utils/nodeStatus.js +48 -48
  375. package/dist/utils/nodeTypes.d.ts +1 -1
  376. package/dist/utils/nodeTypes.js +21 -20
  377. package/dist/utils/nodeWrapper.d.ts +7 -7
  378. package/dist/utils/nodeWrapper.js +21 -19
  379. package/dist/utils/performanceUtils.d.ts +1 -1
  380. package/dist/utils/performanceUtils.js +2 -1
  381. package/dist/utils/sanitize.js +1 -1
  382. package/dist/utils/uischema.d.ts +2 -2
  383. package/dist/utils/uischema.js +8 -8
  384. package/dist/utils/validation.js +20 -8
  385. package/package.json +1 -1
@@ -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>