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.
Files changed (35) hide show
  1. package/package/base.d.ts +1 -1
  2. package/package/base.js +1 -1
  3. package/package/components/EditorBar.svelte +48 -38
  4. package/package/components/EditorBar.svelte.d.ts +2 -0
  5. package/package/components/FlowBuilder.svelte +15 -12
  6. package/package/components/FlowBuilder.svelte.d.ts +2 -2
  7. package/package/components/FlowStatusViewer.svelte +7 -1
  8. package/package/components/FlowStatusViewer.svelte.d.ts +3 -0
  9. package/package/components/FlowStatusViewerInner.svelte +12 -5
  10. package/package/components/FlowViewer.svelte +2 -1
  11. package/package/components/FlowViewer.svelte.d.ts +1 -0
  12. package/package/components/ScriptBuilder.svelte +42 -36
  13. package/package/components/ScriptBuilder.svelte.d.ts +2 -0
  14. package/package/components/ScriptEditor.svelte +3 -1
  15. package/package/components/ScriptEditor.svelte.d.ts +2 -0
  16. package/package/components/apps/components/display/AppText.svelte +8 -3
  17. package/package/components/apps/components/inputs/AppSelect.svelte +45 -29
  18. package/package/components/apps/editor/component/components.d.ts +6 -0
  19. package/package/components/apps/editor/component/components.js +6 -0
  20. package/package/components/custom_ui.d.ts +35 -1
  21. package/package/components/details/WebhooksPanel.svelte +3 -3
  22. package/package/components/flows/content/FlowModuleComponent.svelte +8 -2
  23. package/package/components/flows/content/FlowModuleEarlyStop.svelte +193 -66
  24. package/package/components/flows/content/FlowModuleHeader.svelte +1 -1
  25. package/package/components/flows/content/FlowSettings.svelte +26 -15
  26. package/package/components/flows/flowExplorer.js +3 -0
  27. package/package/components/flows/map/MapItem.svelte +1 -1
  28. package/package/components/flows/propPicker/PropPickerWrapper.svelte +2 -0
  29. package/package/components/flows/propPicker/PropPickerWrapper.svelte.d.ts +1 -0
  30. package/package/components/flows/types.d.ts +2 -0
  31. package/package/components/graph/model.d.ts +3 -0
  32. package/package/components/propertyPicker/PropPickerResult.svelte +6 -1
  33. package/package/components/propertyPicker/PropPickerResult.svelte.d.ts +1 -0
  34. package/package/components/sidebar/changelogs.js +5 -0
  35. package/package.json +1 -1
