windmill-components 1.550.0 → 1.555.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 (268) hide show
  1. package/package/aiStore.d.ts +13 -0
  2. package/package/aiStore.js +70 -0
  3. package/package/common.d.ts +2 -1
  4. package/package/components/AIProviderPicker.svelte +25 -8
  5. package/package/components/ArgEnum.svelte +3 -2
  6. package/package/components/ArgEnum.svelte.d.ts +1 -0
  7. package/package/components/ArgInput.svelte +235 -174
  8. package/package/components/ArgInput.svelte.d.ts +4 -1
  9. package/package/components/ArrayTypeNarrowing.svelte +38 -32
  10. package/package/components/AutoscalingEvents.svelte +21 -5
  11. package/package/components/AutoscalingEvents.svelte.d.ts +4 -18
  12. package/package/components/DateTimeInput.svelte +8 -6
  13. package/package/components/DeployButton.svelte +1 -1
  14. package/package/components/Dev.svelte +6 -4
  15. package/package/components/EditableSchemaForm.svelte +7 -6
  16. package/package/components/Editor.svelte +2 -1
  17. package/package/components/EditorSettings.svelte +5 -5
  18. package/package/components/EditorSettings.svelte.d.ts +4 -18
  19. package/package/components/FakeMonacoPlaceHolder.svelte +4 -2
  20. package/package/components/FakeMonacoPlaceHolder.svelte.d.ts +1 -0
  21. package/package/components/FieldHeader.svelte +5 -7
  22. package/package/components/FirstStepInputs.svelte +1 -1
  23. package/package/components/FlowLoopIterationPreview.svelte.d.ts +1 -1
  24. package/package/components/FlowPlugConnect.svelte +8 -2
  25. package/package/components/FlowPlugConnect.svelte.d.ts +1 -0
  26. package/package/components/FlowPreviewContent.svelte +113 -92
  27. package/package/components/FlowPreviewContent.svelte.d.ts +3 -3
  28. package/package/components/FlowStatusViewer.svelte +3 -2
  29. package/package/components/FlowStatusViewerInner.svelte +1 -1
  30. package/package/components/FolderEditor.svelte +6 -7
  31. package/package/components/GroupEditor.svelte +148 -141
  32. package/package/components/GroupEditor.svelte.d.ts +5 -4
  33. package/package/components/InputTransformForm.svelte +88 -82
  34. package/package/components/InputTransformSchemaForm.svelte +5 -4
  35. package/package/components/InstanceSetting.svelte +17 -9
  36. package/package/components/JsonEditor.svelte +18 -9
  37. package/package/components/JsonEditor.svelte.d.ts +1 -1
  38. package/package/components/JsonInputs.svelte +1 -1
  39. package/package/components/ModulePreviewForm.svelte +23 -19
  40. package/package/components/NumberTypeNarrowing.svelte +32 -16
  41. package/package/components/ObjectStoreConfigSettings.svelte +27 -19
  42. package/package/components/Path.svelte +2 -8
  43. package/package/components/Path.svelte.d.ts +1 -1
  44. package/package/components/ResourceEditor.svelte +3 -10
  45. package/package/components/ResourcePicker.svelte +85 -72
  46. package/package/components/ResourcePicker.svelte.d.ts +2 -0
  47. package/package/components/RunChart.svelte +1 -1
  48. package/package/components/RunForm.svelte +11 -7
  49. package/package/components/S3ArrayHelperButton.svelte +12 -6
  50. package/package/components/S3ArrayHelperButton.svelte.d.ts +1 -0
  51. package/package/components/S3FilePicker.svelte +1 -1
  52. package/package/components/SchemaForm.svelte +18 -10
  53. package/package/components/SchemaForm.svelte.d.ts +7 -1
  54. package/package/components/SchemaFormWithArgPicker.svelte +1 -1
  55. package/package/components/ScriptBuilder.svelte +2 -2
  56. package/package/components/ScriptEditor.svelte +4 -3
  57. package/package/components/ScriptEditor.svelte.d.ts +1 -1
  58. package/package/components/ShareModal.svelte +4 -4
  59. package/package/components/SimpleEditor.svelte +6 -2
  60. package/package/components/SimpleEditor.svelte.d.ts +3 -0
  61. package/package/components/StringTypeNarrowing.svelte +5 -1
  62. package/package/components/SuperadminSettingsInner.svelte +3 -3
  63. package/package/components/TemplateEditor.svelte +18 -9
  64. package/package/components/Toast.svelte +2 -7
  65. package/package/components/Toast.svelte.d.ts +4 -18
  66. package/package/components/Toggle.svelte +17 -7
  67. package/package/components/ToggleHubWorkspaceQuick.svelte +3 -3
  68. package/package/components/WorkerGroup.svelte +2 -14
  69. package/package/components/apps/components/buttons/AppButton.svelte +57 -39
  70. package/package/components/apps/components/display/dbtable/InsertRow.svelte +32 -2
  71. package/package/components/apps/components/display/dbtable/queries/insert.js +2 -1
  72. package/package/components/apps/components/display/dbtable/utils.d.ts +8 -8
  73. package/package/components/apps/components/display/table/utils.js +13 -3
  74. package/package/components/apps/components/helpers/RunnableComponent.svelte +3 -3
  75. package/package/components/apps/components/inputs/currency/CurrencyInput.svelte +2 -1
  76. package/package/components/apps/editor/AppEditorHeader.svelte +33 -271
  77. package/package/components/apps/editor/AppEditorHeaderDeploy.svelte +233 -0
  78. package/package/components/apps/editor/AppEditorHeaderDeploy.svelte.d.ts +18 -0
  79. package/package/components/apps/editor/AppEditorHeaderDeployInitialDraft.svelte +47 -0
  80. package/package/components/apps/editor/AppEditorHeaderDeployInitialDraft.svelte.d.ts +8 -0
  81. package/package/components/apps/editor/GridEditor.svelte +7 -2
  82. package/package/components/apps/editor/appDeploy.svelte.d.ts +1 -0
  83. package/package/components/apps/editor/appDeploy.svelte.js +6 -0
  84. package/package/components/apps/editor/appUtils.d.ts +1 -0
  85. package/package/components/apps/editor/appUtils.js +30 -1
  86. package/package/components/apps/editor/component/ComponentNavigation.svelte +3 -1
  87. package/package/components/apps/editor/component/components.d.ts +3 -3
  88. package/package/components/apps/editor/component/components.js +1 -1
  89. package/package/components/apps/editor/contextPanel/ComponentOutputViewer.svelte +1 -1
  90. package/package/components/apps/editor/inlineScriptsPanel/EmptyInlineScript.svelte +6 -4
  91. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte.d.ts +1 -1
  92. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditorDrawer.svelte.d.ts +1 -1
  93. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte.d.ts +1 -1
  94. package/package/components/apps/editor/settingsPanel/InputsSpecEditor.svelte +58 -8
  95. package/package/components/auditLogs/AuditLogsFilters.svelte +1 -1
  96. package/package/components/common/ResizeTransitionWrapper.svelte +39 -0
  97. package/package/components/common/ResizeTransitionWrapper.svelte.d.ts +12 -0
  98. package/package/components/common/badge/CountBadge.svelte +29 -0
  99. package/package/components/common/badge/CountBadge.svelte.d.ts +8 -0
  100. package/package/components/common/button/Button.svelte +1 -0
  101. package/package/components/common/button/ConnectionButton.svelte +6 -1
  102. package/package/components/common/button/ConnectionButton.svelte.d.ts +2 -0
  103. package/package/components/common/button/RefreshButton.svelte +8 -4
  104. package/package/components/common/button/RefreshButton.svelte.d.ts +3 -0
  105. package/package/components/common/calendarPicker/CalendarPicker.svelte +1 -1
  106. package/package/components/common/fileInput/FileInput.svelte +7 -6
  107. package/package/components/common/fileUpload/S3ArgInput.svelte +11 -9
  108. package/package/components/common/fileUpload/S3ArgInput.svelte.d.ts +1 -0
  109. package/package/components/common/popup/PopupV2.svelte +6 -0
  110. package/package/components/common/toggleButton-v2/ToggleButton.svelte +17 -26
  111. package/package/components/common/toggleButton-v2/ToggleButton.svelte.d.ts +16 -30
  112. package/package/components/common/toggleButton-v2/ToggleButtonGroup.svelte +1 -1
  113. package/package/components/common/toggleButton-v2/ToggleButtonMore.svelte +3 -3
  114. package/package/components/common/toggleButton-v2/ToggleButtonMore.svelte.d.ts +1 -0
  115. package/package/components/copilot/CodeCompletionStatus.svelte +2 -1
  116. package/package/components/copilot/CronGen.svelte +1 -1
  117. package/package/components/copilot/FlowInlineScriptAIButton.svelte +2 -2
  118. package/package/components/copilot/IteratorGen.svelte +30 -25
  119. package/package/components/copilot/IteratorGen.svelte.d.ts +8 -7
  120. package/package/components/copilot/MetadataGen.svelte +4 -3
  121. package/package/components/copilot/PredicateGen.svelte +15 -12
  122. package/package/components/copilot/PredicateGen.svelte.d.ts +5 -4
  123. package/package/components/copilot/RegexGen.svelte +1 -1
  124. package/package/components/copilot/ScriptFix.svelte +1 -1
  125. package/package/components/copilot/ScriptGen.svelte +2 -1
  126. package/package/components/copilot/StepGenQuick.svelte +15 -16
  127. package/package/components/copilot/StepGenQuick.svelte.d.ts +14 -13
  128. package/package/components/copilot/StepInputGen.svelte +50 -36
  129. package/package/components/copilot/StepInputGen.svelte.d.ts +13 -10
  130. package/package/components/copilot/StepInputsGen.svelte +18 -19
  131. package/package/components/copilot/StepInputsGen.svelte.d.ts +4 -18
  132. package/package/components/copilot/autocomplete/Autocompletor.js +1 -1
  133. package/package/components/copilot/autocomplete/request.js +1 -1
  134. package/package/components/copilot/chat/AIChat.svelte +2 -1
  135. package/package/components/copilot/chat/AIChatManager.svelte.js +2 -1
  136. package/package/components/copilot/chat/AiChatLayout.svelte +2 -1
  137. package/package/components/copilot/chat/ProviderModelSelector.svelte +10 -9
  138. package/package/components/copilot/chat/ProviderModelSelector.svelte.d.ts +2 -17
  139. package/package/components/copilot/chat/flow/FlowAIButton.svelte +1 -1
  140. package/package/components/copilot/chat/script/core.js +2 -1
  141. package/package/components/copilot/chat/shared.js +2 -1
  142. package/package/components/copilot/lib.js +2 -1
  143. package/package/components/details/DetailPageLayout.svelte +3 -2
  144. package/package/components/details/DetailPageLayout.svelte.d.ts +1 -0
  145. package/package/components/flows/CreateActionsFlow.svelte +1 -1
  146. package/package/components/flows/FlowChatInterface.svelte +404 -0
  147. package/package/components/flows/FlowChatInterface.svelte.d.ts +19 -0
  148. package/package/components/flows/FlowChatMessage.svelte +41 -0
  149. package/package/components/flows/FlowChatMessage.svelte.d.ts +9 -0
  150. package/package/components/flows/FlowConversationsSidebar.svelte +213 -0
  151. package/package/components/flows/FlowConversationsSidebar.svelte.d.ts +15 -0
  152. package/package/components/flows/FlowEditor.svelte.d.ts +1 -1
  153. package/package/components/flows/FlowModuleIcon.svelte +10 -10
  154. package/package/components/flows/common/FlowCard.svelte +10 -2
  155. package/package/components/flows/common/FlowCard.svelte.d.ts +1 -0
  156. package/package/components/flows/common/FlowCardHeader.svelte +2 -1
  157. package/package/components/flows/common/FlowCardHeader.svelte.d.ts +1 -0
  158. package/package/components/flows/content/DynamicInputHelpBox.svelte +4 -4
  159. package/package/components/flows/content/FlowEditorPanel.svelte.d.ts +1 -1
  160. package/package/components/flows/content/FlowInput.svelte +381 -259
  161. package/package/components/flows/content/FlowInput.svelte.d.ts +1 -1
  162. package/package/components/flows/content/FlowInputsQuick.svelte +55 -34
  163. package/package/components/flows/content/FlowInputsQuick.svelte.d.ts +2 -2
  164. package/package/components/flows/content/FlowModuleComponent.svelte +5 -10
  165. package/package/components/flows/flowInfers.d.ts +60 -0
  166. package/package/components/flows/flowInfers.js +72 -66
  167. package/package/components/flows/{flowStore.d.ts → flowStore.svelte.d.ts} +1 -0
  168. package/package/components/flows/header/FlowPreviewButtons.svelte +1 -1
  169. package/package/components/flows/map/FlowErrorHandlerItem.svelte +4 -2
  170. package/package/components/flows/map/FlowErrorHandlerItem.svelte.d.ts +1 -0
  171. package/package/components/flows/map/FlowModuleSchemaItem.svelte +1 -1
  172. package/package/components/flows/map/FlowModuleSchemaMap.svelte +5 -2
  173. package/package/components/flows/map/FlowStickyNode.svelte +2 -2
  174. package/package/components/flows/map/FlowStickyNode.svelte.d.ts +1 -0
  175. package/package/components/flows/map/InsertModuleButton.svelte +5 -2
  176. package/package/components/flows/map/InsertModuleButton.svelte.d.ts +4 -3
  177. package/package/components/flows/map/InsertModuleInner.svelte +3 -1
  178. package/package/components/flows/map/InsertModuleInner.svelte.d.ts +2 -2
  179. package/package/components/flows/map/VirtualItem.svelte +1 -2
  180. package/package/components/flows/pickers/PickHubScriptQuick.svelte +8 -3
  181. package/package/components/flows/pickers/PickHubScriptQuick.svelte.d.ts +1 -1
  182. package/package/components/flows/pickers/WorkspaceScriptPickerQuick.svelte +15 -12
  183. package/package/components/flows/propPicker/PropPickerWrapper.svelte +1 -15
  184. package/package/components/graph/FlowGraphV2.svelte +2 -1
  185. package/package/components/graph/FlowGraphV2.svelte.d.ts +1 -0
  186. package/package/components/graph/graphBuilder.svelte.d.ts +2 -0
  187. package/package/components/graph/graphBuilder.svelte.js +1 -0
  188. package/package/components/graph/renderers/edges/BaseEdge.svelte +1 -0
  189. package/package/components/graph/renderers/nodes/InputNode.svelte +13 -2
  190. package/package/components/graph/renderers/triggers/TriggersBadge.svelte +2 -27
  191. package/package/components/instanceSettings.js +17 -0
  192. package/package/components/progressBar/ProgressBar.svelte +1 -1
  193. package/package/components/raw_apps/FileEditorIcon.svelte +1 -1
  194. package/package/components/raw_apps/FileEditorIcon.svelte.d.ts +4 -18
  195. package/package/components/raw_apps/RawAppBackgroundRunner.svelte +2 -8
  196. package/package/components/raw_apps/RawAppBackgroundRunner.svelte.d.ts +4 -18
  197. package/package/components/raw_apps/RawAppEditor.svelte +6 -7
  198. package/package/components/raw_apps/RawAppEditorHeader.svelte +48 -301
  199. package/package/components/raw_apps/RawAppEditorHeader.svelte.d.ts +18 -19
  200. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte +10 -16
  201. package/package/components/raw_apps/RawAppInlineScriptEditor.svelte.d.ts +13 -13
  202. package/package/components/raw_apps/RawAppInlineScriptPanelList.svelte +8 -11
  203. package/package/components/raw_apps/RawAppInlineScriptPanelList.svelte.d.ts +1 -2
  204. package/package/components/raw_apps/RawAppInlineScriptRunnable.svelte +0 -1
  205. package/package/components/raw_apps/RawAppInlineScriptsPanel.svelte +7 -13
  206. package/package/components/raw_apps/RawAppInlineScriptsPanel.svelte.d.ts +8 -8
  207. package/package/components/raw_apps/RawAppPreview.svelte +3 -7
  208. package/package/components/raw_apps/RawAppPreview.svelte.d.ts +5 -19
  209. package/package/components/raw_apps/utils.d.ts +1 -1
  210. package/package/components/raw_apps/utils.js +3 -3
  211. package/package/components/runs/RunOption.svelte +2 -2
  212. package/package/components/runs/RunsFilter.svelte +15 -12
  213. package/package/components/runs/RunsFilter.svelte.d.ts +1 -1
  214. package/package/components/schema/EditableSchemaDrawer.svelte +19 -18
  215. package/package/components/schema/FlowPropertyEditor.svelte +9 -2
  216. package/package/components/schema/FlowPropertyEditor.svelte.d.ts +1 -1
  217. package/package/components/schema/PropertyEditor.svelte +22 -26
  218. package/package/components/schema/SchemaFormDND.svelte +3 -2
  219. package/package/components/schema/SchemaFormDND.svelte.d.ts +1 -0
  220. package/package/components/select/DraggableTags.svelte +2 -2
  221. package/package/components/select/MultiSelect.svelte +14 -8
  222. package/package/components/select/Select.svelte +12 -5
  223. package/package/components/select/Select.svelte.d.ts +11 -0
  224. package/package/components/select/SelectDropdown.svelte +98 -46
  225. package/package/components/select/SelectDropdown.svelte.d.ts +10 -0
  226. package/package/components/select/utils.svelte.js +2 -0
  227. package/package/components/settings/CreateToken.svelte +76 -49
  228. package/package/components/settings/WorkspaceUserSettings.svelte +20 -17
  229. package/package/components/sidebar/CriticalAlertTable.svelte +2 -1
  230. package/package/components/sidebar/Linkify.svelte +14 -0
  231. package/package/components/sidebar/Linkify.svelte.d.ts +5 -0
  232. package/package/components/sidebar/WorkspaceMenu.svelte +8 -3
  233. package/package/components/table/tableUtils.js +1 -1
  234. package/package/components/text_input/TextInput.svelte +30 -0
  235. package/package/components/text_input/TextInput.svelte.d.ts +17 -0
  236. package/package/components/triggers/TriggersEditor.svelte +11 -1
  237. package/package/components/triggers/triggers.svelte.d.ts +1 -1
  238. package/package/components/triggers/triggers.svelte.js +8 -4
  239. package/package/components/tutorials/FlowBuilderTutorialErrorHandler.svelte +2 -2
  240. package/package/components/tutorials/FlowBuilderTutorialForLoop.svelte +3 -0
  241. package/package/components/tutorials/FlowBuilderTutorialSimpleFlow.svelte +49 -17
  242. package/package/components/tutorials/Tutorial.svelte +9 -0
  243. package/package/components/tutorials/Tutorial.svelte.d.ts +1 -0
  244. package/package/components/tutorials/app/AppTutorial.svelte +41 -57
  245. package/package/components/tutorials/app/BackgroundRunnablesTutorial.svelte +3 -5
  246. package/package/components/tutorials/app/ConnectionTutorial.svelte +2 -2
  247. package/package/components/tutorials/utils.js +2 -154
  248. package/package/components/vscode.js +16 -8
  249. package/package/components/workspaceSettings/AISettings.svelte +4 -3
  250. package/package/components/workspaceSettings/CreateWorkspace.svelte +2 -2
  251. package/package/components/workspaceSettings/DucklakeSettings.svelte +64 -7
  252. package/package/components/workspaceSettings/StorageSettings.svelte +24 -26
  253. package/package/editorUtils.d.ts +1 -1
  254. package/package/gen/core/OpenAPI.js +1 -1
  255. package/package/gen/schemas.gen.d.ts +82 -1
  256. package/package/gen/schemas.gen.js +82 -1
  257. package/package/gen/services.gen.d.ts +175 -1
  258. package/package/gen/services.gen.js +345 -2
  259. package/package/gen/types.gen.d.ts +1324 -549
  260. package/package/script_helpers.js +5 -5
  261. package/package/services/JobManager.js +4 -2
  262. package/package/stores.d.ts +4 -13
  263. package/package/stores.js +5 -68
  264. package/package/toast.js +2 -1
  265. package/package/utils.d.ts +1 -0
  266. package/package/utils.js +3 -0
  267. package/package.json +3 -3
  268. /package/package/components/flows/{flowStore.js → flowStore.svelte.js} +0 -0
