mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-09 16:05:42 +00:00
feat: add lockable version to scripts inside flows (#972)
* add it to openflow * push * push * final
This commit is contained in:
@@ -426,6 +426,7 @@ mod tests {
|
||||
InputTransform::Static { value: serde_json::json!("test2") },
|
||||
)]
|
||||
.into(),
|
||||
hash: None,
|
||||
},
|
||||
stop_after_if: None,
|
||||
summary: None,
|
||||
@@ -477,6 +478,7 @@ mod tests {
|
||||
value: FlowModuleValue::Script {
|
||||
path: "test".to_string(),
|
||||
input_transforms: HashMap::new(),
|
||||
hash: None,
|
||||
},
|
||||
stop_after_if: Some(StopAfterIf {
|
||||
expr: "previous.isEmpty()".to_string(),
|
||||
|
||||
@@ -12,7 +12,7 @@ use serde::{self, Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
more_serde::{default_false, default_id, default_true, is_default},
|
||||
scripts::{Schema, ScriptLang},
|
||||
scripts::{Schema, ScriptHash, ScriptLang},
|
||||
};
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -200,6 +200,7 @@ pub enum FlowModuleValue {
|
||||
#[serde(alias = "input_transform")]
|
||||
input_transforms: HashMap<String, InputTransform>,
|
||||
path: String,
|
||||
hash: Option<ScriptHash>,
|
||||
},
|
||||
ForloopFlow {
|
||||
iterator: InputTransform,
|
||||
|
||||
@@ -816,19 +816,27 @@ async fn push_next_flow_job(
|
||||
if let Some(it) = sleep_input_transform {
|
||||
let json_value = match it {
|
||||
InputTransform::Static { value } => value,
|
||||
InputTransform::Javascript { expr } => eval_timeout(
|
||||
expr.to_string(),
|
||||
[("result".to_string(), last_result.clone())].into(),
|
||||
None,
|
||||
None,
|
||||
"".to_string(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::ExecutionErr(format!(
|
||||
"Error during isolated evaluation of expression `{expr}`:\n{e}"
|
||||
))
|
||||
})?,
|
||||
InputTransform::Javascript { expr } => {
|
||||
let flow_input = flow_job.args.clone().unwrap_or_else(|| json!({}));
|
||||
|
||||
eval_timeout(
|
||||
expr.to_string(),
|
||||
[
|
||||
("result".to_string(), last_result.clone()),
|
||||
("flow_input".to_string(), flow_input),
|
||||
]
|
||||
.into(),
|
||||
None,
|
||||
None,
|
||||
"".to_string(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::ExecutionErr(format!(
|
||||
"Error during isolated evaluation of expression `{expr}`:\n{e}"
|
||||
))
|
||||
})?
|
||||
}
|
||||
};
|
||||
match json_value {
|
||||
serde_json::Value::Number(n) => {
|
||||
@@ -1445,9 +1453,15 @@ async fn compute_next_flow_transform<'c>(
|
||||
tx,
|
||||
NextFlowTransform::Continue(vec![JobPayload::Identity], NextStatus::NextStep),
|
||||
)),
|
||||
FlowModuleValue::Script { path: script_path, .. } => {
|
||||
let payload =
|
||||
script_path_to_payload(script_path, &mut tx, &flow_job.workspace_id).await?;
|
||||
FlowModuleValue::Script { path: script_path, hash: script_hash, .. } => {
|
||||
let payload = if script_hash.is_none() {
|
||||
script_path_to_payload(script_path, &mut tx, &flow_job.workspace_id).await?
|
||||
} else {
|
||||
JobPayload::ScriptHash {
|
||||
hash: script_hash.clone().unwrap(),
|
||||
path: script_path.to_owned(),
|
||||
}
|
||||
};
|
||||
Ok((
|
||||
tx,
|
||||
NextFlowTransform::Continue(vec![payload], NextStatus::NextStep),
|
||||
|
||||
@@ -270,21 +270,6 @@
|
||||
+Resource
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
btnClasses="mx-1 !font-medium"
|
||||
size="xs"
|
||||
spacingSize="md"
|
||||
color="light"
|
||||
on:click={scriptPicker.openDrawer}
|
||||
{iconOnly}
|
||||
startIcon={{ icon: faEye }}
|
||||
>
|
||||
Script
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Button
|
||||
btnClasses="mx-1 !font-medium"
|
||||
@@ -298,30 +283,45 @@
|
||||
Reset
|
||||
</Button>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
btnClasses="!font-medium"
|
||||
size="xs"
|
||||
spacingSize="md"
|
||||
color="light"
|
||||
on:click={editor.reloadWebsocket}
|
||||
startIcon={{ icon: faRotate }}
|
||||
>
|
||||
{#if !iconOnly}
|
||||
Assistant
|
||||
{/if}
|
||||
<span class="ml-1 -my-1">
|
||||
{#if lang == 'deno'}
|
||||
(<span class={websocketAlive.deno ? 'green' : 'text-red-700'}>Deno</span>)
|
||||
{:else if lang == 'go'}
|
||||
(<span class={websocketAlive.go ? 'green' : 'text-red-700'}>Go</span>)
|
||||
{:else if lang == 'python3'}
|
||||
(<span class={websocketAlive.pyright ? 'green' : 'text-red-700'}>Pyright</span>
|
||||
<span class={websocketAlive.black ? 'green' : 'text-red-700'}>Black</span>)
|
||||
{/if}
|
||||
</span>
|
||||
</Button></div
|
||||
>
|
||||
</div>
|
||||
<div class="py-1">
|
||||
<Button
|
||||
btnClasses="!font-medium"
|
||||
size="xs"
|
||||
spacingSize="md"
|
||||
color="light"
|
||||
on:click={editor.reloadWebsocket}
|
||||
startIcon={{ icon: faRotate }}
|
||||
>
|
||||
{#if !iconOnly}
|
||||
Assistant
|
||||
{/if}
|
||||
<span class="ml-1 -my-1">
|
||||
{#if lang == 'deno'}
|
||||
(<span class={websocketAlive.deno ? 'green' : 'text-red-700'}>Deno</span>)
|
||||
{:else if lang == 'go'}
|
||||
(<span class={websocketAlive.go ? 'green' : 'text-red-700'}>Go</span>)
|
||||
{:else if lang == 'python3'}
|
||||
(<span class={websocketAlive.pyright ? 'green' : 'text-red-700'}>Pyright</span>
|
||||
<span class={websocketAlive.black ? 'green' : 'text-red-700'}>Black</span>)
|
||||
{/if}
|
||||
</span>
|
||||
</Button>
|
||||
<div>
|
||||
<Button
|
||||
btnClasses="mx-1 !font-medium"
|
||||
size="xs"
|
||||
spacingSize="md"
|
||||
color="light"
|
||||
on:click={scriptPicker.openDrawer}
|
||||
{iconOnly}
|
||||
startIcon={{ icon: faEye }}
|
||||
>
|
||||
Script
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
<script lang="ts">
|
||||
import Badge from './common/badge/Badge.svelte'
|
||||
import { truncateHash } from '$lib/utils'
|
||||
import { Building } from 'svelte-lucide'
|
||||
import { Badge } from './common'
|
||||
import IconedResourceType from './IconedResourceType.svelte'
|
||||
|
||||
export let path: string
|
||||
export let hash: string | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div class="flex space-x-2 items-center">
|
||||
<div class="flex space-x-2 items-center w-full shrink min-w-0 {$$props.class}">
|
||||
{#if path.startsWith('hub/')}
|
||||
<div>
|
||||
<IconedResourceType width="20px" height="20px" name={path.split('/')[2]} silent={true} />
|
||||
</div>
|
||||
<span class="text-sm">{path}</span>
|
||||
<span class="text-sm truncate">{path}</span>
|
||||
{:else}
|
||||
<Badge color="blue">Workspace</Badge>
|
||||
<span class="text-sm">{path}</span>
|
||||
<div class="center-center">
|
||||
<Building size="16px" />
|
||||
</div>
|
||||
<span class="text-sm truncate">{path}</span>
|
||||
{#if hash}
|
||||
<Badge>{truncateHash(hash)}</Badge>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</script>
|
||||
|
||||
<Drawer bind:this={logViewer} size="900px">
|
||||
<DrawerContent title={'Expanded Logs'} on:close={logViewer.closeDrawer}>
|
||||
<DrawerContent title="Expanded Logs" on:close={logViewer.closeDrawer}>
|
||||
<div>
|
||||
<pre class="bg-gray-50 text-xs w-full p-2"
|
||||
>{#if content}{content}{:else if isLoading}Waiting for job to start...{:else}No logs are available yet{/if}</pre
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import Icon from 'svelte-awesome'
|
||||
import { ButtonType } from './model'
|
||||
import { goto } from '$app/navigation'
|
||||
import { faArrowsRotate, faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
export let size: ButtonType.Size = 'md'
|
||||
export let spacingSize: ButtonType.Size = size
|
||||
|
||||
@@ -3,13 +3,11 @@
|
||||
import Badge from '$lib/components/common/badge/Badge.svelte'
|
||||
import IconedPath from '$lib/components/IconedPath.svelte'
|
||||
import { RawScript, type FlowModule } from '$lib/gen'
|
||||
import { isEmptyFlowModule } from '../utils'
|
||||
import { truncateHash } from '$lib/utils'
|
||||
|
||||
export let flowModule: FlowModule | undefined = undefined
|
||||
export let title: string | undefined = undefined
|
||||
|
||||
$: shouldPick = flowModule && isEmptyFlowModule(flowModule)
|
||||
|
||||
const languageColors: Record<RawScript.language, BadgeColor> = {
|
||||
[RawScript.language.GO]: 'dark-indigo',
|
||||
[RawScript.language.DENO]: 'dark-blue',
|
||||
@@ -22,18 +20,18 @@
|
||||
class="flex items-center justify-between py-2 px-4 border-b border-gray-300 space-x-2 h-full max-h-12 flex-nowrap"
|
||||
>
|
||||
{#if flowModule}
|
||||
<span class="text-sm w-full">
|
||||
<span class="text-sm w-full mr-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
{#if shouldPick}
|
||||
<span class="font-bold text-xs">Select a step kind</span>
|
||||
{#if flowModule.value.type === 'identity'}
|
||||
<span class="font-bold text-xs">Identity (input copied to output)</span>
|
||||
{:else if flowModule?.value.type === 'rawscript'}
|
||||
<Badge color={languageColors[flowModule?.value.language] ?? 'gray'} capitalize>
|
||||
{flowModule?.value.language}
|
||||
</Badge>
|
||||
<input bind:value={flowModule.summary} placeholder={'Summary'} />
|
||||
<input bind:value={flowModule.summary} placeholder={'Summary'} class="w-full grow" />
|
||||
{:else if flowModule?.value.type === 'script' && 'path' in flowModule.value && flowModule.value.path}
|
||||
<IconedPath path={flowModule.value.path} />
|
||||
<input bind:value={flowModule.summary} placeholder="Summary" class="ml-2" />
|
||||
<IconedPath path={flowModule.value.path} hash={flowModule.value.hash} class="grow" />
|
||||
<input bind:value={flowModule.summary} placeholder="Summary" class="w-full grow" />
|
||||
{/if}
|
||||
</div>
|
||||
</span>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import SchemaForm from '$lib/components/SchemaForm.svelte'
|
||||
import FlowCard from '../common/FlowCard.svelte'
|
||||
import { copyFirstStepSchema, flowStore } from '../flowStore'
|
||||
import { isEmptyFlowModule } from '../utils'
|
||||
import CapturePayload from './CapturePayload.svelte'
|
||||
|
||||
let capturePayload: CapturePayload
|
||||
@@ -29,7 +28,7 @@
|
||||
color="dark"
|
||||
size="sm"
|
||||
disabled={$flowStore.value.modules.length === 0 ||
|
||||
isEmptyFlowModule($flowStore.value.modules[0])}
|
||||
$flowStore.value.modules[0].value.type == 'identity'}
|
||||
on:click={copyFirstStepSchema}
|
||||
>
|
||||
First step's inputs
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { ToggleButton, ToggleButtonGroup } from '$lib/components/common'
|
||||
import WindmillIcon from '$lib/components/icons/WindmillIcon.svelte'
|
||||
import ToggleHubWorkspace from '$lib/components/ToggleHubWorkspace.svelte'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import { RawScript, Script } from '$lib/gen'
|
||||
|
||||
import { faBolt, faBuilding, faCheck, faCode } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faBolt, faCheck, faCode } from '@fortawesome/free-solid-svg-icons'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import FlowScriptPicker from '../pickers/FlowScriptPicker.svelte'
|
||||
import PickHubScript from '../pickers/PickHubScript.svelte'
|
||||
@@ -51,7 +50,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<h3 class="pb-2">
|
||||
<h3 class="pb-2 pt-4">
|
||||
Inline new <span class="text-blue-500">{kind == 'script' ? 'common' : kind}</span> script
|
||||
<Tooltip>
|
||||
Embed a script directly inside a flow instead of saving the script into your workspace for
|
||||
@@ -138,7 +137,7 @@
|
||||
<ToggleHubWorkspace bind:selected={pick_existing} />
|
||||
</PickHubScript>
|
||||
{:else}
|
||||
<WorkspaceScriptPicker bind:filter {kind} on:pick>
|
||||
<WorkspaceScriptPicker displayLock bind:filter {kind} on:pick>
|
||||
<ToggleHubWorkspace bind:selected={pick_existing} />
|
||||
</WorkspaceScriptPicker>
|
||||
{/if}
|
||||
|
||||
@@ -127,6 +127,7 @@
|
||||
<FlowModuleHeader
|
||||
bind:module={flowModule}
|
||||
on:toggleSuspend={() => (selected = 'advanced-suspend')}
|
||||
on:toggleSleep={() => (selected = 'advanced-sleep')}
|
||||
on:toggleRetry={() => (selected = 'advanced-retries')}
|
||||
on:toggleStopAfterIf={() => (selected = 'advanced-early-stop')}
|
||||
on:fork={async () => {
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Button from '$lib/components/common/button/Button.svelte'
|
||||
import { EDITOR_BAR_WIDTH_THRESHOLD } from '$lib/components/EditorBar.svelte'
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import { classNames } from '$lib/utils'
|
||||
import {
|
||||
faArrowRotateForward,
|
||||
faBed,
|
||||
faCodeBranch,
|
||||
faSave,
|
||||
faStop
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { faBed, faCodeBranch, faSave, faStop } from '@fortawesome/free-solid-svg-icons'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { isEmptyFlowModule } from '../utils'
|
||||
import { PhoneIncoming, Repeat } from 'svelte-lucide'
|
||||
|
||||
export let module: FlowModule
|
||||
|
||||
@@ -20,36 +13,38 @@
|
||||
|
||||
let width = 0
|
||||
|
||||
$: shouldPick = isEmptyFlowModule(module)
|
||||
$: iconOnly = width < EDITOR_BAR_WIDTH_THRESHOLD
|
||||
$: moduleRetry = module.retry?.constant || module.retry?.exponential
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row space-x-2" bind:clientWidth={width}>
|
||||
{#if !shouldPick}
|
||||
<span
|
||||
{#if module.value.type === 'script' || module.value.type === 'rawscript'}
|
||||
<button
|
||||
class={classNames('badge', module.stop_after_if ? 'badge-on' : 'badge-off')}
|
||||
on:click={() => dispatch('toggleStopAfterIf')}
|
||||
>
|
||||
<Icon data={faStop} scale={0.8} />
|
||||
</span>
|
||||
<span
|
||||
class={classNames('badge', moduleRetry ? 'badge-on' : 'badge-off')}
|
||||
</button>
|
||||
<button
|
||||
class={classNames('badge', moduleRetry ? 'badge-on' : 'badge-off', 'center-center')}
|
||||
on:click={() => dispatch('toggleRetry')}
|
||||
>
|
||||
<Icon data={faArrowRotateForward} scale={0.8} />
|
||||
</span>
|
||||
<span
|
||||
class={classNames(
|
||||
'badge',
|
||||
Boolean(module.suspend) || Boolean(module.sleep) ? 'badge-on' : 'badge-off'
|
||||
)}
|
||||
on:click={() => dispatch('toggleSuspend')}
|
||||
<Repeat size="14px" />
|
||||
</button>
|
||||
<button
|
||||
class={classNames('badge', Boolean(module.sleep) ? 'badge-on' : 'badge-off')}
|
||||
on:click={() => dispatch('toggleSleep')}
|
||||
>
|
||||
<Icon data={faBed} scale={0.8} />
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class={classNames('badge', Boolean(module.suspend) ? 'badge-on' : 'badge-off')}
|
||||
on:click={() => dispatch('toggleSuspend')}
|
||||
>
|
||||
<PhoneIncoming size="14px" />
|
||||
</button>
|
||||
{/if}
|
||||
{#if module.value.type === 'script' && !shouldPick}
|
||||
{#if module.value.type === 'script'}
|
||||
<div class="w-2" />
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
@@ -62,7 +57,7 @@
|
||||
</Button>
|
||||
{/if}
|
||||
|
||||
{#if module.value.type === 'rawscript' && !shouldPick}
|
||||
{#if module.value.type === 'rawscript'}
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
previousModule !== undefined ||
|
||||
$selectedId == 'failure'}
|
||||
on:pick={async ({ detail }) => {
|
||||
const { path, summary, kind } = detail
|
||||
const [module, state] = await pickScript(path, summary, flowModule.id)
|
||||
const { path, summary, kind, hash } = detail
|
||||
const [module, state] = await pickScript(path, summary, flowModule.id, hash)
|
||||
|
||||
if (kind == Script.kind.APPROVAL) {
|
||||
module.suspend = { required_events: 1, timeout: 1800 }
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Schema } from '$lib/common'
|
||||
import type { Flow, FlowModule, FlowStatusModule } from '$lib/gen'
|
||||
import { writable } from 'svelte/store'
|
||||
import { loadFlowModuleState } from './flowStateUtils'
|
||||
import { emptyFlowModuleState, isEmptyFlowModule } from './utils'
|
||||
import { emptyFlowModuleState } from './utils'
|
||||
|
||||
export type FlowModuleState = {
|
||||
schema: Schema
|
||||
@@ -57,7 +57,7 @@ async function mapFlowModule(flowModule: FlowModule, modulesState: FlowState) {
|
||||
)
|
||||
}
|
||||
|
||||
if (isEmptyFlowModule(flowModule)) {
|
||||
if (value.type === 'identity') {
|
||||
modulesState[flowModule.id] = emptyFlowModuleState()
|
||||
} else {
|
||||
const flowModuleState = await loadFlowModuleState(flowModule)
|
||||
|
||||
@@ -62,11 +62,12 @@ export function nextId(): string {
|
||||
export async function pickScript(
|
||||
path: string,
|
||||
summary: string,
|
||||
id: string
|
||||
id: string,
|
||||
hash?: string
|
||||
): Promise<[FlowModule & { value: PathScript }, FlowModuleState]> {
|
||||
const flowModule: FlowModule & { value: PathScript } = {
|
||||
id,
|
||||
value: { type: 'script', path, input_transforms: {} },
|
||||
value: { type: 'script', path, hash, input_transforms: {} },
|
||||
summary
|
||||
}
|
||||
|
||||
@@ -95,7 +96,7 @@ export async function createLoop(id: string): Promise<[FlowModule, FlowModuleSta
|
||||
value: {
|
||||
type: 'forloopflow',
|
||||
modules: [],
|
||||
iterator: { type: 'javascript', expr: '["iterator require expression that returns an array"]' },
|
||||
iterator: { type: 'javascript', expr: '["dynamic or static array"]' },
|
||||
skip_failures: true
|
||||
}
|
||||
}
|
||||
@@ -200,7 +201,7 @@ export async function createScriptFromInlineScript(
|
||||
|
||||
const availablePath = await findNextAvailablePath(path)
|
||||
|
||||
await ScriptService.createScript({
|
||||
const hash = await ScriptService.createScript({
|
||||
workspace: get(workspaceStore)!,
|
||||
requestBody: {
|
||||
path: availablePath,
|
||||
@@ -214,7 +215,7 @@ export async function createScriptFromInlineScript(
|
||||
}
|
||||
})
|
||||
|
||||
return pickScript(availablePath, flowModule.summary ?? '', flowModule.id)
|
||||
return pickScript(availablePath, flowModule.summary ?? '', flowModule.id, hash)
|
||||
}
|
||||
|
||||
export function deleteFlowStateById(id: string) {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<script lang="ts">
|
||||
import Badge from '$lib/components/common/badge/Badge.svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { faBed, faRepeat, faStop, faTimesCircle } from '@fortawesome/free-solid-svg-icons'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { PhoneIncoming, Repeat } from 'svelte-lucide'
|
||||
|
||||
export let isFirst: boolean = false
|
||||
export let isLast: boolean = false
|
||||
@@ -38,21 +40,38 @@
|
||||
margin
|
||||
)}
|
||||
>
|
||||
<div class="absolute text-sm right-14 -bottom-3 flex flex-row gap-1">
|
||||
<div class="absolute text-sm right-12 -bottom-3 flex flex-row gap-1 z-20">
|
||||
{#if retry}
|
||||
<div class="bg-white rounded border text-gray-600 px-1">
|
||||
<Icon scale={0.8} data={faRepeat} />
|
||||
</div>
|
||||
<Popover notClickable>
|
||||
<div class="bg-white rounded border text-gray-700 px-1 center-center">
|
||||
<Repeat size="14px" />
|
||||
</div>
|
||||
<span slot="text">Retries</span>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if earlyStop}
|
||||
<div class="bg-white rounded border text-gray-600 px-1">
|
||||
<Icon scale={0.8} data={faStop} />
|
||||
</div>
|
||||
<Popover notClickable>
|
||||
<div class="bg-white rounded border text-gray-700 px-1">
|
||||
<Icon scale={0.8} data={faStop} />
|
||||
</div>
|
||||
<span slot="text">Early Stop</span>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if suspend || sleep}
|
||||
<div class="bg-white rounded border text-gray-600 px-1">
|
||||
<Icon scale={0.8} data={faBed} />
|
||||
</div>
|
||||
{#if sleep}
|
||||
<Popover notClickable>
|
||||
<div class="bg-white rounded border text-gray-700 px-1">
|
||||
<Icon scale={0.8} data={faBed} />
|
||||
</div>
|
||||
<span slot="text">Sleep</span>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if suspend}
|
||||
<Popover notClickable>
|
||||
<div class="bg-white rounded border text-gray-700 px-1 center-center">
|
||||
<PhoneIncoming size="12px" />
|
||||
</div>
|
||||
<span slot="text">Suspend</span>
|
||||
</Popover>
|
||||
{/if}
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import FlowErrorHandlerItem from './FlowErrorHandlerItem.svelte'
|
||||
import RemoveStepConfirmationModal from '../content/RemoveStepConfirmationModal.svelte'
|
||||
import { emptyFlowModuleState, isEmptyFlowModule } from '../utils'
|
||||
import { emptyFlowModuleState } from '../utils'
|
||||
import MapItem from './MapItem.svelte'
|
||||
import FlowSettingsItem from './FlowSettingsItem.svelte'
|
||||
import FlowInputsItem from './FlowInputsItem.svelte'
|
||||
@@ -112,7 +112,7 @@
|
||||
<MapItem
|
||||
bind:mod
|
||||
on:delete={(event) => {
|
||||
if (event.detail.detail.shiftKey || isEmptyFlowModule(mod)) {
|
||||
if (event.detail.detail.shiftKey || mod.value.type === 'identity') {
|
||||
removeAtIndex(index)
|
||||
} else {
|
||||
indexToRemove = index
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script lang="ts">
|
||||
import { Menu } from '$lib/components/common'
|
||||
import { faCode, faCodeBranch, faPlus, faRepeat } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCode, faCodeBranch, faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { Repeat } from 'svelte-lucide'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
@@ -26,17 +27,20 @@
|
||||
tabindex="-1"
|
||||
>
|
||||
<Icon data={faCode} scale={0.8} class="mr-1" />
|
||||
Script or Module
|
||||
Script
|
||||
</button>
|
||||
<button
|
||||
class="w-full text-left p-2 hover:bg-gray-100"
|
||||
class="w-full inline-flex gap-1 text-left p-2 hover:bg-gray-100"
|
||||
on:click={() => {
|
||||
close()
|
||||
dispatch('new', 'forloop')
|
||||
}}
|
||||
role="menuitem"
|
||||
>
|
||||
<Icon data={faRepeat} scale={0.8} class="mr-1" />
|
||||
<span>
|
||||
<Repeat size="14px" />
|
||||
</span>
|
||||
|
||||
For Loop
|
||||
</button>
|
||||
|
||||
|
||||
@@ -7,17 +7,11 @@
|
||||
import InsertModuleButton from './InsertModuleButton.svelte'
|
||||
import FlowBranchOneMap from './FlowBranchOneMap.svelte'
|
||||
import FlowBranchAllMap from './FlowBranchAllMap.svelte'
|
||||
import {
|
||||
faArrowRotateForward,
|
||||
faBuilding,
|
||||
faCode,
|
||||
faCodeBranch,
|
||||
faLongArrowDown,
|
||||
faRepeat
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { faCodeBranch, faLongArrowDown } from '@fortawesome/free-solid-svg-icons'
|
||||
import Icon from 'svelte-awesome'
|
||||
import IconedResourceType from '$lib/components/IconedResourceType.svelte'
|
||||
import LanguageIcon from '$lib/components/common/languageIcons/LanguageIcon.svelte'
|
||||
import { Building, Repeat } from 'svelte-lucide'
|
||||
|
||||
export let mod: FlowModule
|
||||
|
||||
@@ -53,7 +47,7 @@
|
||||
{...itemProps}
|
||||
>
|
||||
<div slot="icon">
|
||||
<Icon data={faArrowRotateForward} scale={1.1} />
|
||||
<Repeat size="16px" />
|
||||
</div>
|
||||
</FlowModuleSchemaItem>
|
||||
<div class="flex flex-row w-full">
|
||||
@@ -125,7 +119,7 @@
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<Icon data={faBuilding} scale={1.1} />
|
||||
<Building size="14px" />
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -6,15 +6,18 @@
|
||||
import { Badge, Skeleton } from '$lib/components/common'
|
||||
import { fade } from 'svelte/transition'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { emptyString, truncateHash } from '$lib/utils'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
|
||||
export let kind: 'script' | 'trigger' | 'approval' | 'failure' = 'script'
|
||||
export let isTemplate: boolean | undefined = undefined
|
||||
export let displayLock = false
|
||||
|
||||
type Item = {
|
||||
path: string
|
||||
summary?: string
|
||||
description?: string
|
||||
hash?: string
|
||||
}
|
||||
|
||||
let items: Item[] | undefined = undefined
|
||||
@@ -36,6 +39,7 @@
|
||||
).sort()
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
let lockHash = displayLock
|
||||
</script>
|
||||
|
||||
<SearchItems
|
||||
@@ -47,6 +51,7 @@
|
||||
<div class="flex flex-col min-h-0">
|
||||
<div class="w-full flex mt-1 items-center gap-2 mb-3">
|
||||
<slot />
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search Workspace Scripts"
|
||||
@@ -75,16 +80,24 @@
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if displayLock}
|
||||
<div class="flex flex-row-reverse">
|
||||
<Toggle
|
||||
bind:checked={lockHash}
|
||||
options={{ left: 'Latest version', right: 'Lock current hash permanently' }}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{#if filter.length > 0 && filteredItems.length == 0}
|
||||
<p class="py-5">No items found</p>
|
||||
{/if}
|
||||
<ul class="divide-y divide-gray-200 overflow-auto">
|
||||
{#each filteredItems as { path, summary, description, marked }}
|
||||
{#each filteredItems as { path, hash, summary, description, marked }}
|
||||
<li class="flex flex-row w-full">
|
||||
<button
|
||||
class="py-4 px-1 gap-1 flex flex-row grow hover:bg-blue-50 bg-white transition-all text-black"
|
||||
on:click={() => {
|
||||
dispatch('pick', { path })
|
||||
dispatch('pick', { path, hash: lockHash ? hash : undefined })
|
||||
}}
|
||||
>
|
||||
<div class="flex flex-col">
|
||||
@@ -102,6 +115,7 @@
|
||||
</div>
|
||||
<div class="text-xs font-light italic text-left">{description ?? ''}</div>
|
||||
</div>
|
||||
{#if lockHash}<Badge large baseClass="ml-4">{truncateHash(hash ?? '')}</Badge>{/if}
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
|
||||
@@ -212,9 +212,6 @@ export function charsToNumber(n: string): number {
|
||||
return res - 1
|
||||
}
|
||||
|
||||
export function isEmptyFlowModule(flowModule: FlowModule): boolean {
|
||||
return flowModule.value.type === 'identity'
|
||||
}
|
||||
|
||||
export async function findNextAvailablePath(path: string): Promise<string> {
|
||||
try {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { workspaceStore, usersWorkspaceStore } from '$lib/stores'
|
||||
import { classNames } from '$lib/utils'
|
||||
import { faBuilding } from '@fortawesome/free-solid-svg-icons'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { Building } from 'svelte-lucide'
|
||||
|
||||
import Menu from '../common/menu/Menu.svelte'
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
'group w-full flex items-center text-white hover:bg-gray-50 hover:text-gray-900 focus:ring-4 focus:outline-none focus:ring-gray-300 px-2 py-2 text-sm font-medium rounded-md h-8 '
|
||||
)}
|
||||
>
|
||||
<Icon
|
||||
data={faBuilding}
|
||||
class={classNames('flex-shrink-0 h-4 w-4', isCollapsed ? '-mr-1' : 'mr-2')}
|
||||
/>
|
||||
<div class="center-center mr-2">
|
||||
<Building size="16px" />
|
||||
</div>
|
||||
|
||||
{#if !isCollapsed}
|
||||
<span class={classNames('whitespace-pre truncate')}> {$workspaceStore} </span>
|
||||
|
||||
@@ -202,6 +202,8 @@ components:
|
||||
$ref: "#/components/schemas/InputTransform"
|
||||
path:
|
||||
type: string
|
||||
hash:
|
||||
type: string
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
|
||||
Reference in New Issue
Block a user