windmill-components 1.504.0 → 1.504.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 (87) hide show
  1. package/package/components/AppWrapper.svelte +0 -3
  2. package/package/components/{DBManagerDrawerButton.svelte → DBManagerDrawer.svelte} +38 -38
  3. package/package/components/DBManagerDrawer.svelte.d.ts +7 -0
  4. package/package/components/DarkModeObserver.svelte +30 -16
  5. package/package/components/DarkModeObserver.svelte.d.ts +9 -4
  6. package/package/components/Dev.svelte +1 -1
  7. package/package/components/EditorBar.svelte +108 -67
  8. package/package/components/FlowBuilder.svelte +2 -3
  9. package/package/components/FlowStatusViewerInner.svelte +19 -0
  10. package/package/components/FlowWrapper.svelte +0 -3
  11. package/package/components/ItemPicker.svelte +1 -1
  12. package/package/components/ItemPicker.svelte.d.ts +1 -1
  13. package/package/components/MoveDrawer.svelte.d.ts +2 -2
  14. package/package/components/ResourcePicker.svelte +11 -4
  15. package/package/components/S3FilePicker.svelte +10 -3
  16. package/package/components/S3FilePicker.svelte.d.ts +6 -4
  17. package/package/components/ScriptBuilder.svelte +3 -1
  18. package/package/components/ScriptEditor.svelte +31 -3
  19. package/package/components/ScriptEditor.svelte.d.ts +3 -1
  20. package/package/components/ScriptWrapper.svelte +0 -3
  21. package/package/components/ShareModal.svelte.d.ts +1 -1
  22. package/package/components/apps/editor/AppEditor.svelte +1 -1
  23. package/package/components/apps/editor/AppEditorHeader.svelte +1 -1
  24. package/package/components/apps/editor/GridEditor.svelte +1 -1
  25. package/package/components/apps/editor/SubGridEditor.svelte +1 -1
  26. package/package/components/apps/editor/component/componentCallbacks.svelte.js +1 -1
  27. package/package/components/apps/editor/componentsPanel/ComponentList.svelte +1 -1
  28. package/package/components/apps/editor/settingsPanel/ComponentPanel.svelte +1 -1
  29. package/package/components/apps/editor/settingsPanel/DeleteComponent.svelte +1 -1
  30. package/package/components/apps/types.d.ts +2 -2
  31. package/package/components/assets/AssetsDropdownButton.svelte +178 -0
  32. package/package/components/assets/AssetsDropdownButton.svelte.d.ts +16 -0
  33. package/package/components/assets/AssetsUsageDrawer.svelte +43 -0
  34. package/package/components/assets/AssetsUsageDrawer.svelte.d.ts +12 -0
  35. package/package/components/assets/lib.d.ts +16 -0
  36. package/package/components/assets/lib.js +60 -0
  37. package/package/components/auditLogs/AuditLogsFilters.svelte.d.ts +1 -1
  38. package/package/components/common/OnChange.svelte +18 -0
  39. package/package/components/common/OnChange.svelte.d.ts +21 -0
  40. package/package/components/common/button/UndoRedo.svelte +2 -2
  41. package/package/components/common/button/UndoRedo.svelte.d.ts +5 -6
  42. package/package/components/copilot/chat/script/core.js +4 -4
  43. package/package/components/custom_ui.d.ts +1 -0
  44. package/package/components/flows/content/FlowModuleComponent.svelte +20 -1
  45. package/package/components/flows/types.d.ts +15 -1
  46. package/package/components/graph/FlowGraphV2.svelte +111 -8
  47. package/package/components/graph/FlowGraphV2.svelte.d.ts +2 -0
  48. package/package/components/graph/graphBuilder.svelte.d.ts +14 -1
  49. package/package/components/graph/renderers/edges/BaseEdge.svelte +10 -1
  50. package/package/components/graph/renderers/edges/EmptyEdge.svelte +7 -1
  51. package/package/components/graph/renderers/edges/EmptyEdge.svelte.d.ts +3 -0
  52. package/package/components/graph/renderers/nodes/AssetNode.svelte +257 -0
  53. package/package/components/graph/renderers/nodes/AssetNode.svelte.d.ts +21 -0
  54. package/package/components/graph/renderers/nodes/AssetsOverflowedNode.svelte +55 -0
  55. package/package/components/graph/renderers/nodes/AssetsOverflowedNode.svelte.d.ts +7 -0
  56. package/package/components/icons/AssetGenericIcon.svelte +16 -0
  57. package/package/components/icons/AssetGenericIcon.svelte.d.ts +10 -0
  58. package/package/components/icons/AssetResIcon.svelte +32 -0
  59. package/package/components/icons/AssetResIcon.svelte.d.ts +9 -0
  60. package/package/components/icons/AssetS3Icon.svelte +30 -0
  61. package/package/components/icons/AssetS3Icon.svelte.d.ts +9 -0
  62. package/package/components/icons/AssetVarIcon.svelte +31 -0
  63. package/package/components/icons/AssetVarIcon.svelte.d.ts +9 -0
  64. package/package/components/meltComponents/Popover.svelte +2 -0
  65. package/package/components/meltComponents/Popover.svelte.d.ts +2 -0
  66. package/package/components/schema/EditableSchemaSdkWrapper.svelte +1 -4
  67. package/package/components/script_builder.d.ts +2 -0
  68. package/package/components/settings/WorkspaceOperatorSettings.svelte +20 -17
  69. package/package/components/sidebar/OperatorMenu.svelte +5 -0
  70. package/package/components/sidebar/SidebarContent.svelte +9 -1
  71. package/package/components/tutorials/app/AppTutorial.svelte +1 -1
  72. package/package/components/tutorials/app/ConnectionTutorial.svelte +1 -1
  73. package/package/gen/schemas.gen.d.ts +69 -1
  74. package/package/gen/schemas.gen.js +69 -1
  75. package/package/gen/services.gen.d.ts +20 -1
  76. package/package/gen/services.gen.js +37 -0
  77. package/package/gen/types.gen.d.ts +103 -0
  78. package/package/history.svelte.d.ts +2 -2
  79. package/package/history.svelte.js +6 -6
  80. package/package/infer.d.ts +2 -0
  81. package/package/infer.js +31 -5
  82. package/package/svelte5Utils.svelte.d.ts +2 -1
  83. package/package/svelte5Utils.svelte.js +3 -2
  84. package/package/utils.d.ts +12 -0
  85. package/package/utils.js +22 -0
  86. package/package.json +5 -5
  87. package/package/components/DBManagerDrawerButton.svelte.d.ts +0 -9
