windmill-components 1.407.2 → 1.408.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.
@@ -104,6 +104,9 @@ let inputCat = computeInputCat(type, format, itemsType?.type, enum_, contentEnco
104
104
  $: inputCat = computeInputCat(type, format, itemsType?.type, enum_, contentEncoding);
105
105
  let rawValue = undefined;
106
106
  function computeDefaultValue(nvalue, inputCat, defaultValue, nnullable) {
107
+ if (label == 'toString' && typeof value == 'function') {
108
+ value = undefined;
109
+ }
107
110
  if ((value == undefined || value == null) && !ignoreValueUndefined) {
108
111
  value = defaultValue;
109
112
  if (defaultValue === undefined || defaultValue === null) {
@@ -547,7 +547,7 @@ let seeS3PreviewFileFromList = '';
547
547
  >
548
548
  </button>
549
549
  {:else if !result?.disable_download}
550
- <FileDownload s3object={result} />
550
+ <FileDownload {workspaceId} s3object={result} />
551
551
  <button
552
552
  class="text-secondary underline text-2xs whitespace-nowrap"
553
553
  on:click={() => {
@@ -15,9 +15,9 @@ export let format;
15
15
  export let contentEncoding;
16
16
  export let customErrorMessage;
17
17
  export let minRows = undefined;
18
- export let disableCreate = false;
19
- export let disableVariablePicker = false;
20
- export let password = false;
18
+ export let disableCreate = undefined;
19
+ export let disableVariablePicker = undefined;
20
+ export let password = undefined;
21
21
  export let noExtra = false;
22
22
  export let dateFormat;
23
23
  export let enumLabels = undefined;
@@ -219,7 +219,7 @@ function onEnumKeyChange(oldKey, newKey) {
219
219
  <Toggle
220
220
  size="xs"
221
221
  options={{ right: 'Disallow creating custom values' }}
222
- checked={disableCreate != undefined && disableCreate}
222
+ checked={disableCreate}
223
223
  on:change={(e) => {
224
224
  if (e.detail) {
225
225
  disableCreate = true
@@ -292,7 +292,7 @@ function onEnumKeyChange(oldKey, newKey) {
292
292
  <Toggle
293
293
  size="xs"
294
294
  options={{ right: 'Disable variable picker' }}
295
- checked={disableVariablePicker != undefined && disableVariablePicker}
295
+ checked={disableVariablePicker}
296
296
  on:change={(e) => {
297
297
  if (e.detail) {
298
298
  disableVariablePicker = true
@@ -310,7 +310,11 @@ function onEnumKeyChange(oldKey, newKey) {
310
310
  options={{ right: 'Is Password' }}
311
311
  checked={password}
312
312
  on:change={(e) => {
313
- password = e.detail
313
+ if (e.detail) {
314
+ password = true
315
+ } else {
316
+ password = undefined
317
+ }
314
318
  }}
315
319
  />
316
320
  {/if}
@@ -1,5 +1,6 @@
1
1
  import { sendUserToast } from '../../../../toast';
2
2
  import { waitJob } from '../../../waitJob';
3
+ import { base } from '../../../../base';
3
4
  export function computeGlobalContext(world, extraContext = {}) {
4
5
  return {
5
6
  ...Object.fromEntries(Object.entries(world?.outputsById ?? {})
@@ -16,7 +17,7 @@ export function computeGlobalContext(world, extraContext = {}) {
16
17
  function create_context_function_template(eval_string, contextKeys, noReturn) {
17
18
  let hasReturnAsLastLine = noReturn || eval_string.split('\n').some((x) => x.startsWith('return '));
18
19
  return `
19
- return async function (context, state, createProxy, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal, open, close, validate, invalidate, validateAll, clearFiles, showToast, waitJob, askNewResource) {
20
+ return async function (context, state, createProxy, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal, open, close, validate, invalidate, validateAll, clearFiles, showToast, waitJob, askNewResource, downloadFile) {
20
21
  "use strict";
21
22
  ${contextKeys && contextKeys.length > 0
22
23
  ? `let ${contextKeys.map((key) => ` ${key} = createProxy('${key}', context['${key}'])`)};`
@@ -143,5 +144,64 @@ export async function eval_like(text, context = {}, state, editor, controlCompon
143
144
  sendUserToast(message, error);
144
145
  }, async (id) => waitJob(id), (id) => {
145
146
  controlComponents[id]?.askNewResource?.();
147
+ }, (input, filename) => {
148
+ const handleError = (error) => {
149
+ console.error('Error downloading file:', error);
150
+ sendUserToast(`Error downloading file: ${error.message}. Ensure it is a valid URL, a base64 encoded data URL (data:...), or a valid S3 object.`, true);
151
+ };
152
+ const isBase64 = (str) => {
153
+ try {
154
+ return btoa(atob(str)) === str;
155
+ }
156
+ catch (err) {
157
+ return false;
158
+ }
159
+ };
160
+ const downloadFile = (url, downloadFilename) => {
161
+ const link = document.createElement('a');
162
+ link.href = url;
163
+ link.download = downloadFilename || 'download';
164
+ document.body.appendChild(link);
165
+ link.click();
166
+ document.body.removeChild(link);
167
+ };
168
+ if (typeof input === 'object' && input.s3) {
169
+ const workspaceId = computeGlobalContext(worldStore).ctx.workspace;
170
+ const s3href = `${base}/api/w/${workspaceId}/job_helpers/download_s3_file?file_key=${input?.s3}${input?.storage ? `&storage=${input.storage}` : ''}`;
171
+ downloadFile(s3href, filename || input.s3);
172
+ }
173
+ else if (typeof input === 'string') {
174
+ if (input.startsWith('data:')) {
175
+ downloadFile(input, filename);
176
+ }
177
+ else if (isBase64(input)) {
178
+ const base64Url = `data:application/octet-stream;base64,${input}`;
179
+ downloadFile(base64Url, filename);
180
+ }
181
+ else if (/^(http|https):\/\//.test(input) || input.startsWith('/')) {
182
+ const url = input.startsWith('/') ? `${window.location.origin}${input}` : input;
183
+ fetch(url)
184
+ .then(response => {
185
+ if (!response.ok) {
186
+ throw new Error(`HTTP error! status: ${response.status}`);
187
+ }
188
+ return response.blob();
189
+ })
190
+ .then(blob => {
191
+ const objectUrl = URL.createObjectURL(blob);
192
+ const urlParts = input.split('/');
193
+ const inferredFilename = urlParts[urlParts.length - 1];
194
+ downloadFile(objectUrl, filename || inferredFilename);
195
+ URL.revokeObjectURL(objectUrl);
196
+ })
197
+ .catch(handleError);
198
+ }
199
+ else {
200
+ handleError(new Error('The input must be a valid URL, a base64 encoded string, or a valid S3 object.'));
201
+ }
202
+ }
203
+ else {
204
+ handleError(new Error('The input must be a string or an object with a getAuthenticatedUrl method.'));
205
+ }
146
206
  });
147
207
  }
@@ -15,7 +15,7 @@ export let extraKey = undefined;
15
15
  export let onFileChange = undefined;
16
16
  let resolvedConfig = initConfig(components['s3fileinputcomponent'].initialData.configuration, configuration);
17
17
  let fileUploads = writable([]);
18
- const { app, worldStore, componentControl, runnableComponents } = getContext('AppViewerContext');
18
+ const { app, worldStore, componentControl, runnableComponents, workspace } = getContext('AppViewerContext');
19
19
  $componentControl[id] = {
20
20
  clearFiles: () => {
21
21
  outputs.result.set([]);
@@ -100,6 +100,7 @@ let forceDisplayUploads = false;
100
100
  customResourceType="s3"
101
101
  customClass={css?.container?.class}
102
102
  customStyle={css?.container?.style}
103
+ {workspace}
103
104
  on:addition={(evt) => {
104
105
  const curr = outputs.result.peak()
105
106
  outputs.result.set(curr.concat(evt.detail))
@@ -249,6 +249,13 @@ declare async function waitJob(id: string): Promise<any>;
249
249
  */
250
250
  declare async function askNewResource(id: string): void;
251
251
 
252
+ /**
253
+ * Download a file from a url or base64 encoded string
254
+ * @param input url, base64 encoded string, [dataUrl](https://developer.mozilla.org/en-US/docs/Web/URI/Schemes/data) or S3 object
255
+ * @param [fileName] name of the file to download (optional)
256
+ */
257
+ declare function downloadFile(input: string | {s3: string, storage?: string}, fileName?: string): void;
258
+
252
259
  `
253
260
  : ''}
254
261
 
@@ -2,6 +2,7 @@
2
2
  import { Download } from 'lucide-svelte';
3
3
  import { base } from '../../../base';
4
4
  export let s3object;
5
+ export let workspaceId = undefined;
5
6
  </script>
6
7
 
7
8
  <a
@@ -9,9 +10,9 @@ export let s3object;
9
10
  border border-dashed border-gray-400 hover:border-blue-500
10
11
  focus-within:border-blue-500 hover:bg-blue-50 dark:hover:bg-frost-900 focus-within:bg-blue-50
11
12
  duration-200 rounded-lg p-1 gap-2"
12
- href={`${base}/api/w/${$workspaceStore}/job_helpers/download_s3_file?file_key=${s3object?.s3}${
13
- s3object?.storage ? `&storage=${s3object.storage}` : ''
14
- }`}
13
+ href={`${base}/api/w/${workspaceId ?? $workspaceStore}/job_helpers/download_s3_file?file_key=${
14
+ s3object?.s3
15
+ }${s3object?.storage ? `&storage=${s3object.storage}` : ''}`}
15
16
  download={s3object?.s3.split('/').pop() ?? 'unnamed_download.file'}
16
17
  >
17
18
  <Download />
@@ -2,6 +2,7 @@ import { SvelteComponent } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
4
  s3object: any;
5
+ workspaceId?: string | undefined;
5
6
  };
6
7
  events: {
7
8
  [evt: string]: CustomEvent<any>;
@@ -20,6 +20,7 @@ export let randomFileKey = false;
20
20
  export let pathTransformer = undefined; // function taking as input {file: File} and returning a string
21
21
  export let forceDisplayUploads = false;
22
22
  export let defaultValue = undefined;
23
+ export let workspace = undefined;
23
24
  const dispatch = createEventDispatcher();
24
25
  let fileUploads = writable([]);
25
26
  async function handleChange(files) {
@@ -130,7 +131,7 @@ async function uploadFileToS3(fileToUpload, fileToUploadKey) {
130
131
  }
131
132
  }
132
133
  });
133
- xhr?.open('POST', `/api/w/${$workspaceStore}/job_helpers/upload_s3_file?${params.toString()}`, true);
134
+ xhr?.open('POST', `/api/w/${workspace ?? $workspaceStore}/job_helpers/upload_s3_file?${params.toString()}`, true);
134
135
  xhr?.setRequestHeader('Content-Type', 'application/octet-stream');
135
136
  xhr?.send(fileToUpload);
136
137
  }));
@@ -161,7 +162,7 @@ async function uploadFileToS3(fileToUpload, fileToUploadKey) {
161
162
  }
162
163
  async function deleteFile(fileKey) {
163
164
  await HelpersService.deleteS3File({
164
- workspace: $workspaceStore,
165
+ workspace: workspace ?? $workspaceStore,
165
166
  fileKey: fileKey
166
167
  });
167
168
  dispatch('deletion', { path: fileKey });
@@ -12,6 +12,7 @@ declare const __propDef: {
12
12
  pathTransformer?: any;
13
13
  forceDisplayUploads?: boolean | undefined;
14
14
  defaultValue?: string | undefined;
15
+ workspace?: string | undefined;
15
16
  };
16
17
  events: {
17
18
  addition: CustomEvent<any>;
@@ -1,4 +1,5 @@
1
- <script>export let white = false;
1
+ <script>import { customIcon } from './store';
2
+ export let white = false;
2
3
  export let size = '24px';
3
4
  export let color = undefined;
4
5
  export let spin = undefined;
@@ -92,50 +93,69 @@ let lessSaturatedColor;
92
93
  $: color ? (lessSaturatedColor = reduceSaturation(color, -16)) : (lessSaturatedColor = undefined);
93
94
  </script>
94
95
 
95
- <!-- SVG Icon with customizable color -->
96
- <svg
97
- class={$$props.class}
98
- class:animate-[spin_2s_linear_infinite]={spin === 'veryfast'}
99
- class:animate-[spin_5s_linear_infinite]={spin === 'fast'}
100
- class:animate-[spin_15s_linear_infinite]={spin === 'medium'}
101
- class:animate-[spin_50s_linear_infinite]={spin === 'slow'}
102
- version="1.1"
103
- id="Calque_1"
104
- xmlns="http://www.w3.org/2000/svg"
105
- xmlns:xlink="http://www.w3.org/1999/xlink"
106
- x="0px"
107
- y="0px"
108
- width={size}
109
- height={size}
110
- viewBox="0 0 256 256"
111
- style="enable-background:new 0 0 256 256;"
112
- xml:space="preserve"
113
- >
114
- <g>
115
- <!-- Use color or fallback to defaults (white or blue) -->
116
- <polygon
117
- fill={lessSaturatedColor || (white ? '#cccccc' : '#bcd4fc')}
118
- points="134.78,14.22 114.31,48.21 101.33,69.75 158.22,69.75 177.97,36.95 191.67,14.22"
96
+ {#if customIcon.white || customIcon.normal}
97
+ {#if white}
98
+ <img
99
+ src={customIcon.white}
100
+ alt="Windmill Custom icon"
101
+ width={size}
102
+ height={size}
103
+ class={$$props.class}
119
104
  />
120
- <polygon
121
- fill={color || (white ? '#ffffff' : '#3b82f6')}
122
- points="227.55,69.75 186.61,69.75 101.33,69.75 129.78,119.02 158.16,119.02 228.61,119.02 256,119.02"
105
+ {:else}
106
+ <img
107
+ src={customIcon.normal}
108
+ alt="Windmill Custom icon"
109
+ width={size}
110
+ height={size}
111
+ class={$$props.class}
123
112
  />
124
- <polygon
125
- fill={color || (white ? '#ffffff' : '#3b82f6')}
126
- points="136.93,132.47 116.46,167.93 73.82,241.78 130.71,241.78 144.9,217.2 180.13,156.18 193.82,132.46"
127
- />
128
- <polygon
129
- fill={color || (white ? '#ffffff' : '#3b82f6')}
130
- points="121.7,131.95 101.23,96.49 58.59,22.63 30.15,71.91 44.34,96.49 79.57,157.5 93.26,181.22"
131
- />
132
- <polygon
133
- fill={lessSaturatedColor || (white ? '#cccccc' : '#bcd4fc')}
134
- points="64.81,131.95 25.15,131.21 0,130.74 28.44,180.01 66.73,180.72 93.26,181.21"
135
- />
136
- <polygon
137
- fill={lessSaturatedColor || (white ? '#cccccc' : '#bcd4fc')}
138
- points="165.38,181.74 184.58,216.46 196.75,238.47 225.19,189.2 206.66,155.69 193.83,132.46"
139
- />
140
- </g>
141
- </svg>
113
+ {/if}
114
+ {:else}
115
+ <svg
116
+ class={$$props.class}
117
+ class:animate-[spin_2s_linear_infinite]={spin === 'veryfast'}
118
+ class:animate-[spin_5s_linear_infinite]={spin === 'fast'}
119
+ class:animate-[spin_15s_linear_infinite]={spin === 'medium'}
120
+ class:animate-[spin_50s_linear_infinite]={spin === 'slow'}
121
+ version="1.1"
122
+ id="Calque_1"
123
+ xmlns="http://www.w3.org/2000/svg"
124
+ xmlns:xlink="http://www.w3.org/1999/xlink"
125
+ x="0px"
126
+ y="0px"
127
+ width={size}
128
+ height={size}
129
+ viewBox="0 0 256 256"
130
+ style="enable-background:new 0 0 256 256;"
131
+ xml:space="preserve"
132
+ >
133
+ <g>
134
+ <!-- Use color or fallback to defaults (white or blue) -->
135
+ <polygon
136
+ fill={lessSaturatedColor || (white ? '#cccccc' : '#bcd4fc')}
137
+ points="134.78,14.22 114.31,48.21 101.33,69.75 158.22,69.75 177.97,36.95 191.67,14.22"
138
+ />
139
+ <polygon
140
+ fill={color || (white ? '#ffffff' : '#3b82f6')}
141
+ points="227.55,69.75 186.61,69.75 101.33,69.75 129.78,119.02 158.16,119.02 228.61,119.02 256,119.02"
142
+ />
143
+ <polygon
144
+ fill={color || (white ? '#ffffff' : '#3b82f6')}
145
+ points="136.93,132.47 116.46,167.93 73.82,241.78 130.71,241.78 144.9,217.2 180.13,156.18 193.82,132.46"
146
+ />
147
+ <polygon
148
+ fill={color || (white ? '#ffffff' : '#3b82f6')}
149
+ points="121.7,131.95 101.23,96.49 58.59,22.63 30.15,71.91 44.34,96.49 79.57,157.5 93.26,181.22"
150
+ />
151
+ <polygon
152
+ fill={lessSaturatedColor || (white ? '#cccccc' : '#bcd4fc')}
153
+ points="64.81,131.95 25.15,131.21 0,130.74 28.44,180.01 66.73,180.72 93.26,181.21"
154
+ />
155
+ <polygon
156
+ fill={lessSaturatedColor || (white ? '#cccccc' : '#bcd4fc')}
157
+ points="165.38,181.74 184.58,216.46 196.75,238.47 225.19,189.2 206.66,155.69 193.83,132.46"
158
+ />
159
+ </g>
160
+ </svg>
161
+ {/if}
@@ -21,20 +21,20 @@ export let type = undefined;
21
21
  export let oneOf = undefined;
22
22
  export let required = false;
23
23
  export let pattern = undefined;
24
- export let password = false;
24
+ export let password = undefined;
25
25
  export let variableEditor = undefined;
26
26
  export let itemPicker = undefined;
27
- export let nullable = false;
28
- export let disabled = false;
27
+ export let nullable = undefined;
28
+ export let disabled = undefined;
29
29
  export let defaultValue = undefined;
30
30
  export let propsNames = [];
31
31
  export let showExpr = undefined;
32
32
  export let extra = {};
33
33
  export let customErrorMessage = undefined;
34
34
  export let itemsType = undefined;
35
- export let properties = {};
36
- export let order = [];
37
- export let requiredProperty = [];
35
+ export let properties = undefined;
36
+ export let order = undefined;
37
+ export let requiredProperty = undefined;
38
38
  export let displayWebhookWarning = true;
39
39
  export let lightweightMode = false;
40
40
  function getOneOfWithoutLabel(oneOf) {
@@ -97,7 +97,7 @@ function schemaUpdate(changedSchema) {
97
97
  return {
98
98
  ...v,
99
99
  properties: {
100
- ...v.properties,
100
+ ...(v.properties ?? {}),
101
101
  label: {
102
102
  type: 'string',
103
103
  enum: [v.title ?? '']
@@ -352,7 +352,14 @@ let initialObjectSelected = Object.keys(schema?.properties ?? {}).length == 0 ?
352
352
  }}
353
353
  lightMode
354
354
  size="xs"
355
- bind:checked={nullable}
355
+ checked={nullable}
356
+ on:change={(event) => {
357
+ if (event?.detail) {
358
+ nullable = true
359
+ } else {
360
+ nullable = undefined
361
+ }
362
+ }}
356
363
  disabled={required}
357
364
  />
358
365
  {/if}
@@ -363,7 +370,14 @@ let initialObjectSelected = Object.keys(schema?.properties ?? {}).length == 0 ?
363
370
  }}
364
371
  lightMode
365
372
  size="xs"
366
- bind:checked={disabled}
373
+ checked={disabled}
374
+ on:change={(event) => {
375
+ if (event?.detail) {
376
+ disabled = true
377
+ } else {
378
+ disabled = undefined
379
+ }
380
+ }}
367
381
  />
368
382
  </div>
369
383
 
@@ -10,7 +10,7 @@ declare const __propDef: {
10
10
  oneOf?: SchemaProperty[] | undefined;
11
11
  required?: boolean | undefined;
12
12
  pattern?: undefined | string;
13
- password?: boolean | undefined;
13
+ password?: undefined | boolean;
14
14
  variableEditor?: VariableEditor | undefined;
15
15
  itemPicker?: ItemPicker | undefined;
16
16
  nullable?: boolean | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "windmill-components",
3
- "version": "1.407.2",
3
+ "version": "1.408.1",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "build": "vite build",