Files
windmill/frontend/src/lib/components/apps/editor/RunnableJobPanel.svelte
claude[bot] a91a7fc741 feat: implement SSE for job updates polling (#6174)
* feat: implement SSE for job updates polling

- Add /getupdate_sse/:id endpoint for real-time job updates via Server-Sent Events
- SSE streams job status, logs, progress, and flow status updates
- Auto-stops streaming when job completes
- Frontend uses EventSource with graceful fallback to polling on errors
- Reduces server load and improves real-time responsiveness
- Update OpenAPI spec with new SSE endpoint definition

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>

* all

* all

* v1

* rename test job loader

* iter2

* nit

* all

* all

* improve

* upgrade

* try catch assets json parse

* only display loader of custom component if custom component is rendered

* Fix required properties (#6221)

* whitelabelling nits

* remove fdsfs (#6222)

* add refresh on worker tag select

* whitelabel nits

* merge

* merge

* done

* nit fix

* all

* app db

* all

* all

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <rubenfiszel@users.noreply.github.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Tammo Ippen <tammo.ippen@posteo.de>
Co-authored-by: Diego Imbert <70353967+diegoimbert@users.noreply.github.com>
2025-07-18 22:48:51 +00:00

90 lines
2.6 KiB
Svelte

<script lang="ts">
import { getContext, untrack } from 'svelte'
import type { AppEditorContext } from '../types'
import JobLoader from '$lib/components/JobLoader.svelte'
import type { Job } from '$lib/gen'
import RunnableJobPanelInner from './RunnableJobPanelInner.svelte'
interface Props {
float?: boolean
hidden?: boolean
testJob?: Job | undefined
jobToWatch?: { componentId: string; job: string } | undefined
width?: number | undefined
}
let {
float = true,
hidden = false,
testJob = $bindable(undefined),
jobToWatch = $bindable(undefined),
width = undefined
}: Props = $props()
const { runnableJobEditorPanel, selectedComponentInEditor } =
getContext<AppEditorContext>('AppEditorContext')
let testIsLoading = $state(false)
let jobLoader: JobLoader | undefined = $state()
function updateSelectedJob() {
const selectedComponent = $selectedComponentInEditor
if (selectedComponent) {
const backendJob = $runnableJobEditorPanel.jobs[selectedComponent]
if (backendJob) {
if (jobToWatch?.job == backendJob) {
return
}
if (jobToWatch?.componentId != selectedComponent) {
testJob = undefined
}
jobToWatch = { componentId: selectedComponent, job: backendJob }
jobLoader?.watchJob(backendJob)
} else {
testJob = undefined
jobToWatch = undefined
}
}
}
let logDrawerOpen = false
let resultDrawerOpen = false
$effect(() => {
;($runnableJobEditorPanel.focused || !float) &&
$selectedComponentInEditor &&
$runnableJobEditorPanel.jobs &&
untrack(() => {
updateSelectedJob()
})
})
</script>
<JobLoader bind:this={jobLoader} bind:isLoading={testIsLoading} bind:job={testJob} />
{#if ($runnableJobEditorPanel.focused && $selectedComponentInEditor) || logDrawerOpen || resultDrawerOpen || !float}
{@const frontendJob = $runnableJobEditorPanel?.frontendJobs[$selectedComponentInEditor ?? '']}
<!-- svelte-ignore a11y_no_static_element_interactions -->
{#if float}
<div
class="absolute z-[100] top-0 right-0 border-t h-full"
style="width: {$runnableJobEditorPanel.width}px; transform: translateX({$runnableJobEditorPanel.width}px);"
>
<RunnableJobPanelInner {testIsLoading} {frontendJob} {testJob} />
</div>
{:else}
<div
class="flex flex-col min-w-0 grow h-full"
style={width !== undefined ? `width:${width}px;` : ''}
>
{#if $selectedComponentInEditor}
<RunnableJobPanelInner {testIsLoading} {frontendJob} {testJob} />
{:else if !hidden}
<div class="text-sm text-secondary text-center py-8 px-2 border-l h-full">
Logs and results will be displayed here
</div>
{/if}
</div>
{/if}
{/if}