windmill-components 1.382.1 → 1.382.3
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 +2 -1
- package/package/components/FlowViewer.svelte.d.ts +1 -0
- package/package/components/ScriptBuilder.svelte +42 -36
- package/package/components/ScriptBuilder.svelte.d.ts +2 -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/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.json +1 -1
package/package/base.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare let base: string;
|
package/package/base.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export let base = import.meta.env.VITE_BASE_URL ?? '';
|
|
@@ -43,6 +43,7 @@ export let diffEditor = undefined;
|
|
|
43
43
|
export let args;
|
|
44
44
|
export let noHistory = false;
|
|
45
45
|
export let saveToWorkspace = false;
|
|
46
|
+
export let customUi = {};
|
|
46
47
|
let contextualVariablePicker;
|
|
47
48
|
let variablePicker;
|
|
48
49
|
let resourcePicker;
|
|
@@ -284,6 +285,7 @@ let historyBrowserDrawerOpen = false;
|
|
|
284
285
|
{/if}
|
|
285
286
|
</DrawerContent>
|
|
286
287
|
</Drawer>
|
|
288
|
+
|
|
287
289
|
<ItemPicker
|
|
288
290
|
bind:this={contextualVariablePicker}
|
|
289
291
|
pickCallback={(path, name) => {
|
|
@@ -484,7 +486,7 @@ $res = json_decode(curl_exec($ch));`)
|
|
|
484
486
|
class="rounded-full w-2 h-2 mx-2 {validCode ? 'bg-green-300' : 'bg-red-300'}"
|
|
485
487
|
/>
|
|
486
488
|
<div class="flex items-center gap-0.5">
|
|
487
|
-
{#if showContextVarPicker}
|
|
489
|
+
{#if showContextVarPicker && customUi?.contextVar != false}
|
|
488
490
|
<Button
|
|
489
491
|
title="Add context variable"
|
|
490
492
|
color="light"
|
|
@@ -497,7 +499,7 @@ $res = json_decode(curl_exec($ch));`)
|
|
|
497
499
|
>+Context Var
|
|
498
500
|
</Button>
|
|
499
501
|
{/if}
|
|
500
|
-
{#if showVarPicker}
|
|
502
|
+
{#if showVarPicker && customUi?.variable != false}
|
|
501
503
|
<Button
|
|
502
504
|
title="Add variable"
|
|
503
505
|
color="light"
|
|
@@ -527,7 +529,7 @@ $res = json_decode(curl_exec($ch));`)
|
|
|
527
529
|
</Button>
|
|
528
530
|
{/if}
|
|
529
531
|
|
|
530
|
-
{#if showResourceTypePicker}
|
|
532
|
+
{#if showResourceTypePicker && customUi?.type != false}
|
|
531
533
|
<Button
|
|
532
534
|
title="Add resource type"
|
|
533
535
|
btnClasses="!font-medium text-tertiary"
|
|
@@ -555,37 +557,41 @@ $res = json_decode(curl_exec($ch));`)
|
|
|
555
557
|
Reset
|
|
556
558
|
</Button>
|
|
557
559
|
|
|
558
|
-
{#if
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
<span class="-my-1">
|
|
575
|
-
{#if lang == 'deno'}
|
|
576
|
-
(<span class={websocketAlive.deno ? 'green' : 'text-red-700'}>Deno</span>)
|
|
577
|
-
{:else if lang == 'go'}
|
|
578
|
-
(<span class={websocketAlive.go ? 'green' : 'text-red-700'}>Go</span>)
|
|
579
|
-
{:else if lang == 'python3'}
|
|
580
|
-
(<span class={websocketAlive.pyright ? 'green' : 'text-red-700'}>Pyright</span>
|
|
581
|
-
<span class={websocketAlive.ruff ? 'green' : 'text-red-700'}>Ruff</span>)
|
|
582
|
-
{:else if lang == 'bash'}
|
|
583
|
-
(<span class={websocketAlive.shellcheck ? 'green' : 'text-red-700'}>Shellcheck</span>)
|
|
560
|
+
{#if customUi?.assistants != false}
|
|
561
|
+
{#if lang == 'deno' || lang == 'python3' || lang == 'go' || lang == 'bash'}
|
|
562
|
+
<Button
|
|
563
|
+
btnClasses="!font-medium text-tertiary"
|
|
564
|
+
size="xs"
|
|
565
|
+
spacingSize="md"
|
|
566
|
+
color="light"
|
|
567
|
+
on:click={() => editor?.reloadWebsocket()}
|
|
568
|
+
startIcon={{
|
|
569
|
+
icon: RotateCw,
|
|
570
|
+
classes: websocketAlive[lang] == false ? 'animate-spin' : ''
|
|
571
|
+
}}
|
|
572
|
+
title="Reload assistants"
|
|
573
|
+
>
|
|
574
|
+
{#if !iconOnly}
|
|
575
|
+
Assistants
|
|
584
576
|
{/if}
|
|
585
|
-
|
|
586
|
-
|
|
577
|
+
<span class="-my-1">
|
|
578
|
+
{#if lang == 'deno'}
|
|
579
|
+
(<span class={websocketAlive.deno ? 'green' : 'text-red-700'}>Deno</span>)
|
|
580
|
+
{:else if lang == 'go'}
|
|
581
|
+
(<span class={websocketAlive.go ? 'green' : 'text-red-700'}>Go</span>)
|
|
582
|
+
{:else if lang == 'python3'}
|
|
583
|
+
(<span class={websocketAlive.pyright ? 'green' : 'text-red-700'}>Pyright</span>
|
|
584
|
+
<span class={websocketAlive.ruff ? 'green' : 'text-red-700'}>Ruff</span>)
|
|
585
|
+
{:else if lang == 'bash'}
|
|
586
|
+
(<span class={websocketAlive.shellcheck ? 'green' : 'text-red-700'}>Shellcheck</span
|
|
587
|
+
>)
|
|
588
|
+
{/if}
|
|
589
|
+
</span>
|
|
590
|
+
</Button>
|
|
591
|
+
{/if}
|
|
587
592
|
{/if}
|
|
588
|
-
|
|
593
|
+
|
|
594
|
+
{#if collabMode && customUi?.multiplayer != false}
|
|
589
595
|
<div class="flex items-center px-3">
|
|
590
596
|
<Toggle
|
|
591
597
|
options={{ right: '' }}
|
|
@@ -619,11 +625,15 @@ $res = json_decode(curl_exec($ch));`)
|
|
|
619
625
|
</div>
|
|
620
626
|
{/if}
|
|
621
627
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
628
|
+
{#if customUi?.autoformatting != false}
|
|
629
|
+
<FormatOnSave />
|
|
630
|
+
{/if}
|
|
631
|
+
{#if customUi?.aiGen != false}
|
|
632
|
+
<ScriptGen {editor} {diffEditor} {lang} {iconOnly} {args} />
|
|
633
|
+
{/if}
|
|
634
|
+
{#if customUi?.aiFix != false}
|
|
635
|
+
<CodeCompletionStatus />
|
|
636
|
+
{/if}
|
|
627
637
|
</div>
|
|
628
638
|
</div>
|
|
629
639
|
|
|
@@ -642,7 +652,7 @@ $res = json_decode(curl_exec($ch));`)
|
|
|
642
652
|
History
|
|
643
653
|
</Button>
|
|
644
654
|
{/if}
|
|
645
|
-
{#if SCRIPT_EDITOR_SHOW_EXPLORE_OTHER_SCRIPTS}
|
|
655
|
+
{#if SCRIPT_EDITOR_SHOW_EXPLORE_OTHER_SCRIPTS && customUi?.library != false}
|
|
646
656
|
<Button
|
|
647
657
|
btnClasses="!font-medium text-tertiary"
|
|
648
658
|
size="xs"
|
|
@@ -3,6 +3,7 @@ export declare const EDITOR_BAR_WIDTH_THRESHOLD = 1044;
|
|
|
3
3
|
import type Editor from './Editor.svelte';
|
|
4
4
|
import type { SupportedLanguage } from '../common';
|
|
5
5
|
import type DiffEditor from './DiffEditor.svelte';
|
|
6
|
+
import type { EditorBarUi } from './custom_ui';
|
|
6
7
|
declare const __propDef: {
|
|
7
8
|
props: {
|
|
8
9
|
lang: SupportedLanguage | 'bunnative' | undefined;
|
|
@@ -28,6 +29,7 @@ declare const __propDef: {
|
|
|
28
29
|
args: Record<string, any>;
|
|
29
30
|
noHistory?: boolean | undefined;
|
|
30
31
|
saveToWorkspace?: boolean | undefined;
|
|
32
|
+
customUi?: EditorBarUi | undefined;
|
|
31
33
|
};
|
|
32
34
|
events: {
|
|
33
35
|
toggleCollabMode: CustomEvent<any>;
|
|
@@ -330,7 +330,8 @@ setContext('FlowEditorContext', {
|
|
|
330
330
|
testStepStore,
|
|
331
331
|
saveDraft,
|
|
332
332
|
initialPath,
|
|
333
|
-
flowInputsStore: writable({})
|
|
333
|
+
flowInputsStore: writable({}),
|
|
334
|
+
customUi
|
|
334
335
|
});
|
|
335
336
|
async function loadSchedule() {
|
|
336
337
|
loadFlowSchedule(initialPath, $workspaceStore)
|
|
@@ -403,17 +404,19 @@ function generateIds() {
|
|
|
403
404
|
];
|
|
404
405
|
}
|
|
405
406
|
const dropdownItems = [];
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
407
|
+
if (customUi.topBar?.extraDeployOptions != false) {
|
|
408
|
+
if (savedFlow?.draft_only === false || savedFlow?.draft_only === undefined) {
|
|
409
|
+
dropdownItems.push({
|
|
410
|
+
label: 'Exit & see details',
|
|
411
|
+
onClick: () => dispatch('details', $pathStore)
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
if (!newFlow) {
|
|
415
|
+
dropdownItems.push({
|
|
416
|
+
label: 'Fork',
|
|
417
|
+
onClick: () => window.open(`/flows/add?template=${initialPath}`)
|
|
418
|
+
});
|
|
419
|
+
}
|
|
417
420
|
}
|
|
418
421
|
let flowCopilotContext = {
|
|
419
422
|
drawerStore: writable(undefined),
|
|
@@ -3,7 +3,7 @@ import { type Flow, type OpenFlow } from '../gen';
|
|
|
3
3
|
import { type Writable } from 'svelte/store';
|
|
4
4
|
import type { FlowState } from './flows/flowState';
|
|
5
5
|
import type DiffDrawer from './DiffDrawer.svelte';
|
|
6
|
-
import type {
|
|
6
|
+
import type { FlowBuilderWhitelabelCustomUi } from './custom_ui';
|
|
7
7
|
declare const __propDef: {
|
|
8
8
|
props: {
|
|
9
9
|
initialPath?: string | undefined;
|
|
@@ -18,7 +18,7 @@ declare const __propDef: {
|
|
|
18
18
|
draft?: Flow | undefined;
|
|
19
19
|
}) | undefined;
|
|
20
20
|
diffDrawer?: DiffDrawer | undefined;
|
|
21
|
-
customUi?:
|
|
21
|
+
customUi?: FlowBuilderWhitelabelCustomUi | undefined;
|
|
22
22
|
computeUnlockedSteps?: ((flow: Flow) => {
|
|
23
23
|
[k: string]: string;
|
|
24
24
|
}) | undefined;
|
|
@@ -8,6 +8,9 @@ export let workspaceId = undefined;
|
|
|
8
8
|
export let flowStateStore = writable({});
|
|
9
9
|
export let selectedJobStep = undefined;
|
|
10
10
|
export let hideFlowResult = false;
|
|
11
|
+
export let hideTimeline = false;
|
|
12
|
+
export let hideDownloadInGraph = false;
|
|
13
|
+
export let hideNodeDefinition = false;
|
|
11
14
|
export let isOwner = false;
|
|
12
15
|
export let wideResults = false;
|
|
13
16
|
let lastJobId = jobId;
|
|
@@ -16,7 +19,10 @@ let suspendStatus = writable({});
|
|
|
16
19
|
setContext('FlowStatusViewer', {
|
|
17
20
|
flowStateStore,
|
|
18
21
|
suspendStatus,
|
|
19
|
-
retryStatus
|
|
22
|
+
retryStatus,
|
|
23
|
+
hideDownloadInGraph,
|
|
24
|
+
hideNodeDefinition,
|
|
25
|
+
hideTimeline
|
|
20
26
|
});
|
|
21
27
|
function loadOwner(path) {
|
|
22
28
|
isOwner = loadIsOwner(path, $userStore, workspaceId ?? $workspaceStore);
|
|
@@ -8,6 +8,9 @@ declare const __propDef: {
|
|
|
8
8
|
flowStateStore?: Writable<FlowState> | undefined;
|
|
9
9
|
selectedJobStep?: string | undefined;
|
|
10
10
|
hideFlowResult?: boolean | undefined;
|
|
11
|
+
hideTimeline?: boolean | undefined;
|
|
12
|
+
hideDownloadInGraph?: boolean | undefined;
|
|
13
|
+
hideNodeDefinition?: boolean | undefined;
|
|
11
14
|
isOwner?: boolean | undefined;
|
|
12
15
|
wideResults?: boolean | undefined;
|
|
13
16
|
};
|
|
@@ -22,7 +22,7 @@ import Alert from './common/alert/Alert.svelte';
|
|
|
22
22
|
import FlowGraphViewerStep from './FlowGraphViewerStep.svelte';
|
|
23
23
|
import FlowGraphV2 from './graph/FlowGraphV2.svelte';
|
|
24
24
|
const dispatch = createEventDispatcher();
|
|
25
|
-
let { flowStateStore, retryStatus, suspendStatus } = getContext('FlowStatusViewer');
|
|
25
|
+
let { flowStateStore, retryStatus, suspendStatus, hideDownloadInGraph, hideTimeline, hideNodeDefinition } = getContext('FlowStatusViewer');
|
|
26
26
|
export let jobId;
|
|
27
27
|
export let workspaceId = undefined;
|
|
28
28
|
export let flowJobIds = undefined;
|
|
@@ -654,7 +654,7 @@ let wrapperHeight = 0;
|
|
|
654
654
|
{/if}
|
|
655
655
|
{/each}
|
|
656
656
|
</div>
|
|
657
|
-
{:else if innerModules.length > 0}
|
|
657
|
+
{:else if innerModules.length > 0 && (job.raw_flow?.modules.length ?? 0) > 0}
|
|
658
658
|
<ul class="w-full">
|
|
659
659
|
<h3 class="text-md leading-6 font-bold text-primary border-b mb-4 py-2">
|
|
660
660
|
Step-by-step
|
|
@@ -774,6 +774,8 @@ let wrapperHeight = 0;
|
|
|
774
774
|
</li>
|
|
775
775
|
{/each}
|
|
776
776
|
</ul>
|
|
777
|
+
{:else}
|
|
778
|
+
<div class="p-2 text-tertiary text-sm italic">Empty flow</div>
|
|
777
779
|
{/if}
|
|
778
780
|
</div>
|
|
779
781
|
</div>
|
|
@@ -800,7 +802,7 @@ let wrapperHeight = 0;
|
|
|
800
802
|
</div>
|
|
801
803
|
|
|
802
804
|
<FlowGraphV2
|
|
803
|
-
download
|
|
805
|
+
download={!hideDownloadInGraph}
|
|
804
806
|
minHeight={wrapperHeight}
|
|
805
807
|
success={jobId != undefined && isSuccess(job?.['success'])}
|
|
806
808
|
flowModuleStates={$localModuleStates}
|
|
@@ -840,9 +842,14 @@ let wrapperHeight = 0;
|
|
|
840
842
|
class="border-l border-tertiary-inverse pt-1 overflow-auto min-h-[700px] flex flex-col z-0 h-full"
|
|
841
843
|
>
|
|
842
844
|
<Tabs bind:selected={rightColumnSelect}>
|
|
843
|
-
|
|
845
|
+
{#if !hideTimeline}
|
|
846
|
+
<Tab value="timeline"><span class="font-semibold text-md">Timeline</span></Tab>
|
|
847
|
+
{/if}
|
|
844
848
|
<Tab value="node_status"><span class="font-semibold">Node status</span></Tab>
|
|
845
|
-
|
|
849
|
+
{#if !hideNodeDefinition}
|
|
850
|
+
<Tab value="node_definition"><span class="font-semibold">Node definition</span></Tab
|
|
851
|
+
>
|
|
852
|
+
{/if}
|
|
846
853
|
{#if Object.keys(job?.flow_status?.user_states ?? {}).length > 0}
|
|
847
854
|
<Tab value="user_states"><span class="font-semibold">User States</span></Tab>
|
|
848
855
|
{/if}
|
|
@@ -21,6 +21,7 @@ $: flowFiltered = {
|
|
|
21
21
|
export let noGraph = false;
|
|
22
22
|
export let tab = noGraph ? 'schema' : 'ui';
|
|
23
23
|
export let noSummary = false;
|
|
24
|
+
export let noGraphDownload = false;
|
|
24
25
|
let rawType = 'yaml';
|
|
25
26
|
let open = {};
|
|
26
27
|
if (initialOpen) {
|
|
@@ -73,7 +74,7 @@ function toAny(x) {
|
|
|
73
74
|
<div class="text-secondary text-xs italic mb-4">No inputs</div>
|
|
74
75
|
{/if}
|
|
75
76
|
|
|
76
|
-
<FlowGraphViewer download {noSide} {flow} overflowAuto />
|
|
77
|
+
<FlowGraphViewer download={!noGraphDownload} {noSide} {flow} overflowAuto />
|
|
77
78
|
</div>
|
|
78
79
|
</TabContent>
|
|
79
80
|
<TabContent value="raw"
|
|
@@ -43,6 +43,7 @@ export let savedScript = undefined;
|
|
|
43
43
|
export let searchParams = new URLSearchParams();
|
|
44
44
|
export let disableHistoryChange = false;
|
|
45
45
|
export let replaceStateFn = (url) => window.history.replaceState(null, '', url);
|
|
46
|
+
export let customUi = {};
|
|
46
47
|
let metadataOpen = showMeta ||
|
|
47
48
|
(initialPath == '' &&
|
|
48
49
|
searchParams.get('state') == undefined &&
|
|
@@ -344,7 +345,7 @@ async function saveDraft(forceSave = false) {
|
|
|
344
345
|
loadingDraft = false;
|
|
345
346
|
}
|
|
346
347
|
function computeDropdownItems(initialPath) {
|
|
347
|
-
let dropdownItems = initialPath != ''
|
|
348
|
+
let dropdownItems = initialPath != '' && customUi?.topBar?.extraDeployOptions != false
|
|
348
349
|
? [
|
|
349
350
|
{
|
|
350
351
|
label: 'Deploy & Stay here',
|
|
@@ -965,32 +966,34 @@ function langToLanguage(lang) {
|
|
|
965
966
|
{$scheduleStore.cron ?? ''}
|
|
966
967
|
</Button>
|
|
967
968
|
{/if}
|
|
968
|
-
|
|
969
|
-
<div>
|
|
970
|
-
<
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
<Badge
|
|
976
|
-
color="gray"
|
|
977
|
-
class="center-center !bg-surface-secondary !text-tertiary !h-[28px] !w-[70px] rounded-none hover:!bg-surface-hover transition-all"
|
|
969
|
+
{#if customUi?.topBar?.path != false}
|
|
970
|
+
<div class="flex justify-start w-full border rounded-md overflow-hidden">
|
|
971
|
+
<div>
|
|
972
|
+
<button
|
|
973
|
+
on:click={async () => {
|
|
974
|
+
metadataOpen = true
|
|
975
|
+
}}
|
|
978
976
|
>
|
|
979
|
-
<
|
|
980
|
-
|
|
981
|
-
|
|
977
|
+
<Badge
|
|
978
|
+
color="gray"
|
|
979
|
+
class="center-center !bg-surface-secondary !text-tertiary !h-[28px] !w-[70px] rounded-none hover:!bg-surface-hover transition-all"
|
|
980
|
+
>
|
|
981
|
+
<Pen size={12} class="mr-2" /> Path
|
|
982
|
+
</Badge>
|
|
983
|
+
</button>
|
|
984
|
+
</div>
|
|
985
|
+
<input
|
|
986
|
+
type="text"
|
|
987
|
+
readonly
|
|
988
|
+
value={script.path}
|
|
989
|
+
size={script.path?.length || 50}
|
|
990
|
+
class="font-mono !text-xs !min-w-[96px] !max-w-[300px] !w-full !h-[28px] !my-0 !py-0 !border-l-0 !rounded-l-none !border-0 !shadow-none"
|
|
991
|
+
on:focus={({ currentTarget }) => {
|
|
992
|
+
currentTarget.select()
|
|
993
|
+
}}
|
|
994
|
+
/>
|
|
982
995
|
</div>
|
|
983
|
-
|
|
984
|
-
type="text"
|
|
985
|
-
readonly
|
|
986
|
-
value={script.path}
|
|
987
|
-
size={script.path?.length || 50}
|
|
988
|
-
class="font-mono !text-xs !min-w-[96px] !max-w-[300px] !w-full !h-[28px] !my-0 !py-0 !border-l-0 !rounded-l-none !border-0 !shadow-none"
|
|
989
|
-
on:focus={({ currentTarget }) => {
|
|
990
|
-
currentTarget.select()
|
|
991
|
-
}}
|
|
992
|
-
/>
|
|
993
|
-
</div>
|
|
996
|
+
{/if}
|
|
994
997
|
</div>
|
|
995
998
|
|
|
996
999
|
{#if $enterpriseLicense && initialPath != ''}
|
|
@@ -1021,17 +1024,19 @@ function langToLanguage(lang) {
|
|
|
1021
1024
|
<span class="hidden lg:flex"> Diff </span>
|
|
1022
1025
|
</div>
|
|
1023
1026
|
</Button>
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1027
|
+
{#if customUi?.topBar?.settings != false}
|
|
1028
|
+
<Button
|
|
1029
|
+
color="light"
|
|
1030
|
+
variant="border"
|
|
1031
|
+
size="xs"
|
|
1032
|
+
on:click={() => {
|
|
1033
|
+
metadataOpen = true
|
|
1034
|
+
}}
|
|
1035
|
+
startIcon={{ icon: Settings }}
|
|
1036
|
+
>
|
|
1037
|
+
<span class="hidden lg:flex"> Settings </span>
|
|
1038
|
+
</Button>
|
|
1039
|
+
{/if}
|
|
1035
1040
|
<Button
|
|
1036
1041
|
loading={loadingDraft}
|
|
1037
1042
|
size="xs"
|
|
@@ -1085,6 +1090,7 @@ function langToLanguage(lang) {
|
|
|
1085
1090
|
</div>
|
|
1086
1091
|
|
|
1087
1092
|
<ScriptEditor
|
|
1093
|
+
{customUi}
|
|
1088
1094
|
collabMode
|
|
1089
1095
|
edit={initialPath != ''}
|
|
1090
1096
|
on:format={() => {
|
|
@@ -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;
|
|
@@ -15,6 +16,7 @@ declare const __propDef: {
|
|
|
15
16
|
searchParams?: URLSearchParams | undefined;
|
|
16
17
|
disableHistoryChange?: boolean | undefined;
|
|
17
18
|
replaceStateFn?: ((url: string) => void) | undefined;
|
|
19
|
+
customUi?: ScriptBuilderWhitelabelCustomUi | undefined;
|
|
18
20
|
setCode?: ((code: string) => void) | undefined;
|
|
19
21
|
};
|
|
20
22
|
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
|
};
|
|
@@ -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>
|
|
@@ -51,7 +51,7 @@ $: moduleRetry = module.retry?.constant || module.retry?.exponential;
|
|
|
51
51
|
<svelte:fragment slot="text">Cache</svelte:fragment>
|
|
52
52
|
</Popover>
|
|
53
53
|
{/if}
|
|
54
|
-
{#if module.stop_after_if}
|
|
54
|
+
{#if module.stop_after_if || module.stop_after_all_iters_if}
|
|
55
55
|
<Popover
|
|
56
56
|
placement="bottom"
|
|
57
57
|
class="center-center rounded p-2 bg-blue-100 text-blue-800 border border-blue-300 hover:bg-blue-200 dark:bg-frost-700 dark:text-frost-100 dark:border-frost-600"
|
|
@@ -23,7 +23,7 @@ import ErrorHandlerToggleButton from '../../details/ErrorHandlerToggleButton.sve
|
|
|
23
23
|
import WorkerTagPicker from '../../WorkerTagPicker.svelte';
|
|
24
24
|
import MetadataGen from '../../copilot/MetadataGen.svelte';
|
|
25
25
|
export let noEditor;
|
|
26
|
-
const { selectedId, flowStore, initialPath, previewArgs, pathStore, schedule } = getContext('FlowEditorContext');
|
|
26
|
+
const { selectedId, flowStore, initialPath, previewArgs, pathStore, schedule, customUi } = getContext('FlowEditorContext');
|
|
27
27
|
let hostname = BROWSER ? window.location.protocol + '//' + window.location.host : 'SSR';
|
|
28
28
|
$: url = `${hostname}/api/w/${$workspaceStore}/jobs/run/f/${$pathStore}`;
|
|
29
29
|
$: syncedUrl = `${hostname}/api/w/${$workspaceStore}/jobs/run_wait_result/f/${$pathStore}`;
|
|
@@ -42,22 +42,33 @@ let dirtyPath = false;
|
|
|
42
42
|
<div class="h-full flex-1">
|
|
43
43
|
<Tabs bind:selected={$selectedId}>
|
|
44
44
|
<Tab value="settings-metadata">Metadata</Tab>
|
|
45
|
-
{#if !noEditor}
|
|
45
|
+
{#if !noEditor && customUi?.settingsTabs?.schedule != false}
|
|
46
46
|
<Tab value="settings-schedule" active={$schedule.enabled}>Schedule</Tab>
|
|
47
47
|
{/if}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
{#if customUi?.settingsTabs?.sharedDiretory != false}
|
|
49
|
+
<Tab value="settings-same-worker" active={$flowStore.value.same_worker}>
|
|
50
|
+
Shared Directory
|
|
51
|
+
</Tab>
|
|
52
|
+
{/if}
|
|
53
|
+
{#if customUi?.settingsTabs?.earlyStop != false}
|
|
54
|
+
<Tab value="settings-early-stop" active={Boolean($flowStore.value.skip_expr)}>
|
|
55
|
+
Early Stop
|
|
56
|
+
</Tab>
|
|
57
|
+
{/if}
|
|
58
|
+
{#if customUi?.settingsTabs?.earlyReturn != false}
|
|
59
|
+
<Tab value="settings-early-return" active={Boolean($flowStore.value.early_return)}>
|
|
60
|
+
Early Return
|
|
61
|
+
</Tab>
|
|
62
|
+
{/if}
|
|
63
|
+
{#if customUi?.settingsTabs?.workerGroup != false}
|
|
64
|
+
<Tab value="settings-worker-group">Worker Group</Tab>
|
|
65
|
+
{/if}
|
|
66
|
+
{#if customUi?.settingsTabs?.concurrency != false}
|
|
67
|
+
<Tab value="settings-concurrency">Concurrency</Tab>
|
|
68
|
+
{/if}
|
|
69
|
+
{#if customUi?.settingsTabs?.cache != false}
|
|
70
|
+
<Tab value="settings-cache" active={Boolean($flowStore.value.cache_ttl)}>Cache</Tab>
|
|
71
|
+
{/if}
|
|
61
72
|
<svelte:fragment slot="content">
|
|
62
73
|
<TabContent value="settings-metadata" class="p-4 h-full overflow-auto">
|
|
63
74
|
<Section label="Metadata">
|
|
@@ -23,7 +23,7 @@ const dispatch = createEventDispatcher();
|
|
|
23
23
|
$: itemProps = {
|
|
24
24
|
selected: $selectedId === mod.id,
|
|
25
25
|
retry: mod.retry?.constant != undefined || mod.retry?.exponential != undefined,
|
|
26
|
-
earlyStop: mod.stop_after_if != undefined,
|
|
26
|
+
earlyStop: mod.stop_after_if != undefined || mod.stop_after_all_iters_if != undefined,
|
|
27
27
|
suspend: Boolean(mod.suspend),
|
|
28
28
|
sleep: Boolean(mod.sleep),
|
|
29
29
|
cache: Boolean(mod.cache_ttl),
|
|
@@ -10,6 +10,7 @@ import { writable } from 'svelte/store';
|
|
|
10
10
|
import { twMerge } from 'tailwind-merge';
|
|
11
11
|
export let pickableProperties;
|
|
12
12
|
export let result = undefined;
|
|
13
|
+
export let extraResults = undefined;
|
|
13
14
|
export let flow_input = undefined;
|
|
14
15
|
export let error = false;
|
|
15
16
|
export let displayContext = true;
|
|
@@ -53,6 +54,7 @@ setContext('PropPickerWrapper', {
|
|
|
53
54
|
{#if result}
|
|
54
55
|
<PropPickerResult
|
|
55
56
|
{result}
|
|
57
|
+
{extraResults}
|
|
56
58
|
{flow_input}
|
|
57
59
|
on:select={({ detail }) => {
|
|
58
60
|
if (!notSelectable && !$propPickerConfig) {
|
|
@@ -5,6 +5,7 @@ import type { Writable } from 'svelte/store';
|
|
|
5
5
|
import type ScriptEditorDrawer from './content/ScriptEditorDrawer.svelte';
|
|
6
6
|
import type { FlowState } from './flowState';
|
|
7
7
|
import type { Schedule } from './scheduleUtils';
|
|
8
|
+
import type { FlowBuilderWhitelabelCustomUi } from '../custom_ui';
|
|
8
9
|
export type FlowInput = Record<string, {
|
|
9
10
|
flowStepWarnings?: Record<string, {
|
|
10
11
|
message: string;
|
|
@@ -33,4 +34,5 @@ export type FlowEditorContext = {
|
|
|
33
34
|
saveDraft: () => void;
|
|
34
35
|
initialPath: string;
|
|
35
36
|
flowInputsStore: Writable<FlowInput>;
|
|
37
|
+
customUi: FlowBuilderWhitelabelCustomUi;
|
|
36
38
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script>import { createEventDispatcher } from 'svelte';
|
|
2
2
|
import ObjectViewer from './ObjectViewer.svelte';
|
|
3
3
|
export let result;
|
|
4
|
+
export let extraResults = undefined;
|
|
4
5
|
export let flow_input = undefined;
|
|
5
6
|
const dispatch = createEventDispatcher();
|
|
6
7
|
</script>
|
|
@@ -8,7 +9,11 @@ const dispatch = createEventDispatcher();
|
|
|
8
9
|
<div class="w-full px-2">
|
|
9
10
|
<span class="font-bold text-sm">Result</span>
|
|
10
11
|
<div class="overflow-y-auto mb-2 w-full">
|
|
11
|
-
<ObjectViewer
|
|
12
|
+
<ObjectViewer
|
|
13
|
+
allowCopy={false}
|
|
14
|
+
json={{ result, ...(extraResults ? extraResults : {}) }}
|
|
15
|
+
on:select
|
|
16
|
+
/>
|
|
12
17
|
</div>
|
|
13
18
|
{#if flow_input}
|
|
14
19
|
<span class="font-bold text-sm">Flow Input</span>
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
const changelogs = [
|
|
2
|
+
{
|
|
3
|
+
label: 'Continue on disapproval/timeout',
|
|
4
|
+
href: 'https://www.windmill.dev/changelog/continue-on-disapproval',
|
|
5
|
+
date: '2024-08-14'
|
|
6
|
+
},
|
|
2
7
|
{
|
|
3
8
|
label: 'Nativets runtime supports npm packages and relative imports',
|
|
4
9
|
href: 'https://www.windmill.dev/changelog/native-runtime-imports',
|