@@ -0,0 +1,404 @@
1
+ <script lang="ts">import { Button, Alert } from '../common';
2
+ import { MessageCircle, Loader2, ArrowUp } from 'lucide-svelte';
3
+ import { FlowConversationService } from '../../gen';
4
+ import { workspaceStore } from '../../stores';
5
+ import { sendUserToast } from '../../toast';
6
+ import autosize from '../../autosize';
7
+ import { waitJob } from '../waitJob';
8
+ import { tick } from 'svelte';
9
+ import FlowChatMessage from './FlowChatMessage.svelte';
10
+ let { onRunFlow, conversationId, refreshConversations, deploymentInProgress = false, createConversation, useStreaming = false, path } = $props();
11
+ let messages = $state([]);
12
+ let inputMessage = $state('');
13
+ let isLoading = $state(false);
14
+ let isLoadingMessages = $state(false);
15
+ let messagesContainer = $state();
16
+ let page = $state(1);
17
+ let perPage = 50;
18
+ let hasMoreMessages = $state(false);
19
+ let loadingMoreMessages = $state(false);
20
+ let scrollTimeout = undefined;
21
+ let inputElement = $state();
22
+ let currentEventSource = $state();
23
+ const conversationsCache = $state({});
24
+ // Auto-scroll to bottom when messages change
25
+ $effect(() => {
26
+ const scroll = async () => {
27
+ if (messages.length > 0) {
28
+ await tick();
29
+ scrollToBottom();
30
+ }
31
+ };
32
+ scroll();
33
+ });
34
+ // Cleanup EventSource on unmount
35
+ $effect(() => {
36
+ return () => {
37
+ if (currentEventSource) {
38
+ currentEventSource.close();
39
+ }
40
+ };
41
+ });
42
+ export function fillInputMessage(message) {
43
+ inputMessage = message;
44
+ }
45
+ export function focusInput() {
46
+ inputElement?.focus();
47
+ }
48
+ export function clearMessages() {
49
+ messages = [];
50
+ inputMessage = '';
51
+ page = 1;
52
+ }
53
+ export async function loadConversationMessages(convId) {
54
+ page = 1;
55
+ await loadMessages(true);
56
+ }
57
+ async function loadMessages(reset) {
58
+ if (!$workspaceStore || !conversationId)
59
+ return;
60
+ if (reset) {
61
+ if (conversationsCache[conversationId]) {
62
+ messages = conversationsCache[conversationId];
63
+ return;
64
+ }
65
+ isLoadingMessages = true;
66
+ }
67
+ else {
68
+ loadingMoreMessages = true;
69
+ }
70
+ const pageToFetch = reset ? 1 : page + 1;
71
+ try {
72
+ const previousScrollHeight = messagesContainer?.scrollHeight || 0;
73
+ const response = await FlowConversationService.listConversationMessages({
74
+ workspace: $workspaceStore,
75
+ conversationId: conversationId,
76
+ page: pageToFetch,
77
+ perPage: perPage
78
+ });
79
+ if (reset) {
80
+ conversationsCache[conversationId] = response;
81
+ messages = response;
82
+ isLoadingMessages = false;
83
+ await new Promise((resolve) => setTimeout(resolve, 100));
84
+ scrollToBottom();
85
+ }
86
+ else {
87
+ messages = [...response, ...messages];
88
+ page = pageToFetch;
89
+ // Restore scroll position
90
+ await new Promise((resolve) => setTimeout(resolve, 50));
91
+ if (messagesContainer) {
92
+ messagesContainer.scrollTop = messagesContainer.scrollHeight - previousScrollHeight;
93
+ }
94
+ }
95
+ hasMoreMessages = response.length === perPage;
96
+ }
97
+ catch (error) {
98
+ console.error('Failed to load messages:', error);
99
+ sendUserToast('Failed to load messages: ' + error);
100
+ }
101
+ finally {
102
+ isLoadingMessages = false;
103
+ loadingMoreMessages = false;
104
+ }
105
+ }
106
+ function handleScroll() {
107
+ if (scrollTimeout)
108
+ clearTimeout(scrollTimeout);
109
+ scrollTimeout = setTimeout(() => {
110
+ if (!messagesContainer || !hasMoreMessages || loadingMoreMessages)
111
+ return;
112
+ if (messagesContainer.scrollTop <= 10) {
113
+ loadMessages(false);
114
+ }
115
+ }, 200);
116
+ }
117
+ function scrollToBottom() {
118
+ if (messagesContainer) {
119
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
120
+ }
121
+ }
122
+ async function pollJobResult(jobId, messageId) {
123
+ try {
124
+ const result = await waitJob(jobId);
125
+ // Job completed successfully, update the message with the result
126
+ messages = messages.map((msg) => msg.id === messageId
127
+ ? {
128
+ ...msg,
129
+ loading: false,
130
+ content: formatJobResult(result)
131
+ }
132
+ : msg);
133
+ }
134
+ catch (error) {
135
+ console.error('Error polling job result:', error);
136
+ // Job failed, update the message with error
137
+ messages = messages.map((msg) => msg.id === messageId
138
+ ? {
139
+ ...msg,
140
+ loading: false,
141
+ content: 'Error: ' + (error?.message || String(error))
142
+ }
143
+ : msg);
144
+ }
145
+ }
146
+ function formatJobResult(result) {
147
+ if (result === null || result === undefined) {
148
+ return 'No result returned';
149
+ }
150
+ // If result is an object with an output field, use that
151
+ if (typeof result === 'object' && result.output !== undefined) {
152
+ if (typeof result.output === 'string') {
153
+ return result.output;
154
+ }
155
+ return JSON.stringify(result.output, null, 2);
156
+ }
157
+ if (typeof result === 'string') {
158
+ return result;
159
+ }
160
+ if (typeof result === 'object') {
161
+ return JSON.stringify(result, null, 2);
162
+ }
163
+ return String(result);
164
+ }
165
+ function parseStreamDeltas(streamData) {
166
+ const lines = streamData.trim().split('\n');
167
+ let content = '';
168
+ for (const line of lines) {
169
+ if (!line.trim())
170
+ continue;
171
+ try {
172
+ const parsed = JSON.parse(line);
173
+ if (parsed.type === 'token_delta' && parsed.content) {
174
+ content += parsed.content;
175
+ }
176
+ }
177
+ catch (e) {
178
+ console.error('Failed to parse stream line:', line, e);
179
+ }
180
+ }
181
+ return content;
182
+ }
183
+ async function sendMessage() {
184
+ if (!inputMessage.trim() || isLoading)
185
+ return;
186
+ const isNewConversation = messages.length === 0;
187
+ // Generate a new conversation ID if we don't have one
188
+ let currentConversationId = conversationId;
189
+ if (!conversationId) {
190
+ const newConversationId = await createConversation?.({ clearMessages: false });
191
+ currentConversationId = newConversationId;
192
+ }
193
+ if (!currentConversationId) {
194
+ console.error('No conversation ID found');
195
+ return;
196
+ }
197
+ // Invalidate the conversation cache
198
+ delete conversationsCache[currentConversationId];
199
+ const userMessage = {
200
+ id: crypto.randomUUID(),
201
+ content: inputMessage.trim(),
202
+ created_at: new Date().toISOString(),
203
+ message_type: 'user',
204
+ conversation_id: currentConversationId
205
+ };
206
+ messages = [...messages, userMessage];
207
+ const messageContent = inputMessage.trim();
208
+ inputMessage = '';
209
+ isLoading = true;
210
+ try {
211
+ // Add assistant message placeholder
212
+ const assistantMessageId = crypto.randomUUID();
213
+ const assistantMessage = {
214
+ id: assistantMessageId,
215
+ content: '',
216
+ created_at: new Date().toISOString(),
217
+ message_type: 'assistant',
218
+ conversation_id: currentConversationId,
219
+ job_id: '',
220
+ loading: true
221
+ };
222
+ messages = [...messages, assistantMessage];
223
+ if (useStreaming && path) {
224
+ // Close any existing EventSource
225
+ if (currentEventSource) {
226
+ currentEventSource.close();
227
+ }
228
+ // Track stream state for this message
229
+ let accumulatedContent = '';
230
+ try {
231
+ // Encode the payload as base64
232
+ const payload = { user_message: messageContent };
233
+ const payloadBase64 = btoa(JSON.stringify(payload));
234
+ // Build the EventSource URL
235
+ const streamUrl = `/api/w/${$workspaceStore}/jobs/run_and_stream/f/${path}`;
236
+ const url = new URL(streamUrl, window.location.origin);
237
+ url.searchParams.set('payload', payloadBase64);
238
+ url.searchParams.set('memory_id', currentConversationId);
239
+ url.searchParams.set('poll_delay_ms', '50');
240
+ // Create EventSource connection
241
+ const eventSource = new EventSource(url.toString());
242
+ currentEventSource = eventSource;
243
+ eventSource.onmessage = (event) => {
244
+ try {
245
+ const data = JSON.parse(event.data);
246
+ if (data.type === 'update') {
247
+ // Process new stream content
248
+ if (data.new_result_stream) {
249
+ const newContent = parseStreamDeltas(data.new_result_stream);
250
+ accumulatedContent += newContent;
251
+ // Update message content
252
+ messages = messages.map((msg) => msg.id === assistantMessageId
253
+ ? {
254
+ ...msg,
255
+ content: accumulatedContent,
256
+ loading: accumulatedContent.length === 0
257
+ }
258
+ : msg);
259
+ }
260
+ // Handle completion
261
+ if (data.completed && data.only_result) {
262
+ const finalContent = data.only_result.output ||
263
+ accumulatedContent ||
264
+ JSON.stringify(data.only_result.error);
265
+ messages = messages.map((msg) => msg.id === assistantMessageId
266
+ ? {
267
+ ...msg,
268
+ content: finalContent,
269
+ loading: false
270
+ }
271
+ : msg);
272
+ eventSource.close();
273
+ currentEventSource = undefined;
274
+ isLoading = false;
275
+ }
276
+ }
277
+ }
278
+ catch (error) {
279
+ console.error('Error processing stream event:', error);
280
+ }
281
+ };
282
+ eventSource.onerror = (error) => {
283
+ console.error('EventSource error:', error);
284
+ messages = messages.map((msg) => msg.id === assistantMessageId
285
+ ? {
286
+ ...msg,
287
+ content: accumulatedContent || 'Stream error occurred',
288
+ loading: false
289
+ }
290
+ : msg);
291
+ eventSource.close();
292
+ currentEventSource = undefined;
293
+ isLoading = false;
294
+ sendUserToast('Stream error occurred', true);
295
+ };
296
+ }
297
+ catch (error) {
298
+ console.error('Stream connection error:', error);
299
+ messages = messages.map((msg) => msg.id === assistantMessageId
300
+ ? {
301
+ ...msg,
302
+ content: 'Failed to connect to stream',
303
+ loading: false
304
+ }
305
+ : msg);
306
+ isLoading = false;
307
+ sendUserToast('Failed to connect to stream', true);
308
+ }
309
+ }
310
+ else {
311
+ const jobId = await onRunFlow(messageContent, currentConversationId);
312
+ if (!jobId) {
313
+ console.error('No jobId returned from onRunFlow');
314
+ return;
315
+ }
316
+ pollJobResult(jobId, assistantMessageId);
317
+ }
318
+ scrollToBottom();
319
+ }
320
+ catch (error) {
321
+ console.error('Error running flow:', error);
322
+ sendUserToast('Failed to run flow: ' + error, true);
323
+ }
324
+ finally {
325
+ if (!useStreaming) {
326
+ isLoading = false;
327
+ }
328
+ }
329
+ if (isNewConversation) {
330
+ await refreshConversations?.();
331
+ }
332
+ await tick();
333
+ focusInput();
334
+ }
335
+ function handleKeyDown(event) {
336
+ if (event.key === 'Enter' && !event.shiftKey) {
337
+ event.preventDefault();
338
+ sendMessage();
339
+ }
340
+ }
341
+ </script>
342
+
343
+ <div class="flex flex-col h-full w-full max-w-7xl mx-auto">
344
+ <div class="flex-1 flex flex-col min-h-0 w-full">
345
+ <!-- Messages Container -->
346
+ <div
347
+ bind:this={messagesContainer}
348
+ class="flex-1 overflow-y-auto p-4 space-y-4 bg-background"
349
+ onscroll={handleScroll}
350
+ >
351
+ {#if deploymentInProgress}
352
+ <Alert type="warning" title="Deployment in progress" size="xs" />
353
+ {/if}
354
+ {#if isLoadingMessages}
355
+ <div class="flex items-center justify-center">
356
+ <Loader2 size={24} class="animate-spin" />
357
+ </div>
358
+ {:else if messages.length === 0}
359
+ <div class="text-center text-tertiary py-8">
360
+ <MessageCircle size={48} class="mx-auto mb-4 opacity-50" />
361
+ <p class="text-lg font-medium">Start a conversation</p>
362
+ <p class="text-sm">Send a message to run the flow and see the results</p>
363
+ </div>
364
+ {:else}
365
+ {#each messages as message (message.id)}
366
+ <FlowChatMessage {message} />
367
+ {/each}
368
+ {/if}
369
+ </div>
370
+
371
+ <!-- Chat Input -->
372
+ <div class="p-2 bg-surface">
373
+ <div
374
+ class="flex items-center gap-2 rounded-lg border border-gray-200 dark:border-gray-600 bg-surface"
375
+ class:opacity-50={deploymentInProgress}
376
+ >
377
+ <textarea
378
+ bind:this={inputElement}
379
+ bind:value={inputMessage}
380
+ use:autosize
381
+ onkeydown={handleKeyDown}
382
+ placeholder={deploymentInProgress
383
+ ? 'Chat is disabled during deployment...'
384
+ : 'Type your message here...'}
385
+ class="flex-1 min-h-[24px] max-h-32 resize-none !border-0 !bg-transparent text-sm placeholder-gray-400 !outline-none !ring-0 p-0 !shadow-none focus:!border-0 focus:!outline-none focus:!ring-0 focus:!shadow-none"
386
+ disabled={deploymentInProgress}
387
+ rows={3}
388
+ ></textarea>
389
+ <div class="flex-shrink-0 pr-2">
390
+ <Button
391
+ color="blue"
392
+ size="xs2"
393
+ btnClasses="!rounded-full !p-1.5"
394
+ startIcon={{ icon: ArrowUp }}
395
+ disabled={!inputMessage?.trim() || isLoading || deploymentInProgress}
396
+ on:click={sendMessage}
397
+ iconOnly
398
+ title={deploymentInProgress ? 'Deployment in progress' : 'Send message (Enter)'}
399
+ />
400
+ </div>
401
+ </div>
402
+ </div>
403
+ </div>
404
+ </div>
@@ -0,0 +1,19 @@
1
+ interface Props {
2
+ onRunFlow: (userMessage: string, conversationId: string) => Promise<string | undefined>;
3
+ useStreaming?: boolean;
4
+ refreshConversations?: () => Promise<void>;
5
+ conversationId?: string;
6
+ deploymentInProgress?: boolean;
7
+ createConversation: (options: {
8
+ clearMessages?: boolean;
9
+ }) => Promise<string>;
10
+ path?: string;
11
+ }
12
+ declare const FlowChatInterface: import("svelte").Component<Props, {
13
+ fillInputMessage: (message: string) => void;
14
+ focusInput: () => void;
15
+ clearMessages: () => void;
16
+ loadConversationMessages: (convId: string) => Promise<void>;
17
+ }, "">;
18
+ type FlowChatInterface = ReturnType<typeof FlowChatInterface>;
19
+ export default FlowChatInterface;
@@ -0,0 +1,41 @@
1
+ <script lang="ts">import { Markdown } from 'svelte-exmarkdown';
2
+ import { gfmPlugin } from 'svelte-exmarkdown/gfm';
3
+ import { Loader2 } from 'lucide-svelte';
4
+ import CodeDisplay from '../copilot/chat/script/CodeDisplay.svelte';
5
+ import LinkRenderer from '../copilot/chat/LinkRenderer.svelte';
6
+ let { message } = $props();
7
+ </script>
8
+
9
+ <div class="flex {message.message_type === 'user' ? 'justify-end' : 'justify-start'}">
10
+ <div
11
+ class="max-w-[90%] min-w-0 rounded-lg p-3 {message.message_type === 'user'
12
+ ? 'bg-surface-secondary'
13
+ : 'bg-surface border border-gray-200 dark:border-gray-600'}"
14
+ >
15
+ {#if message.message_type === 'user'}
16
+ <p class="whitespace-pre-wrap text-sm break-words">{message.content}</p>
17
+ {:else if message.loading}
18
+ <div class="flex items-center gap-2 text-tertiary">
19
+ <Loader2 size={16} class="animate-spin" />
20
+ <span>Processing...</span>
21
+ </div>
22
+ {:else if message.content}
23
+ <div class="prose prose-sm dark:prose-invert prose-ul:!pl-6 break-words">
24
+ <Markdown
25
+ md={message.content}
26
+ plugins={[
27
+ gfmPlugin(),
28
+ {
29
+ renderer: {
30
+ pre: CodeDisplay,
31
+ a: LinkRenderer
32
+ }
33
+ }
34
+ ]}
35
+ />
36
+ </div>
37
+ {:else}
38
+ <p class="text-tertiary text-sm">No result</p>
39
+ {/if}
40
+ </div>
41
+ </div>
@@ -0,0 +1,9 @@
1
+ import type { FlowConversationMessage } from '../../gen';
2
+ interface Props {
3
+ message: FlowConversationMessage & {
4
+ loading?: boolean;
5
+ };
6
+ }
7
+ declare const FlowChatMessage: import("svelte").Component<Props, {}, "">;
8
+ type FlowChatMessage = ReturnType<typeof FlowChatMessage>;
9
+ export default FlowChatMessage;
@@ -0,0 +1,213 @@
1
+ <script lang="ts">import { Button } from '../common';
2
+ import { MessageCircle, Plus, Trash2, PanelLeftClose, PanelLeftOpen, Loader2 } from 'lucide-svelte';
3
+ import { workspaceStore } from '../../stores';
4
+ import { FlowConversationService } from '../../gen';
5
+ import { sendUserToast } from '../../toast';
6
+ import CountBadge from '../common/badge/CountBadge.svelte';
7
+ import InfiniteList from '../InfiniteList.svelte';
8
+ import { untrack } from 'svelte';
9
+ import { twMerge } from 'tailwind-merge';
10
+ let { flowPath, selectedConversationId, onNewConversation, onSelectConversation, onDeleteConversation } = $props();
11
+ let isExpanded = $state(false);
12
+ let infiniteList = $state();
13
+ let conversations = $state([]);
14
+ let deletingConversationId = $state(undefined);
15
+ export async function refreshConversations() {
16
+ return await infiniteList?.loadData('forceRefresh');
17
+ }
18
+ export async function addNewConversation(conversationId, username) {
19
+ // Check if there's already a draft conversation
20
+ const existingDraft = conversations.find((c) => c.isDraft);
21
+ if (existingDraft) {
22
+ // Select the existing draft instead of creating a new one
23
+ onSelectConversation(existingDraft.id, true);
24
+ return existingDraft.id;
25
+ }
26
+ // Create a new conversation object and add it to the top of the list
27
+ const newConversation = {
28
+ id: conversationId,
29
+ workspace_id: $workspaceStore,
30
+ flow_path: flowPath,
31
+ title: 'New chat',
32
+ created_at: new Date().toISOString(),
33
+ updated_at: new Date().toISOString(),
34
+ created_by: username,
35
+ isDraft: true
36
+ };
37
+ // Prepend to conversations list
38
+ conversations = [newConversation, ...conversations];
39
+ return conversationId;
40
+ }
41
+ async function loadConversations(page, perPage) {
42
+ if (!$workspaceStore || !flowPath)
43
+ return [];
44
+ try {
45
+ const response = await FlowConversationService.listFlowConversations({
46
+ workspace: $workspaceStore,
47
+ flowPath: flowPath,
48
+ page: page,
49
+ perPage: perPage
50
+ });
51
+ return response;
52
+ }
53
+ catch (error) {
54
+ console.error('Failed to load conversations:', error);
55
+ sendUserToast('Failed to load conversations', true);
56
+ return [];
57
+ }
58
+ }
59
+ async function deleteConversation(conversationId) {
60
+ try {
61
+ deletingConversationId = conversationId;
62
+ await FlowConversationService.deleteFlowConversation({
63
+ workspace: $workspaceStore,
64
+ conversationId
65
+ });
66
+ onDeleteConversation(conversationId);
67
+ sendUserToast('Conversation deleted successfully');
68
+ }
69
+ catch (error) {
70
+ console.error('Failed to delete conversation:', error);
71
+ sendUserToast('Failed to delete conversation', true);
72
+ throw error;
73
+ }
74
+ finally {
75
+ deletingConversationId = undefined;
76
+ }
77
+ }
78
+ function getConversationTitle(conversation) {
79
+ return conversation.title || `Conversation ${conversation.created_at.slice(0, 10)}`;
80
+ }
81
+ // Initialize InfiniteList when component mounts or flowPath changes
82
+ $effect(() => {
83
+ if ($workspaceStore && flowPath) {
84
+ if (infiniteList) {
85
+ untrack(() => {
86
+ infiniteList?.setLoader(loadConversations);
87
+ infiniteList?.setDeleteItemFn(deleteConversation);
88
+ });
89
+ }
90
+ }
91
+ });
92
+ </script>
93
+
94
+ <div
95
+ class="flex flex-col h-full bg-surface border-r border-gray-200 dark:border-gray-700 transition-all duration-300 {isExpanded
96
+ ? 'w-60'
97
+ : 'w-16'}"
98
+ >
99
+ <!-- Header -->
100
+ <div class="flex-shrink-0 p-2 border-b border-gray-200 dark:border-gray-700">
101
+ <div class="flex flex-col gap-2">
102
+ <Button
103
+ size="sm"
104
+ color="light"
105
+ startIcon={{ icon: isExpanded ? PanelLeftClose : PanelLeftOpen }}
106
+ onclick={() => (isExpanded = !isExpanded)}
107
+ iconOnly={!isExpanded}
108
+ btnClasses="!justify-start"
109
+ label="Conversations"
110
+ >
111
+ Conversations
112
+ </Button>
113
+ <Button
114
+ size="sm"
115
+ color="light"
116
+ startIcon={{ icon: Plus }}
117
+ onclick={() => onNewConversation({ clearMessages: true })}
118
+ title="Start new conversation"
119
+ iconOnly={!isExpanded}
120
+ btnClasses="!justify-start"
121
+ label="New chat"
122
+ >
123
+ New chat
124
+ </Button>
125
+ </div>
126
+ </div>
127
+
128
+ <!-- Conversations List -->
129
+ {#if !isExpanded}
130
+ <!-- Collapsed state - show single chat icon with badge -->
131
+ <div class="p-2 flex flex-col items-center mt-2">
132
+ <button
133
+ class="relative w-[23px] h-[23px] rounded-md center-center hover:bg-surface-hover transition-all duration-100 text-secondary hover:text-primary group"
134
+ onclick={() => (isExpanded = true)}
135
+ title="{conversations.length} conversation{conversations.length !== 1 ? 's' : ''}"
136
+ >
137
+ <MessageCircle size={16} />
138
+ <CountBadge count={conversations.length} small={true} alwaysVisible={true} />
139
+ </button>
140
+ </div>
141
+ {/if}
142
+
143
+ <!-- Always mount InfiniteList, but hide it when collapsed -->
144
+ <div class="flex-1 overflow-hidden" class:hidden={!isExpanded}>
145
+ <InfiniteList
146
+ bind:this={infiniteList}
147
+ bind:items={conversations}
148
+ selectedItemId={selectedConversationId}
149
+ noBorder={true}
150
+ rounded={false}
151
+ >
152
+ {#snippet children({ item: conversation, hover })}
153
+ <div
154
+ class={twMerge(
155
+ 'w-full p-1',
156
+ selectedConversationId === conversation.id
157
+ ? 'bg-blue-200/30 text-blue-500 dark:bg-blue-600/30 text-blue-400'
158
+ : ''
159
+ )}
160
+ >
161
+ <Button
162
+ color="transparent"
163
+ size="xs"
164
+ onclick={() => onSelectConversation(conversation.id, conversation.isDraft)}
165
+ >
166
+ <span class="flex-1 text-left text-sm font-medium text-primary truncate">
167
+ {getConversationTitle(conversation)}
168
+ </span>
169
+ <button
170
+ class="ml-2 p-1 rounded hover:bg-red-100 dark:hover:bg-red-900/30 text-red-500 transition-all {hover ||
171
+ deletingConversationId === conversation.id
172
+ ? 'opacity-100'
173
+ : 'opacity-0'}"
174
+ disabled={deletingConversationId === conversation.id}
175
+ onclick={(e) => {
176
+ e.stopPropagation()
177
+ if (conversation.isDraft) {
178
+ // just remove first conversation as it is the draft
179
+ conversations = [...conversations.slice(1)]
180
+ onDeleteConversation(conversation.id)
181
+ } else {
182
+ infiniteList?.deleteItem(conversation.id)
183
+ }
184
+ }}
185
+ title="Delete conversation"
186
+ >
187
+ {#if deletingConversationId === conversation.id}
188
+ <Loader2 size={14} class="animate-spin" />
189
+ {:else}
190
+ <Trash2 size={14} />
191
+ {/if}
192
+ </button>
193
+ </Button>
194
+ </div>
195
+ {/snippet}
196
+
197
+ {#snippet empty()}
198
+ <div class="p-4 text-center">
199
+ <p class="text-sm text-secondary mb-2">No conversations yet</p>
200
+ </div>
201
+ {/snippet}
202
+ </InfiniteList>
203
+ </div>
204
+
205
+ {#if isExpanded}
206
+ <!-- Footer -->
207
+ <div class="flex-shrink-0 p-4 border-t border-gray-200 dark:border-gray-700">
208
+ <p class="text-xs text-tertiary">
209
+ {conversations.length} conversation{conversations.length !== 1 ? 's' : ''}
210
+ </p>
211
+ </div>
212
+ {/if}
213
+ </div>