diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index b3757a38e7..fa2fc1296b 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -140,6 +140,7 @@ const selectedIdStore = writable('settings') const scheduleStore = writable(undefined) + const previewArgsStore = writable>(undefined) function select(selectedId: string) { selectedIdStore.set(selectedId) @@ -149,7 +150,8 @@ selectedId: selectedIdStore, schedule: scheduleStore, select, - path: initialPath + path: initialPath, + previewArgs: previewArgsStore }) $: { diff --git a/frontend/src/lib/components/FlowPreview.svelte b/frontend/src/lib/components/FlowPreview.svelte index 366e26f3b7..9743957678 100644 --- a/frontend/src/lib/components/FlowPreview.svelte +++ b/frontend/src/lib/components/FlowPreview.svelte @@ -13,7 +13,7 @@ export let flow: Flow export let schema: Schema - const [i, j] = indexes.split('-').map(Number) + const [i, j] = indexes.split('-').filter(Boolean).map(Number) let stepArgs: Record = {} let jobId: string diff --git a/frontend/src/lib/components/FlowPreviewContent.svelte b/frontend/src/lib/components/FlowPreviewContent.svelte index 0fb1f00f8d..da4dcf96a4 100644 --- a/frontend/src/lib/components/FlowPreviewContent.svelte +++ b/frontend/src/lib/components/FlowPreviewContent.svelte @@ -11,18 +11,16 @@ import { flowStore } from './flows/flowStore' import type { FlowEditorContext } from './flows/types' import { runFlowPreview } from './flows/utils' - import FlowStatusViewer from './FlowStatusViewer.svelte' import SchemaForm from './SchemaForm.svelte' + import FlowStatusViewer from '../components/FlowStatusViewer.svelte' export let previewMode: 'upTo' | 'whole' - let args: Record = {} - let jobId: string | undefined = undefined let isValid: boolean = false let intervalState: 'idle' | 'canceled' | 'done' | 'running' = 'idle' - const { selectedId } = getContext('FlowEditorContext') + const { selectedId, previewArgs } = getContext('FlowEditorContext') function extractFlow(previewMode: 'upTo' | 'whole') { if (previewMode === 'whole') { @@ -104,7 +102,7 @@
- +
{#if intervalState === 'running'} @@ -128,7 +126,12 @@ Cancel {:else} - {/if} @@ -140,9 +143,11 @@ on:jobsLoaded={(e) => { intervalState = 'done' const [parentIndex] = $selectedId.split('-') - const configIndex = - previewMode === 'upTo' ? Number(parentIndex) : $flowStateStore.modules.length - mapJobResultsToFlowState(e.detail, 'upto', configIndex, undefined) + + const upToIndex = + previewMode === 'upTo' ? Number(parentIndex) + 1 : $flowStateStore.modules.length + + mapJobResultsToFlowState(e.detail, 'upto', upToIndex, undefined) }} root={true} /> diff --git a/frontend/src/lib/components/FlowStatusViewer.svelte b/frontend/src/lib/components/FlowStatusViewer.svelte index d1fc78f78f..adfacaa981 100644 --- a/frontend/src/lib/components/FlowStatusViewer.svelte +++ b/frontend/src/lib/components/FlowStatusViewer.svelte @@ -127,5 +127,5 @@ {/if} {:else} - No script selected + Job starting... {/if} diff --git a/frontend/src/lib/components/InputTransformForm.svelte b/frontend/src/lib/components/InputTransformForm.svelte index 9bcb9b8b4b..c0aed6970a 100644 --- a/frontend/src/lib/components/InputTransformForm.svelte +++ b/frontend/src/lib/components/InputTransformForm.svelte @@ -2,16 +2,18 @@ import type { Schema } from '$lib/common' import type { InputTransform } from '$lib/gen' import type { InputCat } from '$lib/utils' - import { faChain } from '@fortawesome/free-solid-svg-icons' - import { Button, Tooltip } from 'flowbite-svelte' - import Icon from 'svelte-awesome' + import { getContext } from 'svelte' + import ArgInput from './ArgInput.svelte' import FieldHeader from './FieldHeader.svelte' import DynamicInputHelpBox from './flows/content/DynamicInputHelpBox.svelte' + import type { PropPickerWrapperContext } from './flows/propPicker/PropPickerWrapper.svelte' import { codeToStaticTemplate, getDefaultExpr, isCodeInjection } from './flows/utils' - import OverlayPropertyPicker from './propertyPicker/OverlayPropertyPicker.svelte' import SimpleEditor from './SimpleEditor.svelte' import Toggle from './Toggle.svelte' + import { Button, Tooltip } from 'flowbite-svelte' + import Icon from 'svelte-awesome' + import { faChain } from '@fortawesome/free-solid-svg-icons' export let schema: Schema export let arg: InputTransform | any @@ -19,9 +21,7 @@ export let extraLib: string = 'missing extraLib' export let inputCheck: { [id: string]: boolean } export let importPath: string | undefined = undefined - export let pickableProperties: Object | undefined = undefined - let overlays: { [id: string]: OverlayPropertyPicker } = {} let monacos: { [id: string]: SimpleEditor } = {} let inputCats: { [id: string]: InputCat } = {} @@ -60,16 +60,6 @@ return inputCat === 'string' || inputCat === 'sql' } - function focusProp(argName: string, monacoEditor: boolean = false) { - Object.keys(overlays).forEach((k) => { - if (k == argName && (isStaticTemplate(inputCats[argName]) || monacoEditor)) { - overlays[k].focus() - } else { - overlays[k].unfocus() - } - }) - } - function connectProperty(argName: string, rawValue: string) { if (isStaticTemplate(inputCats[argName])) { arg.value = `\$\{${rawValue}}` @@ -86,6 +76,8 @@ } $: checked = propertyType == 'javascript' + + const { focusProp } = getContext('PropPickerWrapper') {#if arg != undefined} @@ -137,14 +129,10 @@
{#if propertyType === undefined || !checked} - { - if (detail.pickerVariation === 'connect') { - connectProperty(argName, detail.propPath) - } else { - const toAppend = `\$\{${detail.propPath}}` + { + focusProp(argName, 'append', (path) => { + const toAppend = `\$\{${path}}` arg.value = `${arg.value ?? ''}${toAppend}` if (monacos[argName]) { monacos[argName].setCode(arg.value) @@ -152,66 +140,64 @@ if (isStaticTemplate(inputCats[argName])) { setPropertyType(arg.value) } + }) + }} + label={argName} + bind:editor={monacos[argName]} + bind:description={schema.properties[argName].description} + bind:value={arg.value} + type={schema.properties[argName].type} + required={schema.required.includes(argName)} + bind:pattern={schema.properties[argName].pattern} + bind:valid={inputCheck[argName]} + defaultValue={schema.properties[argName].default} + bind:enum_={schema.properties[argName].enum} + bind:format={schema.properties[argName].format} + contentEncoding={schema.properties[argName].contentEncoding} + bind:itemsType={schema.properties[argName].items} + properties={schema.properties[argName].properties} + displayHeader={false} + bind:inputCat={inputCats[argName]} + on:input={(e) => { + if (isStaticTemplate(inputCats[argName])) { + setPropertyType(e.detail.rawValue) } }} > - focusProp(argName)} - label={argName} - bind:editor={monacos[argName]} - bind:description={schema.properties[argName].description} - bind:value={arg.value} - type={schema.properties[argName].type} - required={schema.required.includes(argName)} - bind:pattern={schema.properties[argName].pattern} - bind:valid={inputCheck[argName]} - defaultValue={schema.properties[argName].default} - bind:enum_={schema.properties[argName].enum} - bind:format={schema.properties[argName].format} - contentEncoding={schema.properties[argName].contentEncoding} - bind:itemsType={schema.properties[argName].items} - properties={schema.properties[argName].properties} - displayHeader={false} - bind:inputCat={inputCats[argName]} - on:input={(e) => { - if (isStaticTemplate(inputCats[argName])) { - setPropertyType(e.detail.rawValue) - } - }} - > -
-
overlays[argName]?.focus('connect')}> - - - -
+
+
{ + focusProp(argName, 'connect', (path) => { + connectProperty(argName, path) + }) + }} + > + + +
- - +
+ {:else if checked} {#if arg.expr != undefined} - { - monacos[argName].insertAtCursor(event.detail.propPath) - }} - > -
- focusProp(argName, true)} - bind:code={arg.expr} - lang="javascript" - class="few-lines-editor" - {extraLib} - extraLibPath="file:///node_modules/@types/windmill@{importPath}/index.d.ts" - shouldBindKey={false} - /> -
-
+
+ { + focusProp(argName, 'insert', (path) => { + monacos[argName].insertAtCursor(path) + }) + }} + bind:code={arg.expr} + lang="javascript" + class="few-lines-editor" + {extraLib} + extraLibPath="file:///node_modules/@types/windmill@{importPath}/index.d.ts" + shouldBindKey={false} + /> +
{/if} {:else} diff --git a/frontend/src/lib/components/SchemaForm.svelte b/frontend/src/lib/components/SchemaForm.svelte index f7796aaf3b..6822effb97 100644 --- a/frontend/src/lib/components/SchemaForm.svelte +++ b/frontend/src/lib/components/SchemaForm.svelte @@ -2,7 +2,9 @@ import type { Schema } from '$lib/common' import type { InputTransform } from '$lib/gen' import { allTrue } from '$lib/utils' + import { getContext, hasContext } from 'svelte' import ArgInput from './ArgInput.svelte' + import type { PropPickerWrapperContext } from './flows/propPicker/PropPickerWrapper.svelte' import InputTransformForm from './InputTransformForm.svelte' export let inputTransform = false @@ -10,7 +12,6 @@ export let args: Record = {} export let editableSchema = false export let isValid: boolean = true - export let pickableProperties: Object | undefined = undefined export let extraLib: string = 'missing extraLib' export let importPath: string | undefined = undefined @@ -31,6 +32,16 @@ } $: schema?.properties && removeExtraKey() + + function getFocusFunction() { + if (hasContext('PropPickerWrapper')) { + const { focus } = getContext('PropPickerWrapper') + return focus + } + return (value: string | undefined) => {} + } + + const focus = getFocusFunction()
@@ -42,7 +53,6 @@ bind:schema bind:argName bind:inputCheck - bind:pickableProperties bind:extraLib bind:importPath /> @@ -61,6 +71,9 @@ contentEncoding={schema.properties[argName].contentEncoding} properties={schema.properties[argName].properties} bind:itemsType={schema.properties[argName].items} + on:focus={() => { + focus && focus(argName) + }} {editableSchema} /> {/if} diff --git a/frontend/src/lib/components/common/tabs/Tabs.svelte b/frontend/src/lib/components/common/tabs/Tabs.svelte index 6af7dab370..e8f157cebc 100644 --- a/frontend/src/lib/components/common/tabs/Tabs.svelte +++ b/frontend/src/lib/components/common/tabs/Tabs.svelte @@ -22,7 +22,7 @@ }) -
+
diff --git a/frontend/src/lib/components/common/toggleButton/ToggleButton.svelte b/frontend/src/lib/components/common/toggleButton/ToggleButton.svelte new file mode 100644 index 0000000000..5a9a17b890 --- /dev/null +++ b/frontend/src/lib/components/common/toggleButton/ToggleButton.svelte @@ -0,0 +1,24 @@ + + + diff --git a/frontend/src/lib/components/common/toggleButton/ToggleButtonGroup.svelte b/frontend/src/lib/components/common/toggleButton/ToggleButtonGroup.svelte new file mode 100644 index 0000000000..d19bd7285e --- /dev/null +++ b/frontend/src/lib/components/common/toggleButton/ToggleButtonGroup.svelte @@ -0,0 +1,39 @@ + + + + +
+ +
diff --git a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte index 4b8b208e2d..2f1d8127fe 100644 --- a/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte +++ b/frontend/src/lib/components/flows/content/FlowEditorPanel.svelte @@ -3,7 +3,6 @@ import type { FlowEditorContext } from '../types' import FlowModuleWrapper from './FlowModuleWrapper.svelte' - import FlowSchedules from './FlowSchedules.svelte' import FlowSettings from './FlowSettings.svelte' import FlowInput from './FlowInput.svelte' import FlowLoop from './FlowLoop.svelte' diff --git a/frontend/src/lib/components/flows/content/FlowModule.svelte b/frontend/src/lib/components/flows/content/FlowModule.svelte index 8525e19820..fd1cc87949 100644 --- a/frontend/src/lib/components/flows/content/FlowModule.svelte +++ b/frontend/src/lib/components/flows/content/FlowModule.svelte @@ -27,9 +27,9 @@ import FlowModuleHeader from './FlowModuleHeader.svelte' import { flowStateStore, type FlowModuleSchema } from '../flowState' import { scriptLangToEditorLang } from '$lib/utils' + import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte' import { getContext } from 'svelte' import type { FlowEditorContext } from '../types' - import Toggle from '$lib/components/Toggle.svelte' import FlowModuleAdvancedSettings from './FlowModuleAdvancedSettings.svelte' export let indexes: string @@ -97,7 +97,6 @@ shouldDisableLoopCreation={indexes.length > 1 || parentIndex == 0} on:loop={() => { applyCreateLoop() - select(['loop', $selectedId].join('-')) }} on:pick={(e) => apply(pickScript, e.detail.path)} @@ -119,6 +118,8 @@ {#if flowModule.value.type === 'rawscript'} @@ -137,46 +138,36 @@ {/if} - + Inputs - Test + Test Advanced -
-
- -
- -
-
- +
+ + + + + + +
- - - {#if ('path' in flowModule.value && flowModule.value.path) || ('language' in flowModule.value && flowModule.value.language)} - undefined} - class="overflow-x-auto" - type="text" - bind:value={flowModule.summary} - placeholder="Summary" - /> - {/if} - +
+
- + +
- -
+
+
diff --git a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte index 3bbbffd14c..7764f28ca9 100644 --- a/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte +++ b/frontend/src/lib/components/flows/content/FlowModuleWrapper.svelte @@ -4,7 +4,7 @@ import type { FlowEditorContext } from '../types' import FlowModule from './FlowModule.svelte' - const { selectedId } = getContext('FlowEditorContext') + const { selectedId, previewArgs } = getContext('FlowEditorContext') function selectedIdToIndexes(selectedId: string): number[] { return selectedId.split('-').map(Number) @@ -17,8 +17,8 @@ {#each $flowStateStore.modules[parentIndex].childFlowModules ?? [] as fa, index} {#if index === childIndex} Configuration Schedule - +
Flow permissions depend on their path. Select the group - + diff --git a/frontend/src/lib/components/flows/flowStateUtils.ts b/frontend/src/lib/components/flows/flowStateUtils.ts index ff61afbbfd..9858ad2092 100644 --- a/frontend/src/lib/components/flows/flowStateUtils.ts +++ b/frontend/src/lib/components/flows/flowStateUtils.ts @@ -1,13 +1,5 @@ import type { Schema } from '$lib/common' -import { - CompletedJob, - Job, - Script, - ScriptService, - type Flow, - type FlowModule, - type RawScript -} from '$lib/gen' +import { CompletedJob, Job, Script, ScriptService, type FlowModule, type RawScript } from '$lib/gen' import { initialCode } from '$lib/script_helpers' import { userStore, workspaceStore } from '$lib/stores' import { @@ -208,10 +200,11 @@ export function getStepPropPicker( args: Record ): StepPropPicker { const isInsideLoop: boolean = indexes.length > 1 - const [parentIndex] = indexes + const [parentIndex, childIndex] = indexes const flowInput = schemaToObject(flowInputSchema, args) const results = getPreviousResults(flowState.modules, parentIndex) + const lastResult = results.length > 0 ? results[results.length - 1] : undefined if (isInsideLoop) { @@ -281,25 +274,30 @@ export type JobResult = { export function mapJobResultsToFlowState( jobs: JobResult, config: 'upto' | 'justthis', - i: number, + parentIndex: number, j: number | undefined ): void { - if (!Array.isArray(jobs.innerJobs) || jobs.innerJobs.length === 0) { - return - } - if (config === 'justthis') { const job = jobs.job as CompletedJob flowStateStore.update((flowState: FlowState) => { - if (j) { - flowState[i].childFlowModules[j].previewResult = job.result - } else { - flowState[i].previewResult = job.result + if (flowState.modules) { + const childFlowModules = flowState.modules[parentIndex].childFlowModules + if (j && childFlowModules) { + childFlowModules[j].previewResult = job.result + flowState.modules[parentIndex].childFlowModules = childFlowModules + } else { + flowState.modules[parentIndex].previewResult = job.result + } } + return flowState }) } else { + if (!Array.isArray(jobs.innerJobs) || jobs.innerJobs.length === 0) { + return + } + const results = jobs.innerJobs.map(({ job, loopJobs }) => { if (Array.isArray(loopJobs) && loopJobs.length > 0) { return loopJobs.map(({ job }) => { @@ -315,12 +313,12 @@ export function mapJobResultsToFlowState( }) flowStateStore.update((flowState: FlowState) => { - if (!Array.isArray(flowState)) { + if (!Array.isArray(flowState.modules)) { return flowState } - const modules = flowState.modules.map((flowModuleSchema: FlowModuleSchema, index) => { - if (index <= i) { + const modules = flowState.modules.map((flowModuleSchema: FlowModuleSchema, index: number) => { + if (index <= parentIndex) { flowModuleSchema.previewResult = results[index] } diff --git a/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte new file mode 100644 index 0000000000..2b67d94370 --- /dev/null +++ b/frontend/src/lib/components/flows/propPicker/PropPickerWrapper.svelte @@ -0,0 +1,61 @@ + + + + + + +
+ +
+
+ +
+ { + $propPickerConfig?.onSelect(detail) + propPickerConfig.set(undefined) + }} + /> +
+
+
diff --git a/frontend/src/lib/components/flows/types.ts b/frontend/src/lib/components/flows/types.ts index 6b9e4de2bd..662fef994d 100644 --- a/frontend/src/lib/components/flows/types.ts +++ b/frontend/src/lib/components/flows/types.ts @@ -6,4 +6,5 @@ export type FlowEditorContext = { select: (id: string) => void schedule: Writable path: string + previewArgs: Writable> } diff --git a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte index 59aca04fd3..cdba6819cc 100644 --- a/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte +++ b/frontend/src/lib/components/propertyPicker/ObjectViewer.svelte @@ -39,15 +39,19 @@ {#if keys.length > 0} - {#if level != 0}(-){/if} + {#if level != 0} + (-) + + {/if}
    {#each keys as key, index}
  • - + {#if getTypeAsString(json[key]) === 'object'} - import { createEventDispatcher } from 'svelte' - import { createPopperActions } from 'svelte-popperjs' - import PropPicker from './PropPicker.svelte' - - const [popperRef, popperContent] = createPopperActions({ - placement: 'bottom', - strategy: 'fixed' - }) - - export let pickableProperties: Object | undefined - let isOpen = false - let isFocused = false - - let timeout: NodeJS.Timeout - - type PickerVariation = 'append' | 'connect' - let pickerVariation: PickerVariation = 'append' - - export function unfocus() { - isFocused = false - close() - } - export function focus(newPickerVariation?: PickerVariation) { - if (newPickerVariation) { - pickerVariation = newPickerVariation - } - isFocused = true - open() - } - - function open() { - if (isFocused) { - clearTimeout(timeout) - !isOpen && (isOpen = true) - } - } - function close() { - if (pickerVariation === 'append') { - timeout = setTimeout(() => (isOpen = false), 50) - } - } - - function closePropertyPicker() { - timeout = setTimeout(() => (isOpen = false), 50) - } - - const dispatch = createEventDispatcher() - - -
    -
    - -
    - - {#if isOpen} -
    - { - dispatch('select', { propPath: event.detail, pickerVariation }) - isOpen = false - pickerVariation = 'append' - }} - /> -
    - {/if} -
    - - diff --git a/frontend/src/lib/components/propertyPicker/PropPicker.svelte b/frontend/src/lib/components/propertyPicker/PropPicker.svelte index 3d896086ea..5c4416a2ec 100644 --- a/frontend/src/lib/components/propertyPicker/PropPicker.svelte +++ b/frontend/src/lib/components/propertyPicker/PropPicker.svelte @@ -1,4 +1,9 @@ -
    -
    - - +
    +
    + Context +
    + {#if $propPickerConfig} + + {`Selected input: ${$propPickerConfig?.propName}`} + + + {`Mode: ${$propPickerConfig?.insertionMode}`} + + + {/if} +
    + +
    +
    +
    diff --git a/frontend/src/lib/components/script_editor/LogPanel.svelte b/frontend/src/lib/components/script_editor/LogPanel.svelte index 385bbde676..eaaa1e1321 100644 --- a/frontend/src/lib/components/script_editor/LogPanel.svelte +++ b/frontend/src/lib/components/script_editor/LogPanel.svelte @@ -65,7 +65,7 @@ Last save -
    +
    {#if previewJob && previewJob.logs}{previewJob.logs}
    diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts
    index c4205f402e..a97e3147a6 100644
    --- a/frontend/src/lib/utils.ts
    +++ b/frontend/src/lib/utils.ts
    @@ -54,8 +54,9 @@ export function displayDate(dateString: string | undefined): string {
     	if (date.toString() === 'Invalid Date') {
     		return ''
     	} else {
    -		return `${date.getFullYear()}/${date.getMonth() + 1
    -			}/${date.getDate()} at ${date.toLocaleTimeString()}`
    +		return `${date.getFullYear()}/${
    +			date.getMonth() + 1
    +		}/${date.getDate()} at ${date.toLocaleTimeString()}`
     	}
     }
     
    @@ -561,7 +562,6 @@ export function classNames(...classes: string[]): string {
     	return classes.filter(Boolean).join(' ')
     }
     
    -
     export function scriptLangToEditorLang(lang: Script.language): 'typescript' | 'python' | 'go' {
     	if (lang == 'deno') {
     		return 'typescript'
    @@ -570,4 +570,4 @@ export function scriptLangToEditorLang(lang: Script.language): 'typescript' | 'p
     	} else {
     		return lang
     	}
    -}
    \ No newline at end of file
    +}