windmill-components 1.532.0 → 1.537.1
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.
- package/package/components/ArgInput.svelte +25 -18
- package/package/components/Auth0Setting.svelte +8 -3
- package/package/components/Dev.svelte +5 -4
- package/package/components/DiffDrawer.svelte +2 -2
- package/package/components/DiffEditor.svelte +34 -37
- package/package/components/DiffEditor.svelte.d.ts +23 -39
- package/package/components/EditableSchemaForm.svelte +42 -51
- package/package/components/EditableSchemaForm.svelte.d.ts +2 -3
- package/package/components/Editor.svelte +30 -9
- package/package/components/Editor.svelte.d.ts +5 -0
- package/package/components/FlowBuilder.svelte +7 -4
- package/package/components/FlowPreviewContent.svelte +3 -3
- package/package/components/FlowStatusViewer.svelte +28 -0
- package/package/components/FlowStatusViewerInner.svelte +72 -20
- package/package/components/FlowStatusViewerInner.svelte.d.ts +7 -0
- package/package/components/ModulePreview.svelte +2 -1
- package/package/components/ModulePreview.svelte.d.ts +1 -0
- package/package/components/ModulePreviewForm.svelte +72 -65
- package/package/components/ModulePreviewResultViewer.svelte +13 -18
- package/package/components/ModuleTest.svelte +6 -5
- package/package/components/ModuleTest.svelte.d.ts +1 -0
- package/package/components/OktaSetting.svelte +8 -3
- package/package/components/Portal.svelte +11 -7
- package/package/components/Portal.svelte.d.ts +19 -39
- package/package/components/RunForm.svelte +2 -2
- package/package/components/RunForm.svelte.d.ts +1 -1
- package/package/components/RunFormAdvancedPopup.svelte +13 -1
- package/package/components/SchemaForm.svelte +1 -2
- package/package/components/ScriptBuilder.svelte +1 -1
- package/package/components/ScriptEditor.svelte +21 -7
- package/package/components/SimpleEditor.svelte +0 -1
- package/package/components/apps/components/layout/AppModal.svelte +2 -2
- package/package/components/apps/editor/component/ComponentNavigation.svelte +3 -2
- package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte +1 -1
- package/package/components/apps/editor/inlineScriptsPanel/InlineScriptRunnableByPath.svelte +0 -1
- package/package/components/apps/editor/settingsPanel/ArrayStaticInputEditor.svelte +3 -1
- package/package/components/apps/editor/settingsPanel/GridCondition.svelte +3 -1
- package/package/components/apps/editor/settingsPanel/GridNavbar.svelte +3 -1
- package/package/components/apps/editor/settingsPanel/GridTab.svelte +3 -1
- package/package/components/apps/editor/settingsPanel/OneOfInputSpecsEditor.svelte +55 -53
- package/package/components/apps/editor/settingsPanel/TableActions.svelte +3 -1
- package/package/components/common/button/model.d.ts +1 -1
- package/package/components/common/drawer/Disposable.svelte +51 -30
- package/package/components/common/drawer/Disposable.svelte.d.ts +12 -44
- package/package/components/common/drawer/Drawer.svelte +15 -11
- package/package/components/copilot/MetadataGen.svelte +14 -3
- package/package/components/copilot/chat/AIChatInput.svelte +0 -1
- package/package/components/copilot/chat/AIChatManager.svelte.js +3 -3
- package/package/components/copilot/chat/AvailableContextList.svelte +192 -66
- package/package/components/copilot/chat/AvailableContextList.svelte.d.ts +2 -2
- package/package/components/copilot/chat/ContextElementBadge.svelte +3 -3
- package/package/components/copilot/chat/ContextManager.svelte.js +36 -13
- package/package/components/copilot/chat/ContextTextarea.svelte +21 -48
- package/package/components/copilot/chat/ToolContentDisplay.svelte +10 -1
- package/package/components/copilot/chat/ToolExecutionDisplay.svelte +3 -3
- package/package/components/copilot/chat/context.d.ts +7 -2
- package/package/components/copilot/chat/flow/FlowAIChat.svelte +110 -8
- package/package/components/copilot/chat/flow/core.d.ts +11 -0
- package/package/components/copilot/chat/flow/core.js +121 -3
- package/package/components/copilot/chat/flow/uiIntents.d.ts +8 -0
- package/package/components/copilot/chat/flow/uiIntents.js +5 -0
- package/package/components/copilot/chat/flow/useUiIntent.d.ts +5 -0
- package/package/components/copilot/chat/flow/useUiIntent.js +12 -0
- package/package/components/copilot/chat/monaco-adapter.d.ts +22 -4
- package/package/components/copilot/chat/monaco-adapter.js +55 -16
- package/package/components/copilot/chat/script/core.js +3 -2
- package/package/components/copilot/chat/shared.d.ts +3 -2
- package/package/components/copilot/chat/shared.js +24 -12
- package/package/components/copilot/lib.js +12 -7
- package/package/components/copilot/shared.d.ts +1 -1
- package/package/components/copilot/shared.js +16 -10
- package/package/components/flows/FlowEditor.svelte +4 -2
- package/package/components/flows/FlowEditor.svelte.d.ts +1 -0
- package/package/components/flows/FlowModuleIcon.svelte +8 -8
- package/package/components/flows/common/FlowCardHeader.svelte +4 -1
- package/package/components/flows/content/FlowBranchesAllWrapper.svelte +6 -0
- package/package/components/flows/content/FlowBranchesOneWrapper.svelte +6 -0
- package/package/components/flows/content/FlowEditorPanel.svelte +2 -1
- package/package/components/flows/content/FlowEditorPanel.svelte.d.ts +1 -0
- package/package/components/flows/content/FlowInput.svelte +31 -34
- package/package/components/flows/content/FlowInput.svelte.d.ts +1 -0
- package/package/components/flows/content/FlowLoop.svelte +7 -0
- package/package/components/flows/content/FlowModuleComponent.svelte +37 -44
- package/package/components/flows/content/FlowModuleScript.svelte +1 -1
- package/package/components/flows/content/FlowModuleSuspend.svelte +16 -18
- package/package/components/flows/content/FlowWhileLoop.svelte +6 -0
- package/package/components/flows/content/ScriptEditorDrawer.svelte +9 -11
- package/package/components/flows/dfs.d.ts +1 -1
- package/package/components/flows/dfs.js +6 -6
- package/package/components/flows/flowInfers.js +7 -7
- package/package/components/flows/flowStateUtils.svelte.js +1 -2
- package/package/components/flows/map/FlowModuleSchemaItem.svelte +12 -26
- package/package/components/flows/map/MapItem.svelte +8 -4
- package/package/components/flows/map/VirtualItem.svelte +1 -1
- package/package/components/flows/pickers/TopLevelNode.svelte +1 -1
- package/package/components/flows/propPicker/InputPickerInner.svelte +5 -5
- package/package/components/flows/propPicker/OutputPickerInner.svelte +143 -118
- package/package/components/flows/propPicker/OutputPickerInner.svelte.d.ts +7 -16
- package/package/components/flows/{testSteps.svelte.d.ts → stepsInputArgs.svelte.d.ts} +2 -1
- package/package/components/flows/{testSteps.svelte.js → stepsInputArgs.svelte.js} +15 -3
- package/package/components/flows/types.d.ts +16 -3
- package/package/components/flows/utils.js +3 -0
- package/package/components/graph/FlowGraphV2.svelte +1 -1
- package/package/components/graph/renderers/nodes/AIToolNode.svelte +4 -4
- package/package/components/graph/renderers/nodes/NewAIToolNode.svelte +71 -54
- package/package/components/propertyPicker/ObjectViewer.svelte +11 -3
- package/package/components/raw_apps/RawAppInlineScriptEditor.svelte +1 -1
- package/package/components/schema/AddPropertyV2.svelte +2 -7
- package/package/components/schema/AddPropertyV2.svelte.d.ts +3 -20
- package/package/components/schema/EditableSchemaDrawer.svelte +109 -115
- package/package/components/schema/EditableSchemaDrawer.svelte.d.ts +2 -1
- package/package/components/schema/EditableSchemaSdkWrapper.svelte +16 -3
- package/package/components/schema/EditableSchemaSdkWrapper.svelte.d.ts +4 -1
- package/package/components/schema/EditableSchemaWrapper.svelte +3 -10
- package/package/components/schema/FlowPropertyEditor.svelte +9 -41
- package/package/components/schema/FlowPropertyEditor.svelte.d.ts +1 -1
- package/package/components/schema/SchemaFormDND.svelte +11 -10
- package/package/components/schema/SchemaFormDND.svelte.d.ts +3 -2
- package/package/components/schema/editable_schema_wrapper.d.ts +0 -3
- package/package/components/settings/PremiumInfo.svelte +7 -2
- package/package/components/triggers/CaptureWrapper.svelte +2 -13
- package/package/components/triggers/CaptureWrapper.svelte.d.ts +1 -1
- package/package/components/triggers/TriggersWrapper.svelte +1 -0
- package/package/components/triggers/http/RouteEditorInner.svelte +1 -1
- package/package/components/triggers/nats/NatsTriggerEditorInner.svelte +23 -20
- package/package/components/triggers/nats/NatsTriggersConfigSection.svelte +15 -27
- package/package/components/triggers/nats/NatsTriggersConfigSection.svelte.d.ts +7 -5
- package/package/components/triggers/websocket/WebsocketTriggerEditorInner.svelte +16 -16
- package/package/hubPaths.json +3 -1
- package/package/script_helpers.d.ts +2 -2
- package/package/script_helpers.js +2 -0
- package/package/stores.d.ts +1 -0
- package/package/stores.js +8 -1
- package/package.json +2 -2
- package/package/components/ModulePreviewResultViewer.svelte.d.ts +0 -28
|
@@ -29,7 +29,7 @@ const isDeletable = $derived(deletable && contextElement.deletable !== false);
|
|
|
29
29
|
<button onclick={isDeletable ? onDelete : undefined} class:cursor-default={!isDeletable}>
|
|
30
30
|
{#if showDelete && isDeletable}
|
|
31
31
|
<X size={16} />
|
|
32
|
-
{:else if contextElement.type === 'flow_module'}
|
|
32
|
+
{:else if contextElement.type === 'flow_module' || contextElement.type === 'flow_module_code_piece'}
|
|
33
33
|
<FlowModuleIcon module={contextElement as FlowModule} size={16} />
|
|
34
34
|
{:else}
|
|
35
35
|
{@const SvelteComponent = icon}
|
|
@@ -37,7 +37,7 @@ const isDeletable = $derived(deletable && contextElement.deletable !== false);
|
|
|
37
37
|
{/if}
|
|
38
38
|
</button>
|
|
39
39
|
<span class="truncate">
|
|
40
|
-
{contextElement.type === 'diff'
|
|
40
|
+
{contextElement.type === 'diff'
|
|
41
41
|
? contextElement.title.replace(/_/g, ' ')
|
|
42
42
|
: contextElement.title}
|
|
43
43
|
</span>
|
|
@@ -65,7 +65,7 @@ const isDeletable = $derived(deletable && contextElement.deletable !== false);
|
|
|
65
65
|
<div class="text-tertiary">Not loaded yet</div>
|
|
66
66
|
{/if}
|
|
67
67
|
</div>
|
|
68
|
-
{:else if contextElement.type === 'code' || contextElement.type === 'code_piece' || contextElement.type === 'diff'}
|
|
68
|
+
{:else if contextElement.type === 'code' || contextElement.type === 'code_piece' || contextElement.type === 'diff' || contextElement.type === 'flow_module_code_piece'}
|
|
69
69
|
<div class="max-w-96 max-h-[300px] text-xs overflow-auto">
|
|
70
70
|
<HighlightCode
|
|
71
71
|
language={contextElement.lang}
|
|
@@ -66,7 +66,7 @@ export default class ContextManager {
|
|
|
66
66
|
newAvailableContext.push({
|
|
67
67
|
type: 'flow_module',
|
|
68
68
|
id: module.id,
|
|
69
|
-
title:
|
|
69
|
+
title: `${module.id}`,
|
|
70
70
|
value: {
|
|
71
71
|
language: 'language' in module.value ? module.value.language : 'bunnative',
|
|
72
72
|
path: 'path' in module.value ? module.value.path : '',
|
|
@@ -215,22 +215,45 @@ export default class ContextManager {
|
|
|
215
215
|
this.scriptOptions = scriptOptions;
|
|
216
216
|
}
|
|
217
217
|
addSelectedLinesToContext(lines, startLine, endLine, moduleId) {
|
|
218
|
-
const title = moduleId ?
|
|
218
|
+
const title = moduleId ? `${moduleId} L${startLine}-L${endLine}` : `L${startLine}-L${endLine}`;
|
|
219
219
|
if (!this.scriptOptions ||
|
|
220
|
-
this.selectedContext.find((c) => c.type === 'code_piece' && c.title === title)
|
|
220
|
+
this.selectedContext.find((c) => (c.type === 'code_piece' && c.title === title) ||
|
|
221
|
+
(c.type === 'flow_module_code_piece' && c.id === moduleId && c.title === title))) {
|
|
221
222
|
return;
|
|
222
223
|
}
|
|
223
|
-
|
|
224
|
-
...this.selectedContext
|
|
225
|
-
{
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
startLine,
|
|
229
|
-
endLine,
|
|
230
|
-
content: lines,
|
|
231
|
-
lang: this.scriptOptions.lang
|
|
224
|
+
if (moduleId) {
|
|
225
|
+
const module = [...this.availableContext, ...this.selectedContext].find((c) => c.type === 'flow_module' && c.id === moduleId);
|
|
226
|
+
if (!module) {
|
|
227
|
+
console.error('Module not found', moduleId);
|
|
228
|
+
return;
|
|
232
229
|
}
|
|
233
|
-
|
|
230
|
+
this.selectedContext = [
|
|
231
|
+
...this.selectedContext,
|
|
232
|
+
{
|
|
233
|
+
type: 'flow_module_code_piece',
|
|
234
|
+
id: moduleId,
|
|
235
|
+
title: title,
|
|
236
|
+
startLine,
|
|
237
|
+
endLine,
|
|
238
|
+
content: lines,
|
|
239
|
+
lang: this.scriptOptions.lang,
|
|
240
|
+
value: module.value
|
|
241
|
+
}
|
|
242
|
+
];
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
this.selectedContext = [
|
|
246
|
+
...this.selectedContext,
|
|
247
|
+
{
|
|
248
|
+
type: 'code_piece',
|
|
249
|
+
title: title,
|
|
250
|
+
startLine,
|
|
251
|
+
endLine,
|
|
252
|
+
content: lines,
|
|
253
|
+
lang: this.scriptOptions.lang
|
|
254
|
+
}
|
|
255
|
+
];
|
|
256
|
+
}
|
|
234
257
|
}
|
|
235
258
|
setFixContext() {
|
|
236
259
|
const codeContext = this.availableContext.find((c) => c.type === 'code');
|
|
@@ -10,7 +10,7 @@ let contextTooltipWord = $state('');
|
|
|
10
10
|
let tooltipPosition = $state({ x: 0, y: 0 });
|
|
11
11
|
let textarea = $state(undefined);
|
|
12
12
|
let tooltipElement = $state(undefined);
|
|
13
|
-
let
|
|
13
|
+
let tooltipCurrentViewNumber = $state(0);
|
|
14
14
|
// Properties to copy for caret position calculation
|
|
15
15
|
const properties = [
|
|
16
16
|
'direction',
|
|
@@ -142,18 +142,17 @@ function handleContextSelection(contextElement) {
|
|
|
142
142
|
updateInstructionsWithContext(contextElement);
|
|
143
143
|
showContextTooltip = false;
|
|
144
144
|
}
|
|
145
|
-
async function updateTooltipPosition(
|
|
146
|
-
if (!textarea
|
|
145
|
+
async function updateTooltipPosition(currentViewItemsNumber) {
|
|
146
|
+
if (!textarea)
|
|
147
147
|
return;
|
|
148
148
|
try {
|
|
149
149
|
const coords = getCaretCoordinates(textarea, textarea.selectionEnd);
|
|
150
150
|
const rect = textarea.getBoundingClientRect();
|
|
151
|
-
const filteredAvailableContext = availableContext.filter((c) => !contextTooltipWord || c.title.toLowerCase().includes(contextTooltipWord.slice(1)));
|
|
152
151
|
const itemHeight = 28; // Estimated height of one item + gap (Button: p-1(8px) + text-xs(16px) = 24px; Parent: gap-1(4px) = 28px)
|
|
153
152
|
const containerPadding = 8; // p-1 top + p-1 bottom = 4px + 4px = 8px
|
|
154
153
|
const maxHeight = 192 + containerPadding; // max-h-48 (192px) + containerPadding (8px)
|
|
155
154
|
// Calculate uncapped height, subtract gap from last item as it's not needed
|
|
156
|
-
const numItems =
|
|
155
|
+
const numItems = currentViewItemsNumber;
|
|
157
156
|
let uncappedHeight = numItems > 0 ? numItems * itemHeight - 4 + containerPadding : containerPadding;
|
|
158
157
|
// Ensure height is at least containerPadding even if no items
|
|
159
158
|
uncappedHeight = Math.max(uncappedHeight, containerPadding);
|
|
@@ -211,59 +210,29 @@ function handleInput(e) {
|
|
|
211
210
|
else {
|
|
212
211
|
showContextTooltip = false;
|
|
213
212
|
contextTooltipWord = '';
|
|
214
|
-
selectedSuggestionIndex = 0;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
function handleKeyPress(e) {
|
|
218
|
-
if (e.key === 'Enter' && !e.shiftKey) {
|
|
219
|
-
e.preventDefault();
|
|
220
|
-
if (contextTooltipWord) {
|
|
221
|
-
const filteredContext = availableContext.filter((c) => !contextTooltipWord || c.title.toLowerCase().includes(contextTooltipWord.slice(1)));
|
|
222
|
-
const contextElement = filteredContext[selectedSuggestionIndex];
|
|
223
|
-
if (contextElement) {
|
|
224
|
-
const isInSelectedContext = selectedContext.find((c) => c.title === contextElement.title && c.type === contextElement.type);
|
|
225
|
-
// If the context element is already in the selected context and the last word in the instructions is the same as the context element title, send request
|
|
226
|
-
if (isInSelectedContext && value.split(' ').pop() === '@' + contextElement.title) {
|
|
227
|
-
onSendRequest();
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
handleContextSelection(contextElement);
|
|
231
|
-
}
|
|
232
|
-
else if (contextTooltipWord === '@' && availableContext.length > 0) {
|
|
233
|
-
handleContextSelection(availableContext[0]);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
onSendRequest();
|
|
238
|
-
}
|
|
239
213
|
}
|
|
240
214
|
}
|
|
241
215
|
function handleKeyDown(e) {
|
|
216
|
+
// Pass to parent first if provided
|
|
242
217
|
if (onKeyDown) {
|
|
243
218
|
onKeyDown(e);
|
|
244
219
|
}
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
e.preventDefault();
|
|
250
|
-
const contextElement = filteredContext[selectedSuggestionIndex];
|
|
251
|
-
if (contextElement) {
|
|
252
|
-
handleContextSelection(contextElement);
|
|
220
|
+
if (showContextTooltip) {
|
|
221
|
+
// avoid new line after Enter in the tooltip
|
|
222
|
+
if (e.key === 'Enter') {
|
|
223
|
+
e.preventDefault();
|
|
253
224
|
}
|
|
225
|
+
return;
|
|
254
226
|
}
|
|
255
|
-
if (e.key === '
|
|
256
|
-
e.preventDefault();
|
|
257
|
-
selectedSuggestionIndex = (selectedSuggestionIndex + 1) % filteredContext.length;
|
|
258
|
-
}
|
|
259
|
-
else if (e.key === 'ArrowUp') {
|
|
227
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
260
228
|
e.preventDefault();
|
|
261
|
-
|
|
262
|
-
(selectedSuggestionIndex - 1 + filteredContext.length) % filteredContext.length;
|
|
229
|
+
onSendRequest();
|
|
263
230
|
}
|
|
264
231
|
}
|
|
265
232
|
$effect(() => {
|
|
266
|
-
|
|
233
|
+
if (showContextTooltip) {
|
|
234
|
+
updateTooltipPosition(tooltipCurrentViewNumber);
|
|
235
|
+
}
|
|
267
236
|
});
|
|
268
237
|
export function focus() {
|
|
269
238
|
textarea?.focus();
|
|
@@ -283,7 +252,6 @@ export function focus() {
|
|
|
283
252
|
</div>
|
|
284
253
|
<textarea
|
|
285
254
|
bind:this={textarea}
|
|
286
|
-
onkeypress={handleKeyPress}
|
|
287
255
|
onkeydown={handleKeyDown}
|
|
288
256
|
bind:value
|
|
289
257
|
use:autosize
|
|
@@ -319,7 +287,12 @@ export function focus() {
|
|
|
319
287
|
}}
|
|
320
288
|
showAllAvailable={true}
|
|
321
289
|
stringSearch={contextTooltipWord.slice(1)}
|
|
322
|
-
|
|
290
|
+
onViewChange={(newNumber) => {
|
|
291
|
+
tooltipCurrentViewNumber = newNumber
|
|
292
|
+
}}
|
|
293
|
+
setShowing={(showing) => {
|
|
294
|
+
showContextTooltip = showing
|
|
295
|
+
}}
|
|
323
296
|
/>
|
|
324
297
|
</div>
|
|
325
298
|
</Portal>
|
|
@@ -13,6 +13,13 @@ function formatJson(obj) {
|
|
|
13
13
|
return obj;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
for (const key in obj) {
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(obj[key]);
|
|
19
|
+
obj[key] = parsed;
|
|
20
|
+
}
|
|
21
|
+
catch { }
|
|
22
|
+
}
|
|
16
23
|
return JSON.stringify(obj, null, 2);
|
|
17
24
|
}
|
|
18
25
|
catch {
|
|
@@ -71,7 +78,9 @@ async function copyToClipboard() {
|
|
|
71
78
|
<div
|
|
72
79
|
class="bg-surface-secondary border border-gray-200 dark:border-gray-700 rounded p-3 overflow-x-auto max-h-64 overflow-y-auto"
|
|
73
80
|
>
|
|
74
|
-
<pre class="text-2xs text-primary whitespace-pre-wrap"
|
|
81
|
+
<pre class="text-2xs text-primary whitespace-pre-wrap"
|
|
82
|
+
>{formatJson($state.snapshot(content))}</pre
|
|
83
|
+
>
|
|
75
84
|
</div>
|
|
76
85
|
{:else}
|
|
77
86
|
<div
|
|
@@ -14,8 +14,8 @@ const hasParameters = $derived(message.parameters !== undefined && Object.keys(m
|
|
|
14
14
|
<!-- Collapsible Header -->
|
|
15
15
|
<button
|
|
16
16
|
class={twMerge(
|
|
17
|
-
|
|
18
|
-
message.needsConfirmation ?
|
|
17
|
+
'w-full p-3 bg-surface-secondary hover:bg-surface-hover transition-colors flex items-center justify-between text-left border-b border-gray-200 dark:border-gray-700',
|
|
18
|
+
message.needsConfirmation ? 'opacity-80' : ''
|
|
19
19
|
)}
|
|
20
20
|
onclick={() => (isExpanded = !isExpanded)}
|
|
21
21
|
disabled={!message.showDetails}
|
|
@@ -46,7 +46,7 @@ const hasParameters = $derived(message.parameters !== undefined && Object.keys(m
|
|
|
46
46
|
{#if isExpanded}
|
|
47
47
|
<div class="p-3 bg-surface space-y-3">
|
|
48
48
|
<!-- Parameters Section -->
|
|
49
|
-
<div class={message.needsConfirmation ?
|
|
49
|
+
<div class={message.needsConfirmation ? 'opacity-80' : ''}>
|
|
50
50
|
<ToolContentDisplay title="Parameters" content={message.parameters} />
|
|
51
51
|
</div>
|
|
52
52
|
|
|
@@ -40,7 +40,7 @@ export interface CodePieceElement {
|
|
|
40
40
|
title: string;
|
|
41
41
|
lang: ScriptLang | 'bunnative';
|
|
42
42
|
}
|
|
43
|
-
export interface
|
|
43
|
+
export interface FlowModuleElement {
|
|
44
44
|
type: 'flow_module';
|
|
45
45
|
id: string;
|
|
46
46
|
title: string;
|
|
@@ -51,7 +51,12 @@ export interface FlowModule {
|
|
|
51
51
|
type: string;
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
-
export
|
|
54
|
+
export interface FlowModuleCodePieceElement extends Omit<CodePieceElement, 'type'> {
|
|
55
|
+
type: 'flow_module_code_piece';
|
|
56
|
+
id: string;
|
|
57
|
+
value: FlowModuleElement['value'];
|
|
58
|
+
}
|
|
59
|
+
export type ContextElement = (CodeElement | ErrorElement | DBElement | DiffElement | CodePieceElement | FlowModuleElement | FlowModuleCodePieceElement) & {
|
|
55
60
|
deletable?: boolean;
|
|
56
61
|
};
|
|
57
62
|
export {};
|
|
@@ -9,7 +9,7 @@ import { loadSchemaFromModule } from '../../../flows/flowInfers';
|
|
|
9
9
|
import { aiChatManager } from '../AIChatManager.svelte';
|
|
10
10
|
import { refreshStateStore } from '../../../../svelte5Utils.svelte';
|
|
11
11
|
import DiffDrawer from '../../../DiffDrawer.svelte';
|
|
12
|
-
let { flowModuleSchemaMap
|
|
12
|
+
let { flowModuleSchemaMap } = $props();
|
|
13
13
|
const { flowStore, flowStateStore, selectedId, currentEditor } = getContext('FlowEditorContext');
|
|
14
14
|
const { exprsToSet } = getContext('FlowCopilotContext') ?? {};
|
|
15
15
|
let affectedModules = $state({});
|
|
@@ -71,13 +71,10 @@ const flowHelpers = {
|
|
|
71
71
|
hasDiff: () => {
|
|
72
72
|
return Object.keys(affectedModules).length > 0;
|
|
73
73
|
},
|
|
74
|
-
acceptAllModuleActions
|
|
75
|
-
for (const
|
|
76
|
-
|
|
77
|
-
deleteStep(id);
|
|
78
|
-
}
|
|
74
|
+
acceptAllModuleActions() {
|
|
75
|
+
for (const id of Object.keys(affectedModules)) {
|
|
76
|
+
this.acceptModuleAction(id);
|
|
79
77
|
}
|
|
80
|
-
affectedModules = {};
|
|
81
78
|
},
|
|
82
79
|
rejectAllModuleActions() {
|
|
83
80
|
for (const id of Object.keys(affectedModules)) {
|
|
@@ -151,7 +148,18 @@ const flowHelpers = {
|
|
|
151
148
|
if (!newModule) {
|
|
152
149
|
throw new Error('Module not found');
|
|
153
150
|
}
|
|
154
|
-
|
|
151
|
+
// Apply the old code to the editor and hide diff editor if the reverted module is a rawscript
|
|
152
|
+
if (newModule.value.type === 'rawscript' &&
|
|
153
|
+
$currentEditor?.type === 'script' &&
|
|
154
|
+
$currentEditor.stepId === id) {
|
|
155
|
+
const aiChatEditorHandler = $currentEditor.editor.getAiChatEditorHandler();
|
|
156
|
+
if (aiChatEditorHandler) {
|
|
157
|
+
aiChatEditorHandler.revertAll({ disableReviewCallback: true });
|
|
158
|
+
$currentEditor.hideDiffMode();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
Object.keys(newModule).forEach((k) => delete newModule[k]);
|
|
162
|
+
Object.assign(newModule, $state.snapshot(oldModule));
|
|
155
163
|
}
|
|
156
164
|
refreshStateStore(flowStore);
|
|
157
165
|
delete affectedModules[id];
|
|
@@ -162,6 +170,15 @@ const flowHelpers = {
|
|
|
162
170
|
if (affectedModules[id]?.action === 'removed') {
|
|
163
171
|
deleteStep(id);
|
|
164
172
|
}
|
|
173
|
+
if (affectedModules[id]?.action === 'modified' &&
|
|
174
|
+
$currentEditor &&
|
|
175
|
+
$currentEditor.type === 'script' &&
|
|
176
|
+
$currentEditor.stepId === id) {
|
|
177
|
+
const aiChatEditorHandler = $currentEditor.editor.getAiChatEditorHandler();
|
|
178
|
+
if (aiChatEditorHandler) {
|
|
179
|
+
aiChatEditorHandler.keepAll({ disableReviewCallback: true });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
165
182
|
delete affectedModules[id];
|
|
166
183
|
},
|
|
167
184
|
// ai chat tools
|
|
@@ -416,6 +433,72 @@ const flowHelpers = {
|
|
|
416
433
|
refreshStateStore(flowStore);
|
|
417
434
|
}
|
|
418
435
|
setModuleStatus(id, 'modified');
|
|
436
|
+
},
|
|
437
|
+
setForLoopOptions: async (id, opts) => {
|
|
438
|
+
const module = getModule(id);
|
|
439
|
+
if (!module) {
|
|
440
|
+
throw new Error('Module not found');
|
|
441
|
+
}
|
|
442
|
+
if (module.value.type !== 'forloopflow') {
|
|
443
|
+
throw new Error('Module is not a forloopflow');
|
|
444
|
+
}
|
|
445
|
+
// Apply skip_failures if provided
|
|
446
|
+
if (typeof opts.skip_failures === 'boolean') {
|
|
447
|
+
module.value.skip_failures = opts.skip_failures;
|
|
448
|
+
}
|
|
449
|
+
// Apply parallel if provided
|
|
450
|
+
if (typeof opts.parallel === 'boolean') {
|
|
451
|
+
module.value.parallel = opts.parallel;
|
|
452
|
+
}
|
|
453
|
+
// Handle parallelism
|
|
454
|
+
if (opts.parallel === false) {
|
|
455
|
+
// If parallel is disabled, clear parallelism
|
|
456
|
+
module.value.parallelism = undefined;
|
|
457
|
+
}
|
|
458
|
+
else if (opts.parallelism !== undefined) {
|
|
459
|
+
if (opts.parallelism === null) {
|
|
460
|
+
// Explicitly clear parallelism
|
|
461
|
+
module.value.parallelism = undefined;
|
|
462
|
+
}
|
|
463
|
+
else if (module.value.parallel || opts.parallel === true) {
|
|
464
|
+
// Only set parallelism if parallel is enabled
|
|
465
|
+
const n = Math.max(1, Math.floor(Math.abs(opts.parallelism)));
|
|
466
|
+
module.value.parallelism = n;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
refreshStateStore(flowStore);
|
|
470
|
+
setModuleStatus(id, 'modified');
|
|
471
|
+
},
|
|
472
|
+
setModuleControlOptions: async (id, opts) => {
|
|
473
|
+
const module = getModule(id);
|
|
474
|
+
if (!module) {
|
|
475
|
+
throw new Error('Module not found');
|
|
476
|
+
}
|
|
477
|
+
// Handle stop_after_if
|
|
478
|
+
if (typeof opts.stop_after_if === 'boolean') {
|
|
479
|
+
if (opts.stop_after_if === false) {
|
|
480
|
+
module.stop_after_if = undefined;
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
module.stop_after_if = {
|
|
484
|
+
expr: opts.stop_after_if_expr ?? '',
|
|
485
|
+
skip_if_stopped: opts.stop_after_if
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Handle skip_if
|
|
490
|
+
if (typeof opts.skip_if === 'boolean') {
|
|
491
|
+
if (opts.skip_if === false) {
|
|
492
|
+
module.skip_if = undefined;
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
module.skip_if = {
|
|
496
|
+
expr: opts.skip_if_expr ?? ''
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
refreshStateStore(flowStore);
|
|
501
|
+
setModuleStatus(id, 'modified');
|
|
419
502
|
}
|
|
420
503
|
};
|
|
421
504
|
function deleteStep(id) {
|
|
@@ -454,6 +537,25 @@ $effect(() => {
|
|
|
454
537
|
const cleanup = aiChatManager.listenForCurrentEditorChanges($currentEditor);
|
|
455
538
|
return cleanup;
|
|
456
539
|
});
|
|
540
|
+
// Automatically show revert review when selecting a rawscript module with pending changes
|
|
541
|
+
$effect(() => {
|
|
542
|
+
if ($currentEditor?.type === 'script' &&
|
|
543
|
+
$selectedId &&
|
|
544
|
+
affectedModules[$selectedId] &&
|
|
545
|
+
$currentEditor.editor.getAiChatEditorHandler()) {
|
|
546
|
+
const moduleLastSnapshot = getModule($selectedId, lastSnapshot);
|
|
547
|
+
const content = moduleLastSnapshot?.value.type === 'rawscript' ? moduleLastSnapshot.value.content : '';
|
|
548
|
+
if (content.length > 0) {
|
|
549
|
+
untrack(() => $currentEditor.editor.reviewAppliedCode(content, {
|
|
550
|
+
onFinishedReview: () => {
|
|
551
|
+
const id = $selectedId;
|
|
552
|
+
flowHelpers.acceptModuleAction(id);
|
|
553
|
+
$currentEditor.hideDiffMode();
|
|
554
|
+
}
|
|
555
|
+
}));
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
});
|
|
457
559
|
let diffDrawer = $state(undefined);
|
|
458
560
|
</script>
|
|
459
561
|
|
|
@@ -33,6 +33,17 @@ export interface FlowAIChatHelpers {
|
|
|
33
33
|
addBranch: (id: string) => Promise<void>;
|
|
34
34
|
removeBranch: (id: string, branchIndex: number) => Promise<void>;
|
|
35
35
|
setForLoopIteratorExpression: (id: string, expression: string) => Promise<void>;
|
|
36
|
+
setForLoopOptions: (id: string, opts: {
|
|
37
|
+
skip_failures?: boolean | null;
|
|
38
|
+
parallel?: boolean | null;
|
|
39
|
+
parallelism?: number | null;
|
|
40
|
+
}) => Promise<void>;
|
|
41
|
+
setModuleControlOptions: (id: string, opts: {
|
|
42
|
+
stop_after_if?: boolean | null;
|
|
43
|
+
stop_after_if_expr?: string | null;
|
|
44
|
+
skip_if?: boolean | null;
|
|
45
|
+
skip_if_expr?: string | null;
|
|
46
|
+
}) => Promise<void>;
|
|
36
47
|
setCode: (id: string, code: string) => Promise<void>;
|
|
37
48
|
}
|
|
38
49
|
declare const newStepSchema: z.ZodUnion<[z.ZodObject<{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ScriptService, JobService } from '../../../../gen';
|
|
2
|
+
import { emitUiIntent } from './uiIntents';
|
|
2
3
|
import YAML from 'yaml';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import uFuzzy from '@leeoniya/ufuzzy';
|
|
@@ -98,6 +99,51 @@ const setForLoopIteratorExpressionSchema = z.object({
|
|
|
98
99
|
expression: z.string().describe('The JavaScript expression to set for the iterator')
|
|
99
100
|
});
|
|
100
101
|
const setForLoopIteratorExpressionToolDef = createToolDef(setForLoopIteratorExpressionSchema, 'set_forloop_iterator_expression', 'Set the iterator JavaScript expression for the given forloop step');
|
|
102
|
+
const setForLoopOptionsSchema = z.object({
|
|
103
|
+
id: z.string().describe('The id of the forloop step to configure'),
|
|
104
|
+
skip_failures: z
|
|
105
|
+
.boolean()
|
|
106
|
+
.nullable()
|
|
107
|
+
.optional()
|
|
108
|
+
.describe('Whether to skip failures in the loop (null to not change)'),
|
|
109
|
+
parallel: z
|
|
110
|
+
.boolean()
|
|
111
|
+
.nullable()
|
|
112
|
+
.optional()
|
|
113
|
+
.describe('Whether to run iterations in parallel (null to not change)'),
|
|
114
|
+
parallelism: z
|
|
115
|
+
.number()
|
|
116
|
+
.int()
|
|
117
|
+
.min(1)
|
|
118
|
+
.nullable()
|
|
119
|
+
.optional()
|
|
120
|
+
.describe('Maximum number of parallel iterations (null to not change)')
|
|
121
|
+
});
|
|
122
|
+
const setForLoopOptionsToolDef = createToolDef(setForLoopOptionsSchema, 'set_forloop_options', 'Set advanced options for a forloop step: skip_failures, parallel, and parallelism');
|
|
123
|
+
const setModuleControlOptionsSchema = z.object({
|
|
124
|
+
id: z.string().describe('The id of the module to configure'),
|
|
125
|
+
stop_after_if: z
|
|
126
|
+
.boolean()
|
|
127
|
+
.nullable()
|
|
128
|
+
.optional()
|
|
129
|
+
.describe('Early stop condition (true to set, false to clear, null to not change)'),
|
|
130
|
+
stop_after_if_expr: z
|
|
131
|
+
.string()
|
|
132
|
+
.nullable()
|
|
133
|
+
.optional()
|
|
134
|
+
.describe('JavaScript expression for early stop condition. Can use `flow_input` or `result`. `result` is the result of the step. `results.<step_id>` is not supported, do not use it. Only used if stop_after_if is true. Example: `flow_input.x > 10` or `result === "failure"`'),
|
|
135
|
+
skip_if: z
|
|
136
|
+
.boolean()
|
|
137
|
+
.nullable()
|
|
138
|
+
.optional()
|
|
139
|
+
.describe('Skip condition (true to set, false to clear, null to not change)'),
|
|
140
|
+
skip_if_expr: z
|
|
141
|
+
.string()
|
|
142
|
+
.nullable()
|
|
143
|
+
.optional()
|
|
144
|
+
.describe('JavaScript expression for skip condition. Can use `flow_input` or `results.<step_id>`. Only used if skip_if is true. Example: `flow_input.x > 10` or `results.a === "failure"`')
|
|
145
|
+
});
|
|
146
|
+
const setModuleControlOptionsToolDef = createToolDef(setModuleControlOptionsSchema, 'set_module_control_options', 'Set control options for any module: stop_after_if (early stop) and skip_if (conditional skip)');
|
|
101
147
|
const setBranchPredicateSchema = z.object({
|
|
102
148
|
id: z.string().describe('The id of the branchone step to set the predicates for'),
|
|
103
149
|
branchIndex: z
|
|
@@ -378,6 +424,64 @@ export const flowTools = [
|
|
|
378
424
|
return `Forloop '${parsedArgs.id}' iterator expression set`;
|
|
379
425
|
}
|
|
380
426
|
},
|
|
427
|
+
{
|
|
428
|
+
def: setForLoopOptionsToolDef,
|
|
429
|
+
fn: async ({ args, helpers, toolId, toolCallbacks }) => {
|
|
430
|
+
const parsedArgs = setForLoopOptionsSchema.parse(args);
|
|
431
|
+
await helpers.setForLoopOptions(parsedArgs.id, {
|
|
432
|
+
skip_failures: parsedArgs.skip_failures,
|
|
433
|
+
parallel: parsedArgs.parallel,
|
|
434
|
+
parallelism: parsedArgs.parallelism
|
|
435
|
+
});
|
|
436
|
+
helpers.selectStep(parsedArgs.id);
|
|
437
|
+
const message = `Set forloop '${parsedArgs.id}' options`;
|
|
438
|
+
toolCallbacks.setToolStatus(toolId, {
|
|
439
|
+
content: message
|
|
440
|
+
});
|
|
441
|
+
return `${message}: ${JSON.stringify(parsedArgs)}`;
|
|
442
|
+
}
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
def: setModuleControlOptionsToolDef,
|
|
446
|
+
fn: async ({ args, helpers, toolId, toolCallbacks }) => {
|
|
447
|
+
const parsedArgs = setModuleControlOptionsSchema.parse(args);
|
|
448
|
+
await helpers.setModuleControlOptions(parsedArgs.id, {
|
|
449
|
+
stop_after_if: parsedArgs.stop_after_if,
|
|
450
|
+
stop_after_if_expr: parsedArgs.stop_after_if_expr,
|
|
451
|
+
skip_if: parsedArgs.skip_if,
|
|
452
|
+
skip_if_expr: parsedArgs.skip_if_expr
|
|
453
|
+
});
|
|
454
|
+
helpers.selectStep(parsedArgs.id);
|
|
455
|
+
// Emit UI intent to show early-stop tab when stop_after_if is configured
|
|
456
|
+
const modules = helpers.getModules();
|
|
457
|
+
const module = findModuleById(modules, parsedArgs.id);
|
|
458
|
+
if (!module) {
|
|
459
|
+
throw new Error(`Module with id '${parsedArgs.id}' not found in flow.`);
|
|
460
|
+
}
|
|
461
|
+
const moduleType = module?.value.type;
|
|
462
|
+
const hasSpecificComponents = ['forloopflow', 'whileloopflow', 'branchall', 'branchone'];
|
|
463
|
+
const prefix = hasSpecificComponents.includes(moduleType) ? `${moduleType}` : 'flow';
|
|
464
|
+
if (typeof parsedArgs.stop_after_if === 'boolean') {
|
|
465
|
+
emitUiIntent({
|
|
466
|
+
kind: 'open_module_tab',
|
|
467
|
+
componentId: `${prefix}-${parsedArgs.id}`,
|
|
468
|
+
tab: 'early-stop'
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
if (typeof parsedArgs.skip_if === 'boolean') {
|
|
472
|
+
emitUiIntent({
|
|
473
|
+
kind: 'open_module_tab',
|
|
474
|
+
componentId: `${prefix}-${parsedArgs.id}`,
|
|
475
|
+
tab: 'skip'
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
const message = `Set module '${parsedArgs.id}' control options`;
|
|
479
|
+
toolCallbacks.setToolStatus(toolId, {
|
|
480
|
+
content: message
|
|
481
|
+
});
|
|
482
|
+
return `${message}: ${JSON.stringify(parsedArgs)}`;
|
|
483
|
+
}
|
|
484
|
+
},
|
|
381
485
|
{
|
|
382
486
|
def: resourceTypeToolDef,
|
|
383
487
|
fn: async ({ args, toolId, workspace, toolCallbacks }) => {
|
|
@@ -584,10 +688,17 @@ When creating new steps, follow this process for EACH step:
|
|
|
584
688
|
|
|
585
689
|
### Special Step Types
|
|
586
690
|
For special step types, follow these additional steps:
|
|
587
|
-
- For forloop steps:
|
|
691
|
+
- For forloop steps:
|
|
692
|
+
- Set the iterator expression using set_forloop_iterator_expression
|
|
693
|
+
- Set advanced options (parallel, parallelism, skip_failures) using set_forloop_options
|
|
588
694
|
- For branchone steps: Set the predicates for each branch using set_branch_predicate
|
|
589
695
|
- For branchall steps: No additional setup needed
|
|
590
696
|
|
|
697
|
+
### Module Control Options
|
|
698
|
+
For any module type, you can set control flow options using set_module_control_options:
|
|
699
|
+
- **stop_after_if**: Early stop condition - stops the module if expression evaluates to true. Can use "flow_input" or "result". "result" is the result of the step. "results.<step_id>" is not supported, do not use it. Example: "flow_input.x > 10" or "result === "failure""
|
|
700
|
+
- **skip_if**: Skip condition - skips the module entirely if expression evaluates to true. Can use "flow_input" or "results.<step_id>". Example: "flow_input.x > 10" or "results.a === "failure""
|
|
701
|
+
|
|
591
702
|
### Step Insertion Rules
|
|
592
703
|
When adding steps, carefully consider the execution order:
|
|
593
704
|
1. Steps are executed in the order they appear in the flow definition, not in the order they were added
|
|
@@ -608,6 +719,7 @@ When adding steps, carefully consider the execution order:
|
|
|
608
719
|
### JavaScript Expressions
|
|
609
720
|
For step inputs, forloop iterator expressions and branch predicates, use JavaScript expressions with these variables:
|
|
610
721
|
- Step results: results.stepid or results.stepid.property_name
|
|
722
|
+
- Break condition (stop_after_if) in for loops: result (contains the result of the last iteration)
|
|
611
723
|
- Loop iterator: flow_input.iter.value (inside loops)
|
|
612
724
|
- Flow inputs: flow_input.property_name
|
|
613
725
|
- Static values: Use JavaScript syntax (e.g., "hello", true, 3)
|
|
@@ -616,6 +728,12 @@ Note: These variables are only accessible in step inputs, forloop iterator expre
|
|
|
616
728
|
|
|
617
729
|
For truly static values in step inputs (those not linked to previous steps or loop iterations), prefer using flow inputs by default unless explicitly specified otherwise. This makes the flow more configurable and reusable. For example, instead of hardcoding an email address in a step input, create a flow input for it.
|
|
618
730
|
|
|
731
|
+
### For Loop Advanced Options
|
|
732
|
+
When configuring for-loop steps, consider these options:
|
|
733
|
+
- **parallel: true** - Run iterations in parallel for independent operations (significantly faster for I/O bound tasks)
|
|
734
|
+
- **parallelism: N** - Limit concurrent iterations (only applies when parallel=true). Use to prevent overwhelming external APIs
|
|
735
|
+
- **skip_failures: true** - Continue processing remaining iterations even if some fail. Failed iterations return error objects as results
|
|
736
|
+
|
|
619
737
|
### Special Modules
|
|
620
738
|
- Preprocessor: Runs before the first step when triggered externally
|
|
621
739
|
- ID: 'preprocessor'
|
|
@@ -649,7 +767,7 @@ If the user wants a specific resource as step input, you should set the step val
|
|
|
649
767
|
content
|
|
650
768
|
};
|
|
651
769
|
}
|
|
652
|
-
export function prepareFlowUserMessage(instructions, flowAndSelectedId, selectedContext) {
|
|
770
|
+
export function prepareFlowUserMessage(instructions, flowAndSelectedId, selectedContext = []) {
|
|
653
771
|
const flow = flowAndSelectedId?.flow;
|
|
654
772
|
const selectedId = flowAndSelectedId?.selectedId;
|
|
655
773
|
// Handle context elements
|
|
@@ -662,7 +780,7 @@ ${instructions}`;
|
|
|
662
780
|
content: userMessage
|
|
663
781
|
};
|
|
664
782
|
}
|
|
665
|
-
const codePieces = selectedContext
|
|
783
|
+
const codePieces = selectedContext.filter((c) => c.type === 'flow_module_code_piece');
|
|
666
784
|
const flowModulesYaml = applyCodePiecesToFlowModules(codePieces, flow.value.modules);
|
|
667
785
|
let flowContent = `## FLOW:
|
|
668
786
|
flow_input schema:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type Writable } from 'svelte/store';
|
|
2
|
+
export type UiIntent = {
|
|
3
|
+
kind: 'open_module_tab';
|
|
4
|
+
componentId: string;
|
|
5
|
+
tab: string;
|
|
6
|
+
};
|
|
7
|
+
export declare const uiIntentStore: Writable<UiIntent | null>;
|
|
8
|
+
export declare function emitUiIntent(intent: UiIntent): void;
|