mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-05 16:02:24 +00:00
feat(frontend): background script can run script and flows (#1515)
* all * progress * all * all
This commit is contained in:
@@ -1,27 +1,17 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type {
|
||||
ConnectedAppInput,
|
||||
RowAppInput,
|
||||
StaticAppInput,
|
||||
UserAppInput
|
||||
} from '../../inputType'
|
||||
import type { AppViewerContext, InlineScript } from '../../types'
|
||||
|
||||
import type { AppViewerContext, HiddenRunnable } from '../../types'
|
||||
import RunnableComponent from './RunnableComponent.svelte'
|
||||
import InitializeComponent from './InitializeComponent.svelte'
|
||||
|
||||
export let id: string
|
||||
export let name: string
|
||||
export let inlineScript: InlineScript | undefined
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let autoRefresh: boolean
|
||||
export let noBackendValue: any = undefined
|
||||
export let recomputeOnInputChanged: boolean
|
||||
export let runnable: HiddenRunnable
|
||||
|
||||
const { worldStore, staticExporter, noBackend } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let result: any = noBackend ? noBackendValue : undefined
|
||||
let result: any = noBackend ? runnable.noBackendValue : undefined
|
||||
|
||||
onMount(() => {
|
||||
$staticExporter[id] = () => {
|
||||
@@ -35,21 +25,18 @@
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if inlineScript}
|
||||
{#if runnable && (runnable.type == 'runnableByPath' || (runnable.type == 'runnableByName' && runnable.inlineScript != undefined))}
|
||||
<RunnableComponent
|
||||
render={false}
|
||||
{id}
|
||||
{fields}
|
||||
{autoRefresh}
|
||||
fields={runnable.fields}
|
||||
autoRefresh={true}
|
||||
bind:result
|
||||
transformer={undefined}
|
||||
{recomputeOnInputChanged}
|
||||
runnable={{
|
||||
name,
|
||||
inlineScript,
|
||||
type: 'runnableByName'
|
||||
}}
|
||||
recomputeOnInputChanged={runnable.recomputeOnInputChanged ?? true}
|
||||
{runnable}
|
||||
wrapperClass="hidden"
|
||||
recomputableByRefreshButton={runnable.autoRefresh ?? true}
|
||||
{outputs}
|
||||
>
|
||||
<slot />
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
export let recomputeOnInputChanged: boolean = true
|
||||
export let loading = false
|
||||
export let refreshOnStart: boolean = false
|
||||
export let recomputableByRefreshButton: boolean
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
@@ -346,7 +347,7 @@
|
||||
}
|
||||
|
||||
$runnableComponents[id] = {
|
||||
autoRefresh: autoRefresh,
|
||||
autoRefresh: autoRefresh && recomputableByRefreshButton,
|
||||
refreshOnStart: refreshOnStart,
|
||||
cb: cancellableRun
|
||||
}
|
||||
|
||||
@@ -124,9 +124,8 @@
|
||||
bind:result
|
||||
runnable={componentInput.runnable}
|
||||
transformer={componentInput.transformer}
|
||||
autoRefresh={componentInput.autoRefresh === undefined
|
||||
? autoRefresh
|
||||
: componentInput.autoRefresh}
|
||||
{autoRefresh}
|
||||
recomputableByRefreshButton={componentInput.autoRefresh ?? true}
|
||||
bind:recomputeOnInputChanged={componentInput.recomputeOnInputChanged}
|
||||
{id}
|
||||
{extraQueryParams}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
import SettingsPanel from './SettingsPanel.svelte'
|
||||
import { secondaryMenu, SecondaryMenu } from './settingsPanel/secondaryMenu'
|
||||
import Popover from '../../Popover.svelte'
|
||||
import { migrateApp } from '../utils'
|
||||
|
||||
export let app: App
|
||||
export let path: string
|
||||
@@ -52,6 +53,8 @@
|
||||
export let summary: string
|
||||
export let fromHub: boolean = false
|
||||
|
||||
migrateApp(app)
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const selectedComponent = writable<string[] | undefined>(undefined)
|
||||
const mode = writable<EditorMode>(initialMode)
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
AppInput,
|
||||
ConnectedAppInput,
|
||||
RowAppInput,
|
||||
Runnable,
|
||||
StaticAppInput,
|
||||
UserAppInput
|
||||
} from '../inputType'
|
||||
@@ -120,7 +121,7 @@
|
||||
)
|
||||
}
|
||||
async function computeTriggerables() {
|
||||
const allTriggers = await Promise.all(
|
||||
const allTriggers = (await Promise.all(
|
||||
allItems($app.grid, $app.subgrids)
|
||||
.flatMap((x) => {
|
||||
let c = x.data as AppComponent
|
||||
@@ -132,27 +133,32 @@
|
||||
})
|
||||
.map(async (input) => {
|
||||
if (input?.type == 'runnable') {
|
||||
const staticInputs = collectStaticFields(input.fields)
|
||||
if (input.runnable?.type == 'runnableByName') {
|
||||
let hex = await hash(input.runnable.inlineScript?.content)
|
||||
return [`rawscript/${hex}`, staticInputs]
|
||||
} else if (input.runnable?.type == 'runnableByPath') {
|
||||
let prefix =
|
||||
input.runnable.runType !== 'hubscript' ? input.runnable.runType : 'script'
|
||||
return [`${prefix}/${input.runnable.path}`, staticInputs]
|
||||
}
|
||||
return await processRunnable(input.runnable, input.fields)
|
||||
}
|
||||
return []
|
||||
})
|
||||
.concat(
|
||||
Object.values($app.hiddenInlineScripts ?? {}).map(async (v) => {
|
||||
let hex = await hash(v.inlineScript?.content)
|
||||
const staticInputs = collectStaticFields(v.fields)
|
||||
return [`rawscript/${hex}`, staticInputs]
|
||||
return await processRunnable(v, v.fields)
|
||||
})
|
||||
)
|
||||
)) as ([string, Record<string, any>] | undefined)[]
|
||||
policy.triggerables = Object.fromEntries(
|
||||
allTriggers.filter(Boolean) as [string, Record<string, any>][]
|
||||
)
|
||||
policy.triggerables = Object.fromEntries(allTriggers.filter((x) => x.length > 0))
|
||||
}
|
||||
|
||||
async function processRunnable(
|
||||
runnable: Runnable,
|
||||
fields: Record<string, any>
|
||||
): Promise<[string, Record<string, any>] | undefined> {
|
||||
const staticInputs = collectStaticFields(fields)
|
||||
if (runnable?.type == 'runnableByName') {
|
||||
let hex = await hash(runnable.inlineScript?.content)
|
||||
return [`rawscript/${hex}`, staticInputs]
|
||||
} else if (runnable?.type == 'runnableByPath') {
|
||||
let prefix = runnable.runType !== 'hubscript' ? runnable.runType : 'script'
|
||||
return [`${prefix}/${runnable.path}`, staticInputs]
|
||||
}
|
||||
}
|
||||
async function createApp(path: string) {
|
||||
await computeTriggerables()
|
||||
@@ -496,7 +502,13 @@
|
||||
</Drawer>
|
||||
|
||||
<Drawer bind:open={jobsDrawerOpen} size="900px">
|
||||
<DrawerContent noPadding title="Debug Runs" on:close={() => (jobsDrawerOpen = false)} tooltip="Look at latests runs to spot potential bugs." documentationLink="https://docs.windmill.dev/docs/apps/app_toolbar#debug-runs">
|
||||
<DrawerContent
|
||||
noPadding
|
||||
title="Debug Runs"
|
||||
on:close={() => (jobsDrawerOpen = false)}
|
||||
tooltip="Look at latests runs to spot potential bugs."
|
||||
documentationLink="https://docs.windmill.dev/docs/apps/app_toolbar#debug-runs"
|
||||
>
|
||||
<Splitpanes class="!overflow-visible">
|
||||
<Pane size={25}>
|
||||
<PanelSection title="Past Runs">
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
import { HiddenComponent } from '../components'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { dfs } from './appUtils'
|
||||
import { migrateApp } from '../utils'
|
||||
|
||||
export let app: App
|
||||
export let appPath: string
|
||||
@@ -33,6 +34,8 @@
|
||||
export let noBackend: boolean = false
|
||||
export let isLocked = false
|
||||
|
||||
migrateApp(app)
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const selectedComponent = writable<string[] | undefined>(undefined)
|
||||
const mode = writable<EditorMode>('preview')
|
||||
@@ -163,17 +166,9 @@
|
||||
</div>
|
||||
|
||||
{#if app.hiddenInlineScripts}
|
||||
{#each app.hiddenInlineScripts as script, index}
|
||||
{#if script}
|
||||
<HiddenComponent
|
||||
id={`bg_${index}`}
|
||||
inlineScript={script.inlineScript}
|
||||
name={script.name}
|
||||
fields={script.fields}
|
||||
recomputeOnInputChanged={script.recomputeOnInputChanged ?? true}
|
||||
autoRefresh={script.autoRefresh ?? false}
|
||||
noBackendValue={script.noBackendValue}
|
||||
/>
|
||||
{#each app.hiddenInlineScripts as runnable, index}
|
||||
{#if runnable}
|
||||
<HiddenComponent id={`bg_${index}`} {runnable} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -127,16 +127,9 @@
|
||||
</div>
|
||||
|
||||
{#if $app.hiddenInlineScripts}
|
||||
{#each $app.hiddenInlineScripts as script, index}
|
||||
{#if script}
|
||||
<HiddenComponent
|
||||
id={`bg_${index}`}
|
||||
inlineScript={script.inlineScript}
|
||||
name={script.name}
|
||||
fields={script.fields}
|
||||
recomputeOnInputChanged={script.recomputeOnInputChanged ?? true}
|
||||
autoRefresh={script.autoRefresh ?? false}
|
||||
/>
|
||||
{#each $app.hiddenInlineScripts as runnable, index}
|
||||
{#if runnable}
|
||||
<HiddenComponent id={`bg_${index}`} {runnable} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}
|
||||
|
||||
@@ -52,22 +52,6 @@
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
$: {
|
||||
if (hiddenInlineScript && hiddenInlineScript?.script.recomputeOnInputChanged === undefined) {
|
||||
hiddenInlineScript.script.recomputeOnInputChanged = true
|
||||
}
|
||||
|
||||
//TODO: remove after migration is done
|
||||
if (
|
||||
hiddenInlineScript &&
|
||||
hiddenInlineScript?.script.doNotRecomputeOnInputChanged != undefined
|
||||
) {
|
||||
hiddenInlineScript.script.recomputeOnInputChanged =
|
||||
!hiddenInlineScript.script.doNotRecomputeOnInputChanged
|
||||
hiddenInlineScript.script.doNotRecomputeOnInputChanged = undefined
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if componentSettings}
|
||||
@@ -97,7 +81,7 @@
|
||||
{/key}
|
||||
{:else if hiddenInlineScript}
|
||||
<BackgroundScriptSettings
|
||||
bind:script={hiddenInlineScript.script}
|
||||
bind:runnable={hiddenInlineScript.script}
|
||||
id={`bg_${hiddenInlineScript.index}`}
|
||||
/>
|
||||
|
||||
|
||||
+3
-1
@@ -7,5 +7,7 @@
|
||||
</script>
|
||||
|
||||
{#each $app.hiddenInlineScripts as action, index}
|
||||
<BackgroundScriptOutput id={`bg_${index}`} name={action.name} first={index === 0} />
|
||||
{#if !action.hidden}
|
||||
<BackgroundScriptOutput id={`bg_${index}`} name={action.name} first={index === 0} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
$search != '' &&
|
||||
($hasResult[id] ||
|
||||
Object.entries($hasResult).some(([key, value]) => value && subids.includes(key)))
|
||||
$: open = $allIdsInPath.includes(id) || $manuallyOpened[id] || inSearch
|
||||
$: open =
|
||||
$allIdsInPath.includes(id) || id == $selectedComponent?.[0] || $manuallyOpened[id] || inSearch
|
||||
|
||||
const hoverColor = {
|
||||
blue: 'hover:bg-blue-100 hover:text-blue-500',
|
||||
@@ -76,12 +77,7 @@
|
||||
})
|
||||
|
||||
$app.hiddenInlineScripts.forEach((x) => {
|
||||
console.log('process', x.name, id)
|
||||
processRunnable(from, to, {
|
||||
name: x.name,
|
||||
inlineScript: x.inlineScript,
|
||||
type: 'runnableByName'
|
||||
})
|
||||
processRunnable(from, to, x)
|
||||
})
|
||||
}
|
||||
propagateRename(id, newId)
|
||||
|
||||
+11
-5
@@ -16,9 +16,11 @@
|
||||
import { defaultCode } from '../component'
|
||||
import InlineScriptList from '../settingsPanel/mainInput/InlineScriptList.svelte'
|
||||
import WorkspaceScriptList from '../settingsPanel/mainInput/WorkspaceScriptList.svelte'
|
||||
import RunnableSelector from '../settingsPanel/mainInput/RunnableSelector.svelte'
|
||||
|
||||
export let name: string
|
||||
export let componentType: string | undefined = undefined
|
||||
export let showScriptPicker = false
|
||||
|
||||
let tab = 'inlinescripts'
|
||||
let filter: string = ''
|
||||
@@ -140,9 +142,12 @@
|
||||
</Drawer>
|
||||
|
||||
<div class="flex flex-col px-4 gap-2 text-sm" in:fly={{ duration: 50 }}>
|
||||
<div class="mt-4 flex justify-between gap-8 mb-2">
|
||||
<div class="font-bold items-baseline">Choose a language:</div>
|
||||
<div class="mt-2 flex justify-between gap-4">
|
||||
<div class="font-bold items-baseline truncate">Choose a language</div>
|
||||
<div class="flex gap-2">
|
||||
{#if showScriptPicker}
|
||||
<RunnableSelector on:pick hideCreateScript />
|
||||
{/if}
|
||||
<Button
|
||||
on:click={() => picker?.openDrawer()}
|
||||
size="xs"
|
||||
@@ -151,8 +156,9 @@
|
||||
startIcon={{ icon: faCodeBranch }}
|
||||
btnClasses="truncate"
|
||||
>
|
||||
Fork a workspace or hub script
|
||||
Fork a script
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
on:click={() => dispatch('delete')}
|
||||
size="xs"
|
||||
@@ -160,7 +166,7 @@
|
||||
variant="border"
|
||||
btnClasses="truncate"
|
||||
>
|
||||
Delete
|
||||
Clear
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -181,7 +187,7 @@
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="mt-4 mb-2 text-sm">Typescript templates</div>
|
||||
<div class="mt-2 mb-2 text-sm">Typescript templates</div>
|
||||
|
||||
<div class="flex gap-2 flex-row flex-wrap mb-4">
|
||||
<FlowScriptPicker
|
||||
|
||||
+4
-48
@@ -2,8 +2,8 @@
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import type { Preview } from '$lib/gen'
|
||||
import { createEventDispatcher, getContext, onMount } from 'svelte'
|
||||
import type { AppViewerContext, CancelablePromise, InlineScript } from '../../types'
|
||||
import { Loader2, Maximize2, Trash2 } from 'lucide-svelte'
|
||||
import type { AppViewerContext, InlineScript } from '../../types'
|
||||
import { Maximize2, Trash2 } from 'lucide-svelte'
|
||||
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import type { Schema } from '$lib/common'
|
||||
@@ -17,6 +17,7 @@
|
||||
import Kbd from '$lib/components/common/kbd/Kbd.svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { buildExtraLib } from '../../utils'
|
||||
import RunButton from './RunButton.svelte'
|
||||
|
||||
let inlineScriptEditorDrawer: InlineScriptEditorDrawer
|
||||
|
||||
@@ -87,8 +88,6 @@
|
||||
inlineScript?.language == 'frontend' && worldStore
|
||||
? buildExtraLib($worldStore?.outputsById ?? {}, id, false, $state, true)
|
||||
: undefined
|
||||
|
||||
let cancelable: CancelablePromise<void> | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if inlineScript}
|
||||
@@ -163,50 +162,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
{#if $runnableComponents[id] != undefined}
|
||||
{#if !runLoading}
|
||||
<Button
|
||||
loading={runLoading}
|
||||
size="xs"
|
||||
color="dark"
|
||||
variant="border"
|
||||
btnClasses="!px-2 !py-1 !bg-gray-700 !text-white hover:!bg-gray-900"
|
||||
on:click={async () => {
|
||||
runLoading = true
|
||||
try {
|
||||
cancelable = $runnableComponents[id]?.cb?.(
|
||||
!transformer ? inlineScript : undefined
|
||||
)
|
||||
await cancelable
|
||||
} catch {}
|
||||
runLoading = false
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
Run
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<Kbd small>{getModifierKey()}</Kbd>
|
||||
<Kbd small><span class="text-lg font-bold">⏎</span></Kbd>
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
size="xs"
|
||||
color="red"
|
||||
variant="border"
|
||||
btnClasses="!px-2 !py-1.5"
|
||||
on:click={async () => {
|
||||
cancelable?.cancel()
|
||||
runLoading = false
|
||||
}}
|
||||
>
|
||||
<Loader2 size={14} class="animate-spin mr-2" />
|
||||
Cancel
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
<RunButton bind:runLoading {id} inlineScript={!transformer ? inlineScript : undefined} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+20
-171
@@ -1,26 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { Drawer, DrawerContent } from '$lib/components/common'
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import FlowModuleScript from '$lib/components/flows/content/FlowModuleScript.svelte'
|
||||
import FlowPathViewer from '$lib/components/flows/content/FlowPathViewer.svelte'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { emptySchema, getScriptByPath, sendUserToast } from '$lib/utils'
|
||||
import {
|
||||
faCodeBranch,
|
||||
faExternalLinkAlt,
|
||||
faEye,
|
||||
faPen,
|
||||
faRefresh
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import type { AppInput, RunnableByPath } from '../../inputType'
|
||||
import { clearResultAppInput, loadSchema } from '../../utils'
|
||||
import type { AppInput, Runnable } from '../../inputType'
|
||||
import { clearResultAppInput } from '../../utils'
|
||||
import EmptyInlineScript from './EmptyInlineScript.svelte'
|
||||
import InlineScriptEditor from './InlineScriptEditor.svelte'
|
||||
import { computeFields } from './utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import InlineScriptRunnableByPath from './InlineScriptRunnableByPath.svelte'
|
||||
|
||||
export let componentInput: AppInput | undefined
|
||||
export let defaultUserInput = false
|
||||
@@ -28,80 +14,20 @@
|
||||
export let id: string
|
||||
export let transformer: boolean
|
||||
|
||||
const { app, stateId } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
async function fork(path: string) {
|
||||
let { content, language, schema } = await getScriptByPath(path)
|
||||
if (componentInput && componentInput.type == 'runnable') {
|
||||
if (!schema || typeof schema == 'string' || Object.keys(schema).length == 0) {
|
||||
schema = emptySchema()
|
||||
await inferArgs(language, content, schema)
|
||||
}
|
||||
componentInput.runnable = {
|
||||
type: 'runnableByName',
|
||||
name: path,
|
||||
inlineScript: {
|
||||
content,
|
||||
language,
|
||||
schema,
|
||||
path
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('componentInput is undefined')
|
||||
}
|
||||
}
|
||||
|
||||
let drawerFlowViewer: Drawer
|
||||
let flowPath: string = ''
|
||||
|
||||
async function refreshScript(x: RunnableByPath) {
|
||||
let { schema } = await getScriptByPath(x.path)
|
||||
if (!deepEqual(x.schema, schema)) {
|
||||
x.schema = schema
|
||||
if (componentInput?.type == 'runnable') {
|
||||
componentInput.fields = computeFields(schema, false, componentInput.fields)
|
||||
}
|
||||
componentInput = componentInput
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshFlow(x: RunnableByPath) {
|
||||
if (componentInput?.type == 'runnable') {
|
||||
const schema = (await loadSchema($workspaceStore ?? '', x.path, 'flow')) ?? emptySchema()
|
||||
if (!deepEqual(x.schema, schema)) {
|
||||
x.schema = schema
|
||||
const nfields = computeFields(schema, false, componentInput.fields)
|
||||
componentInput.fields = nfields
|
||||
componentInput = componentInput
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: if (
|
||||
componentInput &&
|
||||
componentInput.type == 'runnable' &&
|
||||
componentInput?.runnable?.type === 'runnableByPath'
|
||||
) {
|
||||
if (componentInput.runnable.runType == 'script') {
|
||||
refreshScript(componentInput.runnable)
|
||||
} else if (componentInput.runnable.runType == 'flow') {
|
||||
refreshFlow(componentInput.runnable)
|
||||
}
|
||||
}
|
||||
const { app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function deleteInlineScript() {
|
||||
if (componentInput && componentInput.type == 'runnable') {
|
||||
componentInput = clearResultAppInput(componentInput)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={drawerFlowViewer} size="1200px">
|
||||
<DrawerContent title="Flow {flowPath}" on:close={drawerFlowViewer.closeDrawer}>
|
||||
<FlowPathViewer path={flowPath ?? ''} />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
async function fork(runnable: Runnable) {
|
||||
if (componentInput?.type == 'runnable') {
|
||||
componentInput.runnable = runnable
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if transformer}
|
||||
{#if componentInput?.type == 'runnable' && componentInput.transformer}
|
||||
@@ -147,97 +73,20 @@
|
||||
componentInput?.runnable?.type === 'runnableByName'
|
||||
) {
|
||||
componentInput.runnable.inlineScript = e.detail
|
||||
componentInput.autoRefresh = true
|
||||
componentInput.recomputeOnInputChanged = true
|
||||
$app = $app
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{:else if componentInput?.runnable?.type === 'runnableByPath' && componentInput?.runnable?.path}
|
||||
<div class="p-2 h-full flex flex-col gap-2">
|
||||
{#if componentInput.runnable.runType == 'script' || componentInput.runnable.runType == 'hubscript'}
|
||||
<div class="flex flex-row">
|
||||
<Button
|
||||
size="xs"
|
||||
variant="border"
|
||||
startIcon={{ icon: faCodeBranch }}
|
||||
on:click={() => {
|
||||
if (
|
||||
componentInput &&
|
||||
componentInput.type == 'runnable' &&
|
||||
componentInput.runnable?.type === 'runnableByPath'
|
||||
) {
|
||||
fork(componentInput.runnable.path)
|
||||
$app = $app
|
||||
}
|
||||
}}
|
||||
>
|
||||
Fork
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="w-full">
|
||||
{#key $stateId}
|
||||
{#if componentInput.runnable.runType == 'script' || componentInput.runnable.runType == 'hubscript'}
|
||||
<FlowModuleScript path={componentInput.runnable.path} />
|
||||
{:else if componentInput.runnable.runType == 'flow'}
|
||||
<div class="pb-2 flex gap-2 w-full flex-row-reverse">
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faRefresh }}
|
||||
on:click={() => {
|
||||
if (
|
||||
componentInput?.type == 'runnable' &&
|
||||
componentInput.runnable?.type === 'runnableByPath'
|
||||
) {
|
||||
sendUserToast('Refreshing inputs')
|
||||
if (componentInput.runnable.runType == 'script') {
|
||||
refreshScript(componentInput.runnable)
|
||||
} else if (componentInput.runnable.runType == 'flow') {
|
||||
refreshFlow(componentInput.runnable)
|
||||
}
|
||||
$stateId = $stateId + 1
|
||||
}
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faEye }}
|
||||
on:click={() => {
|
||||
flowPath = componentInput?.['runnable']?.path
|
||||
drawerFlowViewer.openDrawer()
|
||||
}}
|
||||
>
|
||||
Expand
|
||||
</Button>
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faPen }}
|
||||
endIcon={{ icon: faExternalLinkAlt }}
|
||||
target="_blank"
|
||||
href="/flows/edit/{componentInput?.['runnable']?.path}?nodraft=true">Edit</Button
|
||||
>
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faEye }}
|
||||
endIcon={{ icon: faExternalLinkAlt }}
|
||||
target="_blank"
|
||||
href="/flows/get/{componentInput?.['runnable']?.path}?workspace={$workspaceStore}"
|
||||
>
|
||||
Details page
|
||||
</Button>
|
||||
</div>
|
||||
<FlowPathViewer path={componentInput.runnable.path} />
|
||||
{:else}
|
||||
Unrecognized runType {componentInput.runnable.runType}
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
</div>
|
||||
<InlineScriptRunnableByPath
|
||||
on:fork={(e) => fork(e.detail)}
|
||||
bind:runnable={componentInput.runnable}
|
||||
bind:fields={componentInput.fields}
|
||||
on:delete
|
||||
{id}
|
||||
/>
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import type { HiddenRunnable } from '../../types'
|
||||
import InlineScriptEditor from './InlineScriptEditor.svelte'
|
||||
import EmptyInlineScript from './EmptyInlineScript.svelte'
|
||||
import InlineScriptRunnableByPath from './InlineScriptRunnableByPath.svelte'
|
||||
import type { Runnable, StaticAppInput } from '../../inputType'
|
||||
|
||||
export let runnable: HiddenRunnable
|
||||
export let id: string
|
||||
|
||||
async function fork(nrunnable: Runnable) {
|
||||
runnable = { ...runnable, ...nrunnable, autoRefresh: true, recomputeOnInputChanged: true }
|
||||
}
|
||||
function onPick(o: { runnable: Runnable; fields: Record<string, StaticAppInput> }) {
|
||||
runnable = {
|
||||
...runnable,
|
||||
...o.runnable,
|
||||
fields: o.fields,
|
||||
autoRefresh: true,
|
||||
recomputeOnInputChanged: true
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if runnable?.type === 'runnableByName' && runnable.inlineScript}
|
||||
<InlineScriptEditor
|
||||
{id}
|
||||
bind:inlineScript={runnable.inlineScript}
|
||||
bind:name={runnable.name}
|
||||
bind:fields={runnable.fields}
|
||||
syncFields
|
||||
on:delete
|
||||
/>
|
||||
{:else if runnable?.type == 'runnableByPath'}
|
||||
<InlineScriptRunnableByPath
|
||||
bind:runnable
|
||||
bind:fields={runnable.fields}
|
||||
on:fork={(e) => fork(e.detail)}
|
||||
on:delete
|
||||
{id}
|
||||
/>
|
||||
{:else}
|
||||
<EmptyInlineScript
|
||||
on:pick={(e) => onPick(e.detail)}
|
||||
name={runnable.name}
|
||||
on:delete
|
||||
showScriptPicker
|
||||
on:new={(e) => {
|
||||
runnable = {
|
||||
type: 'runnableByName',
|
||||
inlineScript: e.detail,
|
||||
name: runnable.name,
|
||||
fields: {},
|
||||
autoRefresh: true,
|
||||
recomputeOnInputChanged: true
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
<script lang="ts">
|
||||
import { Button, Drawer, DrawerContent } from '$lib/components/common'
|
||||
import FlowModuleScript from '$lib/components/flows/content/FlowModuleScript.svelte'
|
||||
import FlowPathViewer from '$lib/components/flows/content/FlowPathViewer.svelte'
|
||||
import { emptySchema, getScriptByPath, sendUserToast } from '$lib/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type {
|
||||
ConnectedAppInput,
|
||||
RowAppInput,
|
||||
RunnableByPath,
|
||||
StaticAppInput,
|
||||
UserAppInput
|
||||
} from '../../inputType'
|
||||
import {
|
||||
faCodeBranch,
|
||||
faExternalLinkAlt,
|
||||
faEye,
|
||||
faPen,
|
||||
faRefresh,
|
||||
faTrashAlt
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { computeFields } from './utils'
|
||||
import { loadSchema } from '../../utils'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import RunButton from './RunButton.svelte'
|
||||
|
||||
export let runnable: RunnableByPath
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let id: string
|
||||
|
||||
const { stateId } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
let drawerFlowViewer: Drawer
|
||||
let flowPath: string = ''
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
async function refreshScript(x: RunnableByPath) {
|
||||
let { schema } = await getScriptByPath(x.path)
|
||||
if (!deepEqual(x.schema, schema)) {
|
||||
x.schema = schema
|
||||
fields = computeFields(schema, false, fields)
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshFlow(x: RunnableByPath) {
|
||||
const schema = (await loadSchema($workspaceStore ?? '', x.path, 'flow')) ?? emptySchema()
|
||||
if (!deepEqual(x.schema, schema)) {
|
||||
x.schema = schema
|
||||
fields = computeFields(schema, false, fields)
|
||||
}
|
||||
}
|
||||
|
||||
async function fork(path: string) {
|
||||
let { content, language, schema } = await getScriptByPath(path)
|
||||
if (!schema || typeof schema == 'string' || Object.keys(schema).length == 0) {
|
||||
schema = emptySchema()
|
||||
await inferArgs(language, content, schema)
|
||||
}
|
||||
dispatch('fork', {
|
||||
type: 'runnableByName',
|
||||
name: path,
|
||||
inlineScript: {
|
||||
content,
|
||||
language,
|
||||
schema,
|
||||
path
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
if (runnable.runType == 'script') {
|
||||
refreshScript(runnable)
|
||||
} else if (runnable.runType == 'flow') {
|
||||
refreshFlow(runnable)
|
||||
}
|
||||
}
|
||||
$: runnable.runType && refresh()
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={drawerFlowViewer} size="1200px">
|
||||
<DrawerContent title="Flow {flowPath}" on:close={drawerFlowViewer.closeDrawer}>
|
||||
<FlowPathViewer path={flowPath ?? ''} />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<div class="p-2 h-full flex flex-col gap-2">
|
||||
<div class="flex flex-row-reverse w-full gap-2">
|
||||
<RunButton hideShortcut {id} />
|
||||
|
||||
<Button
|
||||
size="xs"
|
||||
variant="border"
|
||||
startIcon={{ icon: faCodeBranch }}
|
||||
on:click={() => {
|
||||
fork(runnable.path)
|
||||
}}
|
||||
>
|
||||
Fork
|
||||
</Button>
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faRefresh }}
|
||||
on:click={() => {
|
||||
sendUserToast('Refreshing inputs')
|
||||
refresh()
|
||||
$stateId = $stateId + 1
|
||||
}}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="border"
|
||||
color="red"
|
||||
startIcon={{ icon: faTrashAlt }}
|
||||
on:click={() => {
|
||||
dispatch('delete')
|
||||
}}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
{#if runnable.runType == 'flow'}
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faEye }}
|
||||
on:click={() => {
|
||||
flowPath = runnable.path
|
||||
drawerFlowViewer.openDrawer()
|
||||
}}
|
||||
>
|
||||
Expand
|
||||
</Button>
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faPen }}
|
||||
endIcon={{ icon: faExternalLinkAlt }}
|
||||
target="_blank"
|
||||
href="/flows/edit/{runnable.path}?nodraft=true">Edit</Button
|
||||
>
|
||||
<Button
|
||||
variant="border"
|
||||
size="xs"
|
||||
startIcon={{ icon: faEye }}
|
||||
endIcon={{ icon: faExternalLinkAlt }}
|
||||
target="_blank"
|
||||
href="/flows/get/{runnable.path}?workspace={$workspaceStore}"
|
||||
>
|
||||
Details page
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="w-full">
|
||||
{#key $stateId}
|
||||
{#if runnable.runType == 'script' || runnable.runType == 'hubscript'}
|
||||
<div class="border">
|
||||
<FlowModuleScript path={runnable.path} />
|
||||
</div>
|
||||
{:else if runnable.runType == 'flow'}
|
||||
<FlowPathViewer path={runnable.path} />
|
||||
{:else}
|
||||
Unrecognized runType {runnable.runType}
|
||||
{/if}
|
||||
{/key}
|
||||
</div>
|
||||
</div>
|
||||
+14
-24
@@ -5,9 +5,9 @@
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import InlineScriptsPanelList from './InlineScriptsPanelList.svelte'
|
||||
import InlineScriptEditor from './InlineScriptEditor.svelte'
|
||||
import EmptyInlineScript from './EmptyInlineScript.svelte'
|
||||
import InlineScriptsPanelWithTable from './InlineScriptsPanelWithTable.svelte'
|
||||
import { findGridItem } from '../appUtils'
|
||||
import InlineScriptHiddenRunnable from './InlineScriptHiddenRunnable.svelte'
|
||||
|
||||
const { app, runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { selectedComponentInEditor } = getContext<AppEditorContext>('AppEditorContext')
|
||||
@@ -18,12 +18,17 @@
|
||||
$app.hiddenInlineScripts.splice(index, 1)
|
||||
$app.hiddenInlineScripts = [...$app.hiddenInlineScripts]
|
||||
} else {
|
||||
$app.hiddenInlineScripts[index].hidden = true
|
||||
$app.hiddenInlineScripts[index].inlineScript = undefined
|
||||
$app.hiddenInlineScripts[index].name = `Background Script ${index}`
|
||||
$app.hiddenInlineScripts[index] = {
|
||||
hidden: true,
|
||||
inlineScript: undefined,
|
||||
name: `Background Script ${index}`,
|
||||
fields: {},
|
||||
type: 'runnableByName'
|
||||
}
|
||||
$app.hiddenInlineScripts = $app.hiddenInlineScripts
|
||||
}
|
||||
|
||||
$selectedComponentInEditor = undefined
|
||||
delete $runnableComponents[`bg_${index}`]
|
||||
}
|
||||
|
||||
@@ -70,27 +75,12 @@
|
||||
{/key}
|
||||
{:else if hiddenInlineScript > -1}
|
||||
{#key hiddenInlineScript}
|
||||
{#if $app.hiddenInlineScripts?.[hiddenInlineScript]?.inlineScript}
|
||||
<InlineScriptEditor
|
||||
{#if $app.hiddenInlineScripts?.[hiddenInlineScript]}
|
||||
<InlineScriptHiddenRunnable
|
||||
on:delete={() => deleteBackgroundScript(hiddenInlineScript)}
|
||||
id={`bg_${hiddenInlineScript}`}
|
||||
bind:inlineScript={$app.hiddenInlineScripts[hiddenInlineScript].inlineScript}
|
||||
bind:name={$app.hiddenInlineScripts[hiddenInlineScript].name}
|
||||
bind:fields={$app.hiddenInlineScripts[hiddenInlineScript].fields}
|
||||
syncFields
|
||||
on:delete={() => deleteBackgroundScript(hiddenInlineScript)}
|
||||
/>
|
||||
{:else}
|
||||
<EmptyInlineScript
|
||||
name={$app.hiddenInlineScripts[hiddenInlineScript].name}
|
||||
on:delete={() => deleteBackgroundScript(hiddenInlineScript)}
|
||||
on:new={(e) => {
|
||||
if ($app.hiddenInlineScripts[hiddenInlineScript]) {
|
||||
$app.hiddenInlineScripts[hiddenInlineScript].inlineScript = e.detail
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{/key}
|
||||
bind:runnable={$app.hiddenInlineScripts[hiddenInlineScript]}
|
||||
/>{/if}{/key}
|
||||
{:else}
|
||||
<div class="text-sm text-gray-500 text-center py-8 px-2">
|
||||
No script found at id {$selectedComponentInEditor}
|
||||
|
||||
+10
-6
@@ -23,11 +23,15 @@
|
||||
$: runnables = getAppScripts($app.grid, $app.subgrids)
|
||||
|
||||
// When selected component changes, update selectedScriptComponentId
|
||||
$: if (
|
||||
$selectedComponent != $selectedComponentInEditor &&
|
||||
!$selectedComponentInEditor?.includes('_transformer')
|
||||
) {
|
||||
$selectedComponentInEditor = $selectedComponent?.[0]
|
||||
$: $selectedComponent && handleSelectedComponent()
|
||||
|
||||
function handleSelectedComponent() {
|
||||
if (
|
||||
$selectedComponent != $selectedComponentInEditor &&
|
||||
!$selectedComponentInEditor?.includes('_transformer')
|
||||
) {
|
||||
$selectedComponentInEditor = $selectedComponent?.[0]
|
||||
}
|
||||
}
|
||||
|
||||
function createBackgroundScript() {
|
||||
@@ -57,7 +61,7 @@
|
||||
name: newScriptPath,
|
||||
inlineScript: undefined,
|
||||
autoRefresh: true,
|
||||
|
||||
type: 'runnableByName',
|
||||
fields: {}
|
||||
})
|
||||
$app.hiddenInlineScripts = $app.hiddenInlineScripts
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext, CancelablePromise, InlineScript } from '../../types'
|
||||
import { Button, Kbd } from '$lib/components/common'
|
||||
import { getModifierKey } from '$lib/utils'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
|
||||
export let id: string
|
||||
export let inlineScript: InlineScript | undefined = undefined
|
||||
export let runLoading = false
|
||||
export let hideShortcut = false
|
||||
|
||||
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
|
||||
let cancelable: CancelablePromise<void> | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if $runnableComponents[id] != undefined}
|
||||
{#if !runLoading}
|
||||
<Button
|
||||
loading={runLoading}
|
||||
size="xs"
|
||||
color="dark"
|
||||
variant="border"
|
||||
btnClasses="!px-2 !py-1 !bg-gray-700 !text-white hover:!bg-gray-900"
|
||||
on:click={async () => {
|
||||
runLoading = true
|
||||
try {
|
||||
cancelable = $runnableComponents[id]?.cb?.(inlineScript)
|
||||
await cancelable
|
||||
} catch {}
|
||||
runLoading = false
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
Run
|
||||
{#if !hideShortcut}
|
||||
<div class="flex flex-row items-center">
|
||||
<Kbd small>{getModifierKey()}</Kbd>
|
||||
<Kbd small><span class="text-lg font-bold">⏎</span></Kbd>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Button>
|
||||
{:else}
|
||||
<Button
|
||||
size="xs"
|
||||
color="red"
|
||||
variant="border"
|
||||
btnClasses="!px-2 !py-1.5"
|
||||
on:click={async () => {
|
||||
cancelable?.cancel()
|
||||
runLoading = false
|
||||
}}
|
||||
>
|
||||
<Loader2 size={14} class="animate-spin mr-2" />
|
||||
Cancel
|
||||
</Button>
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -6,18 +6,20 @@
|
||||
|
||||
export let id: string
|
||||
export let inputSpecs: RichConfigurations
|
||||
export let inputSpecsConfiguration: RichConfigurations = inputSpecs
|
||||
export let inputSpecsConfiguration: RichConfigurations | undefined = undefined
|
||||
export let userInputEnabled: boolean = false
|
||||
export let shouldCapitalize: boolean = true
|
||||
export let rowColumns = false
|
||||
export let resourceOnly = false
|
||||
export let displayType = false
|
||||
|
||||
$: finalInputSpecsConfiguration = inputSpecsConfiguration ?? inputSpecs
|
||||
</script>
|
||||
|
||||
{#if inputSpecs}
|
||||
<div class="w-full flex flex-col gap-4">
|
||||
{#each Object.keys(inputSpecsConfiguration) as k}
|
||||
{#if inputSpecsConfiguration[k]?.type == 'oneOf'}
|
||||
{#each Object.keys(finalInputSpecsConfiguration) as k}
|
||||
{#if finalInputSpecsConfiguration[k]?.type == 'oneOf'}
|
||||
<OneOfInputSpecsEditor
|
||||
key={k}
|
||||
bind:oneOf={inputSpecs[k]}
|
||||
@@ -25,12 +27,12 @@
|
||||
{shouldCapitalize}
|
||||
{resourceOnly}
|
||||
{rowColumns}
|
||||
inputSpecsConfiguration={inputSpecsConfiguration?.[k]?.['configuration']}
|
||||
labels={inputSpecsConfiguration?.[k]?.['labels']}
|
||||
tooltip={inputSpecsConfiguration?.[k]?.['tooltip']}
|
||||
inputSpecsConfiguration={finalInputSpecsConfiguration?.[k]?.['configuration']}
|
||||
labels={finalInputSpecsConfiguration?.[k]?.['labels']}
|
||||
tooltip={finalInputSpecsConfiguration?.[k]?.['tooltip']}
|
||||
/>
|
||||
{:else}
|
||||
{@const meta = inputSpecsConfiguration?.[k]}
|
||||
{@const meta = finalInputSpecsConfiguration?.[k]}
|
||||
<InputsSpecEditor
|
||||
key={k}
|
||||
bind:componentInput={inputSpecs[k]}
|
||||
|
||||
@@ -49,12 +49,16 @@
|
||||
function getActions(hasScript: boolean): ActionType[] {
|
||||
if (hasScript) {
|
||||
return [
|
||||
{
|
||||
label: 'Detach',
|
||||
icon: ExternalLink,
|
||||
color: 'light',
|
||||
callback: detach
|
||||
},
|
||||
...(appInput.runnable?.type === 'runnableByName' && appInput.runnable.inlineScript
|
||||
? ([
|
||||
{
|
||||
label: 'Detach',
|
||||
icon: ExternalLink,
|
||||
color: 'light',
|
||||
callback: detach
|
||||
}
|
||||
] as const)
|
||||
: []),
|
||||
{
|
||||
label: 'Clear',
|
||||
icon: X,
|
||||
|
||||
+21
-2
@@ -1,17 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type { ResultAppInput } from '$lib/components/apps/inputType'
|
||||
import type { ResultAppInput, Runnable, StaticAppInput } from '$lib/components/apps/inputType'
|
||||
import { isScriptByNameDefined, isScriptByPathDefined } from '$lib/components/apps/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppComponent } from '../../component'
|
||||
import RunnableSelector from '../mainInput/RunnableSelector.svelte'
|
||||
import SelectedRunnable from '../SelectedRunnable.svelte'
|
||||
import type { AppEditorContext } from '$lib/components/apps/types'
|
||||
|
||||
export let appInput: ResultAppInput
|
||||
export let defaultUserInput = false
|
||||
export let appComponent: AppComponent
|
||||
|
||||
const { selectedComponentInEditor } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
function onPick({
|
||||
runnable,
|
||||
fields
|
||||
}: {
|
||||
runnable: Runnable
|
||||
fields: Record<string, StaticAppInput>
|
||||
}) {
|
||||
if (appInput.type === 'runnable') {
|
||||
appInput = { ...appInput, runnable, fields }
|
||||
$selectedComponentInEditor = appComponent.id
|
||||
} else {
|
||||
console.warn('Cannot pick runnable for non-runnable input')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if isScriptByPathDefined(appInput) || isScriptByNameDefined(appInput)}
|
||||
<SelectedRunnable {appComponent} bind:appInput />
|
||||
{:else if appInput !== undefined}
|
||||
<RunnableSelector id={appComponent.id} {defaultUserInput} bind:appInput />
|
||||
<RunnableSelector {defaultUserInput} on:pick={(e) => onPick(e.detail)} />
|
||||
{/if}
|
||||
|
||||
+76
-70
@@ -4,27 +4,32 @@
|
||||
import PickHubScript from '$lib/components/flows/pickers/PickHubScript.svelte'
|
||||
import { Building, Globe2 } from 'lucide-svelte'
|
||||
import InlineScriptList from './InlineScriptList.svelte'
|
||||
import type { ResultAppInput } from '$lib/components/apps/inputType'
|
||||
import type { Runnable, StaticAppInput } from '$lib/components/apps/inputType'
|
||||
import WorkspaceScriptList from './WorkspaceScriptList.svelte'
|
||||
import WorkspaceFlowList from './WorkspaceFlowList.svelte'
|
||||
import type { AppEditorContext, AppViewerContext } from '$lib/components/apps/types'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '$lib/components/apps/types'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { Schema } from '$lib/common'
|
||||
import { getAllScriptNames, loadSchema, schemaToInputsSpec } from '$lib/components/apps/utils'
|
||||
import { emptySchema } from '$lib/utils'
|
||||
|
||||
type Tab = 'hubscripts' | 'workspacescripts' | 'workspaceflows' | 'inlinescripts'
|
||||
|
||||
export let appInput: ResultAppInput
|
||||
export let defaultUserInput = false
|
||||
export let id: string
|
||||
export let hideCreateScript = false
|
||||
|
||||
let tab: Tab = 'inlinescripts'
|
||||
let filter: string = ''
|
||||
let picker: Drawer
|
||||
|
||||
const { app, workspace } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { selectedComponentInEditor } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
const dispatch = createEventDispatcher<{
|
||||
pick: {
|
||||
runnable: Runnable
|
||||
fields: Record<string, StaticAppInput>
|
||||
}
|
||||
}>()
|
||||
|
||||
async function loadSchemaFromTriggerable(
|
||||
path: string,
|
||||
@@ -34,48 +39,49 @@
|
||||
}
|
||||
|
||||
async function pickScript(path: string) {
|
||||
if (appInput.type === 'runnable') {
|
||||
const schema = await loadSchemaFromTriggerable(path, 'script')
|
||||
const fields = schemaToInputsSpec(schema, defaultUserInput)
|
||||
appInput.runnable = {
|
||||
type: 'runnableByPath',
|
||||
path,
|
||||
runType: 'script',
|
||||
schema
|
||||
}
|
||||
appInput.fields = fields
|
||||
$selectedComponentInEditor = id
|
||||
}
|
||||
const schema = await loadSchemaFromTriggerable(path, 'script')
|
||||
const fields = schemaToInputsSpec(schema, defaultUserInput)
|
||||
const runnable = {
|
||||
type: 'runnableByPath',
|
||||
path,
|
||||
runType: 'script',
|
||||
schema
|
||||
} as const
|
||||
|
||||
dispatch('pick', {
|
||||
runnable,
|
||||
fields
|
||||
})
|
||||
}
|
||||
|
||||
async function pickFlow(path: string) {
|
||||
if (appInput.type === 'runnable') {
|
||||
const schema = await loadSchemaFromTriggerable(path, 'flow')
|
||||
const fields = schemaToInputsSpec(schema, defaultUserInput)
|
||||
appInput.runnable = {
|
||||
type: 'runnableByPath',
|
||||
path,
|
||||
runType: 'flow',
|
||||
schema
|
||||
}
|
||||
appInput.fields = fields
|
||||
$selectedComponentInEditor = id
|
||||
}
|
||||
const schema = await loadSchemaFromTriggerable(path, 'flow')
|
||||
const fields = schemaToInputsSpec(schema, defaultUserInput)
|
||||
const runnable = {
|
||||
type: 'runnableByPath',
|
||||
path,
|
||||
runType: 'flow',
|
||||
schema
|
||||
} as const
|
||||
dispatch('pick', {
|
||||
runnable,
|
||||
fields
|
||||
})
|
||||
}
|
||||
|
||||
async function pickHubScript(path: string) {
|
||||
if (appInput.type === 'runnable') {
|
||||
const schema = await loadSchemaFromTriggerable(path, 'hubscript')
|
||||
const fields = schemaToInputsSpec(schema, defaultUserInput)
|
||||
appInput.runnable = {
|
||||
type: 'runnableByPath',
|
||||
path,
|
||||
runType: 'hubscript',
|
||||
schema
|
||||
}
|
||||
appInput.fields = fields
|
||||
$selectedComponentInEditor = id
|
||||
}
|
||||
const schema = await loadSchemaFromTriggerable(path, 'hubscript')
|
||||
const fields = schemaToInputsSpec(schema, defaultUserInput)
|
||||
const runnable = {
|
||||
type: 'runnableByPath',
|
||||
path,
|
||||
runType: 'hubscript',
|
||||
schema
|
||||
} as const
|
||||
dispatch('pick', {
|
||||
runnable,
|
||||
fields
|
||||
})
|
||||
}
|
||||
|
||||
function pickInlineScript(name: string) {
|
||||
@@ -83,20 +89,20 @@
|
||||
(script) => script.name === name
|
||||
)
|
||||
const unusedInlineScript = $app.unusedInlineScripts?.[unusedInlineScriptIndex]
|
||||
if (appInput.type === 'runnable' && unusedInlineScript?.inlineScript) {
|
||||
appInput.runnable = {
|
||||
dispatch('pick', {
|
||||
runnable: {
|
||||
type: 'runnableByName',
|
||||
name,
|
||||
inlineScript: unusedInlineScript.inlineScript
|
||||
}
|
||||
},
|
||||
fields: {}
|
||||
})
|
||||
|
||||
$app.unusedInlineScripts.splice(unusedInlineScriptIndex, 1)
|
||||
}
|
||||
$selectedComponentInEditor = id
|
||||
$app = $app
|
||||
$app.unusedInlineScripts.splice(unusedInlineScriptIndex, 1)
|
||||
$app.unusedInlineScripts = $app.unusedInlineScripts
|
||||
}
|
||||
|
||||
function createScript(): string {
|
||||
function createScript() {
|
||||
let index = 0
|
||||
let newScriptPath = `Inline Script ${index}`
|
||||
|
||||
@@ -107,16 +113,14 @@
|
||||
newScriptPath = `Inline Script ${++index}`
|
||||
}
|
||||
|
||||
appInput.runnable = {
|
||||
type: 'runnableByName',
|
||||
name: newScriptPath,
|
||||
inlineScript: undefined
|
||||
}
|
||||
|
||||
appInput = appInput
|
||||
$selectedComponentInEditor = id
|
||||
|
||||
return newScriptPath
|
||||
dispatch('pick', {
|
||||
runnable: {
|
||||
type: 'runnableByName',
|
||||
name: newScriptPath,
|
||||
inlineScript: undefined
|
||||
},
|
||||
fields: {}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -175,16 +179,18 @@
|
||||
</Drawer>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<Button
|
||||
on:click={createScript}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
startIcon={{ icon: faPlus }}
|
||||
btnClasses="truncate w-full"
|
||||
>
|
||||
Create an inline script
|
||||
</Button>
|
||||
{#if !hideCreateScript}
|
||||
<Button
|
||||
on:click={createScript}
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
startIcon={{ icon: faPlus }}
|
||||
btnClasses="truncate w-full"
|
||||
>
|
||||
Create an inline script
|
||||
</Button>
|
||||
{/if}
|
||||
<Button
|
||||
on:click={() => picker?.openDrawer()}
|
||||
size="xs"
|
||||
|
||||
+14
-12
@@ -1,18 +1,18 @@
|
||||
<script lang="ts">
|
||||
import ScriptSettingHeader from './shared/ScriptSettingHeader.svelte'
|
||||
import type { AppViewerContext, HiddenInlineScript } from '$lib/components/apps/types'
|
||||
import type { AppViewerContext, HiddenRunnable } from '$lib/components/apps/types'
|
||||
import ScriptRunConfiguration from './shared/ScriptRunConfiguration.svelte'
|
||||
import BackgroundScriptTriggerBy from './shared/BackgroundScriptTriggerBy.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import ScriptSettingsSection from './shared/ScriptSettingsSection.svelte'
|
||||
|
||||
export let script: HiddenInlineScript
|
||||
export let runnable: HiddenRunnable
|
||||
export let id: string
|
||||
|
||||
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { runnableComponents, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function updateAutoRefresh() {
|
||||
const autoRefresh = script.autoRefresh
|
||||
const autoRefresh = runnable.autoRefresh
|
||||
if ($runnableComponents?.[id]?.autoRefresh !== autoRefresh && autoRefresh !== undefined) {
|
||||
$runnableComponents[id] = {
|
||||
...$runnableComponents[id],
|
||||
@@ -20,21 +20,23 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$: runnable && ($app = $app)
|
||||
</script>
|
||||
|
||||
<div class={'border-y border-gray-200 divide-y'}>
|
||||
<ScriptSettingHeader name={script.name} />
|
||||
|
||||
{#if script.inlineScript}
|
||||
<ScriptSettingHeader name={runnable.name} />
|
||||
{#if runnable.type == 'runnableByPath' || runnable.inlineScript}
|
||||
<ScriptRunConfiguration
|
||||
bind:autoRefresh={script.autoRefresh}
|
||||
bind:recomputeOnInputChanged={script.recomputeOnInputChanged}
|
||||
canConfigureRecomputeOnInputChanged={script.inlineScript?.language !== 'frontend'}
|
||||
bind:autoRefresh={runnable.autoRefresh}
|
||||
bind:recomputeOnInputChanged={runnable.recomputeOnInputChanged}
|
||||
canConfigureRecomputeOnInputChanged={runnable.type == 'runnableByPath' ||
|
||||
runnable.inlineScript?.language !== 'frontend'}
|
||||
on:updateAutoRefresh={updateAutoRefresh}
|
||||
/>
|
||||
<BackgroundScriptTriggerBy
|
||||
bind:script
|
||||
recomputeOnInputChanged={script.recomputeOnInputChanged}
|
||||
bind:script={runnable}
|
||||
recomputeOnInputChanged={runnable.recomputeOnInputChanged}
|
||||
/>
|
||||
{:else}
|
||||
<ScriptSettingsSection title="Language selection">
|
||||
|
||||
+11
-4
@@ -1,17 +1,17 @@
|
||||
<script lang="ts">
|
||||
import type { HiddenInlineScript } from '$lib/components/apps/types'
|
||||
import type { HiddenRunnable } from '$lib/components/apps/types'
|
||||
|
||||
import { getDependencies } from '../utils'
|
||||
import ScriptTriggers from './ScriptTriggers.svelte'
|
||||
|
||||
export let script: HiddenInlineScript
|
||||
export let script: HiddenRunnable
|
||||
export let recomputeOnInputChanged: boolean | undefined = undefined
|
||||
|
||||
$: isFrontend = script.inlineScript?.language === 'frontend'
|
||||
$: isFrontend = script.type == 'runnableByName' && script.inlineScript?.language === 'frontend'
|
||||
$: triggerEvents = script.autoRefresh ? ['start', 'refresh'] : []
|
||||
</script>
|
||||
|
||||
{#if script.inlineScript}
|
||||
{#if script.type == 'runnableByName' && script.inlineScript}
|
||||
<ScriptTriggers
|
||||
bind:inlineScript={script.inlineScript}
|
||||
{triggerEvents}
|
||||
@@ -19,4 +19,11 @@
|
||||
{isFrontend}
|
||||
shoudlDisplayChangeEvents={recomputeOnInputChanged || isFrontend}
|
||||
/>
|
||||
{:else if script.type === 'runnableByName'}
|
||||
<ScriptTriggers
|
||||
dependencies={getDependencies(script.fields)}
|
||||
{triggerEvents}
|
||||
{isFrontend}
|
||||
shoudlDisplayChangeEvents={recomputeOnInputChanged}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
InputConnection,
|
||||
ResultAppInput,
|
||||
RowAppInput,
|
||||
Runnable,
|
||||
StaticAppInput,
|
||||
UploadAppInput,
|
||||
UserAppInput
|
||||
@@ -103,9 +104,10 @@ export type InlineScript = {
|
||||
|
||||
export type AppCssItemName = 'viewer' | 'grid' | AppComponent['type']
|
||||
|
||||
export type HiddenInlineScript = {
|
||||
export type HiddenRunnable = {
|
||||
name: string
|
||||
inlineScript: InlineScript | undefined
|
||||
// inlineScript?: InlineScript | undefined
|
||||
// type?: 'runnableByName' | 'runnableByPath'
|
||||
fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
autoRefresh?: boolean
|
||||
//deprecated and to be removed after migration
|
||||
@@ -113,7 +115,7 @@ export type HiddenInlineScript = {
|
||||
recomputeOnInputChanged?: boolean
|
||||
noBackendValue?: any
|
||||
hidden?: boolean
|
||||
}
|
||||
} & Runnable
|
||||
|
||||
export type App = {
|
||||
grid: GridItem[]
|
||||
@@ -123,7 +125,8 @@ export type App = {
|
||||
name: string
|
||||
inlineScript: InlineScript
|
||||
}>
|
||||
hiddenInlineScripts: Array<HiddenInlineScript>
|
||||
//TODO: should be called hidden runnables but migration tbd
|
||||
hiddenInlineScripts: Array<HiddenRunnable>
|
||||
css?: Partial<Record<AppCssItemName, Record<string, ComponentCssProperty>>>
|
||||
subgrids?: Record<string, GridItem[]>
|
||||
}
|
||||
|
||||
@@ -14,6 +14,19 @@ import type {
|
||||
VerticalAlignment
|
||||
} from './types'
|
||||
|
||||
export function migrateApp(app: App) {
|
||||
app.hiddenInlineScripts.forEach((x) => {
|
||||
if (x.type == undefined) {
|
||||
//@ts-ignore
|
||||
x.type = 'runnableByName'
|
||||
}
|
||||
//TODO: remove after migration is done
|
||||
if (x.doNotRecomputeOnInputChanged != undefined) {
|
||||
x.recomputeOnInputChanged = x.doNotRecomputeOnInputChanged
|
||||
x.doNotRecomputeOnInputChanged = undefined
|
||||
}
|
||||
})
|
||||
}
|
||||
export function allItems(
|
||||
grid: GridItem[],
|
||||
subgrids: Record<string, GridItem[]> | undefined
|
||||
|
||||
Reference in New Issue
Block a user