Files
windmill/frontend/src/lib/components/flows/content/FlowModuleScript.svelte
T
GuilhemandClaude Opus 4.8 c000bbca28 fix(frontend): scope raw-app, flow and script editors to the session workspace (#10015)
* fix(frontend): scope raw-app/flow/script editors to the session workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): scope flow and script editor operations to the session workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): scope flow preview, inline-script creation and datatable schema to the session workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review — thread session workspace through flow resource pickers, script fetch, preview cancel/recording and path collision check

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Claude review — pass session workspace to preview FlowStatusViewer and align FlowChatManager guards

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Pi review — show acting workspace in script-not-found message and fetch picked script from it in EditorBar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 2 — thread session workspace into flow step test, raw-app inline runnable, inline editor toolbars and MCP OAuth path

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 3 — thread session workspace into dynamic-input helpers and the flow-preview argument side panel (history/saved-inputs/captures)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 4 — thread session workspace into nested flow/script drawers, flow chat inputs and the flow input side tabs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 5 — thread session workspace into script-module fork/reload and key the raw-app schema cache by workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 6 — key the DB manager schema cache by acting workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): address Codex review round 7 — thread session workspace into resource-valued arg pickers and the editor variable/resource helper drawers

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(frontend): scope the flow asset explorer's ResourceEditorDrawer to the acting workspace

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: thread acting workspace through flow asset explore controls

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix: thread acting workspace through SQL REPL, secret args, helper forms, S3 inputs, saved inputs

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 01:53:43 +02:00

193 lines
4.9 KiB
Svelte

<script lang="ts" module>
let cachedValues: Record<
string,
{
code: string | undefined
previousCode: string | undefined
language: ScriptLang | undefined
lock: string | undefined
date: string | undefined
notFound: boolean
tag: string | undefined
}
> = {}
</script>
<script lang="ts">
import HighlightCode from '$lib/components/HighlightCode.svelte'
import TimeAgo from '$lib/components/TimeAgo.svelte'
import { ScriptService, type ScriptLang } from '$lib/gen'
import { getScriptByPath, scriptLangToEditorLang } from '$lib/scripts'
import { workspaceStore } from '$lib/stores'
import { Loader2 } from 'lucide-svelte'
import { getContext, untrack } from 'svelte'
import type { FlowEditorContext } from '../types'
interface Props {
path: string
hash?: string | undefined
previousHash?: string | undefined
showDate?: boolean
showAllCode?: boolean
tag?: string | undefined
language?: ScriptLang | undefined
showDiff?: boolean
}
let {
path,
hash = undefined,
previousHash = undefined,
showDate = false,
showAllCode = $bindable(true),
tag = $bindable(undefined),
showDiff = false,
language = $bindable(undefined)
}: Props = $props()
const flowEditorContext = getContext<FlowEditorContext>('FlowEditorContext')
let opWs = $derived(flowEditorContext?.opWorkspace?.() ?? $workspaceStore)
let code: string | undefined = $state()
let previousCode: string | undefined = $state()
let lock: string | undefined = $state(undefined)
let date: string | undefined = $state(undefined)
let notFound = $state(false)
function getCachedKey(path: string, hash: string | undefined) {
return `${opWs}-${path}-${hash ?? ''}`
}
function getCachedValues(path: string, hash: string | undefined) {
const key = getCachedKey(path, hash)
code = cachedValues[key]?.code
language = cachedValues[key]?.language
lock = cachedValues[key]?.lock
date = cachedValues[key]?.date
previousCode = cachedValues[key]?.previousCode
tag = cachedValues[key]?.tag
notFound = cachedValues[key]?.notFound ?? false
}
getCachedValues(
untrack(() => path),
untrack(() => hash)
)
async function loadPreviousCode(previousHash: string) {
try {
const previousScript = await ScriptService.getScriptByHash({
workspace: opWs!,
hash: previousHash
})
previousCode = previousScript.content
const key = getCachedKey(path, previousHash)
cachedValues[key] = {
...(cachedValues[key] ?? {}),
previousCode: previousScript.content
}
} catch (e) {
console.error(e)
}
}
function toggleShowAll() {
showAllCode = !showAllCode
}
async function loadCode(path: string, hash: string | undefined) {
try {
notFound = false
const script = path.startsWith('hub/')
? await getScriptByPath(path!)
: hash
? await ScriptService.getScriptByHash({ workspace: opWs!, hash })
: await getScriptByPath(path!, opWs)
code = script.content
language = script.language
lock = script.lock
date = script.created_at
tag = script.tag
const key = getCachedKey(path, hash)
cachedValues[key] = {
...(cachedValues[key] ?? {}),
code: script.content,
language: script.language,
lock: script.lock,
date: script.created_at,
tag: script.tag
}
} catch (e) {
notFound = true
console.error(e)
}
}
$effect.pre(() => {
hash
path && untrack(() => loadCode(path, hash))
})
$effect.pre(() => {
path && previousHash && untrack(() => loadPreviousCode(previousHash))
})
</script>
<div class="flex flex-col flex-1 h-full overflow-auto p-2">
{#if showDate && date}
<span class="text-xs text-primary mb-4"><TimeAgo agoOnlyIfRecent {date} /></span>
{/if}
{#if tag}
<div class="text-xs text-primary mb-4">tag: {tag}</div>
{/if}
{#if notFound}
<div class="text-red-400">script not found at {path} in workspace {opWs}</div>
{:else if showAllCode}
{#if showDiff}
{#key (previousCode ?? '') + (code ?? '')}
{#await import('$lib/components/DiffEditor.svelte')}
<Loader2 class="animate-spin" />
{:then Module}
<Module.default
open={true}
className="h-screen"
readOnly
automaticLayout
defaultLang={scriptLangToEditorLang(language)}
defaultOriginal={previousCode}
defaultModified={code}
/>
{/await}
{/key}
{:else}
<HighlightCode {language} {code} />
{/if}
{:else}
<div class="code-container h-full">
<HighlightCode {language} code={code?.split('\n').slice(0, 10).join('\n')} />
</div>
<button onclick={toggleShowAll}>Show all</button>
{/if}
{#if lock}
<h3 class="mb-2 mt-6">Lock</h3>
<pre class="bg-surface-secondary text-xs p-2 overflow-auto w-full">{lock}</pre>
{/if}
</div>
<style>
.code-container {
position: relative;
overflow: hidden;
}
.code-container::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 100px;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0), rgb(var(--color-surface)));
pointer-events: none;
}
</style>