windmill-components 1.28.1 → 1.28.4

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.
@@ -50,14 +50,16 @@ $: {
50
50
  evalValueToRaw();
51
51
  validateInput(pattern, value);
52
52
  }
53
- if (defaultValue) {
54
- let stringified = JSON.stringify(defaultValue, null, 4);
55
- if (stringified.length > 50) {
56
- minRows = 3;
57
- }
58
- if (type != 'string') {
59
- minRows = Math.max(minRows, Math.min(stringified.split(/\r\n|\r|\n/).length + 1, maxRows));
60
- }
53
+ }
54
+ $: {
55
+ defaultValue && recomputeRowSize(JSON.stringify(defaultValue, null, 4));
56
+ }
57
+ function recomputeRowSize(str) {
58
+ if (str.length > 50) {
59
+ minRows = 3;
60
+ }
61
+ if (type != 'string') {
62
+ minRows = Math.max(minRows, Math.min(str.split(/\r\n|\r|\n/).length + 1, maxRows));
61
63
  }
62
64
  }
63
65
  export function evalValueToRaw() {
@@ -240,6 +242,7 @@ $: inputCat = computeInputCat(type, format, itemsType?.type, enum_, contentEncod
240
242
  <textarea
241
243
  {disabled}
242
244
  style="min-height: {minHeight}; max-height: {maxHeight}"
245
+ on:input={async () => recomputeRowSize(rawValue ?? '')}
243
246
  class="col-span-10 {valid
244
247
  ? ''
245
248
  : 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'}"
@@ -290,7 +293,10 @@ $: inputCat = computeInputCat(type, format, itemsType?.type, enum_, contentEncod
290
293
  : 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'}"
291
294
  placeholder={defaultValue ?? ''}
292
295
  bind:value
293
- on:input={() => dispatch('input', { rawValue: value, isRaw: false })}
296
+ on:input={async () => {
297
+ recomputeRowSize(value)
298
+ dispatch('input', { rawValue: value, isRaw: false })
299
+ }}
294
300
  />
295
301
  {/if}
296
302
  {#if !required && inputCat != 'resource-object'}
@@ -4,15 +4,12 @@ import TableCustom from './TableCustom.svelte';
4
4
  import { truncate } from '../utils';
5
5
  export let result;
6
6
  let resultKind = inferResultKind(result);
7
- function isArray(obj) {
8
- return Object.prototype.toString.call(obj) === '[object Array]';
9
- }
10
7
  function isRectangularArray(obj) {
11
- if (!isArray(obj) || obj.length == 0) {
8
+ if (!Array.isArray(obj) || obj.length == 0) {
12
9
  return false;
13
10
  }
14
11
  if (!Object.values(obj)
15
- .map(isArray)
12
+ .map(Array.isArray)
16
13
  .reduce((a, b) => a && b)) {
17
14
  return false;
18
15
  }
@@ -28,10 +25,10 @@ function inferResultKind(result) {
28
25
  if (result) {
29
26
  try {
30
27
  let keys = Object.keys(result);
31
- if (keys.length == 1 && isRectangularArray(result[keys[0]])) {
28
+ if (isRectangularArray(result)) {
32
29
  return 'table-row';
33
30
  }
34
- else if (keys.map((k) => isArray(result[k])).reduce((a, b) => a && b)) {
31
+ else if (keys.map((k) => Array.isArray(result[k])).reduce((a, b) => a && b)) {
35
32
  return 'table-col';
36
33
  }
37
34
  else if (keys.length == 1 && keys[0] == 'png') {
@@ -51,7 +48,7 @@ function inferResultKind(result) {
51
48
  </script>
52
49
 
53
50
  {#if result}
54
- {#if Object.keys(result).length > 0}<div>
51
+ {#if typeof result == 'object' && Object.keys(result).length > 0}<div>
55
52
  The result keys are: <b>{Object.keys(result).join(', ')}</b>
56
53
  </div>
57
54
  {/if}
@@ -102,6 +102,9 @@ async function saveFlow() {
102
102
  goto(`/flows/get/${$flowStore.path}`);
103
103
  }
104
104
  async function changeStep(step) {
105
+ if (step === 2 && previewOpen) {
106
+ previewOpen = false;
107
+ }
105
108
  goto(`?step=${step}`);
106
109
  }
107
110
  flowStore.subscribe((flow) => {
@@ -120,7 +123,7 @@ onDestroy(() => {
120
123
  </script>
121
124
 
122
125
  <div class="flex flex-row w-full h-full justify-between">
123
- <div class="flex flex-col max-w-screen-md mb-96 m-auto">
126
+ <div class={`flex flex-col mb-96 m-auto w-1/2`}>
124
127
  <!-- Nav between steps-->
125
128
  <div class="justify-between flex flex-row w-full my-4">
126
129
  <Breadcrumb>
@@ -186,19 +189,6 @@ onDestroy(() => {
186
189
 
187
190
  <Icon data={faPlay} class="ml-2" />
188
191
  </Button>
189
- <div class={`relative h-screen w-1/3 ${previewOpen ? '' : 'hidden'}`}>
190
- <div class="absolute top-0 h-full">
191
- <div class="fixed border-l-2 right-0 h-screen w-1/3">
192
- <FlowPreviewContent
193
- bind:args={scheduleArgs}
194
- on:close={() => (previewOpen = !previewOpen)}
195
- on:change={(e) => {
196
- previewResults.set(jobsToResults(e.detail))
197
- }}
198
- />
199
- </div>
200
- </div>
201
- </div>
202
192
  {:else if step === 2}
203
193
  <ScriptSchema
204
194
  synchronizedHeader={false}
@@ -211,4 +201,20 @@ onDestroy(() => {
211
201
  <p>Loading</p>
212
202
  {/if}
213
203
  </div>
204
+
205
+ <div class={`relative h-screen w-1/3 ${previewOpen ? '' : 'hidden'}`}>
206
+ <div class="absolute top-0 h-full">
207
+ {#if $flowStore && step === 1}
208
+ <div class="fixed border-l-2 right-0 h-screen w-1/3">
209
+ <FlowPreviewContent
210
+ bind:args={scheduleArgs}
211
+ on:close={() => (previewOpen = !previewOpen)}
212
+ on:change={(e) => {
213
+ previewResults.set(jobsToResults(e.detail))
214
+ }}
215
+ />
216
+ </div>
217
+ {/if}
218
+ </div>
219
+ </div>
214
220
  </div>
@@ -55,7 +55,11 @@ let args = {};
55
55
  <ModuleStep bind:open bind:mod bind:args {i} mode={$mode} />
56
56
  {#if i == 0 && $mode == 'pull'}
57
57
  <div class="flex justify-center bg-white shadow p-2">
58
- Starting from here, the flow for loop over items from step 1's result above &nbsp;
58
+ <p>
59
+ Starting from here, the flow for loop over items from the 1rst step's result right above
60
+ &nbsp; <br />We do not support any other kind of for-loop at the moment but we will very
61
+ soon (See <a href="https://github.com/windmill-labs/windmill/issues/350">#350</a>)
62
+ </p>
59
63
  <Tooltip>
60
64
  This flow being in 'Pull' mode, the rest of the flow will for loop over the list of
61
65
  items returned by the trigger script right above. Retrieve the item value using
@@ -7,7 +7,10 @@ import { slide } from 'svelte/transition';
7
7
  import Tabs from './Tabs.svelte';
8
8
  import SchemaViewer from './SchemaViewer.svelte';
9
9
  import FieldHeader from './FieldHeader.svelte';
10
+ import InputTransformsViewer from './InputTransformsViewer.svelte';
11
+ import SvelteMarkdown from 'svelte-markdown';
10
12
  export let flow;
13
+ export let initialOpen = undefined;
11
14
  let flowFiltered = {
12
15
  summary: flow.summary,
13
16
  description: flow.description,
@@ -17,7 +20,9 @@ let flowFiltered = {
17
20
  export let embedded = false;
18
21
  export let tab = 'ui';
19
22
  let open = {};
20
- let hubCode = {};
23
+ if (initialOpen) {
24
+ open[initialOpen] = true;
25
+ }
21
26
  function toAny(x) {
22
27
  return x;
23
28
  }
@@ -37,7 +42,7 @@ function toAny(x) {
37
42
  <div class="flow-root w-full pb-4">
38
43
  {#if !embedded}
39
44
  <h2 class="mt-4">{flow.summary}</h2>
40
- <h3 class="mt-4">{flow.description}</h3>
45
+ <SvelteMarkdown source={flow.description ?? ''} />
41
46
 
42
47
  <p class="font-black text-lg w-full my-4">
43
48
  <span>Inputs</span>
@@ -89,6 +94,7 @@ function toAny(x) {
89
94
  </div>
90
95
  <div class="min-w-0 flex-1 pt-1.5 flex justify-between space-x-4 w-full">
91
96
  <div class="w-full">
97
+ <span class="text-black">{mod?.summary ?? ''}</span>
92
98
  <p class="text-sm text-gray-500">
93
99
  {#if mod?.value?.type == 'script'}
94
100
  Script at path <a
@@ -104,9 +110,10 @@ function toAny(x) {
104
110
  }}
105
111
  class="mb-2 underline text-black"
106
112
  >
107
- View code {open[i] ? '(-)' : '(+)'}</button
113
+ View code and inputs {open[i] ? '(-)' : '(+)'}</button
108
114
  >
109
115
  {#if open[i]}
116
+ <InputTransformsViewer inputTransforms={mod?.input_transform} />
110
117
  <div class="w-full h-full">
111
118
  <iframe
112
119
  style="height: 400px;"
@@ -131,6 +138,8 @@ function toAny(x) {
131
138
 
132
139
  {#if open[i]}
133
140
  <div transition:slide class="border border-black p-2 bg-gray-50 w-full">
141
+ <InputTransformsViewer inputTransforms={mod?.input_transform} />
142
+
134
143
  <Highlight
135
144
  language={mod?.value?.language == 'deno' ? typescript : python}
136
145
  code={mod?.value?.content}
@@ -8,6 +8,7 @@ declare const __propDef: {
8
8
  value: FlowValue;
9
9
  schema?: any;
10
10
  };
11
+ initialOpen?: number | undefined;
11
12
  embedded?: boolean | undefined;
12
13
  tab?: "json" | "schema" | "ui" | undefined;
13
14
  };
@@ -0,0 +1,19 @@
1
+ <script>import { Highlight } from 'svelte-highlight';
2
+ import ObjectViewer from './propertyPicker/ObjectViewer.svelte';
3
+ import typescript from 'svelte-highlight/languages/typescript';
4
+ export let inputTransforms;
5
+ </script>
6
+
7
+ <ul class="mb-1">
8
+ {#each Object.entries(inputTransforms) as [key, val]}
9
+ <li>
10
+ <span class="font-black text-gray-700">{key}</span>: {#if val.type == 'static'}<ObjectViewer
11
+ json={val.value}
12
+ />{:else}
13
+ <span class="inline-block inline-highlight">
14
+ <Highlight offsetTop={0} language={typescript} code={val.expr} />
15
+ </span>
16
+ {/if}
17
+ </li>
18
+ {/each}
19
+ </ul>
@@ -0,0 +1,17 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ import type { InputTransform } from '../gen';
3
+ declare const __propDef: {
4
+ props: {
5
+ inputTransforms: Record<string, InputTransform>;
6
+ };
7
+ events: {
8
+ [evt: string]: CustomEvent<any>;
9
+ };
10
+ slots: {};
11
+ };
12
+ export declare type InputTransformsViewerProps = typeof __propDef.props;
13
+ export declare type InputTransformsViewerEvents = typeof __propDef.events;
14
+ export declare type InputTransformsViewerSlots = typeof __propDef.slots;
15
+ export default class InputTransformsViewer extends SvelteComponentTyped<InputTransformsViewerProps, InputTransformsViewerEvents, InputTransformsViewerSlots> {
16
+ }
17
+ export {};
@@ -13,6 +13,9 @@ export function flowToMode(flow, mode) {
13
13
  if (inp.type == 'javascript') {
14
14
  //@ts-ignore
15
15
  inp.value = undefined;
16
+ inp.expr = inp.expr.split('\n')
17
+ .filter((x) => x != '' && !x.startsWith(`import { previous_result, flow_input, step, variable, resource, params } from 'windmill@`))
18
+ .join('\n');
16
19
  }
17
20
  else {
18
21
  //@ts-ignore
@@ -24,16 +27,17 @@ export function flowToMode(flow, mode) {
24
27
  const triggerModule = newFlow.value.modules[0];
25
28
  const oldModules = newFlow.value.modules.slice(1);
26
29
  if (triggerModule) {
27
- triggerModule.stop_after_if_expr = 'result.res1.length == 0';
30
+ triggerModule.stop_after_if_expr = 'result.length == 0';
28
31
  triggerModule.skip_if_stopped = true;
29
32
  }
30
33
  newFlow.value.modules = newFlow.value.modules.slice(0, 1);
31
34
  if (oldModules.length > 0) {
32
35
  newFlow.value.modules.push({
33
- input_transform: oldModules[0].input_transform,
36
+ //TODO: once we allow arbitrary for loop, we will also allow arbitrary input transform here
37
+ input_transform: {},
34
38
  value: {
35
39
  type: 'forloopflow',
36
- iterator: { type: 'javascript', expr: 'result.res1' },
40
+ iterator: { type: 'javascript', expr: 'result' },
37
41
  value: {
38
42
  modules: oldModules
39
43
  },
@@ -119,18 +123,14 @@ export async function loadSchemaFromModule(module) {
119
123
  schema: emptySchema()
120
124
  };
121
125
  }
126
+ const returnStatementRegex = new RegExp(/\$\{(.*)\}/);
122
127
  export function isCodeInjection(expr) {
123
128
  if (!expr) {
124
129
  return false;
125
130
  }
126
131
  const lines = expr.split('\n');
127
132
  const [returnStatement] = lines.reverse();
128
- const returnStatementRegex = new RegExp(/\$\{(.*)\}/);
129
- if (returnStatementRegex.test(returnStatement)) {
130
- const [_, argName] = returnStatement.split(returnStatementRegex);
131
- return Boolean(argName);
132
- }
133
- return false;
133
+ return returnStatementRegex.test(returnStatement);
134
134
  }
135
135
  export function getDefaultExpr(i, key = 'myfield', previousExpr) {
136
136
  const expr = previousExpr ?? `previous_result.${key}`;
@@ -192,13 +192,17 @@ export async function runFlowPreview(args, flow) {
192
192
  });
193
193
  }
194
194
  function computeFlowInputPull(previewResult, flowInputAsObject) {
195
- const iteratorValues = (previewResult?.res1 && Array.isArray(previewResult.res1)) ?
195
+ const iteratorValues = (previewResult && Array.isArray(previewResult)) ?
196
196
  {
197
- _value: previewResult.res1[0],
198
- _index: `The current index of the iteration as a number (here from 0 to ${previewResult.res1.length - 1})`
197
+ iter: {
198
+ value: previewResult[0],
199
+ index: `The current index of the iteration as a number (here from 0 to ${previewResult.length - 1})`
200
+ }
199
201
  } : {
200
- _value: 'The current value of the iteration as an object',
201
- _index: 'The current index of the iteration as a number'
202
+ iter: {
203
+ value: 'The current value of the iteration as an object',
204
+ index: 'The current index of the iteration as a number'
205
+ }
202
206
  };
203
- return Object.assign(Object.assign(flowInputAsObject, previewResult), iteratorValues);
207
+ return Object.assign(flowInputAsObject, iteratorValues);
204
208
  }
@@ -7,6 +7,7 @@ export let level = 0;
7
7
  export let isLast = true;
8
8
  export let currentPath = '';
9
9
  export let pureViewer = false;
10
+ export let collapsed = level == 3 || Array.isArray(json);
10
11
  const collapsedSymbol = '...';
11
12
  let keys;
12
13
  let isArray;
@@ -18,7 +19,6 @@ $: {
18
19
  openBracket = isArray ? '[' : '{';
19
20
  closeBracket = isArray ? ']' : '}';
20
21
  }
21
- $: collapsed = level == 2;
22
22
  function collapse() {
23
23
  collapsed = !collapsed;
24
24
  }
@@ -6,6 +6,7 @@ declare const __propDef: {
6
6
  isLast?: boolean | undefined;
7
7
  currentPath?: string | undefined;
8
8
  pureViewer?: boolean | undefined;
9
+ collapsed?: boolean | undefined;
9
10
  };
10
11
  events: {
11
12
  select: CustomEvent<any>;
@@ -1,6 +1,6 @@
1
1
  export const OpenAPI = {
2
2
  BASE: '/api',
3
- VERSION: '1.27.2',
3
+ VERSION: '1.28.1',
4
4
  WITH_CREDENTIALS: false,
5
5
  CREDENTIALS: 'include',
6
6
  TOKEN: undefined,
@@ -5,4 +5,5 @@ export declare type FlowModule = {
5
5
  value: FlowModuleValue;
6
6
  stop_after_if_expr?: string;
7
7
  skip_if_stopped?: boolean;
8
+ summary?: string;
8
9
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-components",
3
- "version": "1.28.1",
3
+ "version": "1.28.4",
4
4
  "devDependencies": {
5
5
  "@playwright/test": "^1.24.2",
6
6
  "@sveltejs/adapter-static": "^1.0.0-next.37",
@@ -82,6 +82,7 @@
82
82
  "./components/GroupModal.svelte": "./components/GroupModal.svelte",
83
83
  "./components/IconedResourceType.svelte": "./components/IconedResourceType.svelte",
84
84
  "./components/InputTransformForm.svelte": "./components/InputTransformForm.svelte",
85
+ "./components/InputTransformsViewer.svelte": "./components/InputTransformsViewer.svelte",
85
86
  "./components/InviteGlobalUser.svelte": "./components/InviteGlobalUser.svelte",
86
87
  "./components/InviteUser.svelte": "./components/InviteUser.svelte",
87
88
  "./components/ItemPicker.svelte": "./components/ItemPicker.svelte",
@@ -273,4 +274,4 @@
273
274
  "./user": "./user.js",
274
275
  "./utils": "./utils.js"
275
276
  }
276
- }
277
+ }
@@ -1,4 +1,4 @@
1
- export declare const PYTHON_INIT_CODE = "import os\nimport wmill\nfrom datetime import datetime\n# Our webeditor includes a syntax, type checker through a language server running pyright\n# and the autoformatter Black in our servers. Use Cmd/Ctrl + S to autoformat the code.\n# Beware that the code is only saved when you click Save and not across reload.\n# You can however navigate to any steps safely.\n\"\"\"\nThe client is used to interact with windmill itself through its standard API.\nOne can explore the methods available through autocompletion of `client.XXX`.\nOnly the most common methods are included for ease of use. Request more as\nfeedback if you feel you are missing important ones.\n\"\"\"\ndef main(name: str = \"Nicolas Bourbaki\",\n age: int = 42,\n obj: dict = {\"even\": \"dicts\"},\n l: list = [\"or\", \"lists!\"],\n file_: bytes = bytes(0),\n dtime: datetime = datetime.now()):\n \"\"\"A main function is required for the script to be able to accept arguments.\n Types are recommended.\"\"\"\n print(f\"Hello World and a warm welcome especially to {name}\")\n print(\"and its acolytes..\", age, obj, l, len(file_), dtime)\n # retrieve variables, including secrets by querying the windmill platform.\n # secret fetching is audited by windmill.\n secret = wmill.get_variable(\"g/all/pretty_secret\")\n print(f\"The env variable at `g/all/pretty_secret`: {secret}\")\n # interact with the windmill platform to get the version\n version = wmill.get_version()\n # fetch reserved variables as environment variables\n user = os.environ.get(\"WM_USERNAME\")\n # the return value is then parsed and can be retrieved by other scripts conveniently\n return {\"version\": version, \"splitted\": name.split(), \"user\": user}\n";
1
+ export declare const PYTHON_INIT_CODE = "import os\nimport wmill\nfrom datetime import datetime\n\n\"\"\"\nUse Cmd/Ctrl + S to autoformat the code.\nThe client is used to interact with windmill itself through its standard API.\nOne can explore the methods available through autocompletion of `wmill.XXX`.\n\"\"\"\n\ndef main(name: str = \"Nicolas Bourbaki\",\n age: int = 42,\n obj: dict = {\"even\": \"dicts\"},\n l: list = [\"or\", \"lists!\"],\n file_: bytes = bytes(0),\n dtime: datetime = datetime.now()):\n \"\"\"A main function is required for the script to be able to accept arguments.\n Types are recommended.\"\"\"\n print(f\"Hello World and a warm welcome especially to {name}\")\n print(\"and its acolytes..\", age, obj, l, len(file_), dtime)\n # retrieve variables, including secrets by querying the windmill platform.\n # secret fetching is audited by windmill.\n secret = wmill.get_variable(\"g/all/pretty_secret\")\n print(f\"The env variable at `g/all/pretty_secret`: {secret}\")\n # interact with the windmill platform to get the version\n version = wmill.get_version()\n # fetch reserved variables as environment variables\n user = os.environ.get(\"WM_USERNAME\")\n # the return value is then parsed and can be retrieved by other scripts conveniently\n return {\"version\": version, \"splitted\": name.split(), \"user\": user}\n";
2
2
  export declare const DENO_INIT_CODE: string;
3
3
  export declare const DENO_INIT_CODE_TRIGGER: string;
4
4
  export declare function initialCode(language: 'deno' | 'python3', is_trigger: boolean): string;
package/script_helpers.js CHANGED
@@ -1,16 +1,13 @@
1
1
  export const PYTHON_INIT_CODE = `import os
2
2
  import wmill
3
3
  from datetime import datetime
4
- # Our webeditor includes a syntax, type checker through a language server running pyright
5
- # and the autoformatter Black in our servers. Use Cmd/Ctrl + S to autoformat the code.
6
- # Beware that the code is only saved when you click Save and not across reload.
7
- # You can however navigate to any steps safely.
4
+
8
5
  """
6
+ Use Cmd/Ctrl + S to autoformat the code.
9
7
  The client is used to interact with windmill itself through its standard API.
10
- One can explore the methods available through autocompletion of \`client.XXX\`.
11
- Only the most common methods are included for ease of use. Request more as
12
- feedback if you feel you are missing important ones.
8
+ One can explore the methods available through autocompletion of \`wmill.XXX\`.
13
9
  """
10
+
14
11
  def main(name: str = "Nicolas Bourbaki",
15
12
  age: int = 42,
16
13
  obj: dict = {"even": "dicts"},