windmill-components 1.382.1 → 1.382.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package/base.d.ts +1 -1
- package/package/base.js +1 -1
- package/package/components/EditorBar.svelte +48 -38
- package/package/components/EditorBar.svelte.d.ts +2 -0
- package/package/components/FlowBuilder.svelte +15 -12
- package/package/components/FlowBuilder.svelte.d.ts +2 -2
- package/package/components/FlowStatusViewer.svelte +7 -1
- package/package/components/FlowStatusViewer.svelte.d.ts +3 -0
- package/package/components/FlowStatusViewerInner.svelte +12 -5
- package/package/components/FlowViewer.svelte +6 -46
- package/package/components/FlowViewer.svelte.d.ts +1 -0
- package/package/components/FlowViewerInner.svelte +109 -0
- package/package/components/FlowViewerInner.svelte.d.ts +22 -0
- package/package/components/ScriptBuilder.svelte +48 -40
- package/package/components/ScriptBuilder.svelte.d.ts +3 -0
- package/package/components/ScriptEditor.svelte +3 -1
- package/package/components/ScriptEditor.svelte.d.ts +2 -0
- package/package/components/apps/components/display/AppText.svelte +8 -3
- package/package/components/apps/components/inputs/AppSelect.svelte +45 -29
- package/package/components/apps/editor/component/components.d.ts +6 -0
- package/package/components/apps/editor/component/components.js +6 -0
- package/package/components/custom_ui.d.ts +35 -1
- package/package/components/details/DetailPageDetailPanel.svelte +4 -38
- package/package/components/details/WebhooksPanel.svelte +3 -3
- package/package/components/flows/content/FlowModuleComponent.svelte +8 -2
- package/package/components/flows/content/FlowModuleEarlyStop.svelte +193 -66
- package/package/components/flows/content/FlowModuleHeader.svelte +1 -1
- package/package/components/flows/content/FlowSettings.svelte +26 -15
- package/package/components/flows/flowExplorer.js +3 -0
- package/package/components/flows/map/MapItem.svelte +1 -1
- package/package/components/flows/propPicker/PropPickerWrapper.svelte +2 -0
- package/package/components/flows/propPicker/PropPickerWrapper.svelte.d.ts +1 -0
- package/package/components/flows/types.d.ts +2 -0
- package/package/components/graph/model.d.ts +3 -0
- package/package/components/propertyPicker/PropPickerResult.svelte +6 -1
- package/package/components/propertyPicker/PropPickerResult.svelte.d.ts +1 -0
- package/package/components/sidebar/changelogs.js +5 -0
- package/package/gen/core/OpenAPI.js +1 -1
- package/package/gen/schemas.gen.d.ts +12 -0
- package/package/gen/schemas.gen.js +12 -0
- package/package/gen/services.gen.d.ts +4 -3
- package/package/gen/services.gen.js +7 -5
- package/package/gen/types.gen.d.ts +16 -8
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SvelteComponent } from "svelte";
|
|
2
2
|
import { type NewScript, type NewScriptWithDraft } from '../gen';
|
|
3
3
|
import type DiffDrawer from './DiffDrawer.svelte';
|
|
4
|
+
import type { ScriptBuilderWhitelabelCustomUi } from './custom_ui';
|
|
4
5
|
declare const __propDef: {
|
|
5
6
|
props: {
|
|
6
7
|
script: NewScript;
|
|
@@ -10,11 +11,13 @@ declare const __propDef: {
|
|
|
10
11
|
initialArgs?: Record<string, any> | undefined;
|
|
11
12
|
lockedLanguage?: boolean | undefined;
|
|
12
13
|
showMeta?: boolean | undefined;
|
|
14
|
+
neverShowMeta?: boolean | undefined;
|
|
13
15
|
diffDrawer?: DiffDrawer | undefined;
|
|
14
16
|
savedScript?: NewScriptWithDraft | undefined;
|
|
15
17
|
searchParams?: URLSearchParams | undefined;
|
|
16
18
|
disableHistoryChange?: boolean | undefined;
|
|
17
19
|
replaceStateFn?: ((url: string) => void) | undefined;
|
|
20
|
+
customUi?: ScriptBuilderWhitelabelCustomUi | undefined;
|
|
18
21
|
setCode?: ((code: string) => void) | undefined;
|
|
19
22
|
};
|
|
20
23
|
events: {
|
|
@@ -38,6 +38,7 @@ export let edit = true;
|
|
|
38
38
|
export let noHistory = false;
|
|
39
39
|
export let saveToWorkspace = false;
|
|
40
40
|
export let watchChanges = false;
|
|
41
|
+
export let customUi = {};
|
|
41
42
|
let websocketAlive = {
|
|
42
43
|
pyright: false,
|
|
43
44
|
deno: false,
|
|
@@ -204,6 +205,7 @@ function collabUrl() {
|
|
|
204
205
|
setCollaborationMode()
|
|
205
206
|
}
|
|
206
207
|
}}
|
|
208
|
+
customUi={customUi?.editorBar}
|
|
207
209
|
collabLive={wsProvider?.shouldConnect}
|
|
208
210
|
{collabMode}
|
|
209
211
|
{validCode}
|
|
@@ -221,7 +223,7 @@ function collabUrl() {
|
|
|
221
223
|
{noHistory}
|
|
222
224
|
{saveToWorkspace}
|
|
223
225
|
/>
|
|
224
|
-
{#if !noSyncFromGithub}
|
|
226
|
+
{#if !noSyncFromGithub && customUi?.editorBar?.useVsCode != false}
|
|
225
227
|
<div class="py-1">
|
|
226
228
|
<Button
|
|
227
229
|
target="_blank"
|
|
@@ -3,6 +3,7 @@ import type { Schema, SupportedLanguage } from '../common';
|
|
|
3
3
|
import { type Preview } from '../gen';
|
|
4
4
|
import Editor from './Editor.svelte';
|
|
5
5
|
import DiffEditor from './DiffEditor.svelte';
|
|
6
|
+
import type { ScriptEditorWhitelabelCustomUi } from './custom_ui';
|
|
6
7
|
declare const __propDef: {
|
|
7
8
|
props: {
|
|
8
9
|
schema?: Schema | any;
|
|
@@ -22,6 +23,7 @@ declare const __propDef: {
|
|
|
22
23
|
noHistory?: boolean | undefined;
|
|
23
24
|
saveToWorkspace?: boolean | undefined;
|
|
24
25
|
watchChanges?: boolean | undefined;
|
|
26
|
+
customUi?: ScriptEditorWhitelabelCustomUi | undefined;
|
|
25
27
|
inferSchema?: ((code: string, nlang?: SupportedLanguage) => Promise<void>) | undefined;
|
|
26
28
|
setCollaborationMode?: (() => Promise<void>) | undefined;
|
|
27
29
|
disableCollaboration?: (() => void) | undefined;
|
|
@@ -21,6 +21,11 @@ export let customCss = undefined;
|
|
|
21
21
|
export let render;
|
|
22
22
|
export let editorMode = false;
|
|
23
23
|
let resolvedConfig = initConfig(components['textcomponent'].initialData.configuration, configuration);
|
|
24
|
+
$: editorMode && onEditorMode();
|
|
25
|
+
function onEditorMode() {
|
|
26
|
+
autosize();
|
|
27
|
+
setTimeout(() => autosize(), 50);
|
|
28
|
+
}
|
|
24
29
|
const { app, worldStore, mode, componentControl } = getContext('AppViewerContext');
|
|
25
30
|
let css = initCss($app.css?.textcomponent, customCss);
|
|
26
31
|
let result = undefined;
|
|
@@ -105,6 +110,7 @@ function autosize() {
|
|
|
105
110
|
el.style.cssText = 'height:auto; padding:0';
|
|
106
111
|
el.style.cssText = 'height:' + el.scrollHeight + 'px';
|
|
107
112
|
}
|
|
113
|
+
// console.log(el, el?.scrollHeight)
|
|
108
114
|
}, 0);
|
|
109
115
|
}
|
|
110
116
|
</script>
|
|
@@ -137,7 +143,6 @@ function autosize() {
|
|
|
137
143
|
if (!editorMode) {
|
|
138
144
|
editorMode = true
|
|
139
145
|
document.getElementById(`text-${id}`)?.focus()
|
|
140
|
-
autosize()
|
|
141
146
|
}
|
|
142
147
|
}}
|
|
143
148
|
on:keydown|stopPropagation
|
|
@@ -146,7 +151,7 @@ function autosize() {
|
|
|
146
151
|
<AlignWrapper {verticalAlignment}>
|
|
147
152
|
<textarea
|
|
148
153
|
class={twMerge(
|
|
149
|
-
'whitespace-pre-wrap !outline-none !border-0 !bg-transparent !resize-none !
|
|
154
|
+
'whitespace-pre-wrap !outline-none !border-0 !bg-transparent !resize-none !ring-0 !p-0',
|
|
150
155
|
css?.text?.class,
|
|
151
156
|
'wm-text',
|
|
152
157
|
classes,
|
|
@@ -188,7 +193,7 @@ function autosize() {
|
|
|
188
193
|
class="flex flex-wrap gap-2 pb-0.5 w-full {$mode === 'dnd' &&
|
|
189
194
|
(componentInput?.type == 'template' || componentInput?.type == 'templatev2')
|
|
190
195
|
? 'cursor-text'
|
|
191
|
-
: ''}"
|
|
196
|
+
: 'overflow-auto'}"
|
|
192
197
|
>
|
|
193
198
|
<svelte:element
|
|
194
199
|
this={component}
|
|
@@ -101,6 +101,11 @@ function onChange(e) {
|
|
|
101
101
|
onSelect.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((f) => f()));
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
+
function onNativeChange(e) {
|
|
105
|
+
const target = e.target;
|
|
106
|
+
const value = target.value;
|
|
107
|
+
setValue(value);
|
|
108
|
+
}
|
|
104
109
|
function setValue(nvalue) {
|
|
105
110
|
let result = undefined;
|
|
106
111
|
try {
|
|
@@ -188,35 +193,46 @@ let filterText = '';
|
|
|
188
193
|
}}
|
|
189
194
|
>
|
|
190
195
|
{#if Array.isArray(listItems) && listItems.every((x) => x && typeof x == 'object' && typeof x['label'] == 'string' && `value` in x)}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
:
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
196
|
+
{#if resolvedConfig.nativeHtmlSelect}
|
|
197
|
+
<select class={css?.input?.class} style={css?.input?.style} on:change={onNativeChange}>
|
|
198
|
+
{#if resolvedConfig.placeholder}
|
|
199
|
+
<option value="" disabled selected>{resolvedConfig.placeholder}</option>
|
|
200
|
+
{/if}
|
|
201
|
+
{#each listItems as item (item.value)}
|
|
202
|
+
<option value={item.value} selected={item.value === value}>{item.label}</option>
|
|
203
|
+
{/each}
|
|
204
|
+
</select>
|
|
205
|
+
{:else}
|
|
206
|
+
<Select
|
|
207
|
+
inAppEditor={true}
|
|
208
|
+
--border-radius="0.250rem"
|
|
209
|
+
--clear-icon-color="#6b7280"
|
|
210
|
+
--border={$darkMode ? '1px solid #6b7280' : '1px solid #d1d5db'}
|
|
211
|
+
bind:filterText
|
|
212
|
+
on:filter={handleFilter}
|
|
213
|
+
on:clear={onClear}
|
|
214
|
+
on:change={onChange}
|
|
215
|
+
items={listItems}
|
|
216
|
+
listAutoWidth={resolvedConfig.fullWidth}
|
|
217
|
+
inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
|
|
218
|
+
containerStyles={($darkMode
|
|
219
|
+
? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
|
|
220
|
+
: SELECT_INPUT_DEFAULT_STYLE.containerStyles) + css?.input?.style}
|
|
221
|
+
{value}
|
|
222
|
+
class={css?.input?.class}
|
|
223
|
+
placeholder={resolvedConfig.placeholder}
|
|
224
|
+
disabled={resolvedConfig.disabled}
|
|
225
|
+
on:focus={() => {
|
|
226
|
+
if (!$connectingInput.opened) {
|
|
227
|
+
$selectedComponent = [id]
|
|
228
|
+
}
|
|
229
|
+
}}
|
|
230
|
+
>
|
|
231
|
+
<svelte:fragment slot="item" let:item
|
|
232
|
+
>{#if resolvedConfig.create}{item.created ? 'Add new: ' : ''}{/if}{item.label}
|
|
233
|
+
</svelte:fragment>
|
|
234
|
+
</Select>
|
|
235
|
+
{/if}
|
|
220
236
|
{:else}
|
|
221
237
|
<Popover notClickable placement="bottom" popupClass="!bg-surface border w-96">
|
|
222
238
|
<div
|
|
@@ -2388,6 +2388,12 @@ export declare const components: {
|
|
|
2388
2388
|
readonly fieldType: "boolean";
|
|
2389
2389
|
readonly tooltip: "Preselect first item in the options if no default value is set";
|
|
2390
2390
|
};
|
|
2391
|
+
readonly nativeHtmlSelect: {
|
|
2392
|
+
readonly type: "static";
|
|
2393
|
+
readonly fieldType: "boolean";
|
|
2394
|
+
readonly value: false;
|
|
2395
|
+
readonly tooltip: "Use a native html select instead of the Windmill select component";
|
|
2396
|
+
};
|
|
2391
2397
|
readonly fullWidth: {
|
|
2392
2398
|
readonly type: "static";
|
|
2393
2399
|
readonly fieldType: "boolean";
|
|
@@ -1625,6 +1625,12 @@ This is a paragraph.
|
|
|
1625
1625
|
fieldType: 'boolean',
|
|
1626
1626
|
tooltip: 'Preselect first item in the options if no default value is set'
|
|
1627
1627
|
},
|
|
1628
|
+
nativeHtmlSelect: {
|
|
1629
|
+
type: 'static',
|
|
1630
|
+
fieldType: 'boolean',
|
|
1631
|
+
value: false,
|
|
1632
|
+
tooltip: 'Use a native html select instead of the Windmill select component'
|
|
1633
|
+
},
|
|
1628
1634
|
fullWidth: {
|
|
1629
1635
|
type: 'static',
|
|
1630
1636
|
fieldType: 'boolean',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SupportedLanguage } from '../common';
|
|
2
|
-
export type
|
|
2
|
+
export type FlowBuilderWhitelabelCustomUi = {
|
|
3
3
|
topBar?: {
|
|
4
4
|
path?: boolean;
|
|
5
5
|
export?: boolean;
|
|
@@ -7,8 +7,18 @@ export type WhitelabelCustomUi = {
|
|
|
7
7
|
aiBuilder?: boolean;
|
|
8
8
|
tutorials?: boolean;
|
|
9
9
|
diff?: boolean;
|
|
10
|
+
extraDeployOptions?: boolean;
|
|
10
11
|
};
|
|
11
12
|
settingsPanel?: boolean;
|
|
13
|
+
settingsTabs?: {
|
|
14
|
+
schedule?: boolean;
|
|
15
|
+
sharedDiretory?: boolean;
|
|
16
|
+
earlyStop?: boolean;
|
|
17
|
+
earlyReturn?: boolean;
|
|
18
|
+
workerGroup?: boolean;
|
|
19
|
+
concurrency?: boolean;
|
|
20
|
+
cache?: boolean;
|
|
21
|
+
};
|
|
12
22
|
triggers?: boolean;
|
|
13
23
|
flowNode?: boolean;
|
|
14
24
|
hub?: boolean;
|
|
@@ -22,4 +32,28 @@ export type WhitelabelCustomUi = {
|
|
|
22
32
|
stepAdvancedSettings?: boolean;
|
|
23
33
|
languages?: (SupportedLanguage | 'docker' | 'bunnative')[];
|
|
24
34
|
scriptFork?: boolean;
|
|
35
|
+
editorBar?: EditorBarUi;
|
|
36
|
+
};
|
|
37
|
+
export type EditorBarUi = {
|
|
38
|
+
contextVar?: boolean;
|
|
39
|
+
variable?: boolean;
|
|
40
|
+
type?: boolean;
|
|
41
|
+
assistants?: boolean;
|
|
42
|
+
multiplayer?: boolean;
|
|
43
|
+
autoformatting?: boolean;
|
|
44
|
+
aiGen?: boolean;
|
|
45
|
+
aiFix?: boolean;
|
|
46
|
+
library?: boolean;
|
|
47
|
+
useVsCode?: boolean;
|
|
48
|
+
};
|
|
49
|
+
export type ScriptEditorWhitelabelCustomUi = {
|
|
50
|
+
editorBar?: EditorBarUi;
|
|
51
|
+
};
|
|
52
|
+
export type ScriptBuilderWhitelabelCustomUi = {
|
|
53
|
+
topBar?: {
|
|
54
|
+
path?: boolean;
|
|
55
|
+
settings?: boolean;
|
|
56
|
+
extraDeployOptions?: boolean;
|
|
57
|
+
};
|
|
58
|
+
editorBar?: EditorBarUi;
|
|
25
59
|
};
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
<script>import { Tabs, Tab, TabContent
|
|
2
|
-
import {
|
|
3
|
-
import { CalendarCheck2, Clipboard, MailIcon, Terminal, Webhook } from 'lucide-svelte';
|
|
4
|
-
import { Highlight } from 'svelte-highlight';
|
|
5
|
-
import { yaml } from 'svelte-highlight/languages';
|
|
6
|
-
import json from 'svelte-highlight/languages/json';
|
|
1
|
+
<script>import { Tabs, Tab, TabContent } from '../common';
|
|
2
|
+
import { CalendarCheck2, MailIcon, Terminal, Webhook } from 'lucide-svelte';
|
|
7
3
|
import { Pane, Splitpanes } from 'svelte-splitpanes';
|
|
8
|
-
import YAML from 'yaml';
|
|
9
4
|
import HighlightTheme from '../HighlightTheme.svelte';
|
|
5
|
+
import FlowViewerInner from '../FlowViewerInner.svelte';
|
|
10
6
|
export let triggerSelected = 'webhooks';
|
|
11
7
|
export let flow_json = undefined;
|
|
12
8
|
export let hasStepDetails = false;
|
|
13
9
|
export let isOperator = false;
|
|
14
10
|
export let selected;
|
|
15
|
-
let rawType = 'yaml';
|
|
16
11
|
$: if (hasStepDetails) {
|
|
17
12
|
selected = 'flow_step';
|
|
18
13
|
}
|
|
@@ -90,36 +85,7 @@ $: !hasStepDetails && selected === 'flow_step' && (selected = 'saved_inputs');
|
|
|
90
85
|
</Splitpanes>
|
|
91
86
|
</TabContent>
|
|
92
87
|
<TabContent value="raw" class="flex flex-col flex-1 h-full overflow-auto">
|
|
93
|
-
<
|
|
94
|
-
<Tab value="yaml">YAML</Tab>
|
|
95
|
-
<Tab value="json">JSON</Tab>
|
|
96
|
-
<svelte:fragment slot="content">
|
|
97
|
-
<div class="relative pt-2">
|
|
98
|
-
<Button
|
|
99
|
-
on:click={() =>
|
|
100
|
-
copyToClipboard(
|
|
101
|
-
rawType === 'yaml'
|
|
102
|
-
? YAML.stringify(flow_json)
|
|
103
|
-
: JSON.stringify(flow_json, null, 4)
|
|
104
|
-
)}
|
|
105
|
-
color="light"
|
|
106
|
-
variant="border"
|
|
107
|
-
size="xs"
|
|
108
|
-
startIcon={{ icon: Clipboard }}
|
|
109
|
-
btnClasses="absolute top-2 right-2 w-min"
|
|
110
|
-
>
|
|
111
|
-
Copy content
|
|
112
|
-
</Button>
|
|
113
|
-
<Highlight
|
|
114
|
-
class="overflow-auto px-1"
|
|
115
|
-
language={rawType === 'yaml' ? yaml : json}
|
|
116
|
-
code={rawType === 'yaml'
|
|
117
|
-
? YAML.stringify(flow_json)
|
|
118
|
-
: JSON.stringify(flow_json, null, 4)}
|
|
119
|
-
/>
|
|
120
|
-
</div>
|
|
121
|
-
</svelte:fragment>
|
|
122
|
-
</Tabs>
|
|
88
|
+
<FlowViewerInner flow={flow_json} />
|
|
123
89
|
</TabContent>
|
|
124
90
|
<TabContent value="flow_step" class="flex flex-col flex-1 h-full">
|
|
125
91
|
<slot name="flow_step" />
|
|
@@ -39,9 +39,9 @@ function computeScriptWebhooks(hash, path) {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
function computeFlowWebhooks(path) {
|
|
42
|
-
let
|
|
43
|
-
let urlAsync = `${
|
|
44
|
-
let urlSync = `${
|
|
42
|
+
let webhooksBase = `${$page.url.origin}${base}/api/w/${$workspaceStore}/jobs`;
|
|
43
|
+
let urlAsync = `${webhooksBase}/run/f/${path}`;
|
|
44
|
+
let urlSync = `${webhooksBase}/run_wait_result/f/${path}`;
|
|
45
45
|
return {
|
|
46
46
|
async: {
|
|
47
47
|
path: urlAsync
|
|
@@ -41,7 +41,7 @@ import { loadSchemaFromModule } from '../flowInfers';
|
|
|
41
41
|
import { computeFlowStepWarning, initFlowStepWarnings } from '../utils';
|
|
42
42
|
import { debounce } from '../../../utils';
|
|
43
43
|
import { dfs } from '../dfs';
|
|
44
|
-
const { selectedId, previewArgs, flowStateStore, flowStore, pathStore, saveDraft, flowInputsStore } = getContext('FlowEditorContext');
|
|
44
|
+
const { selectedId, previewArgs, flowStateStore, flowStore, pathStore, saveDraft, flowInputsStore, customUi } = getContext('FlowEditorContext');
|
|
45
45
|
export let flowModule;
|
|
46
46
|
export let failureModule = false;
|
|
47
47
|
export let parentModule = undefined;
|
|
@@ -216,6 +216,7 @@ function setFlowInput(argName) {
|
|
|
216
216
|
{#if flowModule.value.type === 'rawscript' && !noEditor}
|
|
217
217
|
<div class="border-b-2 shadow-sm px-1">
|
|
218
218
|
<EditorBar
|
|
219
|
+
customUi={customUi?.editorBar}
|
|
219
220
|
{validCode}
|
|
220
221
|
{editor}
|
|
221
222
|
{diffEditor}
|
|
@@ -353,7 +354,12 @@ function setFlowInput(argName) {
|
|
|
353
354
|
{#if !$selectedId.includes('failure')}
|
|
354
355
|
<Tab value="runtime">Runtime</Tab>
|
|
355
356
|
<Tab value="cache" active={Boolean(flowModule.cache_ttl)}>Cache</Tab>
|
|
356
|
-
<Tab
|
|
357
|
+
<Tab
|
|
358
|
+
value="early-stop"
|
|
359
|
+
active={Boolean(
|
|
360
|
+
flowModule.stop_after_if || flowModule.stop_after_all_iters_if
|
|
361
|
+
)}
|
|
362
|
+
>
|
|
357
363
|
Early Stop
|
|
358
364
|
</Tab>
|
|
359
365
|
<Tab value="suspend" active={Boolean(flowModule.suspend)}>Suspend</Tab>
|
|
@@ -6,84 +6,211 @@ import { getContext } from 'svelte';
|
|
|
6
6
|
import { NEVER_TESTED_THIS_FAR } from '../models';
|
|
7
7
|
import Section from '../../Section.svelte';
|
|
8
8
|
import { getStepPropPicker } from '../previousResults';
|
|
9
|
+
import { dfs } from '../previousResults';
|
|
9
10
|
const { flowStateStore, flowStore, previewArgs } = getContext('FlowEditorContext');
|
|
10
11
|
export let flowModule;
|
|
11
12
|
let editor = undefined;
|
|
12
13
|
$: stepPropPicker = getStepPropPicker($flowStateStore, undefined, undefined, flowModule.id, $flowStore, $previewArgs, false);
|
|
14
|
+
function checkIfParentLoop(flowStore) {
|
|
15
|
+
const flow = JSON.parse(JSON.stringify(flowStore));
|
|
16
|
+
const parents = dfs(flowModule.id, flow, true);
|
|
17
|
+
for (const parent of parents.slice(1)) {
|
|
18
|
+
if (parent.value.type === 'forloopflow' || parent.value.type === 'whileloopflow') {
|
|
19
|
+
return parent.id;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
$: isLoop = flowModule.value.type === 'forloopflow' || flowModule.value.type === 'whileloopflow';
|
|
25
|
+
$: isBranchAll = flowModule.value.type === 'branchall';
|
|
13
26
|
$: isStopAfterIfEnabled = Boolean(flowModule.stop_after_if);
|
|
27
|
+
$: isStopAfterAllIterationsEnabled = Boolean(flowModule.stop_after_all_iters_if);
|
|
14
28
|
$: result = $flowStateStore[flowModule.id]?.previewResult ?? NEVER_TESTED_THIS_FAR;
|
|
29
|
+
$: parentLoopId = checkIfParentLoop($flowStore);
|
|
15
30
|
</script>
|
|
16
31
|
|
|
17
32
|
<div class="flex flex-col items-start space-y-2 {$$props.class}">
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
33
|
+
{#if !isBranchAll}
|
|
34
|
+
<Section
|
|
35
|
+
label={(isLoop
|
|
36
|
+
? 'Break loop'
|
|
37
|
+
: parentLoopId
|
|
38
|
+
? 'Break parent loop module ' + parentLoopId
|
|
39
|
+
: 'Stop flow early') + (isLoop ? ' (evaluated after each iteration)' : '')}
|
|
40
|
+
class="w-full"
|
|
41
|
+
>
|
|
42
|
+
<svelte:fragment slot="header">
|
|
43
|
+
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/early_stop">
|
|
44
|
+
If defined, at the end of the step, the predicate expression will be evaluated to decide
|
|
45
|
+
if the flow should stop early or break if inside a for/while loop.
|
|
46
|
+
</Tooltip>
|
|
47
|
+
</svelte:fragment>
|
|
25
48
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
<Toggle
|
|
50
|
+
checked={isStopAfterIfEnabled}
|
|
51
|
+
on:change={() => {
|
|
52
|
+
if (isStopAfterIfEnabled && flowModule.stop_after_if) {
|
|
53
|
+
flowModule.stop_after_if = undefined
|
|
54
|
+
} else {
|
|
55
|
+
flowModule.stop_after_if = {
|
|
56
|
+
expr: 'result == undefined',
|
|
57
|
+
skip_if_stopped: false
|
|
58
|
+
}
|
|
35
59
|
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
60
|
+
}}
|
|
61
|
+
options={{
|
|
62
|
+
right: isLoop
|
|
63
|
+
? 'Break loop'
|
|
64
|
+
: parentLoopId
|
|
65
|
+
? 'Break parent loop module'
|
|
66
|
+
: 'Stop flow' + ' if condition met'
|
|
67
|
+
}}
|
|
68
|
+
/>
|
|
69
|
+
|
|
70
|
+
<div
|
|
71
|
+
class="w-full border p-2 flex flex-col {flowModule.stop_after_if
|
|
72
|
+
? ''
|
|
73
|
+
: 'bg-surface-secondary'}"
|
|
74
|
+
>
|
|
75
|
+
{#if flowModule.stop_after_if}
|
|
76
|
+
{@const earlyStopResult = isLoop
|
|
77
|
+
? Array.isArray(result) && result.length > 0
|
|
78
|
+
? result[result.length - 1]
|
|
79
|
+
: result === NEVER_TESTED_THIS_FAR
|
|
80
|
+
? result
|
|
81
|
+
: undefined
|
|
82
|
+
: result}
|
|
83
|
+
{#if !parentLoopId && !isLoop}
|
|
84
|
+
<Toggle
|
|
85
|
+
size="xs"
|
|
86
|
+
bind:checked={flowModule.stop_after_if.skip_if_stopped}
|
|
87
|
+
options={{
|
|
88
|
+
right: 'Label flow as "skipped" if stopped'
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
{/if}
|
|
92
|
+
<span class="mt-2 text-xs font-bold">Stop condition expression</span>
|
|
93
|
+
<div class="border w-full">
|
|
94
|
+
<PropPickerWrapper
|
|
95
|
+
notSelectable
|
|
96
|
+
flow_input={stepPropPicker.pickableProperties.flow_input}
|
|
97
|
+
pickableProperties={undefined}
|
|
98
|
+
result={earlyStopResult}
|
|
99
|
+
extraResults={isLoop ? { all_iters: result } : undefined}
|
|
100
|
+
on:select={({ detail }) => {
|
|
101
|
+
editor?.insertAtCursor(detail)
|
|
102
|
+
editor?.focus()
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<SimpleEditor
|
|
106
|
+
bind:this={editor}
|
|
107
|
+
lang="javascript"
|
|
108
|
+
bind:code={flowModule.stop_after_if.expr}
|
|
109
|
+
class="few-lines-editor"
|
|
110
|
+
extraLib={`declare const result = ${JSON.stringify(earlyStopResult)};` +
|
|
111
|
+
(isLoop ? `\ndeclare const all_iters = ${JSON.stringify(result)};` : '')}
|
|
112
|
+
/>
|
|
113
|
+
</PropPickerWrapper>
|
|
114
|
+
</div>
|
|
115
|
+
{:else}
|
|
116
|
+
{#if !parentLoopId && !isLoop}
|
|
117
|
+
<Toggle
|
|
118
|
+
disabled
|
|
119
|
+
size="xs"
|
|
120
|
+
options={{
|
|
121
|
+
right: 'Label flow as "skipped" if stopped'
|
|
122
|
+
}}
|
|
123
|
+
/>
|
|
124
|
+
{/if}
|
|
125
|
+
<span class="mt-2 text-xs font-bold">Stop condition expression</span>
|
|
126
|
+
<textarea disabled rows="3" class="min-h-[80px]" />
|
|
127
|
+
{/if}
|
|
128
|
+
</div>
|
|
129
|
+
</Section>
|
|
130
|
+
{/if}
|
|
42
131
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
132
|
+
{#if isLoop || isBranchAll}
|
|
133
|
+
<Section
|
|
134
|
+
label={(parentLoopId ? 'Break parent loop module ' + parentLoopId : 'Stop flow early') +
|
|
135
|
+
(isBranchAll
|
|
136
|
+
? ' (evaluated after all branches have been run)'
|
|
137
|
+
: ' (evaluated after all iterations)')}
|
|
138
|
+
class="w-full"
|
|
47
139
|
>
|
|
48
|
-
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
140
|
+
<svelte:fragment slot="header">
|
|
141
|
+
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/early_stop">
|
|
142
|
+
If defined, at the end of the step, the predicate expression will be evaluated to decide
|
|
143
|
+
if the flow should stop early or break if inside a for/while loop.
|
|
144
|
+
</Tooltip>
|
|
145
|
+
</svelte:fragment>
|
|
146
|
+
|
|
147
|
+
<Toggle
|
|
148
|
+
checked={isStopAfterAllIterationsEnabled}
|
|
149
|
+
on:change={() => {
|
|
150
|
+
if (isStopAfterAllIterationsEnabled && flowModule.stop_after_all_iters_if) {
|
|
151
|
+
flowModule.stop_after_all_iters_if = undefined
|
|
152
|
+
} else {
|
|
153
|
+
flowModule.stop_after_all_iters_if = {
|
|
154
|
+
expr: 'result == undefined',
|
|
155
|
+
skip_if_stopped: false
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}}
|
|
159
|
+
options={{
|
|
160
|
+
right: (parentLoopId ? 'Break parent loop module' : 'Stop flow') + ' if condition met'
|
|
161
|
+
}}
|
|
162
|
+
/>
|
|
163
|
+
|
|
164
|
+
<div
|
|
165
|
+
class="w-full border p-2 flex flex-col {flowModule.stop_after_all_iters_if
|
|
166
|
+
? ''
|
|
167
|
+
: 'bg-surface-secondary'}"
|
|
168
|
+
>
|
|
169
|
+
{#if flowModule.stop_after_all_iters_if}
|
|
170
|
+
{#if !parentLoopId}
|
|
171
|
+
<Toggle
|
|
172
|
+
size="xs"
|
|
173
|
+
bind:checked={flowModule.stop_after_all_iters_if.skip_if_stopped}
|
|
174
|
+
options={{
|
|
175
|
+
right: 'Label flow as "skipped" if stopped'
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
{/if}
|
|
179
|
+
<span class="mt-2 text-xs font-bold">Stop condition expression</span>
|
|
180
|
+
<div class="border w-full">
|
|
181
|
+
<PropPickerWrapper
|
|
182
|
+
notSelectable
|
|
183
|
+
flow_input={stepPropPicker.pickableProperties.flow_input}
|
|
184
|
+
pickableProperties={undefined}
|
|
185
|
+
{result}
|
|
186
|
+
on:select={({ detail }) => {
|
|
187
|
+
editor?.insertAtCursor(detail)
|
|
188
|
+
editor?.focus()
|
|
189
|
+
}}
|
|
190
|
+
>
|
|
191
|
+
<SimpleEditor
|
|
192
|
+
bind:this={editor}
|
|
193
|
+
lang="javascript"
|
|
194
|
+
bind:code={flowModule.stop_after_all_iters_if.expr}
|
|
195
|
+
class="few-lines-editor"
|
|
196
|
+
extraLib={`declare const result = ${JSON.stringify(result)};`}
|
|
197
|
+
/>
|
|
198
|
+
</PropPickerWrapper>
|
|
199
|
+
</div>
|
|
200
|
+
{:else}
|
|
201
|
+
{#if !parentLoopId}
|
|
202
|
+
<Toggle
|
|
203
|
+
disabled
|
|
204
|
+
size="xs"
|
|
205
|
+
options={{
|
|
206
|
+
right: 'Label flow as "skipped" if stopped'
|
|
207
|
+
}}
|
|
74
208
|
/>
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
right: 'Label flow as "skipped" if stopped'
|
|
83
|
-
}}
|
|
84
|
-
/> <span class="mt-2 text-xs font-bold">Stop condition expression</span>
|
|
85
|
-
<textarea disabled rows="3" class="min-h-[80px]" />
|
|
86
|
-
{/if}
|
|
87
|
-
</div>
|
|
88
|
-
</Section>
|
|
209
|
+
{/if}
|
|
210
|
+
<span class="mt-2 text-xs font-bold">Stop condition expression</span>
|
|
211
|
+
<textarea disabled rows="3" class="min-h-[80px]" />
|
|
212
|
+
{/if}
|
|
213
|
+
</div>
|
|
214
|
+
</Section>
|
|
215
|
+
{/if}
|
|
89
216
|
</div>
|