diff --git a/frontend/src/lib/components/EditorBar.svelte b/frontend/src/lib/components/EditorBar.svelte index d0e6d05a1a..91516843ab 100644 --- a/frontend/src/lib/components/EditorBar.svelte +++ b/frontend/src/lib/components/EditorBar.svelte @@ -94,7 +94,7 @@ // editor). `undefined` = not applicable; `false` makes the badge red // even if the main function parses. validAssets?: boolean | undefined - kind?: 'script' | 'trigger' | 'approval' + kind?: 'script' | 'trigger' | 'approval' | 'preprocessor' template?: | 'pgsql' | 'mysql' @@ -580,7 +580,13 @@ {#if pick_existing == 'hub'} - + + {:else} diff --git a/frontend/src/lib/components/flows/content/FlowInputs.svelte b/frontend/src/lib/components/flows/content/FlowInputs.svelte index e275df3976..f86d870a1a 100644 --- a/frontend/src/lib/components/flows/content/FlowInputs.svelte +++ b/frontend/src/lib/components/flows/content/FlowInputs.svelte @@ -44,6 +44,11 @@ ? 'approval' : 'script' ) + // The preprocessor slot shows no kind toggle, so `kind` stays 'script' there. Everything that + // tags a script (inline template, pre-made list) must key off this, never off `kind`. + let scriptKind: 'script' | 'failure' | 'approval' | 'trigger' | 'preprocessor' = $derived( + preprocessorModule ? 'preprocessor' : kind + ) let pick_existing: 'workspace' | 'hub' = $state('hub') let filter = $state('') @@ -56,7 +61,7 @@ ) function displayLang(lang: SupportedLanguage | 'docker', kind: string) { - if (preprocessorModule) { + if (kind === 'preprocessor') { return canHavePreprocessor(lang as SupportedLanguage) } @@ -218,21 +223,23 @@