@@ -0,0 +1,257 @@
1
+ <script module lang="ts">export const NODE_WITH_READ_ASSET_Y_OFFSET = 45;
2
+ export const NODE_WITH_WRITE_ASSET_Y_OFFSET = 45;
3
+ export const READ_ASSET_Y_OFFSET = -45;
4
+ export const WRITE_ASSET_Y_OFFSET = 64;
5
+ export const assetDisplaysAsInputInFlowGraph = (a) => !a.access_type || a.access_type === 'r' || a.access_type === 'rw';
6
+ export const assetDisplaysAsOutputInFlowGraph = (a) => a.access_type === 'w' || a.access_type === 'rw';
7
+ let computeAssetNodesCache;
8
+ export function computeAssetNodes(nodes, edges, assetsMap, extraData) {
9
+ if (nodes === computeAssetNodesCache?.[0] && deepEqual(assetsMap, computeAssetNodesCache?.[1]))
10
+ return computeAssetNodesCache[2];
11
+ const MAX_ASSET_ROW_WIDTH = 300;
12
+ const ASSETS_OVERFLOWED_NODE_WIDTH = 25;
13
+ const allAssetNodes = [];
14
+ const allAssetEdges = [];
15
+ const yPosMap = {};
16
+ for (const node of nodes) {
17
+ const assets = assetsMap?.[node.id] ?? [];
18
+ // Each asset can be displayed at the top and bottom
19
+ // i.e once (R or W) or twice (RW)
20
+ const inputAssets = assets.filter(assetDisplaysAsInputInFlowGraph);
21
+ const outputAssets = assets.filter(assetDisplaysAsOutputInFlowGraph);
22
+ const displayedInputAssets = inputAssets.slice(0, 3);
23
+ const displayedOutputAssets = outputAssets.slice(0, 3);
24
+ const overflowedInputAssets = inputAssets.slice(3);
25
+ const overflowedOutputAssets = outputAssets.slice(3);
26
+ // This allows calculating which nodes to offset on the y axis to
27
+ // make space for the asset nodes
28
+ if (inputAssets.length || outputAssets.length)
29
+ yPosMap[node.position.y] = yPosMap[node.position.y] ?? {};
30
+ if (inputAssets.length)
31
+ yPosMap[node.position.y].r = true;
32
+ if (outputAssets.length)
33
+ yPosMap[node.position.y].w = true;
34
+ // All asset nodes displayed on top
35
+ const inputAssetNodes = displayedInputAssets.map((asset, i) => {
36
+ let inputAssetXGap = 12;
37
+ let inputAssetWidth = 150;
38
+ const targetRowW = MAX_ASSET_ROW_WIDTH -
39
+ (overflowedInputAssets.length ? ASSETS_OVERFLOWED_NODE_WIDTH + inputAssetXGap / 2 : 0);
40
+ let totalInputRowWidth = () => inputAssetWidth * displayedInputAssets.length +
41
+ inputAssetXGap * (displayedInputAssets.length - 1);
42
+ if (totalInputRowWidth() > MAX_ASSET_ROW_WIDTH) {
43
+ const mult = targetRowW / totalInputRowWidth();
44
+ inputAssetWidth = inputAssetWidth * mult;
45
+ inputAssetXGap = inputAssetXGap * mult;
46
+ }
47
+ return {
48
+ type: 'asset',
49
+ parentId: node.id,
50
+ data: { asset },
51
+ id: `${node.id}-asset-in-${asset.kind}-${asset.path}`,
52
+ width: inputAssetWidth,
53
+ position: {
54
+ x: displayedInputAssets.length === 1
55
+ ? (NODE.width - inputAssetWidth) / 2 - 10 // Ensure we see the edge
56
+ : (inputAssetWidth + inputAssetXGap) * (i - displayedInputAssets.length / 2) +
57
+ (NODE.width + inputAssetXGap) / 2 +
58
+ (overflowedInputAssets.length
59
+ ? (-ASSETS_OVERFLOWED_NODE_WIDTH - inputAssetXGap) / 2
60
+ : 0),
61
+ y: READ_ASSET_Y_OFFSET
62
+ }
63
+ };
64
+ });
65
+ // All asset nodes displayed on the bottom
66
+ const outputAssetNodes = displayedOutputAssets.map((asset, i) => {
67
+ let outputAssetXGap = 12;
68
+ let outputAssetWidth = 150;
69
+ const targetRowW = MAX_ASSET_ROW_WIDTH -
70
+ (overflowedOutputAssets.length ? ASSETS_OVERFLOWED_NODE_WIDTH + outputAssetXGap / 2 : 0);
71
+ let totalOutputRowWidth = () => outputAssetWidth * displayedOutputAssets.length +
72
+ outputAssetXGap * (displayedOutputAssets.length - 1);
73
+ if (totalOutputRowWidth() > MAX_ASSET_ROW_WIDTH) {
74
+ const mult = targetRowW / totalOutputRowWidth();
75
+ outputAssetWidth = outputAssetWidth * mult;
76
+ outputAssetXGap = outputAssetXGap * mult;
77
+ }
78
+ return {
79
+ type: 'asset',
80
+ parentId: node.id,
81
+ data: { asset },
82
+ id: `${node.id}-asset-out-${asset.kind}-${asset.path}`,
83
+ width: outputAssetWidth,
84
+ position: {
85
+ x: displayedOutputAssets.length === 1
86
+ ? (NODE.width - outputAssetWidth) / 2 - 10 // Ensure we see the edge
87
+ : (outputAssetWidth + outputAssetXGap) * (i - displayedOutputAssets.length / 2) +
88
+ (NODE.width + outputAssetXGap) / 2 +
89
+ (overflowedOutputAssets.length
90
+ ? (-ASSETS_OVERFLOWED_NODE_WIDTH - outputAssetXGap) / 2
91
+ : 0),
92
+ y: WRITE_ASSET_Y_OFFSET
93
+ }
94
+ };
95
+ });
96
+ const inputAssetEdges = inputAssetNodes?.map((n) => ({
97
+ id: `${n.id}-edge`,
98
+ source: n.id ?? '',
99
+ target: n.parentId ?? '',
100
+ type: 'empty',
101
+ data: { class: '!opacity-35 dark:!opacity-20' }
102
+ }));
103
+ const outputAssetEdges = outputAssetNodes?.map((n) => ({
104
+ id: `${n.id}-edge`,
105
+ source: n.parentId ?? '',
106
+ target: n.id ?? '',
107
+ type: 'empty',
108
+ data: { class: '!opacity-35 dark:!opacity-20' }
109
+ }));
110
+ allAssetEdges.push(...(outputAssetEdges ?? []), ...(inputAssetEdges ?? []));
111
+ allAssetNodes.push(...(inputAssetNodes ?? []), ...(outputAssetNodes ?? []));
112
+ // If there are more than 3 assets, we create an overflow node
113
+ if (overflowedInputAssets.length)
114
+ allAssetNodes.push({
115
+ type: 'assetsOverflowed',
116
+ data: { overflowedAssets: overflowedInputAssets },
117
+ id: `${node.id}-assets-overflowed-in`,
118
+ parentId: node.id,
119
+ width: ASSETS_OVERFLOWED_NODE_WIDTH,
120
+ position: {
121
+ x: MAX_ASSET_ROW_WIDTH - ASSETS_OVERFLOWED_NODE_WIDTH - 14,
122
+ y: READ_ASSET_Y_OFFSET
123
+ }
124
+ });
125
+ allAssetEdges.push({
126
+ id: `${node.id}-assets-overflowed-in-edge`,
127
+ source: `${node.id}-assets-overflowed-in`,
128
+ target: node.id,
129
+ type: 'empty',
130
+ data: { class: '!opacity-35 dark:!opacity-20' }
131
+ });
132
+ if (overflowedOutputAssets.length)
133
+ allAssetNodes.push({
134
+ type: 'assetsOverflowed',
135
+ data: { overflowedAssets: overflowedOutputAssets },
136
+ id: `${node.id}-assets-overflowed-out`,
137
+ parentId: node.id,
138
+ width: ASSETS_OVERFLOWED_NODE_WIDTH,
139
+ position: {
140
+ x: MAX_ASSET_ROW_WIDTH - ASSETS_OVERFLOWED_NODE_WIDTH - 14,
141
+ y: WRITE_ASSET_Y_OFFSET
142
+ }
143
+ });
144
+ allAssetEdges.push({
145
+ id: `${node.id}-assets-overflowed-out-edge`,
146
+ source: node.id,
147
+ target: `${node.id}-assets-overflowed-out`,
148
+ type: 'empty',
149
+ data: { class: '!opacity-35 dark:!opacity-25' }
150
+ });
151
+ }
152
+ // Shift all nodes to make space for the new asset nodes
153
+ const sortedNewNodes = clone(nodes.sort((a, b) => a.position.y - b.position.y));
154
+ let currentYOffset = 0;
155
+ let prevYPos = NaN;
156
+ for (const node of sortedNewNodes) {
157
+ if (node.position.y !== prevYPos) {
158
+ if (yPosMap[prevYPos]?.w)
159
+ currentYOffset += NODE_WITH_WRITE_ASSET_Y_OFFSET;
160
+ if (yPosMap[node.position.y]?.r)
161
+ currentYOffset += NODE_WITH_READ_ASSET_Y_OFFSET;
162
+ prevYPos = node.position.y;
163
+ }
164
+ node.position.y += currentYOffset;
165
+ }
166
+ let ret = [
167
+ [...sortedNewNodes, ...allAssetNodes],
168
+ [...edges, ...allAssetEdges]
169
+ ];
170
+ computeAssetNodesCache = [nodes, clone(assetsMap), ret];
171
+ return ret;
172
+ }
173
+ </script>
174
+
175
+ <script lang="ts">import NodeWrapper from './NodeWrapper.svelte';
176
+ import { AlertTriangle } from 'lucide-svelte';
177
+ import { assetEq, formatAssetKind } from '../../../assets/lib';
178
+ import { twMerge } from 'tailwind-merge';
179
+ import { getContext } from 'svelte';
180
+ import ExploreAssetButton, { assetCanBeExplored } from '../../../../../routes/(root)/(logged)/assets/ExploreAssetButton.svelte';
181
+ import { Tooltip } from '../../../meltComponents';
182
+ import { clone, pluralize } from '../../../../utils';
183
+ import AssetGenericIcon from '../../../icons/AssetGenericIcon.svelte';
184
+ import { deepEqual } from 'fast-equals';
185
+ import { NODE } from '../../util';
186
+ import { userStore } from '../../../../stores';
187
+ const flowGraphAssetsCtx = getContext('FlowGraphAssetContext');
188
+ const usageCount = $derived(Object.values(flowGraphAssetsCtx.val.assetsMap ?? {})
189
+ .flat()
190
+ .filter((asset) => assetEq(asset, data.asset)).length);
191
+ let { data } = $props();
192
+ const isSelected = $derived(assetEq(flowGraphAssetsCtx.val.selectedAsset, data.asset));
193
+ const cachedResourceMetadata = $derived(flowGraphAssetsCtx.val.resourceMetadataCache[data.asset.path]);
194
+ </script>
195
+
196
+ <NodeWrapper>
197
+ {#snippet children({ darkMode })}
198
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
199
+ <Tooltip>
200
+ <div
201
+ class={twMerge(
202
+ 'bg-surface h-6 flex items-center gap-1.5 rounded-sm text-tertiary border overflow-clip',
203
+ isSelected ? 'bg-surface-secondary !border-surface-inverse' : 'border-transparent'
204
+ )}
205
+ onmouseenter={() => (flowGraphAssetsCtx.val.selectedAsset = data.asset)}
206
+ onmouseleave={() => (flowGraphAssetsCtx.val.selectedAsset = undefined)}
207
+ >
208
+ <AssetGenericIcon
209
+ assetKind={data.asset.kind}
210
+ fill={''}
211
+ class="shrink-0 ml-1 fill-tertiary stroke-tertiary"
212
+ size="16px"
213
+ />
214
+ <span class="text-3xs truncate flex-1">
215
+ {data.asset.path}
216
+ </span>
217
+ {#if data.asset.kind === 'resource' && cachedResourceMetadata === undefined}
218
+ <Tooltip class={'pr-1 flex items-center justify-center'}>
219
+ <AlertTriangle size={16} class="text-orange-500" />
220
+ <svelte:fragment slot="text">Could not find resource</svelte:fragment>
221
+ </Tooltip>
222
+ {:else if isSelected && assetCanBeExplored(data.asset, cachedResourceMetadata) && !$userStore?.operator}
223
+ <ExploreAssetButton
224
+ btnClasses="rounded-none"
225
+ asset={data.asset}
226
+ noText
227
+ buttonVariant="contained"
228
+ s3FilePicker={flowGraphAssetsCtx.val.s3FilePicker}
229
+ dbManagerDrawer={flowGraphAssetsCtx.val.dbManagerDrawer}
230
+ _resourceMetadata={cachedResourceMetadata}
231
+ />
232
+ {/if}
233
+ </div>
234
+ <svelte:fragment slot="text">
235
+ Used in {pluralize(usageCount, 'step')}<br />
236
+ <a
237
+ href={undefined}
238
+ class={twMerge(
239
+ 'text-xs',
240
+ data.asset.kind === 'resource'
241
+ ? 'text-blue-400 cursor-pointer'
242
+ : 'dark:text-tertiary text-tertiary-inverse'
243
+ )}
244
+ onclick={() => {
245
+ if (data.asset.kind === 'resource')
246
+ flowGraphAssetsCtx.val.resourceEditorDrawer?.initEdit(data.asset.path)
247
+ }}
248
+ >
249
+ {data.asset.path}
250
+ </a><br />
251
+ <span class="dark:text-tertiary text-tertiary-inverse text-xs"
252
+ >{formatAssetKind({ ...data.asset, metadata: cachedResourceMetadata })}</span
253
+ >
254
+ </svelte:fragment>
255
+ </Tooltip>
256
+ {/snippet}
257
+ </NodeWrapper>
@@ -0,0 +1,21 @@
1
+ export declare const NODE_WITH_READ_ASSET_Y_OFFSET = 45;
2
+ export declare const NODE_WITH_WRITE_ASSET_Y_OFFSET = 45;
3
+ export declare const READ_ASSET_Y_OFFSET = -45;
4
+ export declare const WRITE_ASSET_Y_OFFSET = 64;
5
+ export declare const assetDisplaysAsInputInFlowGraph: (a: {
6
+ access_type?: AssetUsageAccessType;
7
+ }) => boolean;
8
+ export declare const assetDisplaysAsOutputInFlowGraph: (a: {
9
+ access_type?: AssetUsageAccessType;
10
+ }) => boolean;
11
+ export declare function computeAssetNodes(nodes: Node[], edges: Edge[], assetsMap: Record<string, AssetWithAccessType[]>, extraData: any): [Node[], Edge[]];
12
+ import type { AssetN } from '../../graphBuilder.svelte';
13
+ import { type AssetWithAccessType } from '../../../assets/lib';
14
+ import type { Edge, Node } from '@xyflow/svelte';
15
+ import type { AssetUsageAccessType } from '../../../../gen';
16
+ interface Props {
17
+ data: AssetN['data'];
18
+ }
19
+ declare const AssetNode: import("svelte").Component<Props, {}, "">;
20
+ type AssetNode = ReturnType<typeof AssetNode>;
21
+ export default AssetNode;
@@ -0,0 +1,55 @@
1
+ <!-- Displays as +n node instead of AssetNode when there are too many of themOverflowedAssetsNode -->
2
+
3
+ <script lang="ts">import { twMerge } from 'tailwind-merge';
4
+ import {} from '../../graphBuilder.svelte';
5
+ import NodeWrapper from './NodeWrapper.svelte';
6
+ import Popover from '../../../meltComponents/Popover.svelte';
7
+ import AssetNode from './AssetNode.svelte';
8
+ import { getContext } from 'svelte';
9
+ import { assetEq } from '../../../assets/lib';
10
+ let { data } = $props();
11
+ const flowGraphAssetsCtx = getContext('FlowGraphAssetContext');
12
+ let isOpen = $state(false);
13
+ let includesSelected = $derived(data.overflowedAssets.some((asset) => assetEq(flowGraphAssetsCtx.val.selectedAsset, asset)));
14
+ let wasOpenedBecauseOfExternalSelected = false;
15
+ $effect(() => {
16
+ if (includesSelected && !isOpen) {
17
+ isOpen = true;
18
+ wasOpenedBecauseOfExternalSelected = true;
19
+ }
20
+ if (wasOpenedBecauseOfExternalSelected && !includesSelected) {
21
+ isOpen = false;
22
+ wasOpenedBecauseOfExternalSelected = false;
23
+ }
24
+ });
25
+ </script>
26
+
27
+ <NodeWrapper>
28
+ {#snippet children({ darkMode })}
29
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
30
+ <Popover
31
+ portal={null}
32
+ usePointerDownOutside
33
+ bind:isOpen
34
+ class={twMerge(
35
+ '!w-full text-2xs font-normal bg-surface h-6 pr-0.5 flex justify-center items-center rounded-sm text-tertiary border',
36
+ includesSelected ? 'bg-surface-secondary border-surface-inverse' : 'border-transparent',
37
+ 'hover:bg-surface-secondary hover:border-surface-inverse active:opacity-55'
38
+ )}
39
+ placement="top"
40
+ >
41
+ <svelte:fragment slot="trigger">
42
+ +{data.overflowedAssets.length}
43
+ </svelte:fragment>
44
+ <svelte:fragment slot="content">
45
+ <ul>
46
+ {#each data.overflowedAssets as asset}
47
+ <li class="w-48">
48
+ <AssetNode data={{ asset }} />
49
+ </li>
50
+ {/each}
51
+ </ul>
52
+ </svelte:fragment>
53
+ </Popover>
54
+ {/snippet}
55
+ </NodeWrapper>
@@ -0,0 +1,7 @@
1
+ import { type AssetsOverflowedN } from '../../graphBuilder.svelte';
2
+ interface Props {
3
+ data: AssetsOverflowedN['data'];
4
+ }
5
+ declare const AssetsOverflowedNode: import("svelte").Component<Props, {}, "">;
6
+ type AssetsOverflowedNode = ReturnType<typeof AssetsOverflowedNode>;
7
+ export default AssetsOverflowedNode;
@@ -0,0 +1,16 @@
1
+ <script lang="ts">import { Pyramid } from 'lucide-svelte';
2
+ import AssetResIcon from './AssetResIcon.svelte';
3
+ import AssetS3Icon from './AssetS3Icon.svelte';
4
+ import AssetVarIcon from './AssetVarIcon.svelte';
5
+ let { assetKind, fill, size, class: className = '' } = $props();
6
+ </script>
7
+
8
+ {#if assetKind == 's3object'}
9
+ <AssetS3Icon {fill} width={size} height={size} class={className} />
10
+ {:else if assetKind == 'resource'}
11
+ <AssetResIcon {fill} width={size} height={size} class={className} />
12
+ {:else if assetKind == 'variable'}
13
+ <AssetVarIcon {fill} width={size} height={size} class={className} />
14
+ {:else}
15
+ <Pyramid {size} color={fill} class={'!fill-none ' + className} />
16
+ {/if}
@@ -0,0 +1,10 @@
1
+ import type { AssetKind } from '../assets/lib';
2
+ interface Props {
3
+ size?: string;
4
+ fill?: string;
5
+ assetKind: AssetKind;
6
+ class?: string;
7
+ }
8
+ declare const AssetGenericIcon: import("svelte").Component<Props, {}, "">;
9
+ type AssetGenericIcon = ReturnType<typeof AssetGenericIcon>;
10
+ export default AssetGenericIcon;
@@ -0,0 +1,32 @@
1
+ <script lang="ts">"use strict";
2
+ let { height = '24px', width = '24px', fill = 'black', class: className = '' } = $props();
3
+ </script>
4
+
5
+ <svg
6
+ {width}
7
+ {height}
8
+ class={className}
9
+ viewBox="0 0 22 22"
10
+ fill="none"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ >
13
+ <g clip-path="url(#clip0_1_2)">
14
+ <path
15
+ d="M11.0586 0.306641C11.3106 0.315989 11.5582 0.378277 11.7822 0.495117C12.0247 0.621655 12.2331 0.805033 12.3897 1.0293L12.3916 1.03223L16.5889 7.10059L15.2285 7.59473L11.6963 2.4873V8.87695L10.2959 9.38574V2.4873L1.75196 14.8457C1.72867 14.8796 1.71294 14.9186 1.70508 14.959C1.69725 14.9995 1.69803 15.0418 1.70704 15.082C1.71605 15.1222 1.73351 15.1601 1.75782 15.1934C1.78213 15.2266 1.81309 15.2546 1.84864 15.2754H1.84766L7.75001 18.6445V20.2568L1.14844 16.4883L1.14258 16.4844C0.940872 16.3666 0.765826 16.2081 0.627936 16.0195C0.489969 15.8308 0.391887 15.6158 0.340827 15.3877C0.289772 15.1595 0.286682 14.9229 0.331061 14.6934C0.375447 14.4641 0.466395 14.2462 0.598639 14.0537L0.599616 14.0518L9.59962 1.03223L9.60157 1.0293C9.75823 0.804889 9.9673 0.62167 10.21 0.495117C10.4336 0.378564 10.6802 0.316161 10.9316 0.306641C10.9528 0.304686 10.9744 0.299805 10.9961 0.299805C11.0172 0.299841 11.038 0.304797 11.0586 0.306641Z"
16
+ {fill}
17
+ stroke="none"
18
+ />
19
+ <path
20
+ d="M15 19.343L12.618 20.82C12.4314 20.936 12.2177 20.9973 12 20.9973C11.7823 20.9973 11.5686 20.936 11.382 20.82L9.582 19.7029C9.40485 19.5928 9.2582 19.4373 9.15623 19.2514C9.05427 19.0654 9.00045 18.8554 9 18.6417V16.6309C9.00045 16.4171 9.05427 16.2071 9.15623 16.0212C9.2582 15.8352 9.40485 15.6797 9.582 15.5696L12 14.0677M15 19.343V15.9296M15 19.343L17.382 20.82C17.5686 20.936 17.7823 20.9973 18 20.9973C18.2177 20.9973 18.4314 20.936 18.618 20.82L20.418 19.7029C20.5951 19.5928 20.7418 19.4373 20.8438 19.2514C20.9457 19.0654 20.9995 18.8554 21 18.6417V16.6309C20.9995 16.4171 20.9457 16.2071 20.8438 16.0212C20.7418 15.8352 20.5951 15.6797 20.418 15.5696L18 14.0677M15 15.9296L12 14.0677M15 15.9296L12 17.7914M15 15.9296L18 14.0677M15 15.9296L18 17.7914M15 15.9296V12.5162M12 14.0677V11.3556C12.0005 11.1419 12.0543 10.9319 12.1562 10.7459C12.2582 10.56 12.4049 10.4045 12.582 10.2944L14.382 9.17726C14.5686 9.06128 14.7823 9 15 9C15.2177 9 15.4314 9.06128 15.618 9.17726L17.418 10.2944C17.5951 10.4045 17.7418 10.56 17.8438 10.7459C17.9457 10.9319 17.9995 11.1419 18 11.3556V14.0677M12 17.7914L9.156 16.0227M12 17.7914V21M18 17.7914L20.844 16.0227M18 17.7914V21M15 12.5162L12.156 10.7474M15 12.5162L17.844 10.7474"
21
+ fill="none"
22
+ stroke={fill}
23
+ stroke-linecap="round"
24
+ stroke-linejoin="round"
25
+ />
26
+ </g>
27
+ <defs>
28
+ <clipPath id="clip0_1_2">
29
+ <rect width="22" height="22" fill="white" />
30
+ </clipPath>
31
+ </defs>
32
+ </svg>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ height?: string;
3
+ width?: string;
4
+ fill?: string;
5
+ class?: string;
6
+ }
7
+ declare const AssetResIcon: import("svelte").Component<Props, {}, "">;
8
+ type AssetResIcon = ReturnType<typeof AssetResIcon>;
9
+ export default AssetResIcon;
@@ -0,0 +1,30 @@
1
+ <script lang="ts">"use strict";
2
+ let { height = '24px', width = '24px', fill = 'black', class: className = '' } = $props();
3
+ </script>
4
+
5
+ <svg
6
+ {width}
7
+ {height}
8
+ class={className}
9
+ viewBox="0 0 22 22"
10
+ fill="none"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ >
13
+ <g clip-path="url(#clip0_1_2)">
14
+ <path
15
+ d="M11.0586 0.306641C11.3106 0.315965 11.5581 0.378249 11.7822 0.495117C12.0248 0.621658 12.2331 0.804989 12.3897 1.0293L12.3916 1.03223L19.9785 12H18.2774L11.6963 2.4873V12H10.2959V2.4873L1.75196 14.8457C1.72866 14.8796 1.71294 14.9186 1.70508 14.959C1.69725 14.9995 1.69803 15.0418 1.70704 15.082C1.71605 15.1222 1.73351 15.1601 1.75782 15.1934C1.78214 15.2266 1.81306 15.2546 1.84864 15.2754H1.84766L7.75001 18.6445V20.2568L1.14844 16.4883L1.14258 16.4844C0.940841 16.3665 0.765835 16.2081 0.627936 16.0195C0.489961 15.8308 0.391884 15.6158 0.340827 15.3877C0.289772 15.1595 0.286682 14.9229 0.331061 14.6934C0.375444 14.464 0.466389 14.2462 0.598639 14.0537L0.599616 14.0518L9.59962 1.03223L9.60157 1.0293C9.75823 0.80485 9.96727 0.621686 10.21 0.495117C10.4336 0.378516 10.6802 0.316163 10.9316 0.306641C10.9529 0.304697 10.9743 0.299805 10.9961 0.299805C11.0173 0.299812 11.0379 0.304792 11.0586 0.306641Z"
16
+ {fill}
17
+ stroke="none"
18
+ />
19
+ <path
20
+ d="M11.72 22.084C11.136 22.084 10.608 21.984 10.136 21.784C9.672 21.584 9.304 21.296 9.032 20.92C8.76 20.544 8.62 20.1 8.612 19.588H10.412C10.436 19.932 10.556 20.204 10.772 20.404C10.996 20.604 11.3 20.704 11.684 20.704C12.076 20.704 12.384 20.612 12.608 20.428C12.832 20.236 12.944 19.988 12.944 19.684C12.944 19.436 12.868 19.232 12.716 19.072C12.564 18.912 12.372 18.788 12.14 18.7C11.916 18.604 11.604 18.5 11.204 18.388C10.66 18.228 10.216 18.072 9.872 17.92C9.536 17.76 9.244 17.524 8.996 17.212C8.756 16.892 8.636 16.468 8.636 15.94C8.636 15.444 8.76 15.012 9.008 14.644C9.256 14.276 9.604 13.996 10.052 13.804C10.5 13.604 11.012 13.504 11.588 13.504C12.452 13.504 13.152 13.716 13.688 14.14C14.232 14.556 14.532 15.14 14.588 15.892H12.74C12.724 15.604 12.6 15.368 12.368 15.184C12.144 14.992 11.844 14.896 11.468 14.896C11.14 14.896 10.876 14.98 10.676 15.148C10.484 15.316 10.388 15.56 10.388 15.88C10.388 16.104 10.46 16.292 10.604 16.444C10.756 16.588 10.94 16.708 11.156 16.804C11.38 16.892 11.692 16.996 12.092 17.116C12.636 17.276 13.08 17.436 13.424 17.596C13.768 17.756 14.064 17.996 14.312 18.316C14.56 18.636 14.684 19.056 14.684 19.576C14.684 20.024 14.568 20.44 14.336 20.824C14.104 21.208 13.764 21.516 13.316 21.748C12.868 21.972 12.336 22.084 11.72 22.084ZM15.1325 15.556C15.1725 14.756 15.4525 14.14 15.9725 13.708C16.5005 13.268 17.1925 13.048 18.0485 13.048C18.6325 13.048 19.1325 13.152 19.5485 13.36C19.9645 13.56 20.2765 13.836 20.4845 14.188C20.7005 14.532 20.8085 14.924 20.8085 15.364C20.8085 15.868 20.6765 16.296 20.4125 16.648C20.1565 16.992 19.8485 17.224 19.4885 17.344V17.392C19.9525 17.536 20.3125 17.792 20.5685 18.16C20.8325 18.528 20.9645 19 20.9645 19.576C20.9645 20.056 20.8525 20.484 20.6285 20.86C20.4125 21.236 20.0885 21.532 19.6565 21.748C19.2325 21.956 18.7205 22.06 18.1205 22.06C17.2165 22.06 16.4805 21.832 15.9125 21.376C15.3445 20.92 15.0445 20.248 15.0125 19.36H16.6445C16.6605 19.752 16.7925 20.068 17.0405 20.308C17.2965 20.54 17.6445 20.656 18.0845 20.656C18.4925 20.656 18.8045 20.544 19.0205 20.32C19.2445 20.088 19.3565 19.792 19.3565 19.432C19.3565 18.952 19.2045 18.608 18.9005 18.4C18.5965 18.192 18.1245 18.088 17.4845 18.088H17.1365V16.708H17.4845C18.6205 16.708 19.1885 16.328 19.1885 15.568C19.1885 15.224 19.0845 14.956 18.8765 14.764C18.6765 14.572 18.3845 14.476 18.0005 14.476C17.6245 14.476 17.3325 14.58 17.1245 14.788C16.9245 14.988 16.8085 15.244 16.7765 15.556H15.1325Z"
21
+ {fill}
22
+ stroke="none"
23
+ />
24
+ </g>
25
+ <defs>
26
+ <clipPath id="clip0_1_2">
27
+ <rect width="22" height="22" fill="white" />
28
+ </clipPath>
29
+ </defs>
30
+ </svg>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ height?: string;
3
+ width?: string;
4
+ fill?: string;
5
+ class?: string;
6
+ }
7
+ declare const AssetS3Icon: import("svelte").Component<Props, {}, "">;
8
+ type AssetS3Icon = ReturnType<typeof AssetS3Icon>;
9
+ export default AssetS3Icon;
@@ -0,0 +1,31 @@
1
+ <script lang="ts">"use strict";
2
+ let { height = '24px', width = '24px', fill = 'black', class: className = '' } = $props();
3
+ </script>
4
+
5
+ <svg
6
+ {width}
7
+ {height}
8
+ class={className}
9
+ viewBox="0 0 22 22"
10
+ fill="none"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ >
13
+ <g clip-path="url(#clip0_1_2)">
14
+ <path
15
+ d="M11.0586 0.306641C11.3106 0.315989 11.5582 0.378277 11.7822 0.495117C12.0247 0.621655 12.2331 0.805033 12.3897 1.0293L12.3916 1.03223L17.9033 9H16.2022L11.6963 2.4873V20.0986L12.75 19.4961V21.1094L12.333 21.3477L12.332 21.3486C11.925 21.5804 11.4645 21.7021 10.9961 21.7021C10.5275 21.7021 10.0664 21.5806 9.65919 21.3486V21.3477L1.14844 16.4883L1.14258 16.4844C0.940872 16.3666 0.765826 16.2081 0.627936 16.0195C0.489969 15.8308 0.391887 15.6158 0.340827 15.3877C0.289772 15.1595 0.286682 14.9229 0.331061 14.6934C0.375447 14.4641 0.466395 14.2462 0.598639 14.0537L0.599616 14.0518L9.59962 1.03223L9.60157 1.0293C9.75823 0.804889 9.9673 0.62167 10.21 0.495117C10.4336 0.378564 10.6802 0.316161 10.9316 0.306641C10.9528 0.304686 10.9744 0.299805 10.9961 0.299805C11.0172 0.299841 11.038 0.304797 11.0586 0.306641ZM1.75196 14.8457C1.72867 14.8796 1.71294 14.9186 1.70508 14.959C1.69725 14.9995 1.69803 15.0418 1.70704 15.082C1.71605 15.1222 1.73351 15.1601 1.75782 15.1934C1.78213 15.2266 1.81309 15.2546 1.84864 15.2754H1.84766L10.2959 20.0986V2.4873L1.75196 14.8457Z"
16
+ {fill}
17
+ stroke="none"
18
+ />
19
+ <path
20
+ d="M18 10.8496C18.1472 10.8496 18.2858 10.9138 18.3857 11.0225C18.4851 11.1306 18.539 11.2749 18.5391 11.4229V11.9785H20.5928C20.7397 11.9786 20.8777 12.042 20.9775 12.1504C21.077 12.2586 21.1318 12.4037 21.1318 12.5518C21.1317 12.6997 21.0769 12.844 20.9775 12.9521C20.8777 13.0606 20.7397 13.124 20.5928 13.124H18.5391V15.9268H19.2959C19.9244 15.9268 20.5246 16.1988 20.9648 16.6777C21.4047 17.1563 21.6504 17.8029 21.6504 18.4746C21.6503 19.1463 21.4046 19.793 20.9648 20.2715C20.5246 20.7503 19.9243 21.0215 19.2959 21.0215H18.5391V21.5771C18.539 21.7251 18.4851 21.8694 18.3857 21.9775C18.2858 22.0862 18.1472 22.1504 18 22.1504C17.8528 22.1504 17.7142 22.0862 17.6143 21.9775C17.5149 21.8694 17.461 21.7251 17.4609 21.5771V21.0215H14.8887C14.7417 21.0214 14.6037 20.958 14.5039 20.8496C14.4044 20.7414 14.3496 20.5963 14.3496 20.4482C14.3497 20.3003 14.4045 20.156 14.5039 20.0479C14.6037 19.9394 14.7417 19.876 14.8887 19.876H17.4609V17.0732H16.7041C16.0756 17.0732 15.4754 16.8012 15.0352 16.3223C14.5953 15.8437 14.3496 15.1971 14.3496 14.5254C14.3497 13.8537 14.5954 13.207 15.0352 12.7285C15.4754 12.2497 16.0757 11.9785 16.7041 11.9785H17.4609V11.4229C17.461 11.2749 17.5149 11.1306 17.6143 11.0225C17.7142 10.9138 17.8528 10.8496 18 10.8496ZM18.5391 19.876H19.2959C19.63 19.876 19.9539 19.7313 20.1943 19.4697C20.4351 19.2078 20.5722 18.85 20.5723 18.4746C20.5723 18.0991 20.4352 17.7405 20.1943 17.4785C19.9539 17.217 19.63 17.0732 19.2959 17.0732H18.5391V19.876ZM16.7041 13.124C16.37 13.124 16.0461 13.2687 15.8057 13.5303C15.5649 13.7922 15.4278 14.15 15.4277 14.5254C15.4277 14.9009 15.5648 15.2595 15.8057 15.5215C16.0461 15.783 16.37 15.9268 16.7041 15.9268H17.4609V13.124H16.7041Z"
21
+ {fill}
22
+ stroke={fill}
23
+ stroke-width="0.3"
24
+ />
25
+ </g>
26
+ <defs>
27
+ <clipPath id="clip0_1_2">
28
+ <rect width="22" height="22" fill="white" />
29
+ </clipPath>
30
+ </defs>
31
+ </svg>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ height?: string;
3
+ width?: string;
4
+ fill?: string;
5
+ class?: string;
6
+ }
7
+ declare const AssetVarIcon: import("svelte").Component<Props, {}, "">;
8
+ type AssetVarIcon = ReturnType<typeof AssetVarIcon>;
9
+ export default AssetVarIcon;
@@ -30,12 +30,14 @@ export let extraProps = {};
30
30
  export let disabled = false;
31
31
  export let documentationLink = undefined;
32
32
  export let disableFocusTrap = false;
33
+ export let escapeBehavior = 'close';
33
34
  let fullScreen = false;
34
35
  const dispatch = createEventDispatcher();
35
36
  const { elements: { trigger, content, arrow, close: closeElement, overlay }, states, options: { closeOnOutsideClick: closeOnOutsideClickOption, positioning }, ids: { content: popoverId } } = createPopover({
36
37
  forceVisible: true,
37
38
  portal,
38
39
  disableFocusTrap,
40
+ escapeBehavior,
39
41
  onOpenChange: ({ curr, next }) => {
40
42
  if (curr != next) {
41
43
  dispatch('openChange', next);
@@ -1,5 +1,6 @@
1
1
  import type { Placement } from '@floating-ui/core';
2
2
  import type { FloatingConfig } from '@melt-ui/svelte/internal/actions/floating';
3
+ import type { EscapeBehaviorType } from '@melt-ui/svelte/internal/actions';
3
4
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
5
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
6
  $$bindings?: Bindings;
@@ -32,6 +33,7 @@ declare const Popover: $$__sveltets_2_IsomorphicComponent<{
32
33
  disabled?: boolean | undefined;
33
34
  documentationLink?: string | undefined | undefined;
34
35
  disableFocusTrap?: boolean | undefined;
36
+ escapeBehavior?: EscapeBehaviorType | undefined;
35
37
  isOpen?: boolean | undefined;
36
38
  close?: (() => void) | undefined;
37
39
  open?: (() => void) | undefined;
@@ -1,9 +1,6 @@
1
1
  <script lang="ts">import EditableSchemaWrapper from './EditableSchemaWrapper.svelte';
2
2
  let { schema: oldSchema, ...props } = $props();
3
3
  let schema = $state(oldSchema);
4
- $effect(() => {
5
- schema = oldSchema;
6
- });
7
4
  </script>
8
5
 
9
- <EditableSchemaWrapper {schema} {...props} />
6
+ <EditableSchemaWrapper {schema} {...props} />
@@ -1,4 +1,5 @@
1
1
  import type { NewScript } from '../gen';
2
+ import type { AssetWithAccessType } from './assets/lib';
2
3
  import type { ScriptBuilderWhitelabelCustomUi } from './custom_ui';
3
4
  import type { DiffDrawerI } from './diff_drawer';
4
5
  import type { ScriptBuilderFunctionExports } from './scriptBuilder';
@@ -7,6 +8,7 @@ import type { NewScriptWithDraftAndDraftTriggers, Trigger } from './triggers/uti
7
8
  export interface ScriptBuilderProps {
8
9
  script: NewScript & {
9
10
  draft_triggers?: Trigger[];
11
+ fallback_access_types?: AssetWithAccessType[];
10
12
  };
11
13
  disableAi?: boolean;
12
14
  fullyLoaded?: boolean;
@@ -15,6 +15,7 @@ let operatorWorkspaceSettings = $state({
15
15
  schedules: true,
16
16
  resources: true,
17
17
  variables: true,
18
+ assets: false,
18
19
  triggers: true,
19
20
  audit_logs: true,
20
21
  groups: true,
@@ -25,6 +26,7 @@ let originalSettings = $state({ ...untrack(() => operatorWorkspaceSettings) });
25
26
  let isChanged = $state(false);
26
27
  let currentWorkspace = $state(null);
27
28
  async function saveSettings() {
29
+ console.log('Saving operator settings:', operatorWorkspaceSettings);
28
30
  try {
29
31
  await WorkspaceService.updateOperatorSettings({
30
32
  workspace: $workspaceStore,
@@ -44,6 +46,7 @@ const descriptions = {
44
46
  schedules: { title: 'Schedules', description: 'View schedules' },
45
47
  resources: { title: 'Resources', description: 'View resources' },
46
48
  variables: { title: 'Variables', description: 'View variables' },
49
+ assets: { title: 'Assets', description: 'View assets' },
47
50
  triggers: { title: 'Triggers', description: 'View all triggers (HTTP, Websocket, Kafka)' },
48
51
  audit_logs: { title: 'Audit Logs', description: 'View audit logs' },
49
52
  groups: { title: 'Groups', description: 'View groups and group members' },
@@ -59,7 +62,10 @@ $effect(() => {
59
62
  workspace: $workspaceStore
60
63
  });
61
64
  if (settings.operator_settings !== null) {
62
- operatorWorkspaceSettings = settings.operator_settings ?? operatorWorkspaceSettings;
65
+ operatorWorkspaceSettings = {
66
+ ...operatorWorkspaceSettings,
67
+ ...(settings.operator_settings ?? {})
68
+ };
63
69
  originalSettings = { ...operatorWorkspaceSettings };
64
70
  }
65
71
  })();
@@ -68,21 +74,8 @@ $effect(() => {
68
74
  $effect(() => {
69
75
  isChanged = JSON.stringify(operatorWorkspaceSettings) !== JSON.stringify(originalSettings);
70
76
  });
71
- let enableAllState = $derived((() => {
72
- const values = Object.values(operatorWorkspaceSettings);
73
- if (values.every((v) => v === true))
74
- return 'true';
75
- if (values.every((v) => v === false))
76
- return 'false';
77
- return undefined;
78
- })());
79
- function toggleAllSettings(event) {
80
- const newValue = event.detail === true;
81
- Object.keys(operatorWorkspaceSettings).forEach((key) => {
82
- operatorWorkspaceSettings[key] = newValue;
83
- });
84
- operatorWorkspaceSettings = { ...operatorWorkspaceSettings };
85
- }
77
+ const allDisabled = $derived(Object.values(operatorWorkspaceSettings).every((value) => value === false));
78
+ const allEnabled = $derived(Object.values(operatorWorkspaceSettings).every((value) => value === true));
86
79
  </script>
87
80
 
88
81
  <div class="mt-6">
@@ -111,7 +104,17 @@ function toggleAllSettings(event) {
111
104
  <Cell head first>Section</Cell>
112
105
  <Cell head>Description</Cell>
113
106
  <Cell head last>
114
- <ToggleButtonGroup bind:selected={enableAllState} on:selected={toggleAllSettings}>
107
+ <ToggleButtonGroup
108
+ bind:selected={
109
+ () => (allDisabled ? 'false' : allEnabled ? 'true' : ''),
110
+ (v) => {
111
+ Object.keys(operatorWorkspaceSettings).forEach((key) => {
112
+ if (v === 'true') operatorWorkspaceSettings[key] = true
113
+ if (v === 'false') operatorWorkspaceSettings[key] = false
114
+ })
115
+ }
116
+ }
117
+ >
115
118
  {#snippet children({ item })}
116
119
  <ToggleButton
117
120
  icon={EyeIcon}
@@ -33,6 +33,11 @@ let secondMenuLinks = $derived([
33
33
  id: 'variables',
34
34
  href: `${base}/variables`
35
35
  },
36
+ {
37
+ label: 'Assets',
38
+ id: 'assets',
39
+ href: `${base}/assets`
40
+ },
36
41
  {
37
42
  label: 'Custom HTTP routes',
38
43
  id: 'triggers',