Files
windmill/frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte
T
Guilhemandellipsis-dev[bot] cec2fac7e4 feat(frontend): run test flow from graph (#6122)
* migrate FlowPreviewContent to svelte 5

* run background preview from input panel

* share local run test

* Show approval in graph is testing in graph

* use component and props instead of portal for approval in graph

* Add a toggle to show module status in graph

* open module result after each run

* Fix module reactivity issue

* Add test flow button

* Extract preview run logic from flowPreviewContent

* Revert "Extract preview run logic from flowPreviewContent"

This reverts commit a39c70a920.

* nit

* lazy load preview content

* create component for flow preview button

* open preview v0

* open preview v1

* connect open preview button

* improve graph run display

* enable cancel preview

* Run test flow from input panel

* nit

* wip

* Use global context instead of module context for moduleTestState

* nit

* fix flow preview rendering

* Add testJob to modulesTest context

* update module status based on individual test data

* fix: clear job status on run preview

* detatch run buttons from input node

* move preview job in FlowEditorContext

* move outputPickerOpenFns to FlowEditorContext

* add result panel

* Add result output picker

* add status to loops and branch

* add open detail button to result panel

* fix test up to

* clean unnecessary binding

* clean

* Make iteration annotation smaller in editmode

* detatch test button to and aproval from node

* prevent flow edition during execution

* Prevent step test run during flow run

* Show approval in graph edges

* prevent opening output popover if node is outside the graph

* fix pointerdownOutside action

* fix test up to dropdown not closing

* fix test up to

* nit

* change job status badge display

* fix running status

* Enable test flow in Dev

* fix darkmode

* fix node panel display in Dev

* fix test flow button positionning

* fix suspend in subflows

* improve lazy load of preview

* prevent preview data unmount on close drawer

* clean code

* move flowjob into flow context

* Revert "move flowjob into flow context"

This reverts commit 939e9dbaaf.

* clean context

* nit

* fix dark mode status view

* fix test button alignment

* clean job status on deleted step

* fix retry bad status display

* Detect flow change

* Update frontend/src/lib/components/flows/header/FlowPreviewButtons.svelte

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>

---------

Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-07-09 16:24:03 +00:00

177 lines
4.2 KiB
Svelte

<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
import FlowPreviewContent from '$lib/components/FlowPreviewContent.svelte'
import { createEventDispatcher, tick } from 'svelte'
import { getContext } from 'svelte'
import type { FlowEditorContext } from '../types'
import { Play } from 'lucide-svelte'
import { aiChatManager } from '$lib/components/copilot/chat/AIChatManager.svelte'
interface Props {
loading?: boolean
onRunPreview?: () => void
onJobDone?: () => void
}
let { loading = false, onRunPreview, onJobDone }: Props = $props()
const { selectedId } = getContext<FlowEditorContext>('FlowEditorContext')
let flowPreviewContent: FlowPreviewContent | undefined = $state(undefined)
let preventEscape = $state(false)
let selectedJobStep: string | undefined = $state(undefined)
let selectedJobStepIsTopLevel: boolean | undefined = $state(undefined)
let selectedJobStepType: 'single' | 'forloop' | 'branchall' = $state('single')
let branchOrIterationN: number = $state(0)
let scrollTop: number = $state(0)
let previewMode: 'upTo' | 'whole' = $state('whole')
let upToId: string | undefined = $state(undefined)
let previewOpen = $state(false)
let deferContent = $state(false)
export async function openPreview(test: boolean = false) {
if (!previewOpen) {
previewOpen = true
await tick()
flowPreviewContent?.refresh()
if (!test) return
}
previewMode = 'whole'
flowPreviewContent?.test()
}
export async function runPreview() {
if (!previewOpen) {
deferContent = true
await tick()
}
previewMode = 'whole'
flowPreviewContent?.refresh()
flowPreviewContent?.test()
}
export function cancelTest() {
flowPreviewContent?.cancelTest()
}
const dispatch = createEventDispatcher()
let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' =
$state('timeline')
let upToDisabled = $derived.by(() => {
const upToSelected = upToId ?? $selectedId
return (
upToSelected == undefined ||
[
'settings',
'settings-metadata',
'settings-schedule',
'settings-retries',
'settings-same-worker',
'settings-graph',
'settings-worker-group',
'settings-cache',
'settings-concurrency',
'settings-early-stop',
'settings-early-return',
'inputs',
'schedules',
'failure',
'preprocessor',
'constants',
'Result',
'Input',
'triggers'
].includes(upToSelected) ||
upToSelected?.includes('branch') ||
aiChatManager.flowAiChatHelpers?.getModuleAction(upToSelected) === 'removed'
)
})
export async function testUpTo(id: string | undefined, openPreview: boolean = false) {
upToId = id
if (upToDisabled) return
if (openPreview) {
previewOpen = true
} else if (!previewOpen) {
deferContent = true
await tick()
}
previewMode = 'upTo'
flowPreviewContent?.refresh()
if (!openPreview) {
flowPreviewContent?.test()
}
}
export function getPreviewMode() {
return previewMode
}
export function getPreviewOpen() {
return previewOpen
}
export function getFlowPreviewContent() {
return flowPreviewContent
}
</script>
<Button
color="dark"
size="xs"
on:click={() => {
previewMode = 'whole'
previewOpen = !previewOpen
if (previewOpen) {
flowPreviewContent?.refresh()
}
}}
startIcon={{ icon: Play }}
id="flow-editor-test-flow"
dropdownItems={!upToDisabled
? [
{
label: 'Test up to ' + $selectedId,
onClick: () => testUpTo($selectedId, true)
}
]
: undefined}
>
Test flow
</Button>
{#if !loading}
<Drawer bind:open={previewOpen} size="75%" {preventEscape} alwaysOpen={deferContent}>
<FlowPreviewContent
bind:this={flowPreviewContent}
open={previewOpen}
bind:scrollTop
bind:previewMode
bind:selectedJobStep
bind:selectedJobStepIsTopLevel
bind:selectedJobStepType
bind:branchOrIterationN
bind:rightColumnSelect
on:close={() => {
// keep the data in the preview content
deferContent = true
previewOpen = false
}}
on:openTriggers={(e) => {
previewOpen = false
dispatch('openTriggers', e.detail)
}}
bind:preventEscape
{onRunPreview}
render={previewOpen}
{onJobDone}
{upToId}
/>
</Drawer>
{/if}