windmill-components 1.118.0 → 1.121.1

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 (43) hide show
  1. package/package/components/ArgInfo.svelte +14 -12
  2. package/package/components/CenteredModal.svelte +7 -9
  3. package/package/components/DisplayResult.svelte +14 -13
  4. package/package/components/EditorBar.svelte +42 -15
  5. package/package/components/EditorBar.svelte.d.ts +1 -0
  6. package/package/components/FlowJobResult.svelte +2 -1
  7. package/package/components/FlowJobResult.svelte.d.ts +1 -0
  8. package/package/components/FlowStatusViewer.svelte +3 -0
  9. package/package/components/JobArgs.svelte +120 -27
  10. package/package/components/LogViewer.svelte +15 -2
  11. package/package/components/LogViewer.svelte.d.ts +1 -0
  12. package/package/components/ModulePreview.svelte +1 -0
  13. package/package/components/ScriptEditor.svelte +1 -0
  14. package/package/components/ScriptVersionHistory.svelte +57 -0
  15. package/package/components/ScriptVersionHistory.svelte.d.ts +16 -0
  16. package/package/components/SuperadminSettings.svelte +5 -8
  17. package/package/components/Uptodate.svelte +26 -0
  18. package/package/components/Uptodate.svelte.d.ts +14 -0
  19. package/package/components/UserSettings.svelte +4 -8
  20. package/package/components/Version.svelte +9 -0
  21. package/package/components/Version.svelte.d.ts +14 -0
  22. package/package/components/apps/components/display/AppLogsComponent.svelte +1 -0
  23. package/package/components/apps/components/display/table/AppTable.svelte +23 -3
  24. package/package/components/apps/components/layout/AppModal.svelte +1 -1
  25. package/package/components/apps/editor/AppEditorHeader.svelte +2 -2
  26. package/package/components/jobs/JobPreview.svelte +1 -0
  27. package/package/components/scriptEditor/LogPanel.svelte +1 -0
  28. package/package/gen/core/OpenAPI.js +1 -1
  29. package/package/gen/index.d.ts +3 -0
  30. package/package/gen/models/FlowModuleValue.d.ts +4 -1
  31. package/package/gen/models/Graphql.d.ts +3 -0
  32. package/package/gen/models/Graphql.js +4 -0
  33. package/package/gen/models/Http.d.ts +3 -0
  34. package/package/gen/models/Http.js +4 -0
  35. package/package/gen/models/Postgresql.d.ts +3 -0
  36. package/package/gen/models/Postgresql.js +4 -0
  37. package/package/gen/models/QueuedJob.d.ts +4 -1
  38. package/package/gen/models/QueuedJob.js +3 -0
  39. package/package/gen/services/JobService.d.ts +9 -0
  40. package/package/gen/services/JobService.js +15 -0
  41. package/package/gen/services/SettingsService.d.ts +6 -0
  42. package/package/gen/services/SettingsService.js +11 -0
  43. package/package.json +9 -2
@@ -1,7 +1,7 @@
1
1
  <script>import { ResourceService } from '../gen';
2
2
  import { workspaceStore } from '../stores';
3
3
  import { copyToClipboard, truncate } from '../utils';
4
- import { ClipboardCopy } from 'lucide-svelte';
4
+ import { ClipboardCopy, Expand } from 'lucide-svelte';
5
5
  import { Button, DrawerContent } from './common';
6
6
  import Drawer from './common/drawer/Drawer.svelte';
7
7
  import ObjectViewer from './propertyPicker/ObjectViewer.svelte';