Inline new {kind == 'script' ? 'action' : kind}{scriptKind == 'script' ? 'action' : scriptKind} script - Embed {kind == 'script' ? 'action' : kind} script directly inside a flow instead - of saving the script into your workspace for reuse. You can always save an inline script to - your workspace later. + Embed {scriptKind == 'script' ? 'action' : scriptKind} script directly inside + a flow instead of saving the script into your workspace for reuse. You can always save an inline + script to your workspace later.
@@ -250,7 +257,7 @@ {/if}
{#each langs.filter((lang) => customUi?.languages == undefined || customUi?.languages?.includes(lang?.[1])) as [label, lang] (lang)} - {#if displayLang(lang, kind)} + {#if displayLang(lang, scriptKind)} { dispatch('new', { language: lang == 'docker' ? 'bash' : lang, - kind, + kind: scriptKind, subkind: lang == 'docker' ? 'docker' : preprocessorModule ? 'preprocessor' : 'flow', summary }) @@ -289,10 +296,13 @@

Use pre-made {kind == 'script' ? 'action' : kind}{scriptKind == 'script' ? 'action' : scriptKind} script

- {#if pick_existing == 'hub'} + {#if preprocessorModule} + + + {:else if pick_existing == 'hub'} diff --git a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte index d1fdf8f412..d7a8c4effa 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleComponent.svelte @@ -109,7 +109,7 @@ preprocessorModule?: boolean parentModule?: FlowModule | undefined previousModule: FlowModule | undefined - scriptKind?: 'script' | 'trigger' | 'approval' + scriptKind?: 'script' | 'trigger' | 'approval' | 'preprocessor' scriptTemplate?: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' noEditor: boolean enableAi: boolean @@ -171,6 +171,11 @@ shellcheck: false }) + // `scriptKind` only records how a step was created this session, so it is back to 'script' on + // any remount. Being a preprocessor is a property of the slot, and the editor bar's reset code + // and script library depend on it, so derive it rather than reading the stale state. + let editorScriptKind = $derived(preprocessorModule ? 'preprocessor' : scriptKind) + let selected = $state(untrack(() => preprocessorModule) ? 'test' : 'inputs') let canShowChatTab = $derived( !preprocessorModule && @@ -864,7 +869,7 @@ {websocketAlive} iconOnly={width < EDITOR_BAR_WIDTH_THRESHOLD} compactHelpers={width < EDITOR_BAR_HELPERS_INLINE_THRESHOLD} - kind={scriptKind} + kind={editorScriptKind} template={scriptTemplate} args={Object.entries(flowModule.value.input_transforms).reduce((acc, [key, obj]) => { acc[key] = obj.type === 'static' ? obj.value : undefined diff --git a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte index 6f832902d0..c5ae6901ac 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte @@ -32,7 +32,7 @@ const { triggersState, triggersCount } = getContext('TriggerContext') - let scriptKind: 'script' | 'trigger' | 'approval' = $state('script') + let scriptKind: 'script' | 'trigger' | 'approval' | 'preprocessor' = $state('script') let scriptTemplate: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' = $state('script') // These pointers are used to easily access previewArgs of parent module, and previous module diff --git a/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte b/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte index 8659fd5404..575744cb04 100644 --- a/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte +++ b/frontend/src/lib/components/flows/pickers/WorkspaceScriptPicker.svelte @@ -26,7 +26,7 @@ const flowEditorContext = getContext('FlowEditorContext') let opWs = $derived(flowEditorContext?.opWorkspace?.() ?? $workspaceStore) interface Props { - kind?: 'script' | 'trigger' | 'approval' | 'failure' + kind?: 'script' | 'trigger' | 'approval' | 'failure' | 'preprocessor' isTemplate?: boolean | undefined displayLock?: boolean filter?: string @@ -122,38 +122,48 @@ />
{/if} - {#if filter.length > 0 && filteredItems.length == 0} - - {/if} -
    - {#each filteredItems as { path, hash, summary, description, marked }} -
  • - -
  • - {/each} -
+ {#if lockHash}{truncateHash(hash ?? '')}{/if} + + + {/each} + + {/if} {:else}
diff --git a/frontend/src/lib/components/flows/pickers/WorkspaceScriptPickerQuick.svelte b/frontend/src/lib/components/flows/pickers/WorkspaceScriptPickerQuick.svelte index 07df5bd5c5..9e3a20b4a7 100644 --- a/frontend/src/lib/components/flows/pickers/WorkspaceScriptPickerQuick.svelte +++ b/frontend/src/lib/components/flows/pickers/WorkspaceScriptPickerQuick.svelte @@ -165,6 +165,11 @@ {#if filteredItems.length == 0}
{kind == 'flow' ? 'No flows found.' : 'No scripts found.'} + {#if kind == 'preprocessor'} +
+ Only workspace scripts whose kind is set to Preprocessor are listed here. +
+ {/if}
{/if}
    diff --git a/frontend/src/lib/script_helpers.test.ts b/frontend/src/lib/script_helpers.test.ts index 1badf03e11..92370f5d2e 100644 --- a/frontend/src/lib/script_helpers.test.ts +++ b/frontend/src/lib/script_helpers.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest' -import { bashRunsInCustomImage } from './script_helpers' +import { bashRunsInCustomImage, getResetCode } from './script_helpers' // bashRunsInCustomImage decides whether the +Variable/+Resource pickers insert a // curl/wget snippet (custom image, no wmill CLI) or the wmill CLI snippet. It must @@ -41,3 +41,26 @@ describe('bashRunsInCustomImage', () => { expect(bashRunsInCustomImage('# shellcheck shell=bash\necho hi')).toBe(false) }) }) + +// Preprocessors must bypass getResetCode's per-language `main` templates: a preprocessor step +// resets through the same button as an action script, and a `main` body cannot run under the +// preprocessor entrypoint. +describe('getResetCode for preprocessors', () => { + // The concrete `language` values a preprocessor step can carry. PREPROCESSOR_SUPPORTED_LANGUAGES + // also holds the 'typescript'/'python' aliases, which no script is ever stored with. + const langs = ['deno', 'bun', 'python3', 'php'] as const + + it('keeps the preprocessor entrypoint in every language that can have one', () => { + for (const lang of langs) { + const code = getResetCode(lang, 'preprocessor', undefined) + expect(code, lang).toContain('preprocessor') + expect(code, lang).not.toContain('function main') + expect(code, lang).not.toContain('def main') + } + }) + + it('still resets action scripts to a main stub', () => { + expect(getResetCode('python3', 'script', undefined)).toContain('def main') + expect(getResetCode('bun', 'script', undefined)).toContain('function main') + }) +}) diff --git a/frontend/src/lib/script_helpers.ts b/frontend/src/lib/script_helpers.ts index 178f4474e7..76ecfa59d8 100644 --- a/frontend/src/lib/script_helpers.ts +++ b/frontend/src/lib/script_helpers.ts @@ -1703,6 +1703,11 @@ export function getResetCode( | 'ci_test_python' | undefined ) { + // Every *_INIT_CODE_CLEAR below is a `main` stub, which cannot run under the preprocessor + // entrypoint. Preprocessors must go through initialCode to keep theirs. + if (kind === 'preprocessor') { + return initialCode(language, kind, subkind) + } if (language === 'deno') { return DENO_INIT_CODE_CLEAR } else if (language === 'python3') {