mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 08:00:45 +00:00
flow vscode extension improvements (#2536)
* flow dev * vscode flow extension improvements
This commit is contained in:
@@ -8,7 +8,8 @@
|
||||
type PathScript,
|
||||
ScriptService,
|
||||
Script,
|
||||
type HubScriptKind
|
||||
type HubScriptKind,
|
||||
type OpenFlow
|
||||
} from '$lib/gen'
|
||||
import { initHistory, push, redo, undo } from '$lib/history'
|
||||
import {
|
||||
@@ -61,10 +62,11 @@
|
||||
import { ignoredTutorials } from './tutorials/ignoredTutorials'
|
||||
|
||||
export let initialPath: string = ''
|
||||
export let newFlow: boolean
|
||||
export let selectedId: string | undefined
|
||||
export let initialArgs: Record<string, any> = {}
|
||||
export let loading = false
|
||||
export let flowStore: Writable<Flow>
|
||||
export let flowStore: Writable<OpenFlow>
|
||||
export let flowStateStore: Writable<FlowState>
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@@ -100,13 +102,13 @@
|
||||
|
||||
$dirtyStore = false
|
||||
localStorage.removeItem('flow')
|
||||
localStorage.removeItem(`flow-${flow.path}`)
|
||||
localStorage.removeItem(`flow-${$pathStore}`)
|
||||
|
||||
if (initialPath == '') {
|
||||
if (newFlow) {
|
||||
await FlowService.createFlow({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path: flow.path,
|
||||
path: $pathStore,
|
||||
summary: flow.summary,
|
||||
description: flow.description ?? '',
|
||||
value: flow.value,
|
||||
@@ -119,11 +121,15 @@
|
||||
}
|
||||
await DraftService.createDraft({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: { path: initialPath == '' ? flow.path : initialPath, typ: 'flow', value: flow }
|
||||
requestBody: {
|
||||
path: newFlow ? $pathStore : initialPath,
|
||||
typ: 'flow',
|
||||
value: flow
|
||||
}
|
||||
})
|
||||
if (initialPath == '') {
|
||||
if (newFlow) {
|
||||
$dirtyStore = false
|
||||
dispatch('saveInitial')
|
||||
dispatch('saveInitial', $pathStore)
|
||||
}
|
||||
sendUserToast('Saved as draft')
|
||||
} catch (error) {
|
||||
@@ -149,13 +155,13 @@
|
||||
// return
|
||||
const { cron, timezone, args, enabled } = $scheduleStore
|
||||
$dirtyStore = false
|
||||
if (initialPath === '') {
|
||||
if (newFlow) {
|
||||
localStorage.removeItem('flow')
|
||||
localStorage.removeItem(`flow-${flow.path}`)
|
||||
localStorage.removeItem(`flow-${$pathStore}`)
|
||||
await FlowService.createFlow({
|
||||
workspace: $workspaceStore!,
|
||||
requestBody: {
|
||||
path: flow.path,
|
||||
path: $pathStore,
|
||||
summary: flow.summary,
|
||||
description: flow.description ?? '',
|
||||
value: flow.value,
|
||||
@@ -164,7 +170,7 @@
|
||||
}
|
||||
})
|
||||
if (enabled) {
|
||||
await createSchedule(flow.path)
|
||||
await createSchedule($pathStore)
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem(`flow-${initialPath}`)
|
||||
@@ -172,7 +178,7 @@
|
||||
workspace: $workspaceStore!,
|
||||
path: initialPath,
|
||||
requestBody: {
|
||||
path: flow.path,
|
||||
path: $pathStore,
|
||||
summary: flow.summary,
|
||||
description: flow.description ?? '',
|
||||
value: flow.value,
|
||||
@@ -183,17 +189,17 @@
|
||||
})
|
||||
const scheduleExists = await ScheduleService.existsSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: flow.path
|
||||
path: $pathStore
|
||||
})
|
||||
if (scheduleExists) {
|
||||
const schedule = await ScheduleService.getSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: flow.path
|
||||
path: $pathStore
|
||||
})
|
||||
if (JSON.stringify(schedule.args) != JSON.stringify(args) || schedule.schedule != cron) {
|
||||
await ScheduleService.updateSchedule({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: flow.path,
|
||||
path: $pathStore,
|
||||
requestBody: {
|
||||
schedule: formatCron(cron),
|
||||
timezone,
|
||||
@@ -204,17 +210,17 @@
|
||||
if (enabled != schedule.enabled) {
|
||||
await ScheduleService.setScheduleEnabled({
|
||||
workspace: $workspaceStore ?? '',
|
||||
path: flow.path,
|
||||
path: $pathStore,
|
||||
requestBody: { enabled }
|
||||
})
|
||||
}
|
||||
} else if (enabled) {
|
||||
await createSchedule(flow.path)
|
||||
await createSchedule($pathStore)
|
||||
}
|
||||
}
|
||||
loadingSave = false
|
||||
$dirtyStore = false
|
||||
dispatch('deploy')
|
||||
dispatch('deploy', $pathStore)
|
||||
} catch (err) {
|
||||
sendUserToast(`The flow could not be saved: ${err.body}`, true)
|
||||
loadingSave = false
|
||||
@@ -234,7 +240,7 @@
|
||||
timeout = setTimeout(() => {
|
||||
try {
|
||||
localStorage.setItem(
|
||||
initialPath ? `flow-${initialPath}` : 'flow',
|
||||
initialPath && initialPath != '' ? `flow-${initialPath}` : 'flow',
|
||||
encodeState({
|
||||
flow: $flowStore,
|
||||
selectedId: $selectedIdStore
|
||||
@@ -262,6 +268,9 @@
|
||||
const scriptEditorDrawer = writable<ScriptEditorDrawer | undefined>(undefined)
|
||||
const moving = writable<{ module: FlowModule; modules: FlowModule[] } | undefined>(undefined)
|
||||
const history = initHistory($flowStore)
|
||||
const pathStore = writable<string>(initialPath)
|
||||
|
||||
$: $pathStore = initialPath
|
||||
|
||||
const testStepStore = writable<Record<string, any>>({})
|
||||
|
||||
@@ -278,6 +287,7 @@
|
||||
history,
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
pathStore,
|
||||
testStepStore,
|
||||
saveDraft,
|
||||
initialPath
|
||||
@@ -300,7 +310,7 @@
|
||||
|
||||
$: selectedId && select(selectedId)
|
||||
|
||||
$: initialPath && $workspaceStore && loadSchedule()
|
||||
$: initialPath && initialPath != '' && $workspaceStore && loadSchedule()
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
let classes = event.target?.['className']
|
||||
@@ -366,11 +376,11 @@
|
||||
}> = [
|
||||
{
|
||||
label: 'Exit & see details',
|
||||
onClick: () => dispatch('details')
|
||||
onClick: () => dispatch('details', $pathStore)
|
||||
}
|
||||
]
|
||||
|
||||
if (initialPath != '') {
|
||||
if (!newFlow) {
|
||||
dropdownItems.push({
|
||||
label: 'Fork',
|
||||
onClick: () => window.open(`/flows/add?template=${initialPath}`)
|
||||
@@ -950,7 +960,7 @@
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={$flowStore.path && $flowStore.path != '' ? $flowStore.path : 'Choose a path'}
|
||||
value={$pathStore && $pathStore != '' ? $pathStore : 'Choose a path'}
|
||||
class="font-mono !text-xs !min-w-[96px] !max-w-[300px] !w-full !h-[28px] !my-0 !py-0 !border-l-0 !rounded-l-none"
|
||||
on:focus={({ currentTarget }) => {
|
||||
currentTarget.select()
|
||||
@@ -959,7 +969,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row space-x-2">
|
||||
{#if $enterpriseLicense && initialPath != ''}
|
||||
{#if $enterpriseLicense && !newFlow}
|
||||
<Awareness />
|
||||
{/if}
|
||||
<FlowBuilderTutorials
|
||||
@@ -992,7 +1002,7 @@
|
||||
size="xs"
|
||||
startIcon={{ icon: faSave }}
|
||||
on:click={() => saveFlow()}
|
||||
dropdownItems={initialPath != '' ? dropdownItems : undefined}
|
||||
dropdownItems={!newFlow ? dropdownItems : undefined}
|
||||
>
|
||||
Deploy
|
||||
</Button>
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { Job, JobService, type Flow, type FlowModule, type RestartedFrom } from '$lib/gen'
|
||||
import {
|
||||
Job,
|
||||
JobService,
|
||||
type Flow,
|
||||
type FlowModule,
|
||||
type RestartedFrom,
|
||||
type OpenFlow
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { faClose, faPlay, faRefresh } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Badge, Button, Drawer, Kbd, Popup } from './common'
|
||||
@@ -33,7 +40,7 @@
|
||||
let isRunning: boolean = false
|
||||
let jobProgressReset: () => void
|
||||
|
||||
const { selectedId, previewArgs, flowStateStore, flowStore, initialPath } =
|
||||
const { selectedId, previewArgs, flowStateStore, flowStore, pathStore, initialPath } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -62,7 +69,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
function extractFlow(previewMode: 'upTo' | 'whole'): Flow {
|
||||
function extractFlow(previewMode: 'upTo' | 'whole'): OpenFlow {
|
||||
if (previewMode === 'whole') {
|
||||
return $flowStore
|
||||
} else {
|
||||
@@ -83,7 +90,7 @@
|
||||
) {
|
||||
jobProgressReset()
|
||||
const newFlow = extractFlow(previewMode)
|
||||
jobId = await runFlowPreview(args, newFlow, restartedFrom)
|
||||
jobId = await runFlowPreview(args, newFlow, $pathStore, restartedFrom)
|
||||
isRunning = true
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
export let editor: Editor
|
||||
export let diffEditor: DiffEditor
|
||||
|
||||
const { flowStore, flowStateStore, testStepStore } =
|
||||
const { flowStore, flowStateStore, testStepStore, pathStore } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
// Test
|
||||
@@ -54,7 +54,7 @@
|
||||
// let jobId: string | undefined = undefined
|
||||
if (val.type == 'rawscript') {
|
||||
await testJobLoader?.runPreview(
|
||||
val.path ?? ($flowStore?.path ?? '') + '/' + mod.id,
|
||||
val.path ?? ($pathStore ?? '') + '/' + mod.id,
|
||||
val.content,
|
||||
val.language,
|
||||
args,
|
||||
|
||||
@@ -76,7 +76,6 @@
|
||||
code = ncode
|
||||
if (editor) {
|
||||
editor.setValue(ncode)
|
||||
console.log(editor, ncode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -150,10 +150,29 @@
|
||||
{#each result ?? [] as value, index}
|
||||
<div class="overflow-auto w-full">
|
||||
<ListWrapper
|
||||
onInputsChange={() => {
|
||||
on:set={(e) => {
|
||||
const { id, value } = e.detail
|
||||
if (!inputs[id]) {
|
||||
inputs[id] = { [index]: value }
|
||||
} else {
|
||||
inputs[id] = { ...inputs[id], [index]: value }
|
||||
}
|
||||
outputs?.inputs.set(inputs, true)
|
||||
}}
|
||||
on:remove={(e) => {
|
||||
const id = e.detail
|
||||
if (inputs?.[id] == undefined) {
|
||||
return
|
||||
}
|
||||
if (index == 0) {
|
||||
delete inputs[id]
|
||||
inputs = { ...inputs }
|
||||
} else {
|
||||
delete inputs[id][index]
|
||||
inputs[id] = { ...inputs[id] }
|
||||
}
|
||||
outputs?.inputs.set(inputs, true)
|
||||
}}
|
||||
bind:inputs
|
||||
{value}
|
||||
{index}
|
||||
>
|
||||
@@ -177,7 +196,7 @@
|
||||
</Carousel>
|
||||
{/key}
|
||||
{:else}
|
||||
<ListWrapper onInputsChange={() => {}} disabled value={undefined} index={0}>
|
||||
<ListWrapper disabled value={undefined} index={0}>
|
||||
<SubGridEditor visible={false} {id} subGridId={`${id}-0`} />
|
||||
</ListWrapper>
|
||||
{#if !Array.isArray(result)}
|
||||
|
||||
@@ -87,6 +87,11 @@
|
||||
if (noBackend && componentInput?.type == 'runnable') {
|
||||
result = componentInput?.['value']
|
||||
}
|
||||
|
||||
if (noBackend) {
|
||||
initializing = false
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
$staticExporter[id] = () => {
|
||||
return result
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/common'
|
||||
import { Badge, Button } from '$lib/components/common'
|
||||
|
||||
import Menu from '$lib/components/details/Menu.svelte'
|
||||
import MenuItem from '$lib/components/common/menu/MenuItem.svelte'
|
||||
@@ -23,6 +23,7 @@
|
||||
export let mainButtons: MainButton[] = []
|
||||
export let menuItems: MenuItemButton[] = []
|
||||
export let title: string
|
||||
export let tag: string | undefined
|
||||
|
||||
export let errorHandlerKind: 'flow' | 'script'
|
||||
export let scriptOrFlowPath: string
|
||||
@@ -33,7 +34,9 @@
|
||||
<div class="mx-auto">
|
||||
<div class="flex w-full flex-wrap md:flex-nowrap justify-end gap-x-2 gap-y-4 h-8 items-center">
|
||||
<div class="grow px-2 sm:w-full inline-flex items-center gap-4">
|
||||
<div class="text-lg min-w-24 font-bold truncate">{title}</div>
|
||||
<div class="text-lg min-w-24 font-bold truncate">{title}</div>{#if tag}
|
||||
<Badge>tag: {tag}</Badge>
|
||||
{/if}
|
||||
<slot />
|
||||
</div>
|
||||
<div class="flex gap-1 md:gap-2 items-center">
|
||||
|
||||
@@ -260,6 +260,7 @@ done`
|
||||
</div>
|
||||
{#key token}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<Tabs selected="rest">
|
||||
<Tab value="rest" size="xs">REST</Tab>
|
||||
{#if SCRIPT_VIEW_SHOW_EXAMPLE_CURL}
|
||||
|
||||
@@ -4,9 +4,13 @@
|
||||
import FlowCardHeader from './FlowCardHeader.svelte'
|
||||
export let title: string | undefined = undefined
|
||||
export let flowModule: FlowModule | undefined = undefined
|
||||
export let noEditor: boolean
|
||||
</script>
|
||||
|
||||
<FlowCardHeader {title} bind:flowModule>
|
||||
<slot name="header" />
|
||||
</FlowCardHeader>
|
||||
{#if !noEditor}
|
||||
<FlowCardHeader {title} bind:flowModule>
|
||||
<slot name="header" />
|
||||
</FlowCardHeader>
|
||||
{/if}
|
||||
|
||||
<slot />
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="overflow-x-auto scrollbar-hidden flex items-center justify-between px-4 py-1 py-space-x-2 flex-nowrap"
|
||||
class="overflow-x-auto scrollbar-hidden flex items-center justify-between px-4 py-1 py-space-x-2 flex-nowrap gap-x-2"
|
||||
>
|
||||
{#if flowModule}
|
||||
<span class="text-sm w-full mr-4">
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import { faClipboard } from '@fortawesome/free-solid-svg-icons'
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
|
||||
const { previewArgs, flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
const { previewArgs, flowStore, pathStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
let drawer: Drawer
|
||||
let interval: NodeJS.Timeout | undefined = undefined
|
||||
@@ -32,14 +32,14 @@
|
||||
async function startCapturePoint() {
|
||||
await CaptureService.createCapture({
|
||||
workspace: $workspaceStore!,
|
||||
path: $flowStore.path
|
||||
path: $pathStore
|
||||
})
|
||||
}
|
||||
|
||||
async function getCaptureInput() {
|
||||
const capture = await CaptureService.getCapture({
|
||||
workspace: $workspaceStore!,
|
||||
path: $flowStore.path
|
||||
path: $pathStore
|
||||
})
|
||||
captureInput = capture
|
||||
jsonSchema = { required: [], properties: {}, ...convert(capture) }
|
||||
@@ -65,10 +65,10 @@
|
||||
class="text-2xl"
|
||||
on:click={(e) => {
|
||||
e.preventDefault()
|
||||
copyToClipboard(`${hostname}/api/w/${$workspaceStore}/capture_u/${$flowStore.path}`)
|
||||
copyToClipboard(`${hostname}/api/w/${$workspaceStore}/capture_u/${$pathStore}`)
|
||||
}}
|
||||
href="{hostname}/api/w/{$workspaceStore}/capture_u/{$flowStore.path}"
|
||||
>{hostname}/api/w/{$workspaceStore}/capture_u/{$flowStore.path}
|
||||
href="{hostname}/api/w/{$workspaceStore}/capture_u/{$pathStore}"
|
||||
>{hostname}/api/w/{$workspaceStore}/capture_u/{$pathStore}
|
||||
<Icon data={faClipboard} /></a
|
||||
>
|
||||
</div>
|
||||
@@ -76,7 +76,7 @@
|
||||
|
||||
<div class="text-xs box mb-4 b">
|
||||
<pre class="overflow-auto"
|
||||
>{`curl -X POST ${hostname}/api/w/${$workspaceStore}/capture_u/${$flowStore.path} \\
|
||||
>{`curl -X POST ${hostname}/api/w/${$workspaceStore}/capture_u/${$pathStore} \\
|
||||
-H 'Content-Type: application/json' \\
|
||||
-d '{"foo": 42}'`}</pre
|
||||
>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import FlowCard from '../common/FlowCard.svelte'
|
||||
|
||||
export let noEditor: boolean
|
||||
export let branch: {
|
||||
summary?: string
|
||||
skip_failure?: boolean
|
||||
@@ -11,7 +12,7 @@
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col">
|
||||
<FlowCard title="Branch">
|
||||
<FlowCard {noEditor} title="Branch">
|
||||
<div slot="header" class="grow">
|
||||
<input bind:value={branch.summary} placeholder={'Summary'} />
|
||||
</div>
|
||||
|
||||
@@ -11,10 +11,11 @@
|
||||
}
|
||||
export let parentModule: FlowModule
|
||||
export let previousModule: FlowModule | undefined
|
||||
export let noEditor: boolean
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col">
|
||||
<FlowCard title="Branch">
|
||||
<FlowCard {noEditor} title="Branch">
|
||||
<div slot="header" class="grow">
|
||||
<input bind:value={branch.summary} placeholder={'Summary'} />
|
||||
</div>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import FlowModuleSuspend from './FlowModuleSuspend.svelte'
|
||||
import FlowModuleMock from './FlowModuleMock.svelte'
|
||||
|
||||
export let noEditor: boolean
|
||||
export let flowModule: FlowModule
|
||||
export let previousModule: FlowModule | undefined
|
||||
|
||||
@@ -23,7 +24,7 @@
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col w-full" id="flow-editor-branch-all-wrapper">
|
||||
<FlowCard title={value.type == 'branchall' ? 'Run all branches' : 'Run one branch'}>
|
||||
<FlowCard {noEditor} title={value.type == 'branchall' ? 'Run all branches' : 'Run one branch'}>
|
||||
<SplitPanesWrapper>
|
||||
<Splitpanes horizontal>
|
||||
<Pane size={flowModule ? 60 : 100}>
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
export let flowModule: FlowModule
|
||||
export let previousModule: FlowModule | undefined
|
||||
export let noEditor: boolean
|
||||
|
||||
let value = flowModule.value as BranchOne
|
||||
$: value = flowModule.value as BranchOne
|
||||
@@ -24,7 +25,7 @@
|
||||
</script>
|
||||
|
||||
<div class="h-full" id="flow-editor-branch-one-wrapper">
|
||||
<FlowCard title="Run one branch">
|
||||
<FlowCard {noEditor} title="Run one branch">
|
||||
<SplitPanesWrapper>
|
||||
<Splitpanes horizontal>
|
||||
<Pane size={flowModule ? 60 : 100}>
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
import InputTransformSchemaForm from '$lib/components/InputTransformSchemaForm.svelte'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
|
||||
export let noEditor: boolean
|
||||
|
||||
let hideOptional = false
|
||||
const { flowStateStore, flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
@@ -82,7 +84,7 @@
|
||||
</script>
|
||||
|
||||
<div class="min-h-full">
|
||||
<FlowCard title="All Static Inputs">
|
||||
<FlowCard {noEditor} title="All Static Inputs">
|
||||
<Toggle slot="header" bind:checked={hideOptional} options={{ left: 'Hide optional inputs' }} />
|
||||
<div class="min-h-full flex-1">
|
||||
<Alert type="info" title="Static Inputs" class="m-4"
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
import FlowConstants from './FlowConstants.svelte'
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
|
||||
export let noEditor = false
|
||||
|
||||
const { selectedId, flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
function checkDup(modules: FlowModule[]): string | undefined {
|
||||
@@ -24,15 +26,15 @@
|
||||
</script>
|
||||
|
||||
{#if $selectedId?.startsWith('settings')}
|
||||
<FlowSettings />
|
||||
<FlowSettings {noEditor} />
|
||||
{:else if $selectedId === 'Input'}
|
||||
<FlowInput />
|
||||
<FlowInput {noEditor} />
|
||||
{:else if $selectedId === 'Result'}
|
||||
<p class="p-4 text-secondary">Nothing to show about the result node. Happy flow building!</p>
|
||||
{:else if $selectedId === 'constants'}
|
||||
<FlowConstants />
|
||||
<FlowConstants {noEditor} />
|
||||
{:else if $selectedId === 'failure'}
|
||||
<FlowFailureModule />
|
||||
<FlowFailureModule {noEditor} />
|
||||
{:else}
|
||||
{@const dup = checkDup($flowStore.value.modules)}
|
||||
{#if dup}
|
||||
@@ -40,7 +42,11 @@
|
||||
{:else}
|
||||
{#key $selectedId}
|
||||
{#each $flowStore.value.modules as flowModule, index (flowModule.id ?? index)}
|
||||
<FlowModuleWrapper bind:flowModule previousModule={$flowStore.value.modules[index - 1]} />
|
||||
<FlowModuleWrapper
|
||||
{noEditor}
|
||||
bind:flowModule
|
||||
previousModule={$flowStore.value.modules[index - 1]}
|
||||
/>
|
||||
{/each}
|
||||
{/key}
|
||||
{/if}
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import FlowModuleWrapper from './FlowModuleWrapper.svelte'
|
||||
|
||||
export let noEditor = false
|
||||
|
||||
const { flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
</script>
|
||||
|
||||
{#if $flowStore.value.failure_module}
|
||||
<FlowModuleWrapper
|
||||
{noEditor}
|
||||
bind:flowModule={$flowStore.value.failure_module}
|
||||
on:delete={() => {
|
||||
$flowStore.value.failure_module = undefined
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import SavedInputs from '$lib/components/SavedInputs.svelte'
|
||||
|
||||
export let noEditor: boolean
|
||||
|
||||
const { flowStore, flowStateStore, previewArgs, initialPath } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
@@ -37,7 +39,7 @@
|
||||
</script>
|
||||
|
||||
<CapturePayload bind:this={capturePayload} />
|
||||
<FlowCard title="Flow Input">
|
||||
<FlowCard {noEditor} title="Flow Input">
|
||||
<div class="p-6">
|
||||
<div class="flex flex-row items-center gap-2 pb-2 border-b border-gray-400">
|
||||
<div>Copy input's schema from</div>
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
export let mod: FlowModule
|
||||
export let parentModule: FlowModule | undefined
|
||||
export let previousModule: FlowModule | undefined
|
||||
export let noEditor: boolean
|
||||
|
||||
let editor: SimpleEditor | undefined = undefined
|
||||
let selected: string = 'early-stop'
|
||||
@@ -39,7 +40,7 @@
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col">
|
||||
<FlowCard title="For loop">
|
||||
<FlowCard {noEditor} title="For loop">
|
||||
<div slot="header" class="grow">
|
||||
<input bind:value={mod.summary} placeholder={'Summary'} />
|
||||
</div>
|
||||
|
||||
@@ -45,16 +45,16 @@
|
||||
import { isCloudHosted } from '$lib/cloud'
|
||||
import { loadSchemaFromModule } from '../flowInfers'
|
||||
|
||||
const { selectedId, previewArgs, flowStateStore, flowStore, saveDraft } =
|
||||
const { selectedId, previewArgs, flowStateStore, flowStore, pathStore, saveDraft } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
export let flowModule: FlowModule
|
||||
export let failureModule: boolean = false
|
||||
|
||||
export let parentModule: FlowModule | undefined = undefined
|
||||
export let previousModule: FlowModule | undefined
|
||||
export let scriptKind: 'script' | 'trigger' | 'approval' = 'script'
|
||||
export let scriptTemplate: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' = 'script'
|
||||
export let noEditor: boolean
|
||||
|
||||
let editor: Editor
|
||||
let diffEditor: DiffEditor
|
||||
@@ -159,7 +159,7 @@
|
||||
|
||||
let forceReload = 0
|
||||
|
||||
let editorPanelSize = flowModule.value.type == 'script' ? 30 : 50
|
||||
let editorPanelSize = noEditor ? 0 : flowModule.value.type == 'script' ? 30 : 50
|
||||
let editorSettingsPanelSize = 100 - editorPanelSize
|
||||
</script>
|
||||
|
||||
@@ -167,7 +167,7 @@
|
||||
|
||||
{#if flowModule.value}
|
||||
<div class="h-full" bind:this={wrapper} bind:clientWidth={width}>
|
||||
<FlowCard bind:flowModule>
|
||||
<FlowCard {noEditor} bind:flowModule>
|
||||
<svelte:fragment slot="header">
|
||||
<FlowModuleHeader
|
||||
bind:module={flowModule}
|
||||
@@ -197,7 +197,8 @@
|
||||
flowModule,
|
||||
$selectedId,
|
||||
$flowStateStore[flowModule.id].schema,
|
||||
$flowStore
|
||||
$flowStore,
|
||||
$pathStore
|
||||
)
|
||||
flowModule = module
|
||||
$flowStateStore[module.id] = state
|
||||
@@ -205,7 +206,7 @@
|
||||
/>
|
||||
</svelte:fragment>
|
||||
|
||||
{#if flowModule.value.type === 'rawscript'}
|
||||
{#if flowModule.value.type === 'rawscript' && !noEditor}
|
||||
<div class="border-b-2 shadow-sm px-1">
|
||||
<EditorBar
|
||||
{validCode}
|
||||
@@ -233,59 +234,63 @@
|
||||
<Splitpanes horizontal>
|
||||
<Pane bind:size={editorPanelSize} minSize={20}>
|
||||
{#if flowModule.value.type === 'rawscript'}
|
||||
{#key flowModule.id}
|
||||
<Editor
|
||||
folding
|
||||
path={flowModule.value.path}
|
||||
bind:websocketAlive
|
||||
bind:this={editor}
|
||||
class="h-full relative"
|
||||
bind:code={flowModule.value.content}
|
||||
deno={flowModule.value.language === RawScript.language.DENO}
|
||||
lang={scriptLangToEditorLang(flowModule.value.language)}
|
||||
automaticLayout={true}
|
||||
cmdEnterAction={async () => {
|
||||
selected = 'test'
|
||||
if ($selectedId == flowModule.id) {
|
||||
{#if !noEditor}
|
||||
{#key flowModule.id}
|
||||
<Editor
|
||||
folding
|
||||
path={flowModule.value.path}
|
||||
bind:websocketAlive
|
||||
bind:this={editor}
|
||||
class="h-full relative"
|
||||
bind:code={flowModule.value.content}
|
||||
deno={flowModule.value.language === RawScript.language.DENO}
|
||||
lang={scriptLangToEditorLang(flowModule.value.language)}
|
||||
automaticLayout={true}
|
||||
cmdEnterAction={async () => {
|
||||
selected = 'test'
|
||||
if ($selectedId == flowModule.id) {
|
||||
if (flowModule.value.type === 'rawscript') {
|
||||
flowModule.value.content = editor.getCode()
|
||||
}
|
||||
await reload(flowModule)
|
||||
modulePreview?.runTestWithStepArgs()
|
||||
}
|
||||
}}
|
||||
on:change={async (event) => {
|
||||
if (flowModule.value.type === 'rawscript') {
|
||||
flowModule.value.content = editor.getCode()
|
||||
flowModule.value.content = event.detail
|
||||
}
|
||||
await reload(flowModule)
|
||||
modulePreview?.runTestWithStepArgs()
|
||||
}
|
||||
}}
|
||||
on:change={async (event) => {
|
||||
if (flowModule.value.type === 'rawscript') {
|
||||
flowModule.value.content = event.detail
|
||||
}
|
||||
await reload(flowModule)
|
||||
}}
|
||||
formatAction={() => {
|
||||
reload(flowModule)
|
||||
saveDraft()
|
||||
}}
|
||||
fixedOverflowWidgets={true}
|
||||
args={Object.entries(flowModule.value.input_transforms).reduce(
|
||||
(acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)}
|
||||
/>
|
||||
<DiffEditor
|
||||
bind:this={diffEditor}
|
||||
automaticLayout
|
||||
fixedOverflowWidgets
|
||||
class="hidden h-full"
|
||||
/>
|
||||
{/key}
|
||||
{:else if flowModule.value.type === 'script'}
|
||||
<div class="border-t">
|
||||
{#key forceReload}
|
||||
<FlowModuleScript path={flowModule.value.path} hash={flowModule.value.hash} />
|
||||
}}
|
||||
formatAction={() => {
|
||||
reload(flowModule)
|
||||
saveDraft()
|
||||
}}
|
||||
fixedOverflowWidgets={true}
|
||||
args={Object.entries(flowModule.value.input_transforms).reduce(
|
||||
(acc, [key, obj]) => {
|
||||
acc[key] = obj.type === 'static' ? obj.value : undefined
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)}
|
||||
/>
|
||||
<DiffEditor
|
||||
bind:this={diffEditor}
|
||||
automaticLayout
|
||||
fixedOverflowWidgets
|
||||
class="hidden h-full"
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
{:else if flowModule.value.type === 'script'}
|
||||
{#if !noEditor}
|
||||
<div class="border-t">
|
||||
{#key forceReload}
|
||||
<FlowModuleScript path={flowModule.value.path} hash={flowModule.value.hash} />
|
||||
{/key}
|
||||
</div>
|
||||
{/if}
|
||||
{:else if flowModule.value.type === 'flow'}
|
||||
<FlowPathViewer path={flowModule.value.path} />
|
||||
{/if}
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
import FlowBranchesAllWrapper from './FlowBranchesAllWrapper.svelte'
|
||||
import FlowBranchesOneWrapper from './FlowBranchesOneWrapper.svelte'
|
||||
|
||||
export let flowModule: FlowModule
|
||||
export let noEditor: boolean = false
|
||||
|
||||
const { selectedId, schedule, flowStateStore } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
export let flowModule: FlowModule
|
||||
let scriptKind: 'script' | 'trigger' | 'approval' = 'script'
|
||||
let scriptTemplate: 'pgsql' | 'mysql' | 'script' | 'docker' | 'powershell' = 'script'
|
||||
|
||||
@@ -34,11 +36,11 @@
|
||||
|
||||
{#if flowModule.id === $selectedId}
|
||||
{#if flowModule.value.type === 'forloopflow'}
|
||||
<FlowLoop bind:mod={flowModule} {parentModule} {previousModule} />
|
||||
<FlowLoop {noEditor} bind:mod={flowModule} {parentModule} {previousModule} />
|
||||
{:else if flowModule.value.type === 'branchone'}
|
||||
<FlowBranchesOneWrapper {previousModule} bind:flowModule />
|
||||
<FlowBranchesOneWrapper {noEditor} {previousModule} bind:flowModule />
|
||||
{:else if flowModule.value.type === 'branchall'}
|
||||
<FlowBranchesAllWrapper {previousModule} bind:flowModule />
|
||||
<FlowBranchesAllWrapper {noEditor} {previousModule} bind:flowModule />
|
||||
{:else if flowModule.value.type === 'identity'}
|
||||
{#if $selectedId == 'failure'}
|
||||
<div class="p-4">
|
||||
@@ -123,6 +125,7 @@
|
||||
{/if}
|
||||
{:else if flowModule.value.type === 'rawscript' || flowModule.value.type === 'script' || flowModule.value.type === 'flow'}
|
||||
<FlowModuleComponent
|
||||
{noEditor}
|
||||
bind:flowModule
|
||||
{parentModule}
|
||||
{previousModule}
|
||||
@@ -156,7 +159,7 @@
|
||||
{/if}
|
||||
{#each flowModule.value.branches as branch, branchIndex (branchIndex)}
|
||||
{#if $selectedId === `${flowModule?.id}-branch-${branchIndex}`}
|
||||
<FlowBranchOneWrapper bind:branch parentModule={flowModule} {previousModule} />
|
||||
<FlowBranchOneWrapper {noEditor} bind:branch parentModule={flowModule} {previousModule} />
|
||||
{:else}
|
||||
{#each branch.modules as submodule, index}
|
||||
<svelte:self
|
||||
@@ -170,7 +173,7 @@
|
||||
{:else if flowModule.value.type === 'branchall'}
|
||||
{#each flowModule.value.branches as branch, branchIndex (branchIndex)}
|
||||
{#if $selectedId === `${flowModule?.id}-branch-${branchIndex}`}
|
||||
<FlowBranchAllWrapper bind:branch />
|
||||
<FlowBranchAllWrapper {noEditor} bind:branch />
|
||||
{:else}
|
||||
{#each branch.modules as submodule, index}
|
||||
<svelte:self
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import ErrorHandlerToggleButton from '$lib/components/details/ErrorHandlerToggleButton.svelte'
|
||||
|
||||
const { selectedId, flowStore, initialPath, previewArgs } =
|
||||
export let noEditor: boolean
|
||||
|
||||
const { selectedId, flowStore, initialPath, previewArgs, pathStore } =
|
||||
getContext<FlowEditorContext>('FlowEditorContext')
|
||||
|
||||
async function loadWorkerGroups() {
|
||||
@@ -38,8 +40,8 @@
|
||||
}
|
||||
|
||||
let hostname = BROWSER ? window.location.protocol + '//' + window.location.host : 'SSR'
|
||||
$: url = `${hostname}/api/w/${$workspaceStore}/jobs/run/f/${$flowStore?.path}`
|
||||
$: syncedUrl = `${hostname}/api/w/${$workspaceStore}/jobs/run_wait_result/f/${$flowStore?.path}`
|
||||
$: url = `${hostname}/api/w/${$workspaceStore}/jobs/run/f/${$pathStore}`
|
||||
$: syncedUrl = `${hostname}/api/w/${$workspaceStore}/jobs/run_wait_result/f/${$pathStore}`
|
||||
|
||||
$: if ($selectedId == 'settings-worker-group') {
|
||||
$workerTags = undefined
|
||||
@@ -55,11 +57,13 @@
|
||||
</script>
|
||||
|
||||
<div class="h-full overflow-hidden">
|
||||
<FlowCard title="Settings">
|
||||
<FlowCard {noEditor} title="Settings">
|
||||
<div class="h-full flex-1">
|
||||
<Tabs bind:selected={$selectedId}>
|
||||
<Tab value="settings-metadata">Metadata</Tab>
|
||||
<Tab value="settings-schedule">Schedule</Tab>
|
||||
{#if !noEditor}
|
||||
<Tab value="settings-schedule">Schedule</Tab>
|
||||
{/if}
|
||||
<Tab value="settings-same-worker">Shared Directory</Tab>
|
||||
<Tab value="settings-early-stop">Early Stop</Tab>
|
||||
<Tab value="settings-worker-group">Worker Group</Tab>
|
||||
@@ -90,17 +94,19 @@
|
||||
/>
|
||||
</Label>
|
||||
|
||||
<Label label="Path">
|
||||
<Path
|
||||
autofocus={false}
|
||||
bind:this={path}
|
||||
bind:dirty={dirtyPath}
|
||||
bind:path={$flowStore.path}
|
||||
{initialPath}
|
||||
namePlaceholder="flow"
|
||||
kind="flow"
|
||||
/>
|
||||
</Label>
|
||||
{#if !noEditor}
|
||||
<Label label="Path">
|
||||
<Path
|
||||
autofocus={false}
|
||||
bind:this={path}
|
||||
bind:dirty={dirtyPath}
|
||||
bind:path={$pathStore}
|
||||
{initialPath}
|
||||
namePlaceholder="flow"
|
||||
kind="flow"
|
||||
/>
|
||||
</Label>
|
||||
{/if}
|
||||
|
||||
<Label label="Description">
|
||||
<textarea
|
||||
@@ -155,7 +161,7 @@
|
||||
<div class="flex flex-row items-center gap-1">
|
||||
<ErrorHandlerToggleButton
|
||||
kind="flow"
|
||||
scriptOrFlowPath={$flowStore.path}
|
||||
scriptOrFlowPath={$pathStore}
|
||||
bind:errorHandlerMuted={$flowStore.ws_error_handler_muted}
|
||||
iconOnly={false}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Flow, FlowModule, InputTransform } from '$lib/gen'
|
||||
import type { FlowModule, InputTransform, OpenFlow } from '$lib/gen'
|
||||
|
||||
type ModuleBranches = FlowModule[][]
|
||||
|
||||
@@ -46,7 +46,7 @@ function exprsOfInputTransforms(x: Record<string, InputTransform>): string[] {
|
||||
.flat()
|
||||
}
|
||||
|
||||
export function getDependentComponents(id: string, flow: Flow): Record<string, string[]> {
|
||||
export function getDependentComponents(id: string, flow: OpenFlow): Record<string, string[]> {
|
||||
let modules = getAllModules(flow.value.modules, flow.value.failure_module)
|
||||
return filterDependentComponents(modules, id)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { Flow } from '$lib/gen/models/Flow'
|
||||
import type { OpenFlow } from '$lib/gen'
|
||||
import { dfs } from './dfs'
|
||||
import type { FlowState } from './flowState'
|
||||
import { charsToNumber, numberToChars } from './idUtils'
|
||||
|
||||
// Computes the next available id
|
||||
export function nextId(flowState: FlowState, fullFlow: Flow): string {
|
||||
export function nextId(flowState: FlowState, fullFlow: OpenFlow): string {
|
||||
const allIds = dfs(fullFlow.value.modules, (fm) => fm.id)
|
||||
const max = allIds.concat(Object.keys(flowState)).reduce((acc, key) => {
|
||||
if (key === 'failure' || key.includes('branch') || key.includes('loop')) {
|
||||
|
||||
@@ -2,11 +2,11 @@ import type { Schema } from '$lib/common'
|
||||
import {
|
||||
Script,
|
||||
ScriptService,
|
||||
type Flow,
|
||||
type FlowModule,
|
||||
type PathFlow,
|
||||
type PathScript,
|
||||
type RawScript
|
||||
type RawScript,
|
||||
type OpenFlow
|
||||
} from '$lib/gen'
|
||||
import { initialCode } from '$lib/script_helpers'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
@@ -175,7 +175,7 @@ async function createInlineScriptModuleFromPath(
|
||||
}
|
||||
}
|
||||
|
||||
export function emptyModule(flowState: FlowState, fullFlow: Flow, flow?: boolean): FlowModule {
|
||||
export function emptyModule(flowState: FlowState, fullFlow: OpenFlow, flow?: boolean): FlowModule {
|
||||
return {
|
||||
id: nextId(flowState, fullFlow),
|
||||
value: { type: 'identity', flow }
|
||||
@@ -186,7 +186,8 @@ export async function createScriptFromInlineScript(
|
||||
flowModule: FlowModule,
|
||||
suffix: string,
|
||||
schema: Schema,
|
||||
flow: Flow
|
||||
flow: OpenFlow,
|
||||
flowPath: string
|
||||
): Promise<[FlowModule & { value: PathScript }, FlowModuleState]> {
|
||||
const user = get(userStore)
|
||||
|
||||
@@ -202,9 +203,9 @@ export async function createScriptFromInlineScript(
|
||||
suffix = others.join('/')
|
||||
}
|
||||
|
||||
const path = `${flow.path}/${suffix}`
|
||||
const path = `${flowPath}/${suffix}`
|
||||
const forkedDescription = wasForked ? `as a fork of ${originalScriptPath}` : ''
|
||||
const description = `This script was edited in place of flow ${flow.path} ${forkedDescription} by ${user?.username}.`
|
||||
const description = `This script was edited in place of flow ${flowPath} ${forkedDescription} by ${user?.username}.`
|
||||
|
||||
const availablePath = await findNextAvailablePath(path)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Flow } from '$lib/gen'
|
||||
import type { Flow, OpenFlow } from '$lib/gen'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { initFlowState, type FlowState } from './flowState'
|
||||
|
||||
@@ -15,7 +15,7 @@ export async function initFlow(
|
||||
flowStore.set(flow)
|
||||
}
|
||||
|
||||
export async function copyFirstStepSchema(flowState: FlowState, flowStore: Writable<Flow>) {
|
||||
export async function copyFirstStepSchema(flowState: FlowState, flowStore: Writable<OpenFlow>) {
|
||||
flowStore.update((flow) => {
|
||||
const firstModuleId = flow.value.modules[0]?.id
|
||||
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
'inputs',
|
||||
'schedules',
|
||||
'failure',
|
||||
'constants'
|
||||
'constants',
|
||||
'Result',
|
||||
'Input'
|
||||
].includes($selectedId) ||
|
||||
$selectedId?.includes('branch')
|
||||
</script>
|
||||
@@ -48,8 +50,8 @@
|
||||
}}
|
||||
startIcon={{ icon: faPlay }}
|
||||
>
|
||||
Test up to
|
||||
<Badge baseClass="ml-1" color="indigo">
|
||||
Test up to
|
||||
<Badge baseClass="ml-1" small color="indigo">
|
||||
{$selectedId}
|
||||
</Badge>
|
||||
</Button>
|
||||
|
||||
@@ -141,11 +141,13 @@
|
||||
'bg-surface border'
|
||||
)}
|
||||
>
|
||||
<svelte:component
|
||||
this={APP_TO_ICON_COMPONENT[item['app']]}
|
||||
height={18}
|
||||
width={18}
|
||||
/>
|
||||
{#if item['app'] in APP_TO_ICON_COMPONENT}
|
||||
<svelte:component
|
||||
this={APP_TO_ICON_COMPONENT[item['app']]}
|
||||
height={18}
|
||||
width={18}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="w-full text-left font-normal">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import type { Flow, FlowModule } from '$lib/gen'
|
||||
import type { FlowModule, OpenFlow } from '$lib/gen'
|
||||
import { schemaToObject } from '$lib/schema'
|
||||
import { getAllSubmodules, getSubModules } from './flowExplorer'
|
||||
import type { FlowState } from './flowState'
|
||||
@@ -18,7 +18,7 @@ type StepPropPicker = {
|
||||
|
||||
type ModuleBranches = FlowModule[][]
|
||||
|
||||
export function dfs(id: string | undefined, flow: Flow, getParents: boolean = true): FlowModule[] {
|
||||
export function dfs(id: string | undefined, flow: OpenFlow, getParents: boolean = true): FlowModule[] {
|
||||
if (id === undefined) {
|
||||
return []
|
||||
}
|
||||
@@ -85,7 +85,7 @@ function getFlowInput(
|
||||
}
|
||||
}
|
||||
|
||||
export function getPreviousIds(id: string, flow: Flow, include_node: boolean): string[] {
|
||||
export function getPreviousIds(id: string, flow: OpenFlow, include_node: boolean): string[] {
|
||||
const df = dfs(id, flow, false)
|
||||
if (!include_node) {
|
||||
df.shift()
|
||||
@@ -110,7 +110,7 @@ export function getStepPropPicker(
|
||||
parentModule: FlowModule | undefined,
|
||||
previousModule: FlowModule | undefined,
|
||||
id: string,
|
||||
flow: Flow,
|
||||
flow: OpenFlow,
|
||||
args: any,
|
||||
include_node: boolean
|
||||
): StepPropPicker {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Flow, FlowModule } from '$lib/gen'
|
||||
import type { FlowModule, OpenFlow } from '$lib/gen'
|
||||
import type { History } from '$lib/history'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type ScriptEditorDrawer from './content/ScriptEditorDrawer.svelte'
|
||||
@@ -11,8 +11,9 @@ export type FlowEditorContext = {
|
||||
schedule: Writable<Schedule>
|
||||
previewArgs: Writable<Record<string, any>>
|
||||
scriptEditorDrawer: Writable<ScriptEditorDrawer | undefined>
|
||||
history: History<Flow>
|
||||
flowStore: Writable<Flow>
|
||||
history: History<OpenFlow>
|
||||
pathStore: Writable<string>
|
||||
flowStore: Writable<OpenFlow & { tag?: string, ws_error_handler_muted?: boolean}>
|
||||
flowStateStore: Writable<FlowState>
|
||||
testStepStore: Writable<Record<string, any>>
|
||||
saveDraft: () => void
|
||||
|
||||
@@ -5,7 +5,8 @@ import {
|
||||
type FlowModule,
|
||||
type InputTransform,
|
||||
type Job,
|
||||
type RestartedFrom
|
||||
type RestartedFrom,
|
||||
type OpenFlow
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { cleanExpr, emptySchema } from '$lib/utils'
|
||||
@@ -68,7 +69,7 @@ export function evalValue(
|
||||
return v
|
||||
}
|
||||
|
||||
export function cleanInputs(flow: Flow | any): Flow {
|
||||
export function cleanInputs(flow: OpenFlow | any): OpenFlow & {tag?: string, ws_error_handler_muted?: boolean}{
|
||||
const newFlow: Flow = JSON.parse(JSON.stringify(flow))
|
||||
newFlow.value.modules.forEach((mod) => {
|
||||
if (mod.value.type == 'rawscript' || mod.value.type == 'script') {
|
||||
@@ -109,14 +110,14 @@ export function jobsToResults(jobs: Job[]) {
|
||||
})
|
||||
}
|
||||
|
||||
export async function runFlowPreview(args: Record<string, any>, flow: Flow, restartedFrom: RestartedFrom | undefined) {
|
||||
export async function runFlowPreview(args: Record<string, any>, flow: OpenFlow & { tag?: string }, path: string, restartedFrom: RestartedFrom | undefined) {
|
||||
const newFlow = flow
|
||||
return await JobService.runFlowPreview({
|
||||
workspace: get(workspaceStore) ?? '',
|
||||
requestBody: {
|
||||
args,
|
||||
value: newFlow.value,
|
||||
path: newFlow.path,
|
||||
path: path,
|
||||
tag: newFlow.tag,
|
||||
restarted_from: restartedFrom,
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@
|
||||
<span>
|
||||
{#if level != 0}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<span class="cursor-pointer border hover:bg-surface-hover px-1 rounded" on:click={collapse}>
|
||||
-
|
||||
</span>
|
||||
@@ -122,6 +123,7 @@
|
||||
{/if}
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<span
|
||||
class="border border-blue-600 rounded px-1 cursor-pointer hover:bg-gray-200"
|
||||
class:hidden={!collapsed}
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
</Portal>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
class={twMerge(
|
||||
'hover:bg-surface-hover cursor-pointer',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Flow, FlowModule } from '$lib/gen'
|
||||
import type { FlowModule, OpenFlow } from '$lib/gen'
|
||||
import { findGridItem } from '../apps/editor/appUtils'
|
||||
import type { App } from '../apps/types'
|
||||
|
||||
@@ -55,7 +55,7 @@ export function selectOptionsBySelector(selector: string, value: string) {
|
||||
}
|
||||
}
|
||||
|
||||
export function isFlowTainted(flow: Flow) {
|
||||
export function isFlowTainted(flow: OpenFlow) {
|
||||
return flow.value.modules.length > 0 || Object.keys(flow?.schema?.properties).length > 0
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export function isAppTainted(app: App) {
|
||||
}
|
||||
|
||||
export function updateFlowModuleById(
|
||||
flow: Flow,
|
||||
flow: OpenFlow,
|
||||
id: string,
|
||||
callback: (module: FlowModule) => void
|
||||
) {
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
let selectedId: string = 'settings-metadata'
|
||||
let loading = false
|
||||
|
||||
let initialPath: string | undefined = undefined
|
||||
|
||||
export const flowStore = writable<Flow>({
|
||||
summary: '',
|
||||
value: { modules: [] },
|
||||
@@ -85,15 +87,16 @@
|
||||
path: templatePath
|
||||
})
|
||||
Object.assign(flow, template)
|
||||
const oldPath = flow.path.split('/')
|
||||
flow.path = `u/${$userStore?.username.split('@')[0]}/${oldPath[oldPath.length - 1]}_fork`
|
||||
const oldPath = templatePath.split('/')
|
||||
console.log(oldPath)
|
||||
initialPath = `u/${$userStore?.username.split('@')[0]}/${oldPath[oldPath.length - 1]}_fork`
|
||||
flow = flow
|
||||
goto('?', { replaceState: true })
|
||||
selectedId = 'settings-metadata'
|
||||
} else if (hubId) {
|
||||
const hub = await FlowService.getHubFlowById({ id: Number(hubId) })
|
||||
delete hub['comments']
|
||||
flow.path = `u/${$userStore?.username}/flow_${hubId}`
|
||||
initialPath = `u/${$userStore?.username}/flow_${hubId}`
|
||||
Object.assign(flow, hub.flow)
|
||||
flow = flow
|
||||
goto('?', { replaceState: true })
|
||||
@@ -120,17 +123,19 @@
|
||||
<UnsavedConfirmationModal />
|
||||
|
||||
<FlowBuilder
|
||||
on:saveInitial={() => {
|
||||
goto(`/flows/edit/${$flowStore.path}?selected=${getSelectedId?.()}`)
|
||||
on:saveInitial={(e) => {
|
||||
goto(`/flows/edit/${e.detail}?selected=${getSelectedId?.()}`)
|
||||
}}
|
||||
on:deploy={() => {
|
||||
goto(`/flows/get/${$flowStore.path}?workspace=${$workspaceStore}`)
|
||||
on:deploy={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
on:details={() => {
|
||||
goto(`/flows/get/${$flowStore.path}?workspace=${$workspaceStore}`)
|
||||
on:details={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
{initialPath}
|
||||
bind:getSelectedId
|
||||
bind:this={flowBuilder}
|
||||
newFlow
|
||||
{flowStore}
|
||||
{flowStateStore}
|
||||
{selectedId}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import type { FlowState } from '$lib/components/flows/flowState'
|
||||
import FlowModuleSchemaMap from '$lib/components/flows/map/FlowModuleSchemaMap.svelte'
|
||||
import FlowEditorPanel from '$lib/components/flows/content/FlowEditorPanel.svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
let testJobLoader: TestJobLoader
|
||||
|
||||
@@ -30,6 +31,8 @@
|
||||
extra_perms: {},
|
||||
schema: emptySchema()
|
||||
} as Flow)
|
||||
|
||||
let initialCode = JSON.stringify($flowStore, null, 4)
|
||||
const flowStateStore = writable({} as FlowState)
|
||||
const scheduleStore = writable({
|
||||
args: {},
|
||||
@@ -43,7 +46,7 @@
|
||||
const history = initHistory($flowStore)
|
||||
|
||||
const testStepStore = writable<Record<string, any>>({})
|
||||
const selectedIdStore = writable('')
|
||||
const selectedIdStore = writable('settings-metadata')
|
||||
|
||||
// function select(selectedId: string) {
|
||||
// selectedIdStore.set(selectedId)
|
||||
@@ -56,6 +59,7 @@
|
||||
scriptEditorDrawer,
|
||||
moving,
|
||||
history,
|
||||
pathStore: writable(''),
|
||||
flowStateStore,
|
||||
flowStore,
|
||||
testStepStore,
|
||||
@@ -165,6 +169,14 @@
|
||||
// }
|
||||
}
|
||||
let editor: SimpleEditor
|
||||
|
||||
$: updateCode(editor, $flowStore)
|
||||
|
||||
function updateCode(editor: SimpleEditor, flow: Flow) {
|
||||
if (editor && !deepEqual(flow, JSON.parse(editor.getCode()))) {
|
||||
editor.setCode(JSON.stringify(flow, null, 4))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown} />
|
||||
@@ -175,28 +187,28 @@
|
||||
<div class="h-full w-full grid grid-cols-2">
|
||||
<SimpleEditor
|
||||
bind:this={editor}
|
||||
code={JSON.stringify($flowStore, null, 4)}
|
||||
code={initialCode}
|
||||
lang="json"
|
||||
on:change={(e) => {
|
||||
const code = e.detail.code
|
||||
try {
|
||||
$flowStore = JSON.parse(code)
|
||||
if (!deepEqual(JSON.parse(code), $flowStore)) {
|
||||
$flowStore = JSON.parse(code)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('issue parsing new change:', code, e)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div class="flex flex-col h-full relative">
|
||||
<div class="flex justify-center pt-1 absolute right-2 top-2">
|
||||
<div class="flex flex-col max-h-screen h-full relative">
|
||||
<div class="flex justify-center pt-1 z-50 absolute right-2 top-2 gap-2">
|
||||
<FlowPreviewButtons />
|
||||
</div>
|
||||
<Splitpanes horizontal class="h-full">
|
||||
<Splitpanes horizontal class="h-full max-h-screen grow">
|
||||
<Pane size={33}>
|
||||
{#if $flowStore?.value?.modules}
|
||||
<FlowModuleSchemaMap
|
||||
disableHeader
|
||||
bind:modules={$flowStore.value.modules}
|
||||
on:change={() => editor?.setCode(JSON.stringify($flowStore, null, 4))}
|
||||
disableAi
|
||||
disableTutorials
|
||||
/>
|
||||
@@ -205,7 +217,7 @@
|
||||
{/if}
|
||||
</Pane>
|
||||
<Pane size={67}>
|
||||
<FlowEditorPanel />
|
||||
<FlowEditorPanel noEditor />
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
|
||||
@@ -96,15 +96,16 @@
|
||||
<UnsavedConfirmationModal />
|
||||
|
||||
<FlowBuilder
|
||||
on:deploy={() => {
|
||||
goto(`/flows/get/${$flowStore.path}?workspace=${$workspaceStore}`)
|
||||
on:deploy={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
on:details={() => {
|
||||
goto(`/flows/get/${$flowStore.path}?workspace=${$workspaceStore}`)
|
||||
on:details={(e) => {
|
||||
goto(`/flows/get/${e.detail}?workspace=${$workspaceStore}`)
|
||||
}}
|
||||
{flowStore}
|
||||
{flowStateStore}
|
||||
initialPath={$page.params.path}
|
||||
newFlow={false}
|
||||
{selectedId}
|
||||
{initialArgs}
|
||||
{loading}
|
||||
|
||||
@@ -255,6 +255,7 @@
|
||||
bind:errorHandlerMuted={flow.ws_error_handler_muted}
|
||||
scriptOrFlowPath={flow.path}
|
||||
errorHandlerKind="flow"
|
||||
tag={flow.tag}
|
||||
>
|
||||
{#if flow?.value?.priority != undefined}
|
||||
<div class="hidden md:block">
|
||||
|
||||
@@ -414,6 +414,7 @@
|
||||
bind:errorHandlerMuted={script.ws_error_handler_muted}
|
||||
errorHandlerKind="script"
|
||||
scriptOrFlowPath={script.path}
|
||||
tag={script.tag}
|
||||
>
|
||||
{#if script?.priority != undefined}
|
||||
<div class="hidden md:block">
|
||||
|
||||
Reference in New Issue
Block a user