mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-09 08:03:50 +00:00
fix(frontend): fix test step behavior (#6427)
* fix flowStateStore val * handle run preview multiple keyboard actions * Synchronise input args and prview args * Fix arg update one step load * fix input ste manually not reseted after preview * rename test steps to stepsInputArgs * simplify job result update * fix job preview logic * fix import * nit * clean * fix test job not displaying when data is pinned * remove job history loader display delay * nit * nit * add error handler to steps input args comparison function * prevent result node to display connection
This commit is contained in:
@@ -48,7 +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'
|
||||
import { StepsInputArgs } from './flows/stepsInputArgs.svelte'
|
||||
import { ModulesTestStates } from './modulesTest.svelte'
|
||||
import type { GraphModuleState } from './graph'
|
||||
|
||||
@@ -457,7 +457,7 @@
|
||||
const scriptEditorDrawer = writable(undefined)
|
||||
const moving = writable<{ id: string } | undefined>(undefined)
|
||||
const history = initHistory(flowStore.val)
|
||||
const testSteps = new TestSteps()
|
||||
const stepsInputArgs = new StepsInputArgs()
|
||||
const selectedIdStore = writable('settings-metadata')
|
||||
const triggersCount = writable<TriggersCount | undefined>(undefined)
|
||||
const modulesTestStates = new ModulesTestStates((moduleId) => {
|
||||
@@ -481,7 +481,7 @@
|
||||
pathStore: writable(''),
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
testSteps,
|
||||
stepsInputArgs,
|
||||
saveDraft: () => {},
|
||||
initialPathStore: writable(''),
|
||||
fakeInitialPath: '',
|
||||
@@ -806,7 +806,7 @@
|
||||
noEditor
|
||||
on:applyArgs={(ev) => {
|
||||
if (ev.detail.kind === 'preprocessor') {
|
||||
testSteps.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
stepsInputArgs.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
$selectedIdStore = 'preprocessor'
|
||||
} else {
|
||||
previewArgsStore.val = ev.detail.args ?? {}
|
||||
@@ -818,6 +818,7 @@
|
||||
isOwner={flowPreviewContent?.getIsOwner()}
|
||||
{suspendStatus}
|
||||
onOpenDetails={flowPreviewButtons?.openPreview}
|
||||
previewOpen={flowPreviewButtons?.getPreviewOpen()}
|
||||
/>
|
||||
{/key}
|
||||
</Pane>
|
||||
|
||||
@@ -77,7 +77,7 @@
|
||||
} from './triggers/utils'
|
||||
import DraftTriggersConfirmationModal from './common/confirmationModal/DraftTriggersConfirmationModal.svelte'
|
||||
import { Triggers } from './triggers/triggers.svelte'
|
||||
import { TestSteps } from './flows/testSteps.svelte'
|
||||
import { StepsInputArgs } from './flows/stepsInputArgs.svelte'
|
||||
import { aiChatManager } from './copilot/chat/AIChatManager.svelte'
|
||||
import type { GraphModuleState } from './graph'
|
||||
import {
|
||||
@@ -571,7 +571,7 @@
|
||||
payloadData: undefined
|
||||
})
|
||||
|
||||
const testSteps = new TestSteps()
|
||||
const stepsInputArgs = new StepsInputArgs()
|
||||
|
||||
function select(selectedId: string) {
|
||||
selectedIdStore.set(selectedId)
|
||||
@@ -592,7 +592,7 @@
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
pathStore,
|
||||
testSteps,
|
||||
stepsInputArgs,
|
||||
saveDraft,
|
||||
initialPathStore,
|
||||
fakeInitialPath,
|
||||
@@ -1129,6 +1129,8 @@
|
||||
bind:this={flowPreviewButtons}
|
||||
{loading}
|
||||
onRunPreview={() => {
|
||||
// Reset manually edited args inputs when running a preview
|
||||
stepsInputArgs.resetManuallyEditedArgs()
|
||||
modulesTestStates.hideJobsInGraph()
|
||||
localModuleStates = {}
|
||||
showJobStatus = true
|
||||
@@ -1170,7 +1172,7 @@
|
||||
{newFlow}
|
||||
on:applyArgs={(ev) => {
|
||||
if (ev.detail.kind === 'preprocessor') {
|
||||
testSteps.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
stepsInputArgs.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
$selectedIdStore = 'preprocessor'
|
||||
}
|
||||
}}
|
||||
@@ -1218,6 +1220,7 @@
|
||||
delete modulesTestStates.states[id]
|
||||
}}
|
||||
{flowHasChanged}
|
||||
previewOpen={flowPreviewButtons?.getPreviewOpen()}
|
||||
/>
|
||||
{:else}
|
||||
<CenteredPage>Loading...</CenteredPage>
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
jobId = await runFlowPreview(args, newFlow, $pathStore, restartedFrom)
|
||||
isRunning = true
|
||||
if (inputSelected) {
|
||||
savedArgs = previewArgs.val
|
||||
savedArgs = $state.snapshot(previewArgs.val)
|
||||
inputSelected = undefined
|
||||
}
|
||||
onRunPreview?.()
|
||||
@@ -166,7 +166,7 @@
|
||||
if (preventEscape) {
|
||||
selectInput(undefined)
|
||||
event.preventDefault()
|
||||
event.stopPropagation
|
||||
event.stopPropagation()
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -506,7 +506,7 @@
|
||||
schema={flowStore.val.schema}
|
||||
bind:args={previewArgs.val}
|
||||
on:change={() => {
|
||||
savedArgs = previewArgs.val
|
||||
savedArgs = $state.snapshot(previewArgs.val)
|
||||
}}
|
||||
bind:isValid
|
||||
helperScript={flowStore.val.schema?.['x-windmill-dyn-select-code'] &&
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
noEditor?: boolean
|
||||
scriptProgress?: any
|
||||
focusArg?: string
|
||||
onJobDone?: () => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -28,7 +29,8 @@
|
||||
testIsLoading = $bindable(false),
|
||||
noEditor = false,
|
||||
scriptProgress = $bindable(undefined),
|
||||
focusArg = undefined
|
||||
focusArg = undefined,
|
||||
onJobDone
|
||||
}: Props = $props()
|
||||
|
||||
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
@@ -46,6 +48,7 @@
|
||||
bind:testIsLoading
|
||||
bind:scriptProgress
|
||||
bind:this={moduleTest}
|
||||
{onJobDone}
|
||||
/>
|
||||
|
||||
<div class="p-4">
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
focusArg = undefined
|
||||
}: Props = $props()
|
||||
|
||||
const { testSteps, flowStateStore, flowStore, previewArgs } =
|
||||
const { stepsInputArgs, flowStateStore, flowStore, previewArgs } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let inputCheck: { [id: string]: boolean } = $state({})
|
||||
@@ -45,12 +45,12 @@
|
||||
let lkeys = Object.keys(schema?.properties ?? {})
|
||||
if (schema?.properties && JSON.stringify(lkeys) != JSON.stringify(keys)) {
|
||||
keys = lkeys
|
||||
untrack(() => testSteps?.removeExtraKey(mod.id, keys))
|
||||
untrack(() => stepsInputArgs?.removeExtraKey(mod.id, keys))
|
||||
}
|
||||
})
|
||||
|
||||
function plugIt(argName: string) {
|
||||
testSteps?.setEvaluatedStepArg(
|
||||
stepsInputArgs?.setEvaluatedStepArg(
|
||||
mod.id,
|
||||
argName,
|
||||
$state.snapshot(evalValue(argName, mod, pickableProperties, true))
|
||||
@@ -102,8 +102,8 @@
|
||||
|
||||
$effect.pre(() => {
|
||||
if (!initialized) {
|
||||
if (testSteps) {
|
||||
testSteps?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
if (stepsInputArgs) {
|
||||
stepsInputArgs?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
initialized = true
|
||||
}
|
||||
}
|
||||
@@ -130,8 +130,8 @@
|
||||
label={argName}
|
||||
description={schema.properties[argName].description}
|
||||
bind:value={
|
||||
() => testSteps?.getStepInputArgs(mod.id, argName),
|
||||
(v) => testSteps?.setStepInputArgs(mod.id, argName, v)
|
||||
() => stepsInputArgs?.getStepInputArgs(mod.id, argName),
|
||||
(v) => stepsInputArgs?.setStepInputArgs(mod.id, argName, v)
|
||||
}
|
||||
type={schema.properties[argName].type}
|
||||
oneOf={schema.properties[argName].oneOf}
|
||||
@@ -152,7 +152,7 @@
|
||||
placeholder={schema.properties[argName].placeholder}
|
||||
/>
|
||||
{/if}
|
||||
{#if testSteps?.isArgManuallySet(mod.id, argName)}
|
||||
{#if stepsInputArgs?.isArgManuallySet(mod.id, argName)}
|
||||
<div class="pt-6 mt-0.5">
|
||||
<Button
|
||||
on:click={() => {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { type Script, type Job, type FlowModule } from '$lib/gen'
|
||||
import OutputPickerInner from '$lib/components/flows/propPicker/OutputPickerInner.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import type { FlowEditorContext } from './flows/types'
|
||||
import type { FlowEditorContext, OutputViewerJob } from './flows/types'
|
||||
import { getContext } from 'svelte'
|
||||
import { getStringError } from './copilot/chat/utils'
|
||||
import AiAgentLogViewer from './AIAgentLogViewer.svelte'
|
||||
@@ -17,9 +17,8 @@
|
||||
editor: Editor | undefined
|
||||
diffEditor: DiffEditor | undefined
|
||||
loopStatus?: { type: 'inside' | 'self'; flow: 'forloopflow' | 'whileloopflow' } | undefined
|
||||
lastJob?: Job | undefined
|
||||
testJob?: Job & { result_stream?: string }
|
||||
scriptProgress?: number | undefined
|
||||
testJob?: Job | undefined
|
||||
mod: FlowModule
|
||||
testIsLoading?: boolean
|
||||
disableMock?: boolean
|
||||
@@ -34,7 +33,6 @@
|
||||
editor,
|
||||
diffEditor,
|
||||
loopStatus = undefined,
|
||||
lastJob = undefined,
|
||||
scriptProgress = $bindable(undefined),
|
||||
testJob = undefined,
|
||||
mod,
|
||||
@@ -46,21 +44,20 @@
|
||||
tagLabel = undefined
|
||||
}: Props = $props()
|
||||
|
||||
const { testSteps } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const { stepsInputArgs } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let selectedJob: Job | undefined = $state(undefined)
|
||||
let preview: 'mock' | 'job' | undefined = $state(undefined)
|
||||
let jobProgressReset: () => void = $state(() => {})
|
||||
|
||||
$effect(() => {
|
||||
if (preview != undefined && testJob) {
|
||||
preview = undefined
|
||||
}
|
||||
})
|
||||
|
||||
let forceJson = $state(false)
|
||||
let outputPickerInner: OutputPickerInner | undefined = $state(undefined)
|
||||
export function getOutputPickerInner() {
|
||||
return outputPickerInner
|
||||
}
|
||||
|
||||
const selectedJob: OutputViewerJob = $derived.by(
|
||||
() => outputPickerInner?.getSelectedJob?.() ?? undefined
|
||||
)
|
||||
const logJob = $derived(testJob ?? selectedJob)
|
||||
const preview = $derived.by(() => outputPickerInner?.getPreview?.())
|
||||
</script>
|
||||
|
||||
<Splitpanes horizontal>
|
||||
@@ -75,7 +72,6 @@
|
||||
{/if}
|
||||
|
||||
<OutputPickerInner
|
||||
{lastJob}
|
||||
{testJob}
|
||||
fullResult
|
||||
moduleId={mod.id}
|
||||
@@ -83,24 +79,22 @@
|
||||
getLogs
|
||||
{onUpdateMock}
|
||||
mock={mod.mock}
|
||||
bind:forceJson
|
||||
bind:selectedJob
|
||||
isLoading={testIsLoading || loadingJob}
|
||||
bind:preview
|
||||
path={`path` in mod.value ? mod.value.path : ''}
|
||||
{loopStatus}
|
||||
{disableMock}
|
||||
{disableHistory}
|
||||
bind:this={outputPickerInner}
|
||||
>
|
||||
{#snippet copilot_fix()}
|
||||
{#if lang && editor && diffEditor && testSteps.getStepArgs(mod.id) && selectedJob?.type === 'CompletedJob' && !selectedJob.success && getStringError(selectedJob.result)}
|
||||
{#if lang && editor && diffEditor && stepsInputArgs.getStepArgs(mod.id) && selectedJob?.type === 'CompletedJob' && !selectedJob.success && getStringError(selectedJob.result)}
|
||||
<ScriptFix {lang} />
|
||||
{/if}
|
||||
{/snippet}
|
||||
</OutputPickerInner>
|
||||
</Pane>
|
||||
<Pane size={35} minSize={10}>
|
||||
{#if (mod.mock?.enabled && preview != 'job') || preview == 'mock'}
|
||||
{#if (mod.mock?.enabled && preview !== 'job' && testJob?.type !== 'QueuedJob') || preview === 'mock'}
|
||||
<LogViewer
|
||||
small
|
||||
content={undefined}
|
||||
@@ -124,9 +118,9 @@
|
||||
jobId={logJob?.id}
|
||||
duration={logJob?.['duration_ms']}
|
||||
mem={logJob?.['mem_peak']}
|
||||
content={logJob?.logs}
|
||||
content={logJob?.['logs']}
|
||||
isLoading={(testIsLoading && logJob?.['running'] == false) || loadingJob}
|
||||
tag={logJob?.tag}
|
||||
tag={logJob?.['tag']}
|
||||
{tagLabel}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
testIsLoading?: boolean
|
||||
noEditor?: boolean
|
||||
scriptProgress?: any
|
||||
onJobDone?: () => void
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -21,10 +22,11 @@
|
||||
testJob = $bindable(undefined),
|
||||
testIsLoading = $bindable(false),
|
||||
noEditor = false,
|
||||
scriptProgress = $bindable(undefined)
|
||||
scriptProgress = $bindable(undefined),
|
||||
onJobDone
|
||||
}: Props = $props()
|
||||
|
||||
const { flowStore, flowStateStore, pathStore, testSteps, previewArgs, modulesTestStates } =
|
||||
const { flowStore, flowStateStore, pathStore, stepsInputArgs, previewArgs, modulesTestStates } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let jobLoader: JobLoader | undefined = $state(undefined)
|
||||
@@ -32,12 +34,12 @@
|
||||
let stepHistoryLoader = getStepHistoryLoaderContext()
|
||||
|
||||
export function runTestWithStepArgs() {
|
||||
runTest(testSteps.getStepArgs(mod.id))
|
||||
runTest(stepsInputArgs.getStepArgs(mod.id))
|
||||
}
|
||||
|
||||
export function loadArgsAndRunTest() {
|
||||
testSteps?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
runTest(testSteps.getStepArgs(mod.id))
|
||||
stepsInputArgs?.updateStepArgs(mod.id, flowStateStore.val, flowStore?.val, previewArgs?.val)
|
||||
runTest(stepsInputArgs.getStepArgs(mod.id))
|
||||
}
|
||||
|
||||
export async function runTest(args: any) {
|
||||
@@ -138,6 +140,7 @@
|
||||
if (modulesTestStates.states[mod.id]) {
|
||||
modulesTestStates.states[mod.id].testJob = testJob
|
||||
}
|
||||
onJobDone?.()
|
||||
}
|
||||
|
||||
export function cancelJob() {
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
onDelete?: (id: string) => void
|
||||
flowHasChanged?: boolean
|
||||
previewOpen: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -91,7 +92,8 @@
|
||||
job,
|
||||
suspendStatus,
|
||||
onDelete,
|
||||
flowHasChanged
|
||||
flowHasChanged,
|
||||
previewOpen
|
||||
}: Props = $props()
|
||||
|
||||
let flowModuleSchemaMap: FlowModuleSchemaMap | undefined = $state()
|
||||
@@ -206,6 +208,7 @@
|
||||
{isOwner}
|
||||
{suspendStatus}
|
||||
onOpenDetails={onOpenPreview}
|
||||
{previewOpen}
|
||||
/>
|
||||
{/if}
|
||||
</Pane>
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
isOwner?: boolean
|
||||
suspendStatus?: StateStore<Record<string, { job: Job; nb: number }>>
|
||||
onOpenDetails?: () => void
|
||||
previewOpen?: boolean
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -49,7 +50,8 @@
|
||||
job,
|
||||
isOwner,
|
||||
suspendStatus,
|
||||
onOpenDetails
|
||||
onOpenDetails,
|
||||
previewOpen = false
|
||||
}: Props = $props()
|
||||
|
||||
const {
|
||||
@@ -95,6 +97,7 @@
|
||||
}}
|
||||
on:applyArgs
|
||||
{onTestFlow}
|
||||
{previewOpen}
|
||||
/>
|
||||
{:else if $selectedId === 'Result'}
|
||||
<FlowResult {noEditor} {job} {isOwner} {suspendStatus} {onOpenDetails} />
|
||||
|
||||
@@ -48,9 +48,10 @@
|
||||
noEditor: boolean
|
||||
disabled: boolean
|
||||
onTestFlow?: () => void
|
||||
previewOpen: boolean
|
||||
}
|
||||
|
||||
let { noEditor, disabled, onTestFlow }: Props = $props()
|
||||
let { noEditor, disabled, onTestFlow, previewOpen }: Props = $props()
|
||||
const {
|
||||
flowStore,
|
||||
previewArgs,
|
||||
@@ -63,7 +64,6 @@
|
||||
let addPropertyV2: AddPropertyV2 | undefined = $state(undefined)
|
||||
let previewSchema: Record<string, any> | undefined = $state(undefined)
|
||||
let payloadData: Record<string, any> | undefined = undefined
|
||||
let previewArguments: Record<string, any> | undefined = $state(previewArgs.val)
|
||||
let dropdownItems: Array<{
|
||||
label: string
|
||||
onClick: () => void
|
||||
@@ -195,7 +195,9 @@
|
||||
|
||||
function handleKeydown(event: KeyboardEvent) {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
|
||||
runPreview()
|
||||
if (!previewOpen) {
|
||||
runPreview()
|
||||
}
|
||||
} else if (event.key === 'Enter' && previewSchema && !preventEnter) {
|
||||
applySchemaAndArgs()
|
||||
connectFirstNode()
|
||||
@@ -205,9 +207,6 @@
|
||||
}
|
||||
|
||||
function runPreview() {
|
||||
if (previewArguments) {
|
||||
previewArgs.val = structuredClone($state.snapshot(previewArguments))
|
||||
}
|
||||
onTestFlow?.()
|
||||
}
|
||||
|
||||
@@ -248,8 +247,8 @@
|
||||
|
||||
async function applySchemaAndArgs() {
|
||||
flowStore.val.schema = applyDiff(flowStore.val.schema, diff)
|
||||
if (previewArguments) {
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArguments))
|
||||
if (previewArgs.val) {
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArgs.val))
|
||||
}
|
||||
updatePreviewSchemaAndArgs(undefined)
|
||||
if ($flowInputEditorState) {
|
||||
@@ -259,11 +258,13 @@
|
||||
|
||||
function updatePreviewArguments(payloadData: Record<string, any> | undefined) {
|
||||
if (!payloadData) {
|
||||
previewArguments = savedPreviewArgs
|
||||
if (savedPreviewArgs) {
|
||||
previewArgs.val = savedPreviewArgs
|
||||
}
|
||||
return
|
||||
}
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArguments))
|
||||
previewArguments = structuredClone($state.snapshot(payloadData))
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArgs.val))
|
||||
previewArgs.val = structuredClone($state.snapshot(payloadData))
|
||||
}
|
||||
|
||||
let tabButtonWidth = 0
|
||||
@@ -372,7 +373,7 @@
|
||||
displayWebhookWarning
|
||||
editTab={$flowInputEditorState?.selectedTab}
|
||||
{previewSchema}
|
||||
bind:args={previewArguments}
|
||||
bind:args={previewArgs.val}
|
||||
bind:editPanelSize={
|
||||
() => {
|
||||
return editPanelSize
|
||||
@@ -399,9 +400,8 @@
|
||||
}}
|
||||
shouldDispatchChanges={true}
|
||||
on:change={() => {
|
||||
previewArguments = previewArguments
|
||||
if (!previewSchema) {
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArguments))
|
||||
savedPreviewArgs = structuredClone($state.snapshot(previewArgs.val))
|
||||
}
|
||||
refreshStateStore(flowStore)
|
||||
}}
|
||||
@@ -560,7 +560,7 @@
|
||||
on:isEditing={(e) => {
|
||||
preventEnter = e.detail
|
||||
}}
|
||||
previewArgs={previewArguments}
|
||||
previewArgs={previewArgs.val}
|
||||
{isValid}
|
||||
limitPayloadSize
|
||||
bind:this={savedInputsPicker}
|
||||
@@ -583,7 +583,7 @@
|
||||
on:select={(e) => {
|
||||
updatePreviewSchemaAndArgs(e.detail ?? undefined)
|
||||
}}
|
||||
selected={!!previewArguments}
|
||||
selected={!!previewArgs.val}
|
||||
bind:this={jsonInputs}
|
||||
/>
|
||||
</FlowInputEditor>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { loadSchemaFromModule } from '../flowInfers'
|
||||
import FlowModuleSkip from './FlowModuleSkip.svelte'
|
||||
import { type Job, JobService } from '$lib/gen'
|
||||
import { type Job } from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { checkIfParentLoop } from '../utils'
|
||||
import ModulePreviewResultViewer from '$lib/components/ModulePreviewResultViewer.svelte'
|
||||
@@ -120,7 +120,6 @@
|
||||
let s3Kind = $state('s3_client')
|
||||
let validCode = $state(true)
|
||||
let width = $state(1200)
|
||||
let lastJob: Job | undefined = $state(undefined)
|
||||
let testJob: Job | undefined = $state(undefined)
|
||||
let testIsLoading = $state(false)
|
||||
let scriptProgress = $state(undefined)
|
||||
@@ -197,43 +196,10 @@
|
||||
let editorSettingsPanelSize = $state(100 - untrack(() => editorPanelSize))
|
||||
let stepHistoryLoader = getStepHistoryLoaderContext()
|
||||
|
||||
let lastJobId: string | undefined = undefined
|
||||
|
||||
function onSelectedIdChange() {
|
||||
if (!flowStateStore?.val?.[$selectedId]?.schema && flowModule) {
|
||||
reload(flowModule)
|
||||
}
|
||||
lastJobId = undefined
|
||||
}
|
||||
|
||||
async function getLastJob() {
|
||||
if (
|
||||
!flowStateStore ||
|
||||
!flowModule.id ||
|
||||
flowStateStore.val[flowModule.id]?.previewResult === 'never tested this far' ||
|
||||
!flowStateStore.val[flowModule.id]?.previewJobId
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
lastJobId == flowStateStore.val[flowModule.id]?.previewJobId ||
|
||||
lastJob?.id == flowStateStore.val[flowModule.id]?.previewJobId ||
|
||||
flowStateStore.val[flowModule.id]?.previewSuccess == undefined
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastJobId = flowStateStore.val[flowModule.id]?.previewJobId
|
||||
|
||||
const job = await JobService.getJob({
|
||||
workspace: $workspaceStore ?? '',
|
||||
id: flowStateStore.val[flowModule.id]?.previewJobId ?? '',
|
||||
noCode: true
|
||||
})
|
||||
if (job && job.type === 'CompletedJob') {
|
||||
lastJobId = flowStateStore.val[flowModule.id]?.previewJobId
|
||||
lastJob = job
|
||||
}
|
||||
}
|
||||
|
||||
let leftPanelSize = $state(0)
|
||||
@@ -270,13 +236,6 @@
|
||||
$effect.pre(() => {
|
||||
$selectedId && untrack(() => onSelectedIdChange())
|
||||
})
|
||||
$effect(() => {
|
||||
if (testJob && testJob.type === 'CompletedJob') {
|
||||
lastJob = $state.snapshot(testJob)
|
||||
} else if ($workspaceStore && $pathStore && flowModule?.id && flowStateStore) {
|
||||
untrack(() => getLastJob())
|
||||
}
|
||||
})
|
||||
let parentLoop = $derived(
|
||||
flowStore.val && flowModule ? checkIfParentLoop(flowStore.val, flowModule.id) : undefined
|
||||
)
|
||||
@@ -322,6 +281,12 @@
|
||||
let rawScriptLang = $derived(
|
||||
flowModule.value.type == 'rawscript' ? flowModule.value.language : undefined
|
||||
)
|
||||
|
||||
let modulePreviewResultViewer: ModulePreviewResultViewer | undefined = $state(undefined)
|
||||
|
||||
function onJobDone() {
|
||||
modulePreviewResultViewer?.getOutputPickerInner()?.setJobPreview()
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onkeydown={onKeyDown} />
|
||||
@@ -596,6 +561,7 @@
|
||||
bind:testIsLoading
|
||||
bind:scriptProgress
|
||||
focusArg={highlightArg}
|
||||
{onJobDone}
|
||||
/>
|
||||
{:else if selected === 'advanced'}
|
||||
<Tabs bind:selected={advancedSelected}>
|
||||
@@ -872,7 +838,7 @@
|
||||
</Pane>
|
||||
{#if selected === 'test'}
|
||||
<Pane minSize={20} class="relative">
|
||||
{#if stepHistoryLoader?.stepStates[flowModule.id]?.initial && lastJob && !flowModule.mock?.enabled}
|
||||
{#if stepHistoryLoader?.stepStates[flowModule.id]?.initial && !flowModule.mock?.enabled}
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
@@ -900,15 +866,15 @@
|
||||
flowModule = flowModule
|
||||
refreshStateStore(flowStore)
|
||||
}}
|
||||
{lastJob}
|
||||
{scriptProgress}
|
||||
{testJob}
|
||||
{scriptProgress}
|
||||
mod={flowModule}
|
||||
{testIsLoading}
|
||||
disableMock={preprocessorModule || failureModule}
|
||||
disableHistory={failureModule}
|
||||
loadingJob={stepHistoryLoader?.stepStates[flowModule.id]?.loadingJobs}
|
||||
tagLabel={customUi?.tagLabel}
|
||||
bind:this={modulePreviewResultViewer}
|
||||
/>
|
||||
</Pane>
|
||||
{/if}
|
||||
|
||||
@@ -144,9 +144,7 @@
|
||||
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)
|
||||
|
||||
@@ -170,30 +168,6 @@
|
||||
updateConnectingData(id, pickableIds, $flowPropPickerConfig, flowStateStore)
|
||||
})
|
||||
|
||||
function updateLastJob(flowStateStore: any | undefined) {
|
||||
if (
|
||||
!flowStateStore ||
|
||||
!id ||
|
||||
flowStateStore.val[id]?.previewResult === 'never tested this far'
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastJob = {
|
||||
id: flowStateStore.val[id]?.previewJobId ?? '',
|
||||
result: flowStateStore.val[id]?.previewResult,
|
||||
type: 'CompletedJob' as const,
|
||||
success: flowStateStore.val[id]?.previewSuccess ?? undefined
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (testJob && testJob.type === 'CompletedJob') {
|
||||
lastJob = $state.snapshot(testJob)
|
||||
} else if (id) {
|
||||
updateLastJob(flowStateStore)
|
||||
}
|
||||
})
|
||||
|
||||
let isConnectingCandidate = $derived(
|
||||
!!id && !!$flowPropPickerConfig && !!pickableIds && Object.keys(pickableIds).includes(id)
|
||||
)
|
||||
@@ -207,6 +181,9 @@
|
||||
const action = $derived(getAiModuleAction(id))
|
||||
|
||||
let testRunDropdownOpen = $state(false)
|
||||
|
||||
let outputPickerInner: OutputPickerInner | undefined = $state(undefined)
|
||||
let historyOpen = $derived.by(() => outputPickerInner?.getHistoryOpen?.() ?? false)
|
||||
</script>
|
||||
|
||||
{#if deletable && id && editId}
|
||||
@@ -271,7 +248,15 @@
|
||||
{@const flowStore = flowEditorContext?.flowStore.val}
|
||||
{@const mod = flowStore?.value ? dfsPreviousResults(id, flowStore, false)[0] : undefined}
|
||||
{#if mod && flowStateStore?.val?.[id]}
|
||||
<ModuleTest bind:this={moduleTest} {mod} bind:testIsLoading bind:testJob />
|
||||
<ModuleTest
|
||||
bind:this={moduleTest}
|
||||
{mod}
|
||||
bind:testIsLoading
|
||||
bind:testJob
|
||||
onJobDone={() => {
|
||||
outputPickerInner?.setJobPreview?.()
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -455,7 +440,6 @@
|
||||
prefix={'results'}
|
||||
connectingData={isConnecting ? connectingData : undefined}
|
||||
{mock}
|
||||
{lastJob}
|
||||
{testJob}
|
||||
moduleId={id}
|
||||
onSelect={selectConnection}
|
||||
@@ -463,12 +447,12 @@
|
||||
{path}
|
||||
{loopStatus}
|
||||
rightMargin
|
||||
bind:derivedHistoryOpen={historyOpen}
|
||||
historyOffset={{ mainAxis: 12, crossAxis: -9 }}
|
||||
clazz="p-1"
|
||||
isLoading={testIsLoading ||
|
||||
(id ? stepHistoryLoader?.stepStates[id]?.loadingJobs : false)}
|
||||
initial={id ? stepHistoryLoader?.stepStates[id]?.initial : undefined}
|
||||
bind:this={outputPickerInner}
|
||||
/>
|
||||
{/snippet}
|
||||
</OutputPicker>
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
{selected}
|
||||
{hover}
|
||||
id={id ?? ''}
|
||||
isConnectingCandidate={true}
|
||||
isConnectingCandidate={nodeKind !== 'result'}
|
||||
variant="virtual"
|
||||
type={outputType}
|
||||
{darkMode}
|
||||
|
||||
@@ -13,14 +13,14 @@
|
||||
|
||||
let { inputTransform, id, onEditInput }: Props = $props()
|
||||
|
||||
const { flowStore, flowStateStore, testSteps, previewArgs } =
|
||||
const { flowStore, flowStateStore, stepsInputArgs, previewArgs } =
|
||||
getContext<FlowEditorContext | undefined>('FlowEditorContext') || {}
|
||||
|
||||
onMount(() => {
|
||||
testSteps?.updateStepArgs(id, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
stepsInputArgs?.updateStepArgs(id, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
})
|
||||
|
||||
const input = $derived(testSteps?.getStepArgs(id))
|
||||
const input = $derived(stepsInputArgs?.getStepArgs(id))
|
||||
</script>
|
||||
|
||||
<div class="p-4 pr-6 h-full overflow-y-auto">
|
||||
@@ -42,10 +42,10 @@
|
||||
{:else if inputTransform[key].type === 'static'}
|
||||
<DollarSign size={12} class="text-tertiary font-mono -my-1" />
|
||||
{/if}
|
||||
{#if testSteps?.isArgManuallySet(id, key)}
|
||||
{#if stepsInputArgs?.isArgManuallySet(id, key)}
|
||||
<button
|
||||
onclick={() => {
|
||||
testSteps?.evalArg(id, key, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
stepsInputArgs?.evalArg(id, key, flowStateStore?.val, flowStore?.val, previewArgs?.val)
|
||||
}}
|
||||
title="Re-evaluate input"
|
||||
class="-my-1 ml-0.5 hover:text-primary dark:hover:text-primary dark:text-gray-500 text-gray-300"
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
import ObjectViewer from '$lib/components/propertyPicker/ObjectViewer.svelte'
|
||||
import StepHistory from './StepHistory.svelte'
|
||||
import { Popover } from '$lib/components/meltComponents'
|
||||
import { untrack } from 'svelte'
|
||||
import { getContext, untrack } from 'svelte'
|
||||
import { Tooltip } from '$lib/components/meltComponents'
|
||||
import type { Job } from '$lib/gen'
|
||||
import DisplayResult from '$lib/components/DisplayResult.svelte'
|
||||
@@ -29,6 +29,7 @@
|
||||
import DisplayResultControlBar from '$lib/components/DisplayResultControlBar.svelte'
|
||||
import { base } from '$lib/base'
|
||||
import { fade } from 'svelte/transition'
|
||||
import type { FlowEditorContext, OutputViewerJob } from '../types'
|
||||
|
||||
interface Props {
|
||||
prefix?: string
|
||||
@@ -44,10 +45,7 @@
|
||||
fullResult?: boolean
|
||||
closeOnOutsideClick?: boolean
|
||||
getLogs?: boolean
|
||||
selectedJob?: SelectedJob
|
||||
forceJson?: boolean
|
||||
isLoading?: boolean
|
||||
preview?: 'mock' | 'job' | undefined
|
||||
hideHeaderBar?: boolean
|
||||
simpleViewer?: any | undefined
|
||||
path?: string
|
||||
@@ -56,7 +54,6 @@
|
||||
rightMargin?: boolean
|
||||
disableMock?: boolean
|
||||
disableHistory?: boolean
|
||||
lastJob?: Job
|
||||
testJob?: Job & { result_stream?: string }
|
||||
derivedHistoryOpen?: boolean // derived from historyOpen
|
||||
historyOffset?: any
|
||||
@@ -72,8 +69,7 @@
|
||||
}
|
||||
|
||||
let {
|
||||
lastJob = undefined,
|
||||
testJob = undefined,
|
||||
testJob,
|
||||
prefix = '',
|
||||
allowCopy = false,
|
||||
connectingData = undefined,
|
||||
@@ -82,10 +78,7 @@
|
||||
fullResult = false,
|
||||
closeOnOutsideClick = false,
|
||||
getLogs = false,
|
||||
selectedJob = $bindable(undefined),
|
||||
forceJson = $bindable(false),
|
||||
isLoading = $bindable(false),
|
||||
preview = $bindable(undefined),
|
||||
hideHeaderBar = false,
|
||||
simpleViewer = undefined,
|
||||
path = '',
|
||||
@@ -94,7 +87,6 @@
|
||||
rightMargin = false,
|
||||
disableMock = false,
|
||||
disableHistory = false,
|
||||
derivedHistoryOpen = $bindable(false),
|
||||
historyOffset = { mainAxis: 8, crossAxis: -4.5 },
|
||||
clazz,
|
||||
copilot_fix,
|
||||
@@ -106,19 +98,6 @@
|
||||
customEmptyJobMessage
|
||||
}: Props = $props()
|
||||
|
||||
type SelectedJob =
|
||||
| ((
|
||||
| Job
|
||||
| {
|
||||
id: string
|
||||
result: unknown
|
||||
type: 'CompletedJob'
|
||||
workspace_id: string
|
||||
success: boolean
|
||||
}
|
||||
) & { preview?: boolean; result_stream?: string; result?: unknown })
|
||||
| undefined
|
||||
|
||||
let jsonView = $state(false)
|
||||
let clientHeight: number = $state(0)
|
||||
let tmpMock: { enabled: boolean; return_value?: unknown } | undefined = $state(undefined)
|
||||
@@ -127,6 +106,8 @@
|
||||
let historyOpen = $state(false)
|
||||
let contentEl: HTMLDivElement | undefined = $state(undefined)
|
||||
let hasOverflow = $state(false)
|
||||
let preview: 'mock' | 'job' | undefined = $state(undefined)
|
||||
let selectedJob: OutputViewerJob = $state(undefined)
|
||||
|
||||
function checkOverflow() {
|
||||
if (contentEl) {
|
||||
@@ -134,26 +115,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
function selectJob(nJob: SelectedJob | undefined) {
|
||||
function selectJob(nJob: OutputViewerJob | undefined) {
|
||||
if (nJob && (nJob.result_stream || nJob.type == 'CompletedJob')) {
|
||||
selectedJob = nJob
|
||||
} else if (job && (job.result_stream || job.type == 'CompletedJob')) {
|
||||
selectedJob = job
|
||||
} else if (lastJob && (lastJob.result_stream || lastJob.type == 'CompletedJob')) {
|
||||
selectedJob = lastJob
|
||||
} else {
|
||||
selectedJob = undefined
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (!job || !(job.result_stream || job.type == 'CompletedJob')) {
|
||||
if (!lastJob || !(lastJob.result_stream || lastJob.type == 'CompletedJob')) {
|
||||
return
|
||||
}
|
||||
selectJob(job)
|
||||
|
||||
if (job.preview && mock?.enabled) {
|
||||
preview = 'job'
|
||||
job.preview = false
|
||||
}
|
||||
selectJob(lastJob)
|
||||
})
|
||||
|
||||
function togglePreview(nPrev: 'mock' | 'job' | undefined) {
|
||||
@@ -174,6 +150,9 @@
|
||||
let toolbarLocationJob: 'self' | 'external' | undefined = $state(undefined)
|
||||
let toolbarLocationMock: 'self' | 'external' | undefined = $state(undefined)
|
||||
|
||||
const flowEditorContext = getContext<FlowEditorContext | undefined>('FlowEditorContext')
|
||||
const flowStateStore = flowEditorContext?.flowStateStore
|
||||
|
||||
function updateCanEditWithDblClick(newValue: boolean) {
|
||||
canEditWithDblClick = newValue
|
||||
if (debounceTimeout) {
|
||||
@@ -224,16 +203,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
let mockUpdateStatus = $derived(
|
||||
function updateLastJob() {
|
||||
if (testJob) {
|
||||
return testJob
|
||||
}
|
||||
if (
|
||||
!flowStateStore ||
|
||||
!moduleId ||
|
||||
flowStateStore.val[moduleId]?.previewResult === 'never tested this far'
|
||||
) {
|
||||
return
|
||||
}
|
||||
return {
|
||||
id: flowStateStore.val[moduleId]?.previewJobId ?? '',
|
||||
result: flowStateStore.val[moduleId]?.previewResult,
|
||||
type: 'CompletedJob' as const,
|
||||
success: flowStateStore.val[moduleId]?.previewSuccess ?? undefined
|
||||
} as Job & { result_stream?: string } & { preview?: boolean }
|
||||
}
|
||||
|
||||
const lastJob = $derived.by(updateLastJob)
|
||||
|
||||
export function setJobPreview() {
|
||||
if (mock?.enabled) {
|
||||
preview = 'job'
|
||||
}
|
||||
}
|
||||
|
||||
const mockUpdateStatus = $derived(
|
||||
preview === 'mock' && !mock?.enabled
|
||||
? 'restore'
|
||||
: preview === 'job' && mock?.enabled && selectedJob?.type === 'CompletedJob'
|
||||
: preview === 'job' && mock?.enabled && selectedJob?.['type'] === 'CompletedJob'
|
||||
? 'override'
|
||||
: undefined
|
||||
)
|
||||
$effect(() => {
|
||||
derivedHistoryOpen = historyOpen
|
||||
})
|
||||
|
||||
$effect(() => {
|
||||
if (displayResultJob && typeof displayResultJob.getToolbarLocation === 'function') {
|
||||
toolbarLocationJob = displayResultJob.getToolbarLocation()
|
||||
@@ -258,23 +262,67 @@
|
||||
}
|
||||
})
|
||||
|
||||
let job = $derived.by(() => {
|
||||
if (testJob) {
|
||||
return { ...testJob, preview: testJob.type === 'CompletedJob' }
|
||||
}
|
||||
if (lastJob) {
|
||||
return { ...lastJob, preview: false }
|
||||
}
|
||||
return undefined
|
||||
}) as SelectedJob | undefined
|
||||
|
||||
let popoverHeight = $derived(customHeight ?? (clientHeight > 0 ? clientHeight : 0))
|
||||
|
||||
// If a test job is running, we override the preview to show the test job
|
||||
const executingTestJob = $derived(testJob && testJob.type === 'QueuedJob')
|
||||
|
||||
const isLoadingAndNotMock = $derived(
|
||||
isLoading && job?.result_stream === undefined && !mock?.enabled
|
||||
isLoading && lastJob?.result_stream === undefined && (!mock?.enabled || executingTestJob)
|
||||
)
|
||||
|
||||
const copilot_fix_render = $derived(copilot_fix)
|
||||
|
||||
export function getPreview() {
|
||||
return preview
|
||||
}
|
||||
|
||||
async function selectJobFromHistory({
|
||||
detail
|
||||
}: {
|
||||
detail: 'mock' | (Job & { getFullJob: () => Promise<Job> })
|
||||
}) {
|
||||
if (!detail) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
if (detail === 'mock') {
|
||||
if (mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
togglePreview('mock')
|
||||
return
|
||||
}
|
||||
if (detail.id === lastJob?.id && !mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
|
||||
isLoading = true
|
||||
|
||||
try {
|
||||
const fullJob = await detail.getFullJob()
|
||||
if (fullJob) {
|
||||
selectJob(fullJob)
|
||||
togglePreview('job')
|
||||
}
|
||||
} finally {
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
|
||||
export function getHistoryOpen() {
|
||||
return historyOpen
|
||||
}
|
||||
|
||||
export function getSelectedJob() {
|
||||
return selectedJob
|
||||
}
|
||||
|
||||
const noHistory = $derived(
|
||||
loopStatus ? (loopStatus.type === 'self' ? 'isLoop' : 'isInsideLoop') : undefined
|
||||
)
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -282,6 +330,7 @@
|
||||
bind:clientHeight
|
||||
style={canEditWithDblClick ? 'cursor: text;' : ''}
|
||||
>
|
||||
<!-- Top toolbar -->
|
||||
<div
|
||||
class={twMerge(
|
||||
'text-xs px-1',
|
||||
@@ -297,89 +346,24 @@
|
||||
)}
|
||||
>
|
||||
<div class="flex flex-row items-center gap-0.5">
|
||||
<!-- History picker -->
|
||||
{#if !disableHistory}
|
||||
<Popover
|
||||
bind:this={stepHistoryPopover}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed',
|
||||
placement: 'left-start',
|
||||
offset: historyOffset,
|
||||
gutter: 0 // hack to make offset effective, see https://github.com/melt-ui/melt-ui/issues/528
|
||||
}}
|
||||
contentClasses="w-[225px] overflow-hidden"
|
||||
{closeOnOutsideClick}
|
||||
usePointerDownOutside={closeOnOutsideClick}
|
||||
disablePopup={!!connectingData || jsonView}
|
||||
bind:isOpen={historyOpen}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="contained"
|
||||
btnClasses="bg-surface h-[27px]"
|
||||
startIcon={{ icon: History }}
|
||||
nonCaptureEvent
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
<div class="rounded-[inherit]" style={`height: ${popoverHeight}px`}>
|
||||
<StepHistory
|
||||
{moduleId}
|
||||
{getLogs}
|
||||
on:select={async ({ detail }) => {
|
||||
if (!detail) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
if (detail === 'mock') {
|
||||
if (mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
togglePreview('mock')
|
||||
return
|
||||
}
|
||||
if (detail.id === job?.id && !mock?.enabled) {
|
||||
togglePreview(undefined)
|
||||
return
|
||||
}
|
||||
|
||||
// Create a timeout to show loading state after 200ms
|
||||
const loadingTimeout = setTimeout(() => {
|
||||
isLoading = true
|
||||
}, 200)
|
||||
|
||||
try {
|
||||
const fullJob = await detail.getFullJob()
|
||||
if (fullJob) {
|
||||
selectJob(fullJob)
|
||||
togglePreview('job')
|
||||
}
|
||||
} finally {
|
||||
// Clear the timeout if operation completed before 200ms
|
||||
clearTimeout(loadingTimeout)
|
||||
isLoading = false
|
||||
}
|
||||
}}
|
||||
mockValue={mock?.return_value}
|
||||
mockEnabled={mock?.enabled}
|
||||
{path}
|
||||
noHistory={loopStatus
|
||||
? loopStatus.type === 'self'
|
||||
? 'isLoop'
|
||||
: 'isInsideLoop'
|
||||
: undefined}
|
||||
/>
|
||||
</div>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{@render historyPicker()}
|
||||
{/if}
|
||||
{#if !isLoadingAndNotMock || mock?.enabled}
|
||||
<!-- Badge-->
|
||||
{#if executingTestJob}
|
||||
<OutputBadge
|
||||
job={testJob}
|
||||
class={twMerge(
|
||||
'min-w-16 min-h-[23px] text-secondary',
|
||||
preview ? 'bg-surface shadow-sm' : ''
|
||||
)}
|
||||
/>
|
||||
{:else if !isLoadingAndNotMock || mock?.enabled}
|
||||
<div
|
||||
class={twMerge(
|
||||
'w-grow min-w-0 flex gap-1 items-center h-[27px] rounded-md group',
|
||||
preview || selectedJob?.id !== job?.id ? 'p-[2px] bg-surface-secondary' : ''
|
||||
preview || selectedJob?.id !== lastJob?.id ? 'p-[2px] bg-surface-secondary' : ''
|
||||
)}
|
||||
>
|
||||
{#if loopStatus?.type === 'self'}
|
||||
@@ -415,7 +399,7 @@
|
||||
job={selectedJob}
|
||||
class={twMerge(
|
||||
'min-w-16 text-secondary',
|
||||
preview || selectedJob?.id !== job?.id ? 'bg-surface shadow-sm h-[23px]' : ''
|
||||
preview || selectedJob?.id !== lastJob?.id ? 'bg-surface shadow-sm h-[23px]' : ''
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
@@ -433,17 +417,9 @@
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{:else if testJob}
|
||||
<!-- {JSON.stringify(testJob)} -->
|
||||
<OutputBadge
|
||||
job={testJob}
|
||||
class={twMerge(
|
||||
'min-w-16 text-secondary',
|
||||
preview || testJob?.id !== job?.id ? 'bg-surface shadow-sm h-[23px]' : ''
|
||||
)}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<!-- Pin button -->
|
||||
{#if !disableMock && !isLoadingAndNotMock}
|
||||
<Tooltip disablePopup={mock?.enabled}>
|
||||
<Button
|
||||
@@ -472,6 +448,7 @@
|
||||
{/if}
|
||||
|
||||
{#if jsonView}
|
||||
<!-- Save button -->
|
||||
<Button
|
||||
size="xs2"
|
||||
color="green"
|
||||
@@ -492,6 +469,7 @@
|
||||
}}
|
||||
disabled={!!error || !tmpMock}
|
||||
/>
|
||||
<!-- Cancel button -->
|
||||
<Button
|
||||
size="xs2"
|
||||
color="red"
|
||||
@@ -503,7 +481,8 @@
|
||||
tmpMock = undefined
|
||||
}}
|
||||
/>
|
||||
{:else if mock?.enabled && !preview}
|
||||
{:else if mock?.enabled && !preview && !executingTestJob}
|
||||
<!-- Edit pin button -->
|
||||
<Tooltip disablePopup={mock?.enabled}>
|
||||
<Button
|
||||
size="xs2"
|
||||
@@ -614,6 +593,7 @@
|
||||
<Loader2 class="animate-spin" />
|
||||
</div>
|
||||
{:else if connectingData !== undefined || simpleViewer}
|
||||
<!-- Display data for input connections -->
|
||||
<ObjectViewer
|
||||
json={moduleId
|
||||
? {
|
||||
@@ -630,6 +610,7 @@
|
||||
{editKey}
|
||||
/>
|
||||
{:else if jsonView}
|
||||
<!-- Json editor for editing mock data -->
|
||||
{#await import('$lib/components/JsonEditor.svelte')}
|
||||
<Loader2 class="animate-spin" />
|
||||
{:then Module}
|
||||
@@ -653,12 +634,12 @@
|
||||
class="h-full"
|
||||
/>
|
||||
{/await}
|
||||
{:else if (mock?.enabled || preview == 'mock') && preview != 'job'}
|
||||
{:else if (mock?.enabled || preview == 'mock') && preview != 'job' && !executingTestJob}
|
||||
<!-- Display data for mock data -->
|
||||
{#if fullResult}
|
||||
<div class="break-words relative h-full">
|
||||
<DisplayResult
|
||||
bind:this={displayResultMock}
|
||||
bind:forceJson
|
||||
workspaceId={undefined}
|
||||
jobId={undefined}
|
||||
result_stream={undefined}
|
||||
@@ -682,12 +663,12 @@
|
||||
/>
|
||||
{/if}
|
||||
{:else if selectedJob != undefined && (selectedJob.result_stream || selectedJob.type == 'CompletedJob')}
|
||||
<!-- Display data for job data -->
|
||||
{#if fullResult}
|
||||
<div class="break-words relative h-full">
|
||||
{#key selectedJob}
|
||||
<DisplayResult
|
||||
bind:this={displayResultJob}
|
||||
bind:forceJson
|
||||
workspaceId={selectedJob?.workspace_id}
|
||||
jobId={selectedJob?.id}
|
||||
result={selectedJob?.result}
|
||||
@@ -715,7 +696,7 @@
|
||||
pureViewer={false}
|
||||
/>
|
||||
{/if}
|
||||
{:else if !job}
|
||||
{:else if !lastJob}
|
||||
<div class="flex flex-col items-center justify-center h-full">
|
||||
<p class="text-xs text-secondary">
|
||||
{customEmptyJobMessage ?? 'Test this step to see results'}{#if !disableMock}
|
||||
@@ -753,6 +734,47 @@
|
||||
</button>
|
||||
{/snippet}
|
||||
|
||||
{#snippet historyPicker()}
|
||||
<Popover
|
||||
bind:this={stepHistoryPopover}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed',
|
||||
placement: 'left-start',
|
||||
offset: historyOffset,
|
||||
gutter: 0 // hack to make offset effective, see https://github.com/melt-ui/melt-ui/issues/528
|
||||
}}
|
||||
contentClasses="w-[225px] overflow-hidden"
|
||||
{closeOnOutsideClick}
|
||||
usePointerDownOutside={closeOnOutsideClick}
|
||||
disablePopup={!!connectingData || jsonView}
|
||||
bind:isOpen={historyOpen}
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<Button
|
||||
color="light"
|
||||
size="xs2"
|
||||
variant="contained"
|
||||
btnClasses="bg-surface h-[27px]"
|
||||
startIcon={{ icon: History }}
|
||||
nonCaptureEvent
|
||||
/>
|
||||
{/snippet}
|
||||
{#snippet content()}
|
||||
<div class="rounded-[inherit]" style={`height: ${popoverHeight}px`}>
|
||||
<StepHistory
|
||||
{moduleId}
|
||||
{getLogs}
|
||||
on:select={selectJobFromHistory}
|
||||
mockValue={mock?.return_value}
|
||||
mockEnabled={mock?.enabled}
|
||||
{path}
|
||||
{noHistory}
|
||||
/>
|
||||
</div>
|
||||
{/snippet}
|
||||
</Popover>
|
||||
{/snippet}
|
||||
|
||||
<style>
|
||||
.dbl-click-editable {
|
||||
cursor: text;
|
||||
|
||||
+19
-5
@@ -8,7 +8,7 @@ import {
|
||||
} from './previousResults'
|
||||
import { evalValue } from './utils'
|
||||
|
||||
export class TestSteps {
|
||||
export class StepsInputArgs {
|
||||
#stepsEvaluated = $state<Record<string, Record<string, any>>>({})
|
||||
#steps = $state<Record<string, Record<string, any>>>({})
|
||||
|
||||
@@ -53,10 +53,20 @@ export class TestSteps {
|
||||
}
|
||||
|
||||
isArgManuallySet(moduleId: string, argName: string): boolean {
|
||||
return (
|
||||
JSON.stringify(this.#steps[moduleId]?.[argName]) !==
|
||||
JSON.stringify(this.#stepsEvaluated[moduleId]?.[argName])
|
||||
)
|
||||
try {
|
||||
const stepsValue = this.#steps[moduleId]?.[argName]
|
||||
const evaluatedValue = this.#stepsEvaluated[moduleId]?.[argName]
|
||||
|
||||
const stepsStr = JSON.stringify(stepsValue)
|
||||
const evaluatedStr = JSON.stringify(evaluatedValue)
|
||||
return stepsStr !== evaluatedStr
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`Error in isArgManuallySet for moduleId: ${moduleId}, argName: ${argName}`,
|
||||
error
|
||||
)
|
||||
return this.#steps[moduleId]?.[argName] !== this.#stepsEvaluated[moduleId]?.[argName]
|
||||
}
|
||||
}
|
||||
|
||||
getManuallyEditedArgs(moduleId: string): string[] {
|
||||
@@ -185,4 +195,8 @@ export class TestSteps {
|
||||
})
|
||||
this.#stepsEvaluated[moduleId] = nargs
|
||||
}
|
||||
|
||||
resetManuallyEditedArgs() {
|
||||
this.#steps = $state.snapshot(this.#stepsEvaluated)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { OpenFlow } from '$lib/gen'
|
||||
import type { Job, OpenFlow } from '$lib/gen'
|
||||
import type { History } from '$lib/history.svelte'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type ScriptEditorDrawer from './content/ScriptEditorDrawer.svelte'
|
||||
@@ -7,7 +7,7 @@ import type { FlowBuilderWhitelabelCustomUi } from '../custom_ui'
|
||||
import type Editor from '../Editor.svelte'
|
||||
import type SimpleEditor from '../SimpleEditor.svelte'
|
||||
import type { StateStore } from '$lib/utils'
|
||||
import type { TestSteps } from './testSteps.svelte'
|
||||
import type { StepsInputArgs } from './stepsInputArgs.svelte'
|
||||
import type { Asset, AssetWithAccessType } from '../assets/lib'
|
||||
import type S3FilePicker from '../S3FilePicker.svelte'
|
||||
import type DbManagerDrawer from '../DBManagerDrawer.svelte'
|
||||
@@ -37,31 +37,31 @@ export type ExtendedOpenFlow = OpenFlow & {
|
||||
|
||||
export type FlowInputEditorState = {
|
||||
selectedTab:
|
||||
| 'inputEditor'
|
||||
| 'history'
|
||||
| 'savedInputs'
|
||||
| 'json'
|
||||
| 'captures'
|
||||
| 'firstStepInputs'
|
||||
| undefined
|
||||
| 'inputEditor'
|
||||
| 'history'
|
||||
| 'savedInputs'
|
||||
| 'json'
|
||||
| 'captures'
|
||||
| 'firstStepInputs'
|
||||
| undefined
|
||||
editPanelSize: number | undefined
|
||||
payloadData: Record<string, any> | undefined
|
||||
}
|
||||
|
||||
export type CurrentEditor =
|
||||
| ((
|
||||
| {
|
||||
type: 'script'
|
||||
editor: Editor
|
||||
showDiffMode: () => void
|
||||
hideDiffMode: () => void
|
||||
diffMode: boolean
|
||||
lastDeployedCode: string | undefined
|
||||
}
|
||||
| { type: 'iterator'; editor: SimpleEditor }
|
||||
) & {
|
||||
stepId: string
|
||||
})
|
||||
| {
|
||||
type: 'script'
|
||||
editor: Editor
|
||||
showDiffMode: () => void
|
||||
hideDiffMode: () => void
|
||||
diffMode: boolean
|
||||
lastDeployedCode: string | undefined
|
||||
}
|
||||
| { type: 'iterator'; editor: SimpleEditor }
|
||||
) & {
|
||||
stepId: string
|
||||
})
|
||||
| undefined
|
||||
|
||||
export type FlowEditorContext = {
|
||||
@@ -75,7 +75,7 @@ export type FlowEditorContext = {
|
||||
flowStore: StateStore<ExtendedOpenFlow>
|
||||
flowInputEditorState: Writable<FlowInputEditorState>
|
||||
flowStateStore: StateStore<FlowState>
|
||||
testSteps: TestSteps
|
||||
stepsInputArgs: StepsInputArgs
|
||||
saveDraft: () => void
|
||||
initialPathStore: Writable<string>
|
||||
fakeInitialPath: string
|
||||
@@ -97,3 +97,16 @@ export type FlowGraphAssetContext = StateStore<{
|
||||
additionalAssetsMap: Record<string, AssetWithAccessType[]>
|
||||
computeAssetsCount: (asset: Asset) => number
|
||||
}>
|
||||
|
||||
export type OutputViewerJob =
|
||||
| ((
|
||||
| Job
|
||||
| {
|
||||
id: string
|
||||
result: unknown
|
||||
type: 'CompletedJob'
|
||||
workspace_id: string
|
||||
success: boolean
|
||||
}
|
||||
) & { result_stream?: string; result?: unknown })
|
||||
| undefined
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
import type { FlowPropPickerConfig, PropPickerContext } from '$lib/components/prop_picker'
|
||||
import type { PickableProperties } from '$lib/components/flows/previousResults'
|
||||
import { Triggers } from '$lib/components/triggers/triggers.svelte'
|
||||
import { TestSteps } from '$lib/components/flows/testSteps.svelte'
|
||||
import { StepsInputArgs } from '$lib/components/flows/stepsInputArgs.svelte'
|
||||
import { ModulesTestStates } from '$lib/components/modulesTest.svelte'
|
||||
|
||||
let token = $page.url.searchParams.get('wm_token') ?? undefined
|
||||
@@ -75,7 +75,7 @@
|
||||
const moving = writable<{ id: string } | undefined>(undefined)
|
||||
const history = initHistory(flowStore.val)
|
||||
|
||||
const testSteps = new TestSteps()
|
||||
const stepsInputArgs = new StepsInputArgs()
|
||||
const selectedIdStore = writable('settings-metadata')
|
||||
const triggersCount = writable<TriggersCount | undefined>(undefined)
|
||||
setContext<TriggerContext>('TriggerContext', {
|
||||
@@ -94,7 +94,7 @@
|
||||
pathStore: writable(''),
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
testSteps,
|
||||
stepsInputArgs,
|
||||
saveDraft: () => {},
|
||||
initialPathStore: writable(''),
|
||||
fakeInitialPath: '',
|
||||
@@ -292,7 +292,7 @@
|
||||
noEditor
|
||||
on:applyArgs={(ev) => {
|
||||
if (ev.detail.kind === 'preprocessor') {
|
||||
testSteps.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
stepsInputArgs.setStepArgs('preprocessor', ev.detail.args ?? {})
|
||||
$selectedIdStore = 'preprocessor'
|
||||
} else {
|
||||
previewArgsStore.val = ev.detail.args ?? {}
|
||||
|
||||
Reference in New Issue
Block a user