package/package/base.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const base: string;
1
+ export declare let base: string;
package/package/base.js CHANGED
@@ -1 +1 @@
1
- export const base = import.meta.env.VITE_BASE_URL ?? '';
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 lang == 'deno' || lang == 'python3' || lang == 'go' || lang == 'bash'}
559
- <Button
560
- btnClasses="!font-medium text-tertiary"
561
- size="xs"
562
- spacingSize="md"
563
- color="light"
564
- on:click={() => editor?.reloadWebsocket()}
565
- startIcon={{
566
- icon: RotateCw,
567
- classes: websocketAlive[lang] == false ? 'animate-spin' : ''
568
- }}
569
- title="Reload assistants"
570
- >
571
- {#if !iconOnly}
572
- Assistants
573
- {/if}
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
- </span>
586
- </Button>
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
- {#if collabMode}
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
- <FormatOnSave />
623
-
624
- <ScriptGen {editor} {diffEditor} {lang} {iconOnly} {args} />
625
-
626
- <CodeCompletionStatus />
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 (savedFlow?.draft_only === false || savedFlow?.draft_only === undefined) {
407
- dropdownItems.push({
408
- label: 'Exit & see details',
409
- onClick: () => dispatch('details', $pathStore)
410
- });
411
- }
412
- if (!newFlow) {
413
- dropdownItems.push({
414
- label: 'Fork',
415
- onClick: () => window.open(`/flows/add?template=${initialPath}`)
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 { WhitelabelCustomUi } from './custom_ui';
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?: WhitelabelCustomUi | undefined;
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
- <Tab value="timeline"><span class="font-semibold text-md">Timeline</span></Tab>
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
- <Tab value="node_definition"><span class="font-semibold">Node definition</span></Tab>
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"
@@ -13,6 +13,7 @@ declare const __propDef: {
13
13
  noGraph?: boolean | undefined;
14
14
  tab?: "schema" | "ui" | "raw" | undefined;
15
15
  noSummary?: boolean | undefined;
16
+ noGraphDownload?: boolean | undefined;
16
17
  };
17
18
  events: {
18
19
  [evt: string]: CustomEvent<any>;
@@ -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
- <div class="flex justify-start w-full border rounded-md overflow-hidden">
969
- <div>
970
- <button
971
- on:click={async () => {
972
- metadataOpen = true
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
- <Pen size={12} class="mr-2" /> Path
980
- </Badge>
981
- </button>
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
- <input
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
- <Button
1025
- color="light"
1026
- variant="border"
1027
- size="xs"
1028
- on:click={() => {
1029
- metadataOpen = true
1030
- }}
1031
- startIcon={{ icon: Settings }}
1032
- >
1033
- <span class="hidden lg:flex"> Settings </span>
1034
- </Button>
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 !overflow-hidden !ring-0 !p-0',
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
- <Select
192
- inAppEditor={true}
193
- --border-radius="0.250rem"
194
- --clear-icon-color="#6b7280"
195
- --border={$darkMode ? '1px solid #6b7280' : '1px solid #d1d5db'}
196
- bind:filterText
197
- on:filter={handleFilter}
198
- on:clear={onClear}
199
- on:change={onChange}
200
- items={listItems}
201
- listAutoWidth={resolvedConfig.fullWidth}
202
- inputStyles={SELECT_INPUT_DEFAULT_STYLE.inputStyles}
203
- containerStyles={($darkMode
204
- ? SELECT_INPUT_DEFAULT_STYLE.containerStylesDark
205
- : SELECT_INPUT_DEFAULT_STYLE.containerStyles) + css?.input?.style}
206
- {value}
207
- class={css?.input?.class}
208
- placeholder={resolvedConfig.placeholder}
209
- disabled={resolvedConfig.disabled}
210
- on:focus={() => {
211
- if (!$connectingInput.opened) {
212
- $selectedComponent = [id]
213
- }
214
- }}
215
- >
216
- <svelte:fragment slot="item" let:item
217
- >{#if resolvedConfig.create}{item.created ? 'Add new: ' : ''}{/if}{item.label}
218
- </svelte:fragment>
219
- </Select>
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 WhitelabelCustomUi = {
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 base = `${$page.url.origin}/api/w/${$workspaceStore}/jobs`;
43
- let urlAsync = `${base}/run/f/${path}`;
44
- let urlSync = `${base}/run_wait_result/f/${path}`;
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 value="early-stop" active={Boolean(flowModule.stop_after_if)}>
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
- <Section label="Early stop/Break" class="w-full">
19
- <svelte:fragment slot="header">
20
- <Tooltip documentationLink="https://www.windmill.dev/docs/flows/early_stop">
21
- If defined, at the end of the step, the predicate expression will be evaluated to decide if
22
- the flow should stop early.
23
- </Tooltip>
24
- </svelte:fragment>
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
- <Toggle
27
- checked={isStopAfterIfEnabled}
28
- on:change={() => {
29
- if (isStopAfterIfEnabled && flowModule.stop_after_if) {
30
- flowModule.stop_after_if = undefined
31
- } else {
32
- flowModule.stop_after_if = {
33
- expr: 'result == undefined',
34
- skip_if_stopped: false
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
- options={{
39
- right: 'Early stop or Break if condition met'
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
- <div
44
- class="w-full border p-2 flex flex-col {flowModule.stop_after_if
45
- ? ''
46
- : 'bg-surface-secondary'}"
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
- {#if flowModule.stop_after_if}
49
- <Toggle
50
- size="xs"
51
- bind:checked={flowModule.stop_after_if.skip_if_stopped}
52
- options={{
53
- right: 'Label flow as "skipped" if stopped'
54
- }}
55
- />
56
- <span class="mt-2 text-xs font-bold">Stop condition expression</span>
57
- <div class="border w-full">
58
- <PropPickerWrapper
59
- notSelectable
60
- flow_input={stepPropPicker.pickableProperties.flow_input}
61
- pickableProperties={undefined}
62
- {result}
63
- on:select={({ detail }) => {
64
- editor?.insertAtCursor(detail)
65
- editor?.focus()
66
- }}
67
- >
68
- <SimpleEditor
69
- bind:this={editor}
70
- lang="javascript"
71
- bind:code={flowModule.stop_after_if.expr}
72
- class="few-lines-editor"
73
- extraLib={`declare const result = ${JSON.stringify(result)};`}
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
- </PropPickerWrapper>
76
- </div>
77
- {:else}
78
- <Toggle
79
- disabled
80
- size="xs"
81
- options={{
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
- <Tab value="settings-same-worker" active={$flowStore.value.same_worker}>
49
- Shared Directory
50
- </Tab>
51
- <Tab value="settings-early-stop" active={Boolean($flowStore.value.skip_expr)}>
52
- Early Stop
53
- </Tab>
54
- <Tab value="settings-early-return" active={Boolean($flowStore.value.early_return)}>
55
- Early Return
56
- </Tab>
57
- <Tab value="settings-worker-group">Worker Group</Tab>
58
- <Tab value="settings-concurrency">Concurrency</Tab>
59
- <Tab value="settings-cache" active={Boolean($flowStore.value.cache_ttl)}>Cache</Tab>
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">
@@ -72,6 +72,9 @@ function getModuleExprs(x) {
72
72
  if (x.stop_after_if?.expr) {
73
73
  exprs.push(x.stop_after_if.expr);
74
74
  }
75
+ if (x.stop_after_all_iters_if?.expr) {
76
+ exprs.push(x.stop_after_all_iters_if.expr);
77
+ }
75
78
  exprs.push(...getExpr(x.sleep));
76
79
  }
77
80
  return exprs;
@@ -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) {
@@ -17,6 +17,7 @@ declare const __propDef: {
17
17
  props: {
18
18
  pickableProperties: PickableProperties | undefined;
19
19
  result?: any;
20
+ extraResults?: any;
20
21
  flow_input?: any;
21
22
  error?: boolean | undefined;
22
23
  displayContext?: boolean | undefined;
@@ -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
  };
@@ -34,6 +34,9 @@ export type FlowStatusViewerContext = {
34
34
  nb: number;
35
35
  job: Job;
36
36
  }>>;
37
+ hideDownloadInGraph?: boolean;
38
+ hideTimeline?: boolean;
39
+ hideNodeDefinition?: boolean;
37
40
  };
38
41
  export type GraphModuleState = {
39
42
  type: FlowStatusModule['type'];
@@ -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 allowCopy={false} json={{ result }} on:select />
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>
@@ -2,6 +2,7 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  result: any;
5
+ extraResults?: any;
5
6
  flow_input?: any;
6
7
  };
7
8
  events: {
@@ -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',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-components",
3
- "version": "1.382.1",
3
+ "version": "1.382.3",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build",