@@ -64,16 +64,18 @@ async function getResource(path) {
64
64
  >
65
65
  {/if}
66
66
  {:else}
67
- <div class="max-h-40 overflow-auto">
68
- <ObjectViewer collapsed={false} topBrackets={true} pureViewer={true} json={value} />
67
+ <div class="relative">
68
+ {#if JSON.stringify(value).length > 120}
69
+ <button
70
+ class="text-xs absolute top-0 right-4 text-gray-500"
71
+ on:click={() => {
72
+ jsonViewerContent = value
73
+ jsonViewer.toggleDrawer()
74
+ }}><Expand size={18} /></button
75
+ >
76
+ {/if}
77
+ <div class="max-h-40 overflow-auto">
78
+ <ObjectViewer collapsed={false} topBrackets={true} pureViewer={true} json={value} />
79
+ </div>
69
80
  </div>
70
- {#if JSON.stringify(value).length > 120}
71
- <button
72
- class="text-xs text-blue-500"
73
- on:click={() => {
74
- jsonViewerContent = value
75
- jsonViewer.toggleDrawer()
76
- }}>See JSON</button
77
- >
78
- {/if}
79
81
  {/if}
@@ -1,13 +1,9 @@
1
- <script>import { SettingsService } from '../gen';
2
- import { onMount } from 'svelte';
3
- import WindmillIcon from './icons/WindmillIcon.svelte';
1
+ <script>import WindmillIcon from './icons/WindmillIcon.svelte';
2
+ import Uptodate from './Uptodate.svelte';
3
+ import Version from './Version.svelte';
4
4
  export let subtitle = undefined;
5
5
  export let title = 'Windmill';
6
6
  export let disableLogo = false;
7
- let version = '';
8
- onMount(async () => {
9
- version = await SettingsService.backendVersion();
10
- });
11
7
  </script>
12
8
 
13
9
  <div class="center-center min-h-screen p-4 relative bg-gray-50">
@@ -39,7 +35,9 @@ onMount(async () => {
39
35
  </div>
40
36
  </div>
41
37
 
42
- <div class="absolute top-0 right-0 text-2xs text-gray-800 italic px-3 py-1">
43
- <span class="font-mono">{version}</span>
38
+ <div class="absolute top-0 right-0 text-2xs text-gray-600 italic px-3 py-1">
39
+ <span class="font-mono flex flex-col">
40
+ <div>Windmill <Version /></div><div><Uptodate /></div>
41
+ </span>
44
42
  </div>
45
43
  </div>
@@ -3,7 +3,7 @@ import { json } from 'svelte-highlight/languages';
3
3
  import TableCustom from './TableCustom.svelte';
4
4
  import { copyToClipboard, truncate } from '../utils';
5
5
  import { Button, Drawer, DrawerContent } from './common';
6
- import { ClipboardCopy } from 'lucide-svelte';
6
+ import { ClipboardCopy, Download, Expand } from 'lucide-svelte';
7
7
  import Portal from 'svelte-portal';
8
8
  import ObjectViewer from './propertyPicker/ObjectViewer.svelte';
9
9
  export let result;
@@ -87,6 +87,7 @@ function inferResultKind(result) {
87
87
  return 'json';
88
88
  }
89
89
  let jsonViewer;
90
+ $: jsonStr = JSON.stringify(result, null, 4);
90
91
  </script>
91
92
 
92
93
  <div class="inline-highlight">
@@ -98,8 +99,8 @@ let jsonViewer;
98
99
  class="mb-2 w-full text-sm relative"
99
100
  >The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b>
100
101
  {#if !disableExpand}
101
- <div class="text-gray-500 text-xs absolute top-5 right-0">
102
- <button on:click={jsonViewer.openDrawer}>Expand</button>
102
+ <div class="text-gray-500 text-xs absolute top-5.5 right-0">
103
+ <button on:click={jsonViewer.openDrawer}><Expand size={16} /></button>
103
104
  </div>
104
105
  {/if}
105
106
  </div>{/if}{#if !forceJson && resultKind == 'table-col'}<div
@@ -218,7 +219,6 @@ let jsonViewer;
218
219
  >
219
220
  </div>
220
221
  {:else}
221
- {@const jsonStr = JSON.stringify(result, null, 4).replace(/\\n/g, '\n')}
222
222
  {#if jsonStr.length > 10000}
223
223
  <div class="text-sm mb-2 text-gray-600">
224
224
  <a
@@ -233,7 +233,7 @@ let jsonViewer;
233
233
  {/if}
234
234
  {/if}
235
235
  {:else}
236
- <div class="text-gray-500 text-sm">No result: {JSON.stringify(result)}</div>
236
+ <div class="text-gray-500 text-sm">No result: {jsonStr}</div>
237
237
  {/if}
238
238
  </div>
239
239
 
@@ -242,25 +242,26 @@ let jsonViewer;
242
242
  <Drawer bind:this={jsonViewer} size="900px">
243
243
  <DrawerContent title="Expanded Result" on:close={jsonViewer.closeDrawer}>
244
244
  <svelte:fragment slot="actions">
245
- <Button
246
- on:click={() => copyToClipboard(JSON.stringify(result, null, 4))}
247
- color="light"
248
- size="xs"
245
+ <a
246
+ class="text-sm text-gray-600 mr-2 inline-flex gap-2 items-center py-2 px-2 hover:bg-gray-100 rounded-lg"
247
+ download="{filename ?? 'result'}.json"
248
+ href="data:text/json;charset=utf-8,{encodeURIComponent(jsonStr)}"
249
+ >Download <Download size={14} /></a
249
250
  >
251
+ <Button on:click={() => copyToClipboard(jsonStr)} color="light" size="xs">
250
252
  <div class="flex gap-2 items-center">Copy to clipboard <ClipboardCopy /> </div>
251
253
  </Button>
252
254
  </svelte:fragment>
253
- {@const str = JSON.stringify(result, null, 4).replace(/\\n/g, '\n')}
254
- {#if str.length > 100000}
255
+ {#if jsonStr.length > 100000}
255
256
  <div class="text-sm mb-2 text-gray-600">
256
257
  <a
257
258
  download="{filename ?? 'result'}.json"
258
- href="data:text/json;charset=utf-8,{encodeURIComponent(str)}">Download</a
259
+ href="data:text/json;charset=utf-8,{encodeURIComponent(jsonStr)}">Download</a
259
260
  >
260
261
  JSON is too large to be displayed in full.
261
262
  </div>
262
263
  {:else}
263
- <Highlight language={json} code={JSON.stringify(result, null, 4).replace(/\\n/g, '\n')} />
264
+ <Highlight language={json} code={jsonStr.replace(/\\n/g, '\n')} />
264
265
  {/if}
265
266
  </DrawerContent>
266
267
  </Drawer>
@@ -2,7 +2,7 @@
2
2
  </script>
3
3
 
4
4
  <script>import { ResourceService, VariableService } from '../gen';
5
- import { faBook, faCube, faDollarSign, faPlus, faRotate, faRotateLeft } from '@fortawesome/free-solid-svg-icons';
5
+ import { faBook, faCube, faDollarSign, faHistory, faPlus, faRotate, faRotateLeft } from '@fortawesome/free-solid-svg-icons';
6
6
  import { workspaceStore } from '../stores';
7
7
  import ItemPicker from './ItemPicker.svelte';
8
8
  import ResourceEditor from './ResourceEditor.svelte';
@@ -22,6 +22,7 @@ import { getScriptByPath } from '../scripts';
22
22
  import Toggle from './Toggle.svelte';
23
23
  import { Link, Users } from 'lucide-svelte';
24
24
  import { capitalize } from '../utils';
25
+ import ScriptVersionHistory from './ScriptVersionHistory.svelte';
25
26
  export let lang;
26
27
  export let editor;
27
28
  export let websocketAlive;
@@ -31,6 +32,7 @@ export let kind = 'script';
31
32
  export let collabMode = false;
32
33
  export let collabLive = false;
33
34
  export let collabUsers = [];
35
+ export let scriptPath = undefined;
34
36
  let contextualVariablePicker;
35
37
  let variablePicker;
36
38
  let resourcePicker;
@@ -94,8 +96,17 @@ function compile(schema) {
94
96
  }
95
97
  return rec(schema.properties, true);
96
98
  }
99
+ let historyBrowserDrawerOpen = false;
97
100
  </script>
98
101
 
102
+ {#if scriptPath}
103
+ <Drawer bind:open={historyBrowserDrawerOpen} size="1200px">
104
+ <DrawerContent title="Versions History" on:close={() => (historyBrowserDrawerOpen = false)}>
105
+ <ScriptVersionHistory {scriptPath} />
106
+ </DrawerContent>
107
+ </Drawer>
108
+ {/if}
109
+
99
110
  <Drawer bind:this={scriptPicker} size="900px">
100
111
  <DrawerContent title="Code" on:close={scriptPicker.closeDrawer}>
101
112
  {#if pick_existing == 'hub'}
@@ -440,20 +451,36 @@ function compile(schema) {
440
451
  </div>
441
452
  </div>
442
453
 
443
- {#if SCRIPT_EDITOR_SHOW_EXPLORE_OTHER_SCRIPTS}
444
- <Button
445
- btnClasses="!font-medium text-gray-600"
446
- size="xs"
447
- spacingSize="md"
448
- color="light"
449
- on:click={scriptPicker.openDrawer}
450
- {iconOnly}
451
- startIcon={{ icon: faBook }}
452
- title="Explore other scripts"
453
- >
454
- Library
455
- </Button>
456
- {/if}
454
+ <div class="flex flex-row items-center gap-2">
455
+ {#if scriptPath}
456
+ <Button
457
+ btnClasses="!font-medium text-gray-600"
458
+ size="xs"
459
+ spacingSize="md"
460
+ color="light"
461
+ on:click={() => (historyBrowserDrawerOpen = true)}
462
+ {iconOnly}
463
+ startIcon={{ icon: faHistory }}
464
+ title="See history"
465
+ >
466
+ History
467
+ </Button>
468
+ {/if}
469
+ {#if SCRIPT_EDITOR_SHOW_EXPLORE_OTHER_SCRIPTS}
470
+ <Button
471
+ btnClasses="!font-medium text-gray-600"
472
+ size="xs"
473
+ spacingSize="md"
474
+ color="light"
475
+ on:click={scriptPicker.openDrawer}
476
+ {iconOnly}
477
+ startIcon={{ icon: faBook }}
478
+ title="Explore other scripts"
479
+ >
480
+ Library
481
+ </Button>
482
+ {/if}
483
+ </div>
457
484
  </div>
458
485
 
459
486
  <style>
@@ -21,6 +21,7 @@ declare const __propDef: {
21
21
  collabUsers?: {
22
22
  name: string;
23
23
  }[] | undefined;
24
+ scriptPath?: string | undefined;
24
25
  };
25
26
  events: {
26
27
  toggleCollabMode: CustomEvent<any>;
@@ -7,6 +7,7 @@ export let col = false;
7
7
  export let noBorder = false;
8
8
  export let loading;
9
9
  export let filename = undefined;
10
+ export let jobId = undefined;
10
11
  </script>
11
12
 
12
13
  <div
@@ -24,6 +25,6 @@ export let filename = undefined;
24
25
  {/if}
25
26
  </div>
26
27
  <div class="overflow-auto {col ? '' : 'max-h-80'} h-full relative">
27
- <LogViewer content={logs ?? ''} isLoading={false} />
28
+ <LogViewer content={logs ?? ''} {jobId} isLoading={false} />
28
29
  </div>
29
30
  </div>
@@ -7,6 +7,7 @@ declare const __propDef: {
7
7
  noBorder?: boolean | undefined;
8
8
  loading: any;
9
9
  filename?: string | undefined;
10
+ jobId?: string | undefined;
10
11
  };
11
12
  events: {
12
13
  [evt: string]: CustomEvent<any>;
@@ -183,6 +183,7 @@ function onJobsLoaded(mod, job) {
183
183
  {#if `result` in job}
184
184
  <div class="w-full h-full">
185
185
  <FlowJobResult
186
+ jobId={job?.id}
186
187
  loading={job['running'] == true}
187
188
  result={job.result}
188
189
  logs={job.logs ?? ''}
@@ -425,6 +426,7 @@ function onJobsLoaded(mod, job) {
425
426
  {@const node = localFlowModuleStates[selectedNode]}
426
427
  {#if selectedNode == 'end'}
427
428
  <FlowJobResult
429
+ jobId={job?.id}
428
430
  filename={job.id}
429
431
  loading={job['running']}
430
432
  noBorder
@@ -463,6 +465,7 @@ function onJobsLoaded(mod, job) {
463
465
  </div>
464
466
 
465
467
  <FlowJobResult
468
+ jobId={job?.id}
466
469
  loading={job['running'] == true}
467
470
  noBorder
468
471
  col
@@ -1,34 +1,127 @@
1
- <script>import ArgInfo from './ArgInfo.svelte';
2
- import { Skeleton } from './common';
1
+ <script>import { ChevronRightSquare, ClipboardCopy, Download, Expand } from 'lucide-svelte';
2
+ import ArgInfo from './ArgInfo.svelte';
3
+ import { Button, Drawer, DrawerContent, Skeleton } from './common';
3
4
  import TableCustom from './TableCustom.svelte';
5
+ import Portal from 'svelte-portal';
6
+ import { Highlight } from 'svelte-highlight';
7
+ import { copyToClipboard } from '../utils';
8
+ import { json } from 'svelte-highlight/languages';
4
9
  export let args;
5
10
  export let tableClass = '';
11
+ let jsonViewer;
12
+ let runLocally;
13
+ let jsonStr = '';
14
+ function pythonCode() {
15
+ return `
16
+ if __name__ == 'main':
17
+ ${Object.entries(args)
18
+ .map(([arg, value]) => {
19
+ return ` ${arg} = ${JSON.stringify(value)}`;
20
+ })
21
+ .join('\n')}
22
+
23
+ main(${Object.keys(args)
24
+ .map((x) => `${x} = ${x}`)
25
+ .join(', ')})
26
+ `;
27
+ }
28
+ function typescriptCode() {
29
+ return `
30
+ if (import.meta.main) {
31
+ ${Object.entries(args)
32
+ .map(([arg, value]) => {
33
+ return ` let ${arg} = ${JSON.stringify(value)}`;
34
+ })
35
+ .join('\n')}
36
+
37
+ await main(...)
38
+ }
39
+ `;
40
+ }
6
41
  </script>
7
42
 
8
- <TableCustom class="py-2 {tableClass}">
9
- <tr slot="header-row">
10
- <th>Argument</th>
11
- <th>Value</th>
12
- </tr>
13
- <tbody slot="body">
14
- {#if args && Object.keys(args).length > 0}
15
- {#each Object.entries(args) as [arg, value]}
43
+ <div class="relative">
44
+ <div class="text-gray-500 text-xs absolute top-8 right-0">
45
+ <button
46
+ on:click={() => {
47
+ jsonStr = JSON.stringify(args, null, 4)
48
+ jsonViewer.openDrawer()
49
+ }}><Expand size={18} /></button
50
+ >
51
+ </div>
52
+ <TableCustom class="py-2 {tableClass}">
53
+ <tr slot="header-row">
54
+ <th>Argument</th>
55
+ <th>Value</th>
56
+ </tr>
57
+ <tbody slot="body">
58
+ {#if args && Object.keys(args).length > 0}
59
+ {#each Object.entries(args) as [arg, value]}
60
+ <tr>
61
+ <td class="font-semibold">{arg}</td>
62
+ <td><ArgInfo {value} /></td>
63
+ </tr>
64
+ {/each}
65
+ {:else if args}
66
+ <tr><div class="text-gray-600 pt-2 pl-1 text-sm">No arguments</div></tr>
67
+ {:else}
16
68
  <tr>
17
- <td class="font-semibold">{arg}</td>
18
- <td><ArgInfo {value} /></td>
69
+ <td>
70
+ <Skeleton layout={[[3], 0.5, [3]]} />
71
+ </td>
72
+ <td>
73
+ <Skeleton layout={[[3], 0.5, [3]]} />
74
+ </td>
19
75
  </tr>
20
- {/each}
21
- {:else if args}
22
- <tr><div class="text-gray-600 pt-2 pl-1 text-sm">No arguments</div></tr>
23
- {:else}
24
- <tr>
25
- <td>
26
- <Skeleton layout={[[3], 0.5, [3]]} />
27
- </td>
28
- <td>
29
- <Skeleton layout={[[3], 0.5, [3]]} />
30
- </td>
31
- </tr>
32
- {/if}
33
- </tbody>
34
- </TableCustom>
76
+ {/if}
77
+ </tbody>
78
+ </TableCustom>
79
+ </div>
80
+
81
+ <Portal>
82
+ <Drawer bind:this={jsonViewer} size="900px">
83
+ <DrawerContent title="Expanded Args" on:close={jsonViewer.closeDrawer}>
84
+ <svelte:fragment slot="actions">
85
+ <a
86
+ class="text-sm text-gray-600 mr-2 inline-flex gap-2 items-center py-2 px-2 hover:bg-gray-100 rounded-lg"
87
+ download="windmill-args.json"
88
+ href="data:text/json;charset=utf-8,{encodeURIComponent(jsonStr)}"
89
+ >Download <Download size={14} /></a
90
+ >
91
+ <Button on:click={runLocally.openDrawer} color="light" size="xs">
92
+ <div class="flex gap-2 items-center">Use in a local script<ChevronRightSquare /> </div>
93
+ </Button>
94
+ <Button on:click={() => copyToClipboard(jsonStr)} color="light" size="xs">
95
+ <div class="flex gap-2 items-center">Copy to clipboard <ClipboardCopy /> </div>
96
+ </Button>
97
+ </svelte:fragment>
98
+ {#if jsonStr.length > 100000}
99
+ <div class="text-sm mb-2 text-gray-600">
100
+ <a
101
+ download="windmill-args.json"
102
+ href="data:text/json;charset=utf-8,{encodeURIComponent(jsonStr)}">Download</a
103
+ >
104
+ JSON is too large to be displayed in full.
105
+ </div>
106
+ {:else}
107
+ <Highlight language={json} code={jsonStr.replace(/\\n/g, '\n')} />
108
+ {/if}
109
+ </DrawerContent>
110
+ </Drawer>
111
+ <Drawer bind:this={runLocally} size="900px">
112
+ <DrawerContent title="Run locally" on:close={runLocally.closeDrawer}>
113
+ <h3 class="mb-2">Envs</h3>
114
+ If using the wmill client in your code, set the following env variables:
115
+ <pre
116
+ ><code
117
+ >BASE_URL="{window.location.origin}"
118
+ WM_TOKEN="{'<TOKEN>'}"</code
119
+ ></pre
120
+ >
121
+ <h3 class="mt-8">TypeScript</h3>
122
+ <pre><code>{typescriptCode()}</code></pre>
123
+ <h3 class="mt-8">Python</h3>
124
+ <pre><code>{pythonCode()}</code></pre>
125
+ </DrawerContent>
126
+ </Drawer>
127
+ </Portal>
@@ -1,10 +1,13 @@
1
- <script>import { Loader2 } from 'lucide-svelte';
2
- import { Drawer, DrawerContent } from './common';
1
+ <script>import { ClipboardCopy, Download, Loader2 } from 'lucide-svelte';
2
+ import { Button, Drawer, DrawerContent } from './common';
3
+ import { copyToClipboard } from '../utils';
4
+ import { workspaceStore } from '../stores';
3
5
  export let content;
4
6
  export let isLoading;
5
7
  export let duration = undefined;
6
8
  export let mem = undefined;
7
9
  export let wrapperClass = '';
10
+ export let jobId = undefined;
8
11
  let scroll = true;
9
12
  let div = null;
10
13
  $: if (content != undefined) {
@@ -19,6 +22,16 @@ let logViewer;
19
22
 
20
23
  <Drawer bind:this={logViewer} size="900px">
21
24
  <DrawerContent title="Expanded Logs" on:close={logViewer.closeDrawer}>
25
+ <svelte:fragment slot="actions">
26
+ <a
27
+ class="text-sm text-gray-600 mr-2 inline-flex gap-2 items-center py-2 px-2 hover:bg-gray-100 rounded-lg"
28
+ download="windmill-logs.json"
29
+ href="/api/w/{$workspaceStore}/jobs_u/get_logs/{jobId}">Download <Download size={14} /></a
30
+ >
31
+ <Button on:click={() => copyToClipboard(content)} color="light" size="xs">
32
+ <div class="flex gap-2 items-center">Copy to clipboard <ClipboardCopy /> </div>
33
+ </Button>
34
+ </svelte:fragment>
22
35
  <div>
23
36
  <pre class="bg-gray-50 text-xs w-full p-2"
24
37
  >{#if content}{content}{:else if isLoading}Waiting for job to start...{:else}No logs are available yet{/if}</pre
@@ -6,6 +6,7 @@ declare const __propDef: {
6
6
  duration?: number | undefined;
7
7
  mem?: number | undefined;
8
8
  wrapperClass?: string | undefined;
9
+ jobId?: string | undefined;
9
10
  scrollToBottom?: (() => void) | undefined;
10
11
  };
11
12
  events: {
@@ -88,6 +88,7 @@ function jobDone() {
88
88
  <Splitpanes horizontal>
89
89
  <Pane size={50} minSize={10}>
90
90
  <LogViewer
91
+ jobId={testJob?.id}
91
92
  duration={testJob?.['duration_ms']}
92
93
  mem={testJob?.['mem_peak']}
93
94
  content={testJob?.logs}
@@ -186,6 +186,7 @@ function collabUrl() {
186
186
  <div class="border-b-2 shadow-sm px-1 pr-4" bind:clientWidth={width}>
187
187
  <div class="flex justify-between space-x-2">
188
188
  <EditorBar
189
+ scriptPath={edit ? path : undefined}
189
190
  on:toggleCollabMode={() => {
190
191
  if (wsProvider?.shouldConnect) {
191
192
  disableCollaboration()
@@ -0,0 +1,57 @@
1
+ <script>import { Pane, Splitpanes } from 'svelte-splitpanes';
2
+ import PanelSection from './apps/editor/settingsPanel/common/PanelSection.svelte';
3
+ import { classNames } from '../utils';
4
+ import { ScriptService } from '../gen';
5
+ import { workspaceStore } from '../stores';
6
+ import { Skeleton } from './common';
7
+ import FlowModuleScript from './flows/content/FlowModuleScript.svelte';
8
+ export let scriptPath;
9
+ let selectedVersion = undefined;
10
+ let versions = undefined;
11
+ async function loadVersions() {
12
+ versions = (await ScriptService.getScriptByPath({ workspace: $workspaceStore, path: scriptPath })).parent_hashes;
13
+ }
14
+ loadVersions();
15
+ </script>
16
+
17
+ <Splitpanes class="!overflow-visible">
18
+ <Pane size={20}>
19
+ <PanelSection title="Past Versions">
20
+ <div class="flex flex-col gap-2 w-full">
21
+ {#if versions}
22
+ {#if versions.length > 0}
23
+ <div class="flex gap-2 flex-col">
24
+ {#each versions ?? [] as version}
25
+ <!-- svelte-ignore a11y-click-events-have-key-events -->
26
+ <div
27
+ class={classNames(
28
+ 'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
29
+ selectedVersion == version ? 'bg-blue-100 text-blue-600' : ''
30
+ )}
31
+ on:click={() => (selectedVersion = version)}
32
+ >
33
+ <span class="text-xs truncate">{version}</span>
34
+ </div>
35
+ {/each}
36
+ </div>
37
+ {:else}
38
+ <div class="text-sm text-gray-500">No items</div>
39
+ {/if}
40
+ {:else}
41
+ <Skeleton layout={[[40], [40], [40], [40], [40]]} />
42
+ {/if}
43
+ </div>
44
+ </PanelSection>
45
+ </Pane>
46
+ <Pane size={80}>
47
+ <div class="h-full w-full overflow-auto">
48
+ {#if selectedVersion}
49
+ {#key selectedVersion}
50
+ <FlowModuleScript path={scriptPath} hash={selectedVersion} />
51
+ {/key}
52
+ {:else}
53
+ <div class="text-sm p-2 text-gray-500">Select a deployment version to see its details</div>
54
+ {/if}
55
+ </div>
56
+ </Pane>
57
+ </Splitpanes>
@@ -0,0 +1,16 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ scriptPath: string;
5
+ };
6
+ events: {
7
+ [evt: string]: CustomEvent<any>;
8
+ };
9
+ slots: {};
10
+ };
11
+ export type ScriptVersionHistoryProps = typeof __propDef.props;
12
+ export type ScriptVersionHistoryEvents = typeof __propDef.events;
13
+ export type ScriptVersionHistorySlots = typeof __propDef.slots;
14
+ export default class ScriptVersionHistory extends SvelteComponentTyped<ScriptVersionHistoryProps, ScriptVersionHistoryEvents, ScriptVersionHistorySlots> {
15
+ }
16
+ export {};
@@ -1,4 +1,4 @@
1
- <script>import { UserService, SettingsService, GlobalUserInfo } from '../gen';
1
+ <script>import { UserService, GlobalUserInfo } from '../gen';
2
2
  import TableCustom from './TableCustom.svelte';
3
3
  import PageHeader from './PageHeader.svelte';
4
4
  import InviteGlobalUser from './InviteGlobalUser.svelte';
@@ -7,10 +7,11 @@ import { sendUserToast } from '../toast';
7
7
  import SearchItems from './SearchItems.svelte';
8
8
  import { page } from '$app/stores';
9
9
  import { goto } from '$app/navigation';
10
+ import Version from './Version.svelte';
11
+ import Uptodate from './Uptodate.svelte';
10
12
  let drawer;
11
13
  let filter = '';
12
14
  export function openDrawer() {
13
- loadVersion();
14
15
  listUsers();
15
16
  drawer?.openDrawer?.();
16
17
  }
@@ -25,12 +26,8 @@ function removeHash() {
25
26
  const hashRemoved = $page.url.href.slice(0, index);
26
27
  goto(hashRemoved);
27
28
  }
28
- let version;
29
29
  let users = [];
30
30
  let filteredUsers = [];
31
- async function loadVersion() {
32
- version = await SettingsService.backendVersion();
33
- }
34
31
  async function listUsers() {
35
32
  users = await UserService.listUsersAsSuperAdmin({ perPage: 100000 });
36
33
  }
@@ -47,8 +44,8 @@ async function listUsers() {
47
44
  <DrawerContent overflow_y={false} title="Superadmin Settings" on:close={closeDrawer}>
48
45
  <div class="flex flex-col h-full">
49
46
  <div>
50
- <div class="text-xs pt-1 text-gray-500">
51
- Windmill {version}
47
+ <div class="text-xs pt-1 text-gray-500 flex flex-col">
48
+ <div>Windmill <Version /></div><div><Uptodate /></div>
52
49
  </div>
53
50
 
54
51
  <PageHeader title="All global users" primary={false} />
@@ -0,0 +1,26 @@
1
+ <script>import { SettingsService } from '../gen';
2
+ import Tooltip from './Tooltip.svelte';
3
+ let uptodate = undefined;
4
+ loadVersion();
5
+ async function loadVersion() {
6
+ try {
7
+ const res = await SettingsService.backendUptodate();
8
+ if (res != 'yes') {
9
+ uptodate = res;
10
+ }
11
+ }
12
+ catch (e) {
13
+ console.warn('Could not fetch latest version', e);
14
+ }
15
+ }
16
+ </script>
17
+
18
+ {#if uptodate}
19
+ <span class="text-blue-400"
20
+ >{uptodate} &nbsp;<Tooltip>
21
+ How to update?<br />
22
+ - docker: `docker compose up`<br />-
23
+ <a href="https://github.com/windmill-labs/windmill-helm-charts#install">helm</a>
24
+ </Tooltip></span
25
+ >
26
+ {/if}
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type UptodateProps = typeof __propDef.props;
10
+ export type UptodateEvents = typeof __propDef.events;
11
+ export type UptodateSlots = typeof __propDef.slots;
12
+ export default class Uptodate extends SvelteComponentTyped<UptodateProps, UptodateEvents, UptodateSlots> {
13
+ }
14
+ export {};
@@ -1,5 +1,5 @@
1
1
  <script>import { usersWorkspaceStore } from '../stores';
2
- import { UserService, SettingsService } from '../gen';
2
+ import { UserService } from '../gen';
3
3
  import { displayDate, copyToClipboard } from '../utils';
4
4
  import { faClipboard, faPlus } from '@fortawesome/free-solid-svg-icons';
5
5
  import TableCustom from './TableCustom.svelte';
@@ -8,7 +8,6 @@ import Icon from 'svelte-awesome';
8
8
  export let scopes = undefined;
9
9
  let newPassword;
10
10
  let passwordError;
11
- let version;
12
11
  let tokens;
13
12
  let newToken;
14
13
  let newTokenLabel;
@@ -21,9 +20,9 @@ import { page } from '$app/stores';
21
20
  import { goto } from '$app/navigation';
22
21
  import { sendUserToast } from '../toast';
23
22
  import Tooltip from './Tooltip.svelte';
23
+ import Version from './Version.svelte';
24
24
  let drawer;
25
25
  export function openDrawer() {
26
- loadVersion();
27
26
  loadLoginType();
28
27
  listTokens();
29
28
  drawer?.openDrawer();
@@ -52,9 +51,6 @@ async function setPassword() {
52
51
  sendUserToast('Specify a new password value to change your passord', true);
53
52
  }
54
53
  }
55
- async function loadVersion() {
56
- version = await SettingsService.backendVersion();
57
- }
58
54
  async function loadLoginType() {
59
55
  login_type = (await UserService.globalWhoami()).login_type;
60
56
  }
@@ -85,8 +81,8 @@ async function deleteToken(tokenPrefix) {
85
81
  <DrawerContent title="User Settings" on:close={closeDrawer}>
86
82
  <div class="flex flex-col h-full">
87
83
  <div>
88
- <div class="text-xs pt-1 pb-2 text-gray-500">
89
- Windmill {version}
84
+ <div class="text-xs pt-1 pb-2 text-gray-500 flex-col flex">
85
+ Windmill <Version />
90
86
  </div>
91
87
 
92
88
  {#if scopes == undefined}
@@ -0,0 +1,9 @@
1
+ <script>import { SettingsService } from '../gen';
2
+ let version = '';
3
+ loadVersion();
4
+ async function loadVersion() {
5
+ version = await SettingsService.backendVersion();
6
+ }
7
+ </script>
8
+
9
+ {version}
@@ -0,0 +1,14 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {};
8
+ };
9
+ export type VersionProps = typeof __propDef.props;
10
+ export type VersionEvents = typeof __propDef.events;
11
+ export type VersionSlots = typeof __propDef.slots;
12
+ export default class Version extends SvelteComponentTyped<VersionProps, VersionEvents, VersionSlots> {
13
+ }
14
+ export {};
@@ -52,6 +52,7 @@ let testJob = undefined;
52
52
  )}
53
53
  >
54
54
  <LogViewer
55
+ jobId={testJob?.id}
55
56
  duration={testJob?.['duration_ms']}
56
57
  mem={testJob?.['mem_peak']}
57
58
  content={testJob?.logs}
@@ -3,7 +3,7 @@ import RunnableWrapper from '../../helpers/RunnableWrapper.svelte';
3
3
  import { writable } from 'svelte/store';
4
4
  import { createSvelteTable, flexRender } from '@tanstack/svelte-table';
5
5
  import AppButton from '../../buttons/AppButton.svelte';
6
- import { classNames, isObject } from '../../../../../utils';
6
+ import { classNames, isObject, sendUserToast } from '../../../../../utils';
7
7
  import DebouncedInput from '../../helpers/DebouncedInput.svelte';
8
8
  import AppTableFooter from './AppTableFooter.svelte';
9
9
  import { tableOptions } from './tableOptions';
@@ -151,6 +151,26 @@ $componentControl[id] = {
151
151
  }
152
152
  }
153
153
  };
154
+ function getHeaderGroups(table) {
155
+ try {
156
+ return table.getHeaderGroups();
157
+ }
158
+ catch (e) {
159
+ sendUserToast("Couldn't render table header groups: " + e, true);
160
+ console.error(e);
161
+ return [];
162
+ }
163
+ }
164
+ function safeVisibleCell(row) {
165
+ try {
166
+ return row.getVisibleCells();
167
+ }
168
+ catch (e) {
169
+ sendUserToast("Couldn't render table header groups: " + e, true);
170
+ console.error(e);
171
+ return [];
172
+ }
173
+ }
154
174
  </script>
155
175
 
156
176
  {#each Object.keys(components['tablecomponent'].initialData.configuration) as key (key)}
@@ -192,7 +212,7 @@ $componentControl[id] = {
192
212
  )}
193
213
  style={css?.tableHeader?.style ?? ''}
194
214
  >
195
- {#each $table.getHeaderGroups() as headerGroup}
215
+ {#each getHeaderGroups($table) as headerGroup}
196
216
  <tr class="divide-x">
197
217
  {#each headerGroup.headers as header}
198
218
  {#if header?.column?.columnDef?.header}
@@ -234,7 +254,7 @@ $componentControl[id] = {
234
254
  : 'divide-gray-200'
235
255
  )}
236
256
  >
237
- {#each row.getVisibleCells() as cell, index (index)}
257
+ {#each safeVisibleCell(row) as cell, index (index)}
238
258
  {#if cell?.column?.columnDef?.cell}
239
259
  {@const context = cell?.getContext()}
240
260
  {#if context}
@@ -86,7 +86,7 @@ let resolvedConfig = initConfig(components['modalcomponent'].initialData.configu
86
86
  `${
87
87
  $mode == 'dnd' ? 'absolute' : 'fixed'
88
88
  } top-0 bottom-0 left-0 right-0 transition-all duration-50`,
89
- open ? 'z-[1100] bg-black bg-opacity-60' : 'hidden'
89
+ open ? 'z-[1100] bg-black bg-opacity-60' : 'h-0 overflow-hidden'
90
90
  )}
91
91
  >
92
92
  <div
@@ -458,7 +458,7 @@ function onKeyDown(event) {
458
458
  </Drawer>
459
459
 
460
460
  <Drawer bind:open={historyBrowserDrawerOpen} size="1200px">
461
- <DrawerContent title="Deployment History" on:close={() => historyBrowserDrawerOpen}>
461
+ <DrawerContent title="Deployment History" on:close={() => (historyBrowserDrawerOpen = false)}>
462
462
  <DeploymentHistory on:restore {versions} />
463
463
  </DrawerContent>
464
464
  </Drawer>
@@ -558,7 +558,7 @@ function onKeyDown(event) {
558
558
  {#if job?.job_kind !== 'flow' && job?.job_kind !== 'flowpreview'}
559
559
  <Splitpanes horizontal class="grow border w-full">
560
560
  <Pane size={50} minSize={10}>
561
- <LogViewer content={job?.logs} isLoading={testIsLoading} />
561
+ <LogViewer jobId={job?.id} content={job?.logs} isLoading={testIsLoading} />
562
562
  </Pane>
563
563
  <Pane size={50} minSize={10} class="text-sm text-gray-600">
564
564
  {#if job != undefined && 'result' in job && job.result != undefined}
@@ -112,6 +112,7 @@ onDestroy(() => {
112
112
  {:else if job && `running` in job ? job.running : false}
113
113
  <div class="text-sm font-semibold text-gray-600 mb-1"> Job is still running </div>
114
114
  <LogViewer
115
+ jobId={job?.id}
115
116
  duration={job?.['duration_ms']}
116
117
  mem={job?.['mem_peak']}
117
118
  content={job?.logs}
@@ -66,6 +66,7 @@ function closeDrawer() {
66
66
  <Splitpanes horizontal>
67
67
  <Pane class="relative">
68
68
  <LogViewer
69
+ jobId={previewJob?.id}
69
70
  duration={previewJob?.['duration_ms']}
70
71
  mem={previewJob?.['mem_peak']}
71
72
  content={previewJob?.logs}
@@ -1,6 +1,6 @@
1
1
  export const OpenAPI = {
2
2
  BASE: '/api',
3
- VERSION: '1.117.0',
3
+ VERSION: '1.121.0',
4
4
  WITH_CREDENTIALS: false,
5
5
  CREDENTIALS: 'include',
6
6
  TOKEN: undefined,
@@ -29,7 +29,9 @@ export type { FlowValue } from './models/FlowValue';
29
29
  export type { Folder } from './models/Folder';
30
30
  export type { ForloopFlow } from './models/ForloopFlow';
31
31
  export { GlobalUserInfo } from './models/GlobalUserInfo';
32
+ export type { Graphql } from './models/Graphql';
32
33
  export type { Group } from './models/Group';
34
+ export type { Http } from './models/Http';
33
35
  export type { Identity } from './models/Identity';
34
36
  export type { Input } from './models/Input';
35
37
  export type { InputTransform } from './models/InputTransform';
@@ -52,6 +54,7 @@ export type { OpenFlowWPath } from './models/OpenFlowWPath';
52
54
  export type { PathFlow } from './models/PathFlow';
53
55
  export type { PathScript } from './models/PathScript';
54
56
  export { Policy } from './models/Policy';
57
+ export type { Postgresql } from './models/Postgresql';
55
58
  export { Preview } from './models/Preview';
56
59
  export { QueuedJob } from './models/QueuedJob';
57
60
  export { RawScript } from './models/RawScript';
@@ -1,8 +1,11 @@
1
1
  import type { BranchAll } from './BranchAll';
2
2
  import type { BranchOne } from './BranchOne';
3
3
  import type { ForloopFlow } from './ForloopFlow';
4
+ import type { Graphql } from './Graphql';
5
+ import type { Http } from './Http';
4
6
  import type { Identity } from './Identity';
5
7
  import type { PathFlow } from './PathFlow';
6
8
  import type { PathScript } from './PathScript';
9
+ import type { Postgresql } from './Postgresql';
7
10
  import type { RawScript } from './RawScript';
8
- export type FlowModuleValue = (RawScript | PathScript | PathFlow | ForloopFlow | BranchOne | BranchAll | Identity);
11
+ export type FlowModuleValue = (RawScript | PathScript | PathFlow | ForloopFlow | BranchOne | BranchAll | Identity | Postgresql | Http | Graphql);
@@ -0,0 +1,3 @@
1
+ export type Graphql = {
2
+ type: 'graphql';
3
+ };
@@ -0,0 +1,4 @@
1
+ /* istanbul ignore file */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ export {};
@@ -0,0 +1,3 @@
1
+ export type Http = {
2
+ type: 'http';
3
+ };
@@ -0,0 +1,4 @@
1
+ /* istanbul ignore file */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ export {};
@@ -0,0 +1,3 @@
1
+ export type Postgresql = {
2
+ type: 'postgresql';
3
+ };
@@ -0,0 +1,4 @@
1
+ /* istanbul ignore file */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ export {};
@@ -44,7 +44,10 @@ export declare namespace QueuedJob {
44
44
  FLOW = "flow",
45
45
  FLOWPREVIEW = "flowpreview",
46
46
  SCRIPT_HUB = "script_hub",
47
- IDENTITY = "identity"
47
+ IDENTITY = "identity",
48
+ HTTP = "http",
49
+ POSTGRESQL = "postgresql",
50
+ GRAPHQL = "graphql"
48
51
  }
49
52
  enum language {
50
53
  PYTHON3 = "python3",
@@ -12,6 +12,9 @@ export var QueuedJob;
12
12
  job_kind["FLOWPREVIEW"] = "flowpreview";
13
13
  job_kind["SCRIPT_HUB"] = "script_hub";
14
14
  job_kind["IDENTITY"] = "identity";
15
+ job_kind["HTTP"] = "http";
16
+ job_kind["POSTGRESQL"] = "postgresql";
17
+ job_kind["GRAPHQL"] = "graphql";
15
18
  })(job_kind = QueuedJob.job_kind || (QueuedJob.job_kind = {}));
16
19
  let language;
17
20
  (function (language) {
@@ -554,6 +554,15 @@ export declare class JobService {
554
554
  workspace: string;
555
555
  id: string;
556
556
  }): CancelablePromise<Job>;
557
+ /**
558
+ * get job logs
559
+ * @returns string job details
560
+ * @throws ApiError
561
+ */
562
+ static getJobLogs({ workspace, id, }: {
563
+ workspace: string;
564
+ id: string;
565
+ }): CancelablePromise<string>;
557
566
  /**
558
567
  * get job updates
559
568
  * @returns any job details
@@ -355,6 +355,21 @@ export class JobService {
355
355
  },
356
356
  });
357
357
  }
358
+ /**
359
+ * get job logs
360
+ * @returns string job details
361
+ * @throws ApiError
362
+ */
363
+ static getJobLogs({ workspace, id, }) {
364
+ return __request(OpenAPI, {
365
+ method: 'GET',
366
+ url: '/w/{workspace}/jobs_u/get_logs/{id}',
367
+ path: {
368
+ 'workspace': workspace,
369
+ 'id': id,
370
+ },
371
+ });
372
+ }
358
373
  /**
359
374
  * get job updates
360
375
  * @returns any job details
@@ -6,6 +6,12 @@ export declare class SettingsService {
6
6
  * @throws ApiError
7
7
  */
8
8
  static backendVersion(): CancelablePromise<string>;
9
+ /**
10
+ * is backend up to date
11
+ * @returns string is backend up to date
12
+ * @throws ApiError
13
+ */
14
+ static backendUptodate(): CancelablePromise<string>;
9
15
  /**
10
16
  * get license id
11
17
  * @returns string get license id (empty if not ee)
@@ -12,6 +12,17 @@ export class SettingsService {
12
12
  url: '/version',
13
13
  });
14
14
  }
15
+ /**
16
+ * is backend up to date
17
+ * @returns string is backend up to date
18
+ * @throws ApiError
19
+ */
20
+ static backendUptodate() {
21
+ return __request(OpenAPI, {
22
+ method: 'GET',
23
+ url: '/uptodate',
24
+ });
25
+ }
15
26
  /**
16
27
  * get license id
17
28
  * @returns string get license id (empty if not ee)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-components",
3
- "version": "1.118.0",
3
+ "version": "1.121.1",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build",
@@ -98,7 +98,7 @@
98
98
  "svelte-timezone-picker": "^2.0.3",
99
99
  "tailwind-merge": "^1.12.0",
100
100
  "vscode-ws-jsonrpc": "3.0.0",
101
- "windmill-parser-wasm": "file:../backend/parsers/windmill-parser-wasm/pkg",
101
+ "windmill-parser-wasm": "^1.120.0",
102
102
  "y-monaco": "^0.1.4",
103
103
  "y-websocket": "^1.5.0",
104
104
  "yjs": "^13.6.4"
@@ -245,6 +245,10 @@
245
245
  "./components/apps/editor/inlineScriptsPanel/utils": {
246
246
  "types": "./package/components/apps/editor/inlineScriptsPanel/utils.d.ts",
247
247
  "default": "./package/components/apps/editor/inlineScriptsPanel/utils.js"
248
+ },
249
+ "./gen/OpenAPI": {
250
+ "types": "./package/gen/OpenAPI.d.ts",
251
+ "default": "./package/gen/OpenAPI.js"
248
252
  }
249
253
  },
250
254
  "files": [
@@ -338,6 +342,9 @@
338
342
  ],
339
343
  "components/apps/editor/inlineScriptsPanel/utils": [
340
344
  "./package/components/apps/editor/inlineScriptsPanel/utils.d.ts"
345
+ ],
346
+ "gen/OpenAPI": [
347
+ "./package/gen/OpenAPI.d.ts"
341
348
  ]
342
349
  }
343
350
  }