windmill-components 1.377.7 → 1.379.2

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 (71) hide show
  1. package/package/ata/apis.d.ts +1 -0
  2. package/package/ata/apis.js +5 -3
  3. package/package/ata/index.d.ts +1 -1
  4. package/package/ata/index.js +2 -1
  5. package/package/components/DateInput.svelte +30 -3
  6. package/package/components/DisplayResult.svelte +19 -10
  7. package/package/components/DisplayResult.svelte.d.ts +1 -0
  8. package/package/components/FlowStatusViewer.svelte +2 -0
  9. package/package/components/FlowStatusViewer.svelte.d.ts +1 -0
  10. package/package/components/FlowStatusViewerInner.svelte +13 -10
  11. package/package/components/FlowStatusViewerInner.svelte.d.ts +1 -0
  12. package/package/components/FlowViewer.svelte.d.ts +1 -1
  13. package/package/components/InstanceSettings.svelte +1 -0
  14. package/package/components/LightweightArgInput.svelte +13 -6
  15. package/package/components/LightweightSchemaForm.svelte +4 -1
  16. package/package/components/LightweightSchemaForm.svelte.d.ts +1 -0
  17. package/package/components/LogViewer.svelte +0 -3
  18. package/package/components/Login.svelte +66 -73
  19. package/package/components/Login.svelte.d.ts +0 -2
  20. package/package/components/ModulePreview.svelte +1 -1
  21. package/package/components/ObjectStoreConfigSettings.svelte +2 -2
  22. package/package/components/Password.svelte +1 -1
  23. package/package/components/Portal.svelte +2 -2
  24. package/package/components/apps/components/GroupWrapper.svelte +3 -2
  25. package/package/components/apps/components/GroupWrapper.svelte.d.ts +2 -1
  26. package/package/components/apps/components/buttons/AppSchemaForm.svelte +16 -13
  27. package/package/components/apps/components/display/AppJobIdLogComponent.svelte +1 -1
  28. package/package/components/apps/components/display/AppRecomputeAll.svelte +2 -2
  29. package/package/components/apps/components/display/table/AppCell.svelte +5 -1
  30. package/package/components/apps/components/display/table/AppTableFooter.svelte +4 -1
  31. package/package/components/apps/components/helpers/InputValue.svelte +4 -3
  32. package/package/components/apps/components/helpers/RunnableComponent.svelte +4 -3
  33. package/package/components/apps/components/helpers/eval.d.ts +2 -1
  34. package/package/components/apps/components/helpers/eval.js +45 -14
  35. package/package/components/apps/components/inputs/AppTextInput.svelte +1 -0
  36. package/package/components/apps/components/layout/AppContainer.svelte +13 -3
  37. package/package/components/apps/editor/AppEditor.svelte +4 -7
  38. package/package/components/apps/editor/AppEditorHeader.svelte +22 -16
  39. package/package/components/apps/editor/AppPreview.svelte +8 -20
  40. package/package/components/apps/editor/AppPreview.svelte.d.ts +3 -1
  41. package/package/components/apps/editor/DeploymentHistory.svelte +1 -1
  42. package/package/components/apps/editor/RunnableJobPanel.svelte +8 -8
  43. package/package/components/apps/editor/inlineScriptsPanel/InlineScriptEditor.svelte +2 -2
  44. package/package/components/apps/editor/settingsPanel/ComponentInputTypeEditor.svelte +5 -1
  45. package/package/components/apps/editor/settingsPanel/GridGroup.svelte +13 -1
  46. package/package/components/apps/types.d.ts +7 -2
  47. package/package/components/apps/utils.js +30 -3
  48. package/package/components/common/drawer/Drawer.svelte +1 -1
  49. package/package/components/flows/previousResults.js +1 -0
  50. package/package/components/jobs/JobPreview.svelte +1 -1
  51. package/package/components/propertyPicker/PropPicker.svelte +2 -1
  52. package/package/components/runs/JobLoader.svelte +19 -7
  53. package/package/components/runs/JobLoader.svelte.d.ts +2 -1
  54. package/package/components/runs/JobPreview.svelte +2 -2
  55. package/package/components/runs/QueuePopover.svelte +1 -1
  56. package/package/components/runs/RunRow.svelte +14 -7
  57. package/package/components/runs/RunRow.svelte.d.ts +1 -0
  58. package/package/components/runs/RunsFilter.svelte +78 -15
  59. package/package/components/runs/RunsFilter.svelte.d.ts +3 -2
  60. package/package/components/runs/RunsQueue.svelte +23 -11
  61. package/package/components/runs/RunsQueue.svelte.d.ts +5 -1
  62. package/package/components/runs/RunsTable.svelte +65 -63
  63. package/package/components/runs/RunsTable.svelte.d.ts +1 -0
  64. package/package/components/scriptEditor/LogPanel.svelte +1 -1
  65. package/package/gen/core/OpenAPI.js +1 -1
  66. package/package/gen/services.gen.d.ts +8 -3
  67. package/package/gen/services.gen.js +16 -4
  68. package/package/gen/types.gen.d.ts +22 -0
  69. package/package.json +2 -9
  70. package/package/components/AppEmbed.svelte +0 -155
  71. package/package/components/AppEmbed.svelte.d.ts +0 -21
@@ -94,20 +94,23 @@ function onDefaultChange() {
94
94
  <div
95
95
  class={twMerge('p-2 overflow-auto h-full', css?.container?.class, 'wm-schema-form')}
96
96
  style={css?.container?.style}
97
- on:pointerdown|stopPropagation={(e) =>
98
- !$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
99
97
  >
100
- <LightweightSchemaForm
101
- defaultValues={resolvedConfig.defaultValues}
102
- dynamicEnums={resolvedConfig.dynamicEnums}
103
- schema={result}
104
- bind:isValid={valid}
105
- bind:args
106
- bind:this={schemaForm}
107
- displayType={Boolean(resolvedConfig.displayType)}
108
- largeGap={Boolean(resolvedConfig.largeGap)}
109
- {css}
110
- />
98
+ <div
99
+ on:pointerdown|stopPropagation={(e) =>
100
+ !$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
101
+ >
102
+ <LightweightSchemaForm
103
+ defaultValues={resolvedConfig.defaultValues}
104
+ dynamicEnums={resolvedConfig.dynamicEnums}
105
+ schema={result}
106
+ bind:isValid={valid}
107
+ bind:args
108
+ bind:this={schemaForm}
109
+ displayType={Boolean(resolvedConfig.displayType)}
110
+ largeGap={Boolean(resolvedConfig.largeGap)}
111
+ {css}
112
+ />
113
+ </div>
111
114
  </div>
112
115
  {:else}
113
116
  <p class="m-2 italic">Empty form (no property)</p>
@@ -84,7 +84,7 @@ $: if (resolvedConfig.jobId) {
84
84
  duration={testJob?.['duration_ms']}
85
85
  mem={testJob?.['mem_peak']}
86
86
  content={testJob?.logs}
87
- isLoading={testIsLoading}
87
+ isLoading={testIsLoading && testJob?.['running'] == false}
88
88
  tag={testJob?.tag}
89
89
  />
90
90
  </div>
@@ -14,7 +14,7 @@ export let customCss = undefined;
14
14
  export let configuration;
15
15
  export let render;
16
16
  export let horizontalAlignment = undefined;
17
- const { app, worldStore } = getContext('AppViewerContext');
17
+ const { app, worldStore, policy } = getContext('AppViewerContext');
18
18
  let resolvedConfig = initConfig(components['recomputeallcomponent'].initialData.configuration, configuration);
19
19
  initOutput($worldStore, id, {
20
20
  loading: undefined
@@ -45,7 +45,7 @@ let css = initCss($app.css?.recomputeallcomponent, customCss);
45
45
  <InitializeComponent {id} />
46
46
 
47
47
  <AlignWrapper {horizontalAlignment}>
48
- {#if render}
48
+ {#if render && policy}
49
49
  <RecomputeAllWrapper
50
50
  containerClass={css?.container?.class}
51
51
  containerStyle={css?.container?.style}
@@ -69,7 +69,11 @@ function saveEdit() {
69
69
  {:else}
70
70
  <!-- svelte-ignore a11y-no-static-element-interactions -->
71
71
  <div on:dblclick={toggleEdit}>
72
- {value}
72
+ {#if typeof value == 'object'}
73
+ {JSON.stringify(value)}
74
+ {:else}
75
+ {value}
76
+ {/if}
73
77
  </div>
74
78
  {/if}
75
79
  </td>
@@ -18,8 +18,11 @@ function convertJSONToCSV(objArray) {
18
18
  for (let i = 0; i < objArray.length; i++) {
19
19
  let line = '';
20
20
  for (let j = 0; j < headers.length; j++) {
21
- const value = objArray[i][headers[j]];
21
+ let value = objArray[i][headers[j]];
22
22
  line += j ? ',' : '';
23
+ if (typeof value != 'string') {
24
+ value = JSON.stringify(value);
25
+ }
23
26
  line += /[\",\n]/.test(value) ? '"' + value.replace(/"/g, '""') + '"' : value;
24
27
  }
25
28
  str += line + '\r\n';
@@ -20,10 +20,11 @@ const rowContext = getContext('RowWrapperContext');
20
20
  const groupContext = getContext('GroupContext');
21
21
  let previousConnectedValue = undefined;
22
22
  let previousConnectedValues = {};
23
+ let groupStore = groupContext?.context;
23
24
  $: fullContext = {
24
25
  iter: iterContext ? $iterContext : undefined,
25
26
  row: rowContext ? $rowContext : undefined,
26
- group: groupContext ? $groupContext : undefined
27
+ group: groupStore ? $groupStore : undefined
27
28
  };
28
29
  $: lastInput?.type == 'evalv2' &&
29
30
  !onDemandOnly &&
@@ -190,7 +191,7 @@ async function evalExpr(input, args) {
190
191
  return;
191
192
  try {
192
193
  const context = computeGlobalContext($worldStore, deepMergeWithPriority(fullContext, args ?? {}));
193
- const r = await eval_like(input.expr, context, $state, $mode == 'dnd', $componentControl, $worldStore, $runnableComponents, false);
194
+ const r = await eval_like(input.expr, context, $state, $mode == 'dnd', $componentControl, $worldStore, $runnableComponents, false, groupContext?.id);
194
195
  error = '';
195
196
  return r;
196
197
  }
@@ -207,7 +208,7 @@ async function getValue(input) {
207
208
  return;
208
209
  if ((input.type === 'template' || input.type == 'templatev2') && isCodeInjection(input.eval)) {
209
210
  try {
210
- const r = await eval_like('`' + input.eval + '`', computeGlobalContext($worldStore, fullContext), $state, $mode == 'dnd', $componentControl, $worldStore, $runnableComponents, false);
211
+ const r = await eval_like('`' + input.eval + '`', computeGlobalContext($worldStore, fullContext), $state, $mode == 'dnd', $componentControl, $worldStore, $runnableComponents, false, groupContext?.id);
211
212
  error = '';
212
213
  return r;
213
214
  }
@@ -196,8 +196,8 @@ async function executeComponent(noToast = false, inlineScriptOverride, setRunnab
196
196
  r = await eval_like(runnable.inlineScript?.content, computeGlobalContext($worldStore, {
197
197
  iter: iterContext ? $iterContext : undefined,
198
198
  row: rowContext ? $rowContext : undefined,
199
- group: groupContext ? $groupContext : undefined
200
- }), $state, isEditor, $componentControl, $worldStore, $runnableComponents, true);
199
+ group: groupContext ? get(groupContext.context) : undefined
200
+ }), $state, isEditor, $componentControl, $worldStore, $runnableComponents, true, groupContext?.id);
201
201
  await setResult(r, job);
202
202
  $state = $state;
203
203
  }
@@ -385,8 +385,9 @@ async function runTransformer(res) {
385
385
  const transformerResult = await eval_like(transformer.content, computeGlobalContext($worldStore, {
386
386
  iter: iterContext ? $iterContext : undefined,
387
387
  row: rowContext ? $rowContext : undefined,
388
+ group: groupContext ? get(groupContext.context) : undefined,
388
389
  result: res
389
- }), $state, isEditor, $componentControl, $worldStore, $runnableComponents, true);
390
+ }), $state, isEditor, $componentControl, $worldStore, $runnableComponents, true, groupContext?.id);
390
391
  return transformerResult;
391
392
  }
392
393
  catch (err) {
@@ -19,6 +19,7 @@ export declare function eval_like(text: string, context: {} | undefined, state:
19
19
  showToast?: (message: string, error?: boolean) => void;
20
20
  waitJob?: (jobId: string) => void;
21
21
  askNewResource?: () => void;
22
+ setGroupValue?: (key: string, value: any) => void;
22
23
  }>, worldStore: World | undefined, runnableComponents: Record<string, {
23
24
  cb?: (() => void)[];
24
- }>, noReturn: boolean): Promise<any>;
25
+ }>, noReturn: boolean, groupContextId: string | undefined): Promise<any>;
@@ -16,10 +16,10 @@ export function computeGlobalContext(world, extraContext = {}) {
16
16
  function create_context_function_template(eval_string, contextKeys, noReturn) {
17
17
  let hasReturnAsLastLine = noReturn || eval_string.split('\n').some((x) => x.startsWith('return '));
18
18
  return `
19
- return async function (context, state, goto, setTab, recompute, getAgGrid, setValue, setSelectedIndex, openModal, closeModal, open, close, validate, invalidate, validateAll, clearFiles, showToast, waitJob, askNewResource) {
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
20
  "use strict";
21
21
  ${contextKeys && contextKeys.length > 0
22
- ? `let ${contextKeys.map((key) => ` ${key} = context['${key}']`)};`
22
+ ? `let ${contextKeys.map((key) => ` ${key} = createProxy('${key}', context['${key}'] ?? {})`)};`
23
23
  : ``}
24
24
  ${hasReturnAsLastLine
25
25
  ? eval_string
@@ -52,21 +52,52 @@ function hashCode(s) {
52
52
  }
53
53
  return hash;
54
54
  }
55
- export async function eval_like(text, context = {}, state, editor, controlComponents, worldStore, runnableComponents, noReturn) {
56
- const proxiedState = new Proxy(state, {
57
- set(target, key, value) {
58
- if (typeof key !== 'string') {
59
- throw new Error('Invalid key');
60
- }
61
- target[key] = value;
62
- let o = worldStore?.newOutput('state', key, value);
63
- o?.set(value, true);
64
- return true;
55
+ export async function eval_like(text, context = {}, state, editor, controlComponents, worldStore, runnableComponents, noReturn, groupContextId) {
56
+ const createProxy = (name, obj) => {
57
+ if (name == 'group' && groupContextId) {
58
+ return createGroupProxy(groupContextId, obj);
65
59
  }
66
- });
60
+ return new Proxy(obj, {
61
+ set(target, key, value) {
62
+ if (name != 'state') {
63
+ throw new Error('Cannot set value on objects that are neither the global state or a container group field');
64
+ }
65
+ if (typeof key !== 'string') {
66
+ throw new Error('Invalid key');
67
+ }
68
+ target[key] = value;
69
+ let o = worldStore?.newOutput(name, key, value);
70
+ o?.set(value, true);
71
+ return true;
72
+ },
73
+ get(obj, prop) {
74
+ if (name != 'state' && prop == 'group') {
75
+ return createGroupProxy(name, obj[prop]);
76
+ }
77
+ else {
78
+ return obj[prop];
79
+ }
80
+ }
81
+ });
82
+ };
83
+ const createGroupProxy = (name, obj) => {
84
+ return new Proxy(obj, {
85
+ set(target, key, value) {
86
+ target[key] = value;
87
+ let o = worldStore?.newOutput(name, 'group', target);
88
+ o?.set(target, true);
89
+ if (typeof key !== 'string') {
90
+ throw new Error('Invalid key');
91
+ }
92
+ controlComponents[name]?.setGroupValue?.(key, value);
93
+ return true;
94
+ }
95
+ });
96
+ };
97
+ const proxiedState = createProxy('state', state);
67
98
  let evaluator = make_context_evaluator(text, Object.keys(context ?? {}), noReturn);
68
99
  // console.log(i, j)
69
- return await evaluator(context, proxiedState, async (x, newTab) => {
100
+ return await evaluator(context, proxiedState, createProxy, async (x, newTab) => {
70
101
  if (newTab || editor) {
71
102
  if (!newTab) {
72
103
  sendUserToast('In editor mode, `goto` opens a new tab to prevent losing your work. To test the redirection , use the preview mode.');
@@ -122,6 +122,7 @@ async function handleAfterIcon() {
122
122
  !$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
123
123
  on:keydown|stopPropagation
124
124
  type="password"
125
+ autocomplete="new-password"
125
126
  bind:value
126
127
  placeholder={resolvedConfig.placeholder}
127
128
  disabled={resolvedConfig.disabled}
@@ -1,4 +1,4 @@
1
- <script>import { getContext } from 'svelte';
1
+ <script>import { getContext, onMount } from 'svelte';
2
2
  import { initOutput } from '../../editor/appUtils';
3
3
  import SubGridEditor from '../../editor/SubGridEditor.svelte';
4
4
  import { initCss } from '../../utils';
@@ -14,7 +14,7 @@ export let componentContainerHeight;
14
14
  export let customCss = undefined;
15
15
  export let render;
16
16
  export let groupFields = undefined;
17
- const { app, focusedGrid, selectedComponent, worldStore, connectingInput } = getContext('AppViewerContext');
17
+ const { app, focusedGrid, selectedComponent, worldStore, connectingInput, componentControl } = getContext('AppViewerContext');
18
18
  let groupContext = writable({});
19
19
  let outputs = initOutput($worldStore, id, { group: $groupContext });
20
20
  $: outputs.group.set($groupContext, true);
@@ -24,6 +24,16 @@ function onFocus() {
24
24
  subGridIndex: 0
25
25
  };
26
26
  }
27
+ onMount(() => {
28
+ $componentControl[id] = {
29
+ setGroupValue: (field, value) => {
30
+ groupContext.update((group) => {
31
+ group[field] = value;
32
+ return group;
33
+ });
34
+ }
35
+ };
36
+ });
27
37
  let css = initCss($app.css?.containercomponent, customCss);
28
38
  </script>
29
39
 
@@ -47,7 +57,7 @@ let css = initCss($app.css?.containercomponent, customCss);
47
57
 
48
58
  <div class="w-full h-full">
49
59
  {#if $app.subgrids?.[`${id}-0`]}
50
- <GroupWrapper {groupContext}>
60
+ <GroupWrapper {id} context={groupContext}>
51
61
  <SubGridEditor
52
62
  visible={render}
53
63
  {id}
@@ -68,8 +68,8 @@ let context = {
68
68
  email: $userStore?.email,
69
69
  groups: $userStore?.groups,
70
70
  username: $userStore?.username,
71
- query: Object.fromEntries(new URLSearchParams(window.location.search).entries()),
72
- hash: window.location.hash,
71
+ query: Object.fromEntries($page.url.searchParams.entries()),
72
+ hash: $page.url.hash,
73
73
  workspace: $workspaceStore,
74
74
  mode: 'editor',
75
75
  summary: $summaryStore,
@@ -114,6 +114,7 @@ setContext('AppViewerContext', {
114
114
  previewTheme,
115
115
  debuggingComponents: writable({}),
116
116
  replaceStateFn: (path) => replaceState(path, $page.state),
117
+ policy: policy,
117
118
  recomputeAllContext: writable({
118
119
  loading: false,
119
120
  componentNumber: 0,
@@ -156,10 +157,6 @@ function saveFrontendDraft() {
156
157
  function hashchange(e) {
157
158
  context.hash = e.newURL.split('#')[1];
158
159
  context = context;
159
- worldStore.update((x) => {
160
- x.outputsById?.['ctx']?.['hash'].set(context.hash, true);
161
- return x;
162
- });
163
160
  }
164
161
  $: context.mode = $mode == 'dnd' ? 'editor' : 'viewer';
165
162
  $: width = $breakpoint === 'sm' ? 'min-w-[400px] max-w-[656px]' : 'min-w-[710px] w-full';
@@ -440,7 +437,7 @@ let stillInJobEnter = false;
440
437
  app={$appStore}
441
438
  appPath={path}
442
439
  {breakpoint}
443
- author={policy.on_behalf_of_email ?? ''}
440
+ {policy}
444
441
  isEditor
445
442
  {context}
446
443
  noBackend={false}
@@ -811,17 +811,19 @@ export function openTroubleshootPanel() {
811
811
  <h2>Public URL</h2>
812
812
  <div class="mt-4" />
813
813
 
814
- <Toggle
815
- options={{
816
- left: `Require login and read-access`,
817
- right: `No login required`
818
- }}
819
- checked={policy.execution_mode == 'anonymous'}
820
- on:change={(e) => {
821
- policy.execution_mode = e.detail ? 'anonymous' : 'publisher'
822
- setPublishState()
823
- }}
824
- />
814
+ <div class="flex gap-2 items-center">
815
+ <Toggle
816
+ options={{
817
+ left: `Require login and read-access`,
818
+ right: `No login required`
819
+ }}
820
+ checked={policy.execution_mode == 'anonymous'}
821
+ on:change={(e) => {
822
+ policy.execution_mode = e.detail ? 'anonymous' : 'publisher'
823
+ setPublishState()
824
+ }}
825
+ />
826
+ </div>
825
827
 
826
828
  <div class="my-6 box">
827
829
  Public url:
@@ -848,10 +850,14 @@ export function openTroubleshootPanel() {
848
850
  of embedding app must be the same as the one of Windmill)</div
849
851
  >
850
852
  </div>
851
-
852
853
  <Alert type="info" title="Only latest deployed app is publicly available">
853
854
  You will still need to deploy the app to make visible the latest changes
854
855
  </Alert>
856
+
857
+ <a
858
+ href="https://www.windmill.dev/docs/advanced/external_auth_with_jwt#embed-public-apps-using-your-own-authentification"
859
+ class="mt-4 text-2xs">Embed this app in your own product to be used by your own users</a
860
+ >
855
861
  {/if}
856
862
  </DrawerContent>
857
863
  </Drawer>
@@ -1020,14 +1026,14 @@ export function openTroubleshootPanel() {
1020
1026
  duration={job?.['duration_ms']}
1021
1027
  jobId={job?.id}
1022
1028
  content={job?.logs}
1023
- isLoading={testIsLoading}
1029
+ isLoading={testIsLoading && job?.['running'] == false}
1024
1030
  tag={job?.tag}
1025
1031
  />
1026
1032
  </Pane>
1027
1033
  <Pane size={50} minSize={10} class="text-sm text-secondary">
1028
- {#if job != undefined && 'result' in job && job.result != undefined}
1029
- <div class="relative h-full px-2">
1030
- <DisplayResult
1034
+ {#if job != undefined && 'result' in job && job.result != undefined}<div
1035
+ class="relative h-full px-2"
1036
+ ><DisplayResult
1031
1037
  workspaceId={$workspaceStore}
1032
1038
  jobId={selectedJobId}
1033
1039
  result={job.result}
@@ -10,7 +10,7 @@ import { columnConfiguration } from '../gridUtils';
10
10
  import { deepEqual } from 'fast-equals';
11
11
  import { dfs, maxHeight } from './appUtils';
12
12
  import { BG_PREFIX, migrateApp } from '../utils';
13
- import { workspaceStore, enterpriseLicense, userStore } from '../../../stores';
13
+ import { workspaceStore, enterpriseLicense } from '../../../stores';
14
14
  import DarkModeObserver from '../../DarkModeObserver.svelte';
15
15
  import { getTheme } from './componentsPanel/themeUtils';
16
16
  import HiddenComponent from '../components/helpers/HiddenComponent.svelte';
@@ -18,19 +18,17 @@ import RecomputeAllComponents from './RecomputeAllComponents.svelte';
18
18
  export let app;
19
19
  export let appPath = '';
20
20
  export let breakpoint = writable('lg');
21
- export let author;
21
+ export let policy = {};
22
22
  export let summary = '';
23
23
  export let workspace = $workspaceStore;
24
24
  export let isEditor = false;
25
+ export let context;
25
26
  export let noBackend = false;
26
27
  export let isLocked = false;
27
28
  export let hideRefreshBar = false;
28
29
  export let replaceStateFn = (path) => window.history.replaceState(null, '', path);
29
30
  export let gotoFn = (path, opt) => window.history.pushState(null, '', path);
30
31
  migrateApp(app);
31
- let email = $userStore?.email ?? 'anonymous';
32
- let groups = $userStore?.groups ?? [];
33
- let username = $userStore?.username ?? 'anonymous';
34
32
  const appStore = writable(app);
35
33
  const selectedComponent = writable(undefined);
36
34
  const mode = writable('preview');
@@ -41,15 +39,11 @@ const connectingInput = writable({
41
39
  });
42
40
  const allIdsInPath = writable([]);
43
41
  let ncontext = {
44
- email,
45
- groups,
46
- username,
47
- query: Object.fromEntries(new URLSearchParams(window.location.search).entries()),
48
- hash: window.location.hash,
42
+ ...context,
49
43
  workspace,
50
44
  mode: 'viewer',
51
45
  summary: summary,
52
- author
46
+ author: policy.on_behalf_of_email
53
47
  };
54
48
  function resizeWindow() {
55
49
  !isEditor && ($breakpoint = window.innerWidth < 769 ? 'sm' : 'lg');
@@ -60,14 +54,7 @@ const darkMode = writable(document.documentElement.classList.contains('dark'));
60
54
  const state = writable({});
61
55
  let parentContext = getContext('AppViewerContext');
62
56
  let worldStore = buildWorld(ncontext);
63
- $: onContextChange({
64
- email,
65
- groups,
66
- username,
67
- workspace,
68
- summary: summary,
69
- author
70
- });
57
+ $: onContextChange(context);
71
58
  function onContextChange(context) {
72
59
  Object.assign(ncontext, context);
73
60
  ncontext = ncontext;
@@ -120,6 +107,7 @@ setContext('AppViewerContext', {
120
107
  debuggingComponents: writable({}),
121
108
  replaceStateFn,
122
109
  gotoFn,
110
+ policy,
123
111
  recomputeAllContext: writable({
124
112
  loading: false,
125
113
  componentNumber: 0,
@@ -215,7 +203,7 @@ $: maxRow = maxHeight($appStore.grid, appHeight, $breakpoint);
215
203
  <h2 class="truncate">{summary}</h2>
216
204
  <RecomputeAllComponents />
217
205
  <div class="text-2xs text-secondary">
218
- {author != '' ? `on behalf of ${author}` : ''}
206
+ {policy.on_behalf_of ? `on behalf of ${policy.on_behalf_of_email}` : ''}
219
207
  </div>
220
208
  </div>
221
209
  </div>
@@ -1,16 +1,18 @@
1
1
  import { SvelteComponent } from "svelte";
2
2
  import { type Writable } from 'svelte/store';
3
3
  import type { App, EditorBreakpoint } from '../types';
4
+ import type { Policy } from '../../../gen';
4
5
  declare const __propDef: {
5
6
  props: {
6
7
  [x: string]: any;
7
8
  app: App;
8
9
  appPath?: string | undefined;
9
10
  breakpoint?: Writable<EditorBreakpoint> | undefined;
10
- author: string;
11
+ policy?: Policy | undefined;
11
12
  summary?: string | undefined;
12
13
  workspace?: string | undefined;
13
14
  isEditor?: boolean | undefined;
15
+ context: Record<string, any>;
14
16
  noBackend?: boolean | undefined;
15
17
  isLocked?: boolean | undefined;
16
18
  hideRefreshBar?: boolean | undefined;
@@ -165,7 +165,7 @@ loadVersions();
165
165
  </Button>
166
166
  </div>
167
167
  </div>
168
- <AppPreview author="" noBackend app={selected.value} />
168
+ <AppPreview noBackend app={selected.value} context={{}} />
169
169
  {:else}
170
170
  <Skeleton layout={[[40]]} />
171
171
  {/if}
@@ -61,27 +61,27 @@ let resultDrawerOpen = false;
61
61
  duration={testJob?.['duration_ms']}
62
62
  mem={testJob?.['mem_peak']}
63
63
  content={testJob?.logs}
64
- isLoading={testIsLoading}
64
+ isLoading={testIsLoading && testJob?.['running'] == false}
65
65
  tag={testJob?.tag}
66
66
  />
67
67
  {/if}
68
68
  </Pane>
69
69
  <Pane size={frontendJob ? 70 : 50} minSize={10} class="text-sm text-tertiary">
70
70
  {#if frontendJob}
71
- <pre class="overflow-x-auto break-words relative h-full px-2 pt-2"
72
- ><DisplayResult bind:drawerOpen={resultDrawerOpen} result={frontendJob} /></pre
73
- >
71
+ <div class="break-words relative h-full px-1">
72
+ <DisplayResult bind:drawerOpen={resultDrawerOpen} result={frontendJob} />
73
+ </div>
74
74
  {:else if testJob != undefined && 'result' in testJob && testJob.result != undefined}
75
- <pre class="overflow-x-auto break-words relative h-full px-2 pt-2"
76
- ><DisplayResult
75
+ <div class="break-words relative h-full px-1">
76
+ <DisplayResult
77
77
  bind:drawerOpen={resultDrawerOpen}
78
78
  workspaceId={testJob?.workspace_id}
79
79
  jobId={testJob?.id}
80
80
  result={testJob.result}
81
- /></pre
81
+ /></div
82
82
  >
83
83
  {:else}
84
- <div class="p-2">
84
+ <div class="px-1 pt-1">
85
85
  {#if testIsLoading}
86
86
  <Loader2 class="animate-spin" />
87
87
  {:else}
@@ -255,7 +255,7 @@ async function inferSuggestions(code) {
255
255
 
256
256
  <!-- {inlineScript.content} -->
257
257
 
258
- <div class="border-y h-full">
258
+ <div class="border-y h-full w-full">
259
259
  {#if !drawerIsOpen}
260
260
  {#if inlineScript.language != 'frontend'}
261
261
  <Editor
@@ -304,7 +304,7 @@ async function inferSuggestions(code) {
304
304
  {:else}
305
305
  <SimpleEditor
306
306
  bind:this={simpleEditor}
307
- class="h-full"
307
+ class="h-full max-w-full"
308
308
  small
309
309
  {extraLib}
310
310
  bind:code={inlineScript.content}
@@ -14,6 +14,7 @@ $: if (componentInput.fieldType == 'template' && componentInput.type == 'static'
14
14
  componentInput['connections'] = [{ componentId: 'ctx', id: 'email' }];
15
15
  }
16
16
  function applyConnection(connection) {
17
+ console.log(connection);
17
18
  const expr = `${connection.componentId}.${connection.path}`;
18
19
  //@ts-ignore
19
20
  componentInput = {
@@ -21,7 +22,10 @@ function applyConnection(connection) {
21
22
  type: 'evalv2',
22
23
  expr: expr,
23
24
  connections: [
24
- { componentId: connection.componentId, id: connection.path.split('.')[0].split('[')[0] }
25
+ {
26
+ componentId: connection.componentId,
27
+ id: connection?.path?.split('.')?.[0]?.split('[')?.[0]
28
+ }
25
29
  ]
26
30
  };
27
31
  evalV2editor?.setCode(expr);
@@ -4,6 +4,7 @@ import Toggle from '../../../Toggle.svelte';
4
4
  import InputsSpecsEditor from './InputsSpecsEditor.svelte';
5
5
  import GroupManagementDrawer from '../componentsPanel/GroupManagementDrawer.svelte';
6
6
  import { Plus } from 'lucide-svelte';
7
+ import { Alert } from '../../../common';
7
8
  export let groupFields;
8
9
  export let item;
9
10
  let groupManagementDrawer = undefined;
@@ -39,7 +40,10 @@ function addField(name) {
39
40
  right: 'container is a component group',
40
41
  rightTooltip: `Group fields allow inner components to depend on the group fields which make the container a
41
42
  group of component that is encapsulated. Inside the group, it is possible to retrieve the values
42
- using \`group.<x />\` where x is the group field name`
43
+ using \`group.x\` where x is the group field name.
44
+
45
+ Group fields are mutable by frontend scripts, so \`group.x = 42\` can be used to set the value inside the group and
46
+ \`a.group.x = 42\` outside of it.`
43
47
  }}
44
48
  />
45
49
  </div>
@@ -106,6 +110,14 @@ function addField(name) {
106
110
  />
107
111
  </div>
108
112
  </div>
113
+
114
+ <div class="mt-2" />
115
+ <Alert size="xs" title="Group fields are mutable" type="info">
116
+ You may set the value of a group field in a frontend script within the group using: <code
117
+ >group.x = 42</code
118
+ >
119
+ and externally using <code>{item.id}.group.x = 42</code>
120
+ </Alert>
109
121
  </PanelSection>
110
122
  {/if}
111
123
 
@@ -1,6 +1,6 @@
1
1
  /// <reference types="svelte" />
2
2
  import type { Schema } from '../../common';
3
- import type { Preview } from '../../gen';
3
+ import type { Policy, Preview } from '../../gen';
4
4
  import type { History } from '../../history';
5
5
  import type { Writable } from 'svelte/store';
6
6
  import type { AppComponent, PresetComponentConfig, RecomputeOthersSource, components } from './editor/component/components';
@@ -127,7 +127,10 @@ export type ListInputs = {
127
127
  set: (id: string, value: any) => void;
128
128
  remove: (id: string) => void;
129
129
  };
130
- export type GroupContext = Writable<Record<string, any>>;
130
+ export type GroupContext = {
131
+ id: string;
132
+ context: Writable<Record<string, any>>;
133
+ };
131
134
  export type AppViewerContext = {
132
135
  worldStore: Writable<World>;
133
136
  app: Writable<App>;
@@ -198,6 +201,7 @@ export type AppViewerContext = {
198
201
  showToast?: (message: string, error?: boolean) => void;
199
202
  recompute?: () => void;
200
203
  askNewResource?: () => void;
204
+ setGroupValue?: (key: string, value: any) => void;
201
205
  }>>;
202
206
  hoverStore: Writable<string | undefined>;
203
207
  allIdsInPath: Writable<string[]>;
@@ -207,6 +211,7 @@ export type AppViewerContext = {
207
211
  debuggingComponents: Writable<Record<string, number>>;
208
212
  replaceStateFn?: ((url: string) => void) | undefined;
209
213
  gotoFn?: ((url: string, opt?: Record<string, any> | undefined) => void) | undefined;
214
+ policy: Policy;
210
215
  recomputeAllContext: Writable<{
211
216
  onClick?: () => void;
212
217
  componentNumber?: number | undefined;