From ea30c3a026a2dbb7d26b6dc291417df267b2f4d0 Mon Sep 17 00:00:00 2001 From: Guilhem Date: Fri, 20 Jun 2025 15:39:24 +0100 Subject: [PATCH] feat(frontend): run steps from graph (#5915) --- frontend/src/lib/components/Dev.svelte | 7 +- .../src/lib/components/FlowBuilder.svelte | 26 +- .../lib/components/FlowPreviewContent.svelte | 9 +- .../src/lib/components/ModulePreview.svelte | 111 ++------ .../lib/components/ModulePreviewForm.svelte | 117 +++++---- .../ModulePreviewResultViewer.svelte | 6 +- frontend/src/lib/components/ModuleTest.svelte | 137 ++++++++++ .../components/common/button/Button.svelte | 4 +- .../lib/components/flows/FlowEditor.svelte | 14 +- .../flows/content/FlowEditorPanel.svelte | 8 +- .../flows/content/FlowModuleComponent.svelte | 21 +- .../flows/content/FlowModuleWrapper.svelte | 16 +- .../flows/header/FlowPreviewButtons.svelte | 11 +- .../flows/map/FlowModuleSchemaItem.svelte | 127 +++++++++- .../flows/map/FlowModuleSchemaMap.svelte | 8 +- .../lib/components/flows/map/MapItem.svelte | 12 +- .../components/flows/map/VirtualItem.svelte | 31 ++- .../flows/map/VirtualItemWrapper.svelte | 27 +- .../lib/components/flows/previousResults.ts | 52 +++- .../flows/propPicker/InputPickerInner.svelte | 66 +++++ .../flows/propPicker/OutputPicker.svelte | 239 ++++++++++++------ .../flows/propPicker/OutputPickerInner.svelte | 23 +- .../lib/components/flows/testSteps.svelte.ts | 183 ++++++++++++++ frontend/src/lib/components/flows/types.ts | 3 +- frontend/src/lib/components/flows/utils.ts | 39 ++- .../lib/components/graph/FlowGraphV2.svelte | 12 +- .../components/graph/graphBuilder.svelte.ts | 2 + .../graph/renderers/edges/BaseEdge.svelte | 2 +- .../graph/renderers/nodes/InputNode.svelte | 1 + .../graph/renderers/nodes/ModuleNode.svelte | 2 + frontend/src/lib/components/graph/util.ts | 2 +- .../components/meltComponents/Popover.svelte | 9 + .../propertyPicker/ObjectViewer.svelte | 19 +- frontend/src/routes/flows/dev/+page.svelte | 7 +- 34 files changed, 1060 insertions(+), 293 deletions(-) create mode 100644 frontend/src/lib/components/ModuleTest.svelte create mode 100644 frontend/src/lib/components/flows/propPicker/InputPickerInner.svelte create mode 100644 frontend/src/lib/components/flows/testSteps.svelte.ts diff --git a/frontend/src/lib/components/Dev.svelte b/frontend/src/lib/components/Dev.svelte index f226430d83..0ee118010f 100644 --- a/frontend/src/lib/components/Dev.svelte +++ b/frontend/src/lib/components/Dev.svelte @@ -48,6 +48,7 @@ import type { FlowPropPickerConfig, PropPickerContext } from './prop_picker' import type { PickableProperties } from './flows/previousResults' import { Triggers } from './triggers/triggers.svelte' + import { TestSteps } from './flows/testSteps.svelte' let flowCopilotContext: FlowCopilotContext = { shouldUpdatePropertyType: writable<{ @@ -436,7 +437,7 @@ const moving = writable<{ id: string } | undefined>(undefined) const history = initHistory(flowStore.val) - const testStepStore = writable>({}) + const testSteps = new TestSteps() const selectedIdStore = writable('settings-metadata') const triggersCount = writable(undefined) @@ -455,7 +456,7 @@ pathStore: writable(''), flowStateStore, flowStore, - testStepStore, + testSteps, saveDraft: () => {}, initialPathStore: writable(''), fakeInitialPath: '', @@ -715,7 +716,7 @@ noEditor on:applyArgs={(ev) => { if (ev.detail.kind === 'preprocessor') { - $testStepStore['preprocessor'] = ev.detail.args ?? {} + testSteps.setStepArgs('preprocessor', ev.detail.args ?? {}) $selectedIdStore = 'preprocessor' } else { previewArgsStore.val = ev.detail.args ?? {} diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index 4c1f766c77..cd09f9c4ee 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -72,6 +72,7 @@ } from './triggers/utils' import DraftTriggersConfirmationModal from './common/confirmationModal/DraftTriggersConfirmationModal.svelte' import { Triggers } from './triggers/triggers.svelte' + import { TestSteps } from './flows/testSteps.svelte' import { aiChatManager } from './copilot/chat/AIChatManager.svelte' interface Props { @@ -526,7 +527,7 @@ payloadData: undefined }) - const testStepStore = writable>({}) + const testSteps = new TestSteps() function select(selectedId: string) { selectedIdStore.set(selectedId) @@ -544,7 +545,7 @@ flowStateStore, flowStore, pathStore, - testStepStore, + testSteps, saveDraft, initialPathStore, fakeInitialPath, @@ -766,6 +767,9 @@ let flowPreviewButtons: FlowPreviewButtons | undefined = $state() + let forceTestTab: Record = $state({}) + let highlightArg: Record = $state({}) + run(() => { initialPathStore.set(initialPath) }) @@ -1020,7 +1024,7 @@ {newFlow} on:applyArgs={(ev) => { if (ev.detail.kind === 'preprocessor') { - $testStepStore['preprocessor'] = ev.detail.args ?? {} + testSteps.setStepArgs('preprocessor', ev.detail.args ?? {}) $selectedIdStore = 'preprocessor' } }} @@ -1028,8 +1032,24 @@ previewArgsStore.val = JSON.parse(JSON.stringify(e.detail)) flowPreviewButtons?.openPreview(true) }} + onTestUpTo={() => { + flowPreviewButtons?.testUpTo() + }} {savedFlow} onDeployTrigger={handleDeployTrigger} + onEditInput={(moduleId, key) => { + selectedIdStore.set(moduleId) + // Use new prop-based system + forceTestTab[moduleId] = true + highlightArg[moduleId] = key + // Reset the force flag after a short delay to allow re-triggering + setTimeout(() => { + forceTestTab[moduleId] = false + highlightArg[moduleId] = undefined + }, 500) + }} + {forceTestTab} + {highlightArg} /> {:else} Loading... diff --git a/frontend/src/lib/components/FlowPreviewContent.svelte b/frontend/src/lib/components/FlowPreviewContent.svelte index 638b63877f..c46f34b92d 100644 --- a/frontend/src/lib/components/FlowPreviewContent.svelte +++ b/frontend/src/lib/components/FlowPreviewContent.svelte @@ -379,7 +379,14 @@ id="flow-editor-test-flow-drawer" shortCut={{ Icon: CornerDownLeft }} > - Test flow + {#if previewMode == 'upTo'} + Test up to + + {$selectedId} + + {:else} + Test flow + {/if} {/if} diff --git a/frontend/src/lib/components/ModulePreview.svelte b/frontend/src/lib/components/ModulePreview.svelte index ffa1530fcf..93ea296ec6 100644 --- a/frontend/src/lib/components/ModulePreview.svelte +++ b/frontend/src/lib/components/ModulePreview.svelte @@ -1,22 +1,13 @@ - jobDone()} +
@@ -130,7 +57,7 @@
{#if testIsLoading} - @@ -139,7 +66,7 @@ color="dark" btnClasses="truncate" size="sm" - on:click={() => runTest(stepArgs)} + on:click={runTestWithStepArgs} shortCut={{ Icon: CornerDownLeft }} @@ -149,5 +76,5 @@ {/if}
- +
diff --git a/frontend/src/lib/components/ModulePreviewForm.svelte b/frontend/src/lib/components/ModulePreviewForm.svelte index 1825d82252..926c5ccff3 100644 --- a/frontend/src/lib/components/ModulePreviewForm.svelte +++ b/frontend/src/lib/components/ModulePreviewForm.svelte @@ -5,77 +5,91 @@ import { RefreshCw } from 'lucide-svelte' import ArgInput from './ArgInput.svelte' import { Button } from './common' - import { getContext, untrack } from 'svelte' + import { getContext, onMount, untrack } from 'svelte' import type { FlowEditorContext } from './flows/types' import { evalValue } from './flows/utils' import type { FlowModule } from '$lib/gen' import type { PickableProperties } from './flows/previousResults' import type SimpleEditor from './SimpleEditor.svelte' import { getResourceTypes } from './resourceTypesStore' + import { twMerge } from 'tailwind-merge' interface Props { schema: Schema | { properties?: Record; required?: string[] } - args?: Record mod: FlowModule pickableProperties: PickableProperties | undefined isValid?: boolean autofocus?: boolean + focusArg?: string } let { schema, - args = $bindable({}), mod, pickableProperties, isValid = $bindable(true), - autofocus = false + autofocus = false, + focusArg = undefined }: Props = $props() - const { testStepStore } = getContext('FlowEditorContext') + const { testSteps, flowStateStore, flowStore, previewArgs } = + getContext('FlowEditorContext') let inputCheck: { [id: string]: boolean } = $state({}) $effect(() => { isValid = allTrue(inputCheck) ?? false }) - $effect(() => { - if (args == undefined || typeof args !== 'object') { - args = {} - } - }) - - function removeExtraKey() { - const nargs = {} - Object.keys(args ?? {}).forEach((key) => { - if (keys.includes(key)) { - nargs[key] = args[key] - } - }) - args = nargs - } - let keys: string[] = $state([]) $effect(() => { let lkeys = Object.keys(schema?.properties ?? {}) if (schema?.properties && JSON.stringify(lkeys) != JSON.stringify(keys)) { keys = lkeys - untrack(() => removeExtraKey()) + untrack(() => testSteps?.removeExtraKey(mod.id, keys)) } }) function plugIt(argName: string) { - args[argName] = structuredClone( - $state.snapshot(evalValue(argName, mod, testStepStore, pickableProperties, true)) + testSteps?.setEvaluatedStepArg( + mod.id, + argName, + $state.snapshot(evalValue(argName, mod, pickableProperties, true)) ) - try { - editor?.[argName]?.setCode(JSON.stringify(args[argName], null, 4)) - } catch { - //ignore - } } let editor: Record = $state({}) + // Animation and highlighting for focusArg + let animateArg: string | undefined = $state(undefined) + $effect(() => { + if (focusArg) { + // Add a slight delay to ensure the form is rendered + setTimeout(() => { + const argElement = document.querySelector(`[data-arg="${focusArg}"]`) + if (argElement) { + // Add highlight animation + animateArg = focusArg + argElement.scrollIntoView({ behavior: 'smooth', block: 'center' }) + + // Focus the input if it exists + const input = argElement.querySelector('input, textarea, select') as + | HTMLInputElement + | HTMLTextAreaElement + | HTMLSelectElement + | null + if (input) { + input.focus() + } + + // Remove highlight after animation + setTimeout(() => { + animateArg = undefined + }, 2000) + } + }, 200) + } + }) + let resourceTypes: string[] | undefined = $state(undefined) async function loadResourceTypes() { @@ -83,21 +97,34 @@ } loadResourceTypes() + + let args = $state(>{}) + + onMount(() => { + testSteps?.updateStepArgs(mod.id, $flowStateStore, flowStore?.val, previewArgs?.val) + args = testSteps?.getStepArgs(mod.id) ?? { value: {} } + }) -
+
{#if keys.length > 0} {#each keys as argName, i (argName)} {#if Object.keys(schema.properties ?? {}).includes(argName)} -
- {#if typeof args == 'object' && schema?.properties?.[argName]} +
+ {#if typeof args.value == 'object' && schema?.properties?.[argName]} {/if} -
- -
+ {#if testSteps?.isArgManuallySet(mod.id, argName)} +
+ +
+ {/if}
{/if} {/each} diff --git a/frontend/src/lib/components/ModulePreviewResultViewer.svelte b/frontend/src/lib/components/ModulePreviewResultViewer.svelte index 70ade6a2a8..ec203979c9 100644 --- a/frontend/src/lib/components/ModulePreviewResultViewer.svelte +++ b/frontend/src/lib/components/ModulePreviewResultViewer.svelte @@ -41,7 +41,7 @@ onUpdateMock }: Props = $props() - const { testStepStore } = getContext('FlowEditorContext') + const { testSteps } = getContext('FlowEditorContext') let selectedJob: Job | undefined = $state(undefined) let fetchingLastJob = false @@ -49,7 +49,7 @@ let jobProgressReset: () => void = $state(() => {}) let nlastJob = $derived.by(() => { - if (testJob) { + if (testJob && testJob.type === 'CompletedJob') { return { ...testJob, preview: true } } if (lastJob) { @@ -90,7 +90,7 @@ {disableHistory} > {#snippet copilot_fix()} - {#if lang && editor && diffEditor && $testStepStore[mod.id] && selectedJob?.type === 'CompletedJob' && !selectedJob.success && getStringError(selectedJob.result)} + {#if lang && editor && diffEditor && testSteps.getStepArgs(mod.id) && selectedJob?.type === 'CompletedJob' && !selectedJob.success && getStringError(selectedJob.result)} {/if} {/snippet} diff --git a/frontend/src/lib/components/ModuleTest.svelte b/frontend/src/lib/components/ModuleTest.svelte new file mode 100644 index 0000000000..705ef031ca --- /dev/null +++ b/frontend/src/lib/components/ModuleTest.svelte @@ -0,0 +1,137 @@ + + + + + jobDone()} + bind:scriptProgress + bind:this={testJobLoader} + bind:isLoading={ + () => testModulesState[mod.id]?.loading ?? false, + (v) => + (testModulesState[mod.id] = { + ...testModulesState[mod.id], + loading: v ?? false, + instances: testModulesState[mod.id]?.instances ?? 0 + }) + } + bind:job={testJob} +/> diff --git a/frontend/src/lib/components/common/button/Button.svelte b/frontend/src/lib/components/common/button/Button.svelte index e342420233..f230d89798 100644 --- a/frontend/src/lib/components/common/button/Button.svelte +++ b/frontend/src/lib/components/common/button/Button.svelte @@ -49,6 +49,7 @@ portal?: string } | undefined = undefined + export let dropdownBtnClasses: string = '' type MenuItem = { label: string @@ -309,7 +310,8 @@ 'rounded-md m-0 p-0 center-center h-full', variant === 'border' ? 'border-0 border-r border-y ' : 'border-0', 'rounded-r-md !rounded-l-none', - size === 'xs2' ? '!w-8' : '!w-10' + size === 'xs2' ? '!w-8' : '!w-10', + dropdownBtnClasses )} > diff --git a/frontend/src/lib/components/flows/FlowEditor.svelte b/frontend/src/lib/components/flows/FlowEditor.svelte index 2fcac3854c..d9bce7c83d 100644 --- a/frontend/src/lib/components/flows/FlowEditor.svelte +++ b/frontend/src/lib/components/flows/FlowEditor.svelte @@ -32,6 +32,10 @@ }) | undefined onDeployTrigger?: (trigger: Trigger) => void + onTestUpTo?: ((id: string) => void) | undefined + onEditInput?: ((moduleId: string, key: string) => void) | undefined + forceTestTab?: Record + highlightArg?: Record } let { @@ -44,7 +48,11 @@ smallErrorHandler = false, newFlow = false, savedFlow = undefined, - onDeployTrigger = () => {} + onDeployTrigger = () => {}, + onTestUpTo = undefined, + onEditInput = undefined, + forceTestTab, + highlightArg }: Props = $props() let flowModuleSchemaMap: FlowModuleSchemaMap | undefined = $state() @@ -93,6 +101,8 @@ } aiChatManager.generateStep(detail.moduleId, detail.lang, detail.instructions) }} + {onTestUpTo} + {onEditInput} /> {/if}
@@ -113,6 +123,8 @@ on:applyArgs on:testWithArgs {onDeployTrigger} + {forceTestTab} + {highlightArg} /> {/if} diff --git a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte index 030d0792a1..41b3d15827 100644 --- a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte +++ b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte @@ -25,6 +25,8 @@ }) | undefined onDeployTrigger?: (trigger: Trigger) => void + forceTestTab?: Record + highlightArg?: Record } let { @@ -33,7 +35,9 @@ newFlow = false, disabledFlowInputs = false, savedFlow = undefined, - onDeployTrigger = () => {} + onDeployTrigger = () => {}, + forceTestTab, + highlightArg }: Props = $props() const { @@ -138,6 +142,8 @@ previousModule={flowStore.val.value.modules[index - 1]} {enableAi} savedModule={savedFlow?.value.modules[index]} + {forceTestTab} + {highlightArg} /> {/each} {/key} diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte index c613b56ed9..70ffe5a3ec 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte @@ -77,6 +77,8 @@ noEditor: boolean enableAi: boolean savedModule?: FlowModule | undefined + forceTestTab?: boolean + highlightArg?: string } let { @@ -89,7 +91,9 @@ scriptTemplate = 'script', noEditor, enableAi, - savedModule = undefined + savedModule = undefined, + forceTestTab = false, + highlightArg = undefined }: Props = $props() let tag: string | undefined = $state(undefined) @@ -274,6 +278,20 @@ onDestroy(() => { $currentEditor = undefined }) + + // Handle force test tab prop with animation + $effect(() => { + if (forceTestTab) { + selected = 'test' + // Add a smooth transition to the test tab + setTimeout(() => { + const testTab = document.querySelector('[value="test"]') + if (testTab) { + testTab.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) + } + }, 100) + } + }) @@ -525,6 +543,7 @@ bind:testJob bind:testIsLoading bind:scriptProgress + focusArg={highlightArg} /> {:else if selected === 'advanced'} diff --git a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte index fa3116631e..a61d1dbdb9 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte @@ -40,6 +40,8 @@ parentModule?: FlowModule | undefined // Pointer to previous module, for easy access to testing results previousModule?: FlowModule | undefined + forceTestTab?: Record + highlightArg?: Record } let { @@ -48,7 +50,9 @@ enableAi = false, savedModule = undefined, parentModule = $bindable(), - previousModule = undefined + previousModule = undefined, + forceTestTab, + highlightArg }: Props = $props() function initializePrimaryScheduleForTriggerScript(module: FlowModule) { @@ -196,6 +200,8 @@ {scriptTemplate} {enableAi} {savedModule} + forceTestTab={forceTestTab?.[flowModule.id]} + highlightArg={highlightArg?.[flowModule.id]} /> {/if} {:else if flowModule.value.type === 'forloopflow' || flowModule.value.type == 'whileloopflow'} @@ -210,6 +216,8 @@ ? savedModule.value.modules[index] : undefined} {enableAi} + {forceTestTab} + {highlightArg} /> {/each} {:else if flowModule.value.type === 'branchone'} @@ -229,6 +237,8 @@ ? savedModule.value.default[index] : undefined} {enableAi} + {forceTestTab} + {highlightArg} /> {/each} {/if} @@ -252,6 +262,8 @@ ? savedModule.value.branches[branchIndex]?.modules[index] : undefined} {enableAi} + {forceTestTab} + {highlightArg} /> {/each} {/if} @@ -271,6 +283,8 @@ savedModule={savedModule?.value.type === 'branchall' ? savedModule.value.branches[branchIndex]?.modules[index] : undefined} + {forceTestTab} + {highlightArg} /> {/each} {/if} diff --git a/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte b/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte index f053c6cb62..c3e80d836f 100644 --- a/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte +++ b/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte @@ -70,6 +70,12 @@ 'triggers' ].includes($selectedId) || $selectedId?.includes('branch') + + export function testUpTo() { + if (upToDisabled) return + previewMode = 'upTo' + previewOpen = true + } {#if !upToDisabled} @@ -78,10 +84,7 @@ disabled={upToDisabled} color="light" variant="border" - on:click={() => { - previewMode = 'upTo' - previewOpen = !previewOpen - }} + on:click={testUpTo} startIcon={{ icon: Play }} > Test up to  diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte index e0f8e3e17b..9b05735105 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte @@ -14,7 +14,9 @@ Square, SkipForward, Pin, - X + X, + Play, + Loader2 } from 'lucide-svelte' import { createEventDispatcher, getContext, untrack } from 'svelte' import { fade } from 'svelte/transition' @@ -23,6 +25,7 @@ import { twMerge } from 'tailwind-merge' import IdEditorInput from '$lib/components/IdEditorInput.svelte' import { dfs } from '../dfs' + import { dfs as dfsPreviousResults } from '../previousResults' import { Drawer } from '$lib/components/common' import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte' import { getDependeeAndDependentComponents } from '../flowExplorer' @@ -32,6 +35,8 @@ import OutputPicker from '$lib/components/flows/propPicker/OutputPicker.svelte' import OutputPickerInner from '$lib/components/flows/propPicker/OutputPickerInner.svelte' import type { FlowState } from '$lib/components/flows/flowState' + import { Button } from '$lib/components/common' + import ModuleTest from '$lib/components/ModuleTest.svelte' interface Props { selected?: boolean @@ -63,7 +68,10 @@ alwaysShowOutputPicker?: boolean loopStatus?: { type: 'inside' | 'self'; flow: 'forloopflow' | 'whileloopflow' } | undefined icon?: import('svelte').Snippet + onTestUpTo?: ((id: string) => void) | undefined + inputTransform?: Record | undefined onUpdateMock?: (mock: { enabled: boolean; return_value?: unknown }) => void + onEditInput?: (moduleId: string, key: string) => void } let { @@ -91,7 +99,10 @@ alwaysShowOutputPicker = false, loopStatus = undefined, icon, - onUpdateMock + onTestUpTo, + inputTransform, + onUpdateMock, + onEditInput }: Props = $props() let pickableIds: Record | undefined = $state(undefined) @@ -116,11 +127,15 @@ let newId: string = $state(id ?? '') + let moduleTest: ModuleTest | undefined = $state(undefined) + let testIsLoading = $state(false) let hover = $state(false) let connectingData: any | undefined = $state(undefined) let lastJob: any | undefined = $state(undefined) let outputPicker: OutputPicker | undefined = $state(undefined) let historyOpen = $state(false) + let testJob: any | undefined = $state(undefined) + let outputPickerBarOpen = $state(false) let flowStateStore = $derived(flowEditorContext?.flowStateStore) @@ -158,10 +173,24 @@ flowStateStore && $flowStateStore && untrack(() => updateLastJob($flowStateStore)) }) + let nlastJob = $derived.by(() => { + if (testJob) { + return { ...testJob, preview: true } + } + if (lastJob) { + return { ...lastJob, preview: false } + } + return undefined + }) + let isConnectingCandidate = $derived( !!id && !!$flowPropPickerConfig && !!pickableIds && Object.keys(pickableIds).includes(id) ) + const outputPickerVisible = $derived( + editMode && (isConnectingCandidate || alwaysShowOutputPicker) && !!id + ) + const icon_render = $derived(icon) @@ -223,22 +252,36 @@ {/if} +{#if deletable && id && flowEditorContext?.flowStore && outputPickerVisible} + {@const flowStore = flowEditorContext?.flowStore.val} + {@const mod = flowStore?.value ? dfsPreviousResults(id, flowStore, false)[0] : undefined} + {#if mod && $flowStateStore[id]} + + {/if} +{/if} +
(hover = true)} onmouseleave={() => (hover = false)} onpointerdown={stopPropagation(preventDefault(() => dispatch('pointerdown')))} > -
+
+
{#if retry}
- {#if editMode && (isConnectingCandidate || alwaysShowOutputPicker)} + {#if outputPickerVisible} {#snippet children({ allowCopy, isConnecting, selectConnection })} {/snippet} @@ -389,10 +438,60 @@
{#if deletable} +
+ {#if (hover || selected) && outputPickerVisible} +
+ {#if !testIsLoading} + + {:else} + + {/if} +
+ {/if} +
+ {/if} + + {/if} +{/snippet} + +{#snippet editKey(key: string)} + +{/snippet} diff --git a/frontend/src/lib/components/flows/propPicker/OutputPicker.svelte b/frontend/src/lib/components/flows/propPicker/OutputPicker.svelte index 864602718f..b62326352f 100644 --- a/frontend/src/lib/components/flows/propPicker/OutputPicker.svelte +++ b/frontend/src/lib/components/flows/propPicker/OutputPicker.svelte @@ -1,10 +1,12 @@ - { +
{ e.preventDefault() e.stopPropagation() }} - bind:this={popover} - allowFullScreen - contentClasses="overflow-hidden resize rounded-md" - contentStyle={`width: calc(${MIN_WIDTH}px); min-width: calc(${MIN_WIDTH}px); height: calc(${MIN_HEIGHT}px); min-height: calc(${MIN_HEIGHT}px);`} - extraProps={{ 'data-prop-picker': true }} - closeOnOtherPopoverOpen - class="outline-none" > - {#snippet trigger({ isOpen })} -
{ - e.preventDefault() - e.stopPropagation() - }} - data-prop-picker - title={`${isOpen ? 'Close' : 'Open'} step output`} - > -
- +
+
+
+ {#if showInput} + { + e.preventDefault() + e.stopPropagation() + }} + allowFullScreen + contentClasses="overflow-hidden resize" + contentStyle={`width: calc(${MIN_WIDTH}px); min-width: calc(${MIN_WIDTH}px); height: calc(${MIN_HEIGHT}px); min-height: calc(${MIN_HEIGHT}px); `} + extraProps={{ 'data-prop-picker': true }} + closeOnOtherPopoverOpen + disableFocusTrap + class="flex-1 h-full" + bind:isOpen={inputOpen} + bind:this={inputPopover} > -
+ In + + {/snippet} + {#snippet content()} + + {/snippet} + + {/if} + { + e.preventDefault() + e.stopPropagation() + }} + bind:this={popover} + allowFullScreen + contentClasses="overflow-hidden resize" + contentStyle={`width: calc(${MIN_WIDTH}px); min-width: calc(${MIN_WIDTH}px); height: calc(${MIN_HEIGHT}px); min-height: calc(${MIN_HEIGHT}px); `} + extraProps={{ 'data-prop-picker': true }} + closeOnOtherPopoverOpen + class="flex-1 h-full" + bind:isOpen={outputOpen} + > + {#snippet trigger({ isOpen })} + - -
- -
+ + + {/snippet} + {#snippet content()} + {@render children?.({ + allowCopy: !$flowPropPickerConfig, + isConnecting: showConnecting, + selectConnection + })} + {/snippet} +
- {/snippet} - {#snippet content()} - {@render children?.({ - allowCopy: !$flowPropPickerConfig, - isConnecting: showConnecting, - selectConnection - })} - {/snippet} - +
+