diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 511069631d..ad5a1e9c67 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -24,9 +24,10 @@ "@redocly/json-to-json-schema": "^0.0.1", "@scalar/openapi-parser": "^0.15.0", "@tanstack/svelte-table": "npm:tanstack-table-8-svelte-5@^0.1", + "@tutorlatin/svelte-tiny-virtual-list": "^3.0.2", "@windmill-labs/svelte-dnd-action": "^0.9.48", "@xterm/addon-fit": "^0.10.0", - "@xyflow/svelte": "^0.1.15", + "@xyflow/svelte": "^1.0.0", "ag-charts-community": "^9.0.1", "ag-charts-enterprise": "^9.0.1", "ag-grid-community": "^31.3.4", @@ -3076,11 +3077,11 @@ "integrity": "sha512-VqAAkydywPpkw63WQhPVKCD3SdwXuihCUVZbbiY3SfSTGQyHmwRoq27y4dmJdZuJwd5JIlQoMPyGvMbUPY0RKQ==" }, "node_modules/@svelte-put/shortcut": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-3.1.1.tgz", - "integrity": "sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-4.1.0.tgz", + "integrity": "sha512-wImNEIkbxAIWFqlfuhcbC+jRPDeRa/uJGIXHMEVVD+jqL9xCwWNnkGQJ6Qb2XVszuRLHlb8SGZDL3Io/h3vs8w==", "peerDependencies": { - "svelte": "^3.55.0 || ^4.0.0 || ^5.0.0" + "svelte": "^5.1.0" } }, "node_modules/@sveltejs/acorn-typescript": { @@ -3274,6 +3275,17 @@ "node": ">=10.13.0" } }, + "node_modules/@tutorlatin/svelte-tiny-virtual-list": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@tutorlatin/svelte-tiny-virtual-list/-/svelte-tiny-virtual-list-3.0.2.tgz", + "integrity": "sha512-Zts/GUrU6aPgmCrL6sGiD1EoiQxUU+r7Q2g9pckEUIDybNkluV2pEYWUsQLQ+28IJlSSHTopv0fhKs3qMUxqEw==", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "svelte": "^5.0.0" + } + }, "node_modules/@types/cookie": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", @@ -3871,22 +3883,21 @@ "peer": true }, "node_modules/@xyflow/svelte": { - "version": "0.1.39", - "resolved": "https://registry.npmjs.org/@xyflow/svelte/-/svelte-0.1.39.tgz", - "integrity": "sha512-QZ5mzNysvJeJW7DxmqI4Urhhef9tclqtPr7WAS5zQF5Gk6k9INwzey4CYNtEZo8XMj9H8lzgoJRmgMPnJEc1kw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@xyflow/svelte/-/svelte-1.0.2.tgz", + "integrity": "sha512-biNnzWRmKF/m7t7iY9wt3/KhoTyA8XGbS0Vix3HCL9vrULDF14R1VsMIOxKZib1GJfG+f1/cD2dQ9kplfPLs/Q==", "dependencies": { - "@svelte-put/shortcut": "3.1.1", - "@xyflow/system": "0.0.59", - "classcat": "^5.0.4" + "@svelte-put/shortcut": "^4.1.0", + "@xyflow/system": "0.0.61" }, "peerDependencies": { - "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" + "svelte": "^5.25.0" } }, "node_modules/@xyflow/system": { - "version": "0.0.59", - "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.59.tgz", - "integrity": "sha512-+xgqYhoBv5F10TQx0SiKZR/DcWtuxFYR+e/LluHb7DMtX4SsMDutZWEJ4da4fDco25jZxw5G9fOlmk7MWvYd5Q==", + "version": "0.0.61", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.61.tgz", + "integrity": "sha512-TsZG/Ez8dzxX6/Ol44LvFqVZsYvyz6dpDlAQZZk6hTL7JLGO5vN3dboRJqMwU8/Qtr5IEv5YBzojjAwIqW1HCA==", "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-selection": "^3.0.10", @@ -4710,11 +4721,6 @@ "consola": "^3.2.3" } }, - "node_modules/classcat": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", - "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==" - }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index bd849ce0b5..e2c05397ce 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -94,8 +94,8 @@ "@scalar/openapi-parser": "^0.15.0", "@tanstack/svelte-table": "npm:tanstack-table-8-svelte-5@^0.1", "@windmill-labs/svelte-dnd-action": "^0.9.48", + "@xyflow/svelte": "^1.0.0", "@xterm/addon-fit": "^0.10.0", - "@xyflow/svelte": "^0.1.15", "ag-charts-community": "^9.0.1", "ag-charts-enterprise": "^9.0.1", "ag-grid-community": "^31.3.4", @@ -135,6 +135,7 @@ "svelte-exmarkdown": "^5.0.0", "svelte-infinite-loading": "^1.4.0", "svelte-tiny-virtual-list": "^2.0.5", + "@tutorlatin/svelte-tiny-virtual-list": "^3.0.2", "tailwind-merge": "^1.13.2", "vscode": "npm:@codingame/monaco-vscode-extension-api@~16.1.1", "vscode-languageclient": "~9.0.1", diff --git a/frontend/src/lib/components/AddUser.svelte b/frontend/src/lib/components/AddUser.svelte index 6144d6e84b..3c1161471a 100644 --- a/frontend/src/lib/components/AddUser.svelte +++ b/frontend/src/lib/components/AddUser.svelte @@ -13,8 +13,8 @@ const dispatch = createEventDispatcher() - let email: string - let username: string + let email: string | undefined = $state() + let username: string | undefined = $state() function handleKeyUp(event: KeyboardEvent) { const key = event.key @@ -24,7 +24,7 @@ } } - let automateUsernameCreation = false + let automateUsernameCreation = $state(false) async function getAutomateUsernameCreationSetting() { automateUsernameCreation = ((await SettingService.getGlobal({ key: 'automate_username_creation' })) as any) ?? false @@ -35,17 +35,17 @@ await WorkspaceService.addUser({ workspace: $workspaceStore!, requestBody: { - email, + email: email!, username: automateUsernameCreation ? undefined : username, is_admin: selected == 'admin', operator: selected == 'operator' } }) sendUserToast(`Added ${email}`) - if (!(await UserService.existsEmail({ email }))) { + if (!(await UserService.existsEmail({ email: email! }))) { let isSuperadmin = $superadmin if (!isCloudHosted()) { - const emailCopy = email + const emailCopy = email! sendUserToast( `User ${email} is not registered yet on the instance. ${ !isSuperadmin @@ -62,7 +62,7 @@ goto('#superadmin-settings') } } - ] + ] : [] ) } @@ -70,47 +70,49 @@ dispatch('new') } - let selected: 'operator' | 'developer' | 'admin' = 'developer' + let selected: 'operator' | 'developer' | 'admin' = $state('developer') - + {#snippet trigger()} - - + {/snippet} + {#snippet content()}
Add a new user Email - + {#if !automateUsernameCreation} Username - + {/if} Role - - - - + + {#snippet children({ item })} + + + + {/snippet}
-
+ {/snippet}
diff --git a/frontend/src/lib/components/ApiConnectForm.svelte b/frontend/src/lib/components/ApiConnectForm.svelte index 53a6bd278e..80e452bbf3 100644 --- a/frontend/src/lib/components/ApiConnectForm.svelte +++ b/frontend/src/lib/components/ApiConnectForm.svelte @@ -23,31 +23,43 @@ import Popover from './meltComponents/Popover.svelte' import Button from './common/button/Button.svelte' import { Loader2, Github, RotateCw, Plus, Minus, Download } from 'lucide-svelte' - import { onDestroy } from 'svelte' + import { onDestroy, untrack } from 'svelte' - export let resourceType: string - export let resourceTypeInfo: ResourceType | undefined - export let args: Record | any = {} - export let linkedSecret: string | undefined = undefined - export let isValid = true - export let linkedSecretCandidates: string[] | undefined = undefined - export let description: string | undefined = undefined + interface Props { + resourceType: string + resourceTypeInfo: ResourceType | undefined + args?: Record | any + linkedSecret?: string | undefined + isValid?: boolean + linkedSecretCandidates?: string[] | undefined + description?: string | undefined + } - let schema = emptySchema() - let notFound = false + let { + resourceType, + resourceTypeInfo, + args = $bindable({}), + linkedSecret = $bindable(undefined), + isValid = $bindable(true), + linkedSecretCandidates = undefined, + description = $bindable(undefined) + }: Props = $props() - let supabaseWizard = false + let schema = $state(emptySchema()) + let notFound = $state(false) - let loadingGithubInstallations = false - let githubInstallations: GetGlobalConnectedRepositoriesResponse = [] - let workspaceGithubInstallations: GetGlobalConnectedRepositoriesResponse = [] - let selectedGHAppAccountId: string | undefined = undefined - let selectedGHAppRepository: string | undefined = undefined - let githubInstallationUrl: string | undefined = undefined + let supabaseWizard = $state(false) + + let loadingGithubInstallations = $state(false) + let githubInstallations: GetGlobalConnectedRepositoriesResponse = $state([]) + let workspaceGithubInstallations: GetGlobalConnectedRepositoriesResponse = $state([]) + let selectedGHAppAccountId: string | undefined = $state(undefined) + let selectedGHAppRepository: string | undefined = $state(undefined) + let githubInstallationUrl: string | undefined = $state(undefined) let installationCheckInterval: number | undefined = undefined - let isCheckingInstallation = false - let importJwt = '' - let githubAppPopover: { open: () => void; close: () => void } | null = null + let isCheckingInstallation = $state(false) + let importJwt = $state('') + let githubAppPopover: { open: () => void; close: () => void } | null = $state(null) async function loadGithubInstallations() { if (!$enterpriseLicense) return @@ -159,9 +171,6 @@ notFound = true } } - $: $workspaceStore && loadSchema() - - $: notFound && rawCode && parseJson() function parseJson() { try { @@ -173,13 +182,9 @@ error = e.message } } - let error = '' - let rawCode = '' - let viewJsonSchema = false - - $: rawCode && parseJson() - - $: textFileContent && parseTextFileContent() + let error = $state('') + let rawCode = $state('') + let viewJsonSchema = $state(false) function switchTab(asJson: boolean) { viewJsonSchema = asJson @@ -193,11 +198,8 @@ } } - $: resourceType == 'postgresql' && isSupabaseAvailable() - $: resourceType == 'git_repository' && $userStore?.is_admin && loadGithubInstallations() - - let connectionString = '' - let validConnectionString = true + let connectionString = $state('') + let validConnectionString = $state(true) function parseConnectionString(close: (_: any) => void) { const regex = /postgres(?:ql)?:\/\/(?[^:@]+)(?::(?[^@]+))?@(?[^:\/?]+)(?::(?\d+))?\/(?[^\?]+)?(?:\?.*sslmode=(?[^&]+))?/ @@ -241,8 +243,8 @@ close(null) } - let rawCodeEditor: SimpleEditor | undefined = undefined - let textFileContent: string + let rawCodeEditor: SimpleEditor | undefined = $state(undefined) + let textFileContent: string | undefined = $state(undefined) function parseTextFileContent() { args = { @@ -250,13 +252,6 @@ } } - $: githubInstallationsNotInWorkspace = githubInstallations.filter((installation) => { - return !workspaceGithubInstallations.some( - (workspaceInstallation) => - workspaceInstallation.installation_id === installation.installation_id - ) - }) - async function deleteInstallation(installation_id: number) { if (!$workspaceStore) { sendUserToast('Failed to delete installation', true) @@ -329,6 +324,34 @@ } } } + $effect(() => { + $workspaceStore && untrack(() => loadSchema()) + }) + $effect(() => { + notFound && rawCode && untrack(() => parseJson()) + }) + $effect(() => { + rawCode && untrack(() => parseJson()) + }) + $effect(() => { + textFileContent && untrack(() => parseTextFileContent()) + }) + $effect(() => { + resourceType == 'postgresql' && untrack(() => isSupabaseAvailable()) + }) + $effect(() => { + resourceType == 'git_repository' && + $userStore?.is_admin && + untrack(() => loadGithubInstallations()) + }) + let githubInstallationsNotInWorkspace = $derived( + githubInstallations.filter((installation) => { + return !workspaceGithubInstallations.some( + (workspaceInstallation) => + workspaceInstallation.installation_id === installation.installation_id + ) + }) + ) {#if !notFound} @@ -346,7 +369,7 @@ placement: 'bottom' }} > - + {#snippet trigger()} - - + {/snippet} + {#snippet content({ close })}
@@ -384,7 +407,7 @@ {/if}
- + {/snippet} {/if} {#if resourceType == 'postgresql' && supabaseWizard} @@ -419,7 +442,7 @@ }} disabled={!$enterpriseLicense || loadingGithubInstallations} > - + {#snippet trigger()} - - + {/snippet} + {#snippet content({ close })}
{#if workspaceGithubInstallations.length > 0} @@ -654,7 +677,7 @@
-
+ {/snippet} {:else} - {/if} - {#if isGoogleSignin} - - {:else} - - {/if} - + {#snippet actions()} +
+ {#if step > 1} + + {/if} + {#if isGoogleSignin} + + {:else} + + {/if} +
+ {/snippet} diff --git a/frontend/src/lib/components/AppConnectInner.svelte b/frontend/src/lib/components/AppConnectInner.svelte index 217d5981f3..5d9bbcbb29 100644 --- a/frontend/src/lib/components/AppConnectInner.svelte +++ b/frontend/src/lib/components/AppConnectInner.svelte @@ -1,4 +1,6 @@ {#if !express} a.localeCompare(b)) - .map((key) => ({ - key - })) + ? connects.map((key) => ({ + key + })) : undefined} bind:filteredItems={filteredConnects} f={(x) => x.key} /> a[0].localeCompare(b[0]))} + items={connectsManual} bind:filteredItems={filteredConnectsManual} f={(x) => x[0]} /> @@ -590,7 +614,7 @@

Scopes

import type { EnumType } from '$lib/common' - import AutoComplete from 'simple-svelte-autocomplete' import { createEventDispatcher } from 'svelte' - import { twMerge } from 'tailwind-merge' + import Select from './Select.svelte' - export let disabled: boolean - export let value: any - export let enum_: EnumType - export let autofocus: boolean | null - export let defaultValue: string | undefined - export let valid: boolean - export let create: boolean - export let required: boolean - export let enumLabels: Record | undefined = undefined + interface Props { + disabled: boolean + value: string | undefined + enum_: EnumType + autofocus: boolean | null + defaultValue: string | undefined + valid: boolean + create: boolean + enumLabels?: Record | undefined + } + + let { + disabled, + value = $bindable(), + enum_, + autofocus, + defaultValue, + valid, + create, + enumLabels = undefined + }: Props = $props() const dispatch = createEventDispatcher() - let customItems: string[] = [] + let customItems: string[] = $state([]) - function onCreate(newItem: string) { - console.log(newItem) - customItems = [...customItems, newItem] + let items = $derived.by(() => { + const l = [...(enum_ ? enum_ : []), ...customItems].map((item) => ({ + value: item, + label: enumLabels?.[item] ?? item + })) + if (create && filterText && l.every((i) => i.value !== filterText)) { + l.push({ value: filterText, label: `Add new: ${filterText}` }) + } + return l + }) - return newItem - } + let filterText = $state('')
- (enumLabels ? enumLabels[val] ?? val : val)} - bind:selectedItem={value} - inputClassName={twMerge( - 'bg-surface-secondary flex', - valid - ? '' - : 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100' - )} - value={value ?? defaultValue} - hideArrow={true} - dropdownClassName="!text-sm !py-2 !rounded-sm !border-gray-200 !border !shadow-md !bg-surface-primary" - className="w-full" - noInputStyles - onFocus={() => { - dispatch('focus') - }} - onBlur={() => { - dispatch('blur') - }} - {create} - {required} - {onCreate} + { + onkeydown={() => { ignoreValueUndefined = true }} class={valid @@ -545,6 +638,9 @@ outerDivClass={'dark:!border-gray-500 !border-gray-300'} {disabled} bind:selected={value} + onremove={(e) => { + if (Array.isArray(value)) value = value.filter((v) => v !== e.option) + }} options={itemsType?.multiselect ?? []} selectedOptionsDraggable={true} onopen={() => { @@ -552,14 +648,17 @@ }} />
- {:else if itemsType?.enum != undefined && Array.isArray(itemsType?.enum) && Array.isArray(value)} + {:else if itemsType?.enum != undefined && Array.isArray(itemsType?.enum) && (Array.isArray(value) || value == undefined)}
{ + if (Array.isArray(value)) value = value.filter((v) => v !== e.option) + }} bind:selected={ - () => [...value], + () => [...(value ?? [])], (v) => { if (!deepEqual(v, value)) { value = v @@ -610,7 +709,7 @@ fileChanged(x, (val) => (value[i] = val))} + onchange={(x) => fileChanged(x, (val) => (value[i] = val))} multiple={false} /> {:else if itemsType?.type == 'object' && itemsType?.resourceType === undefined && itemsType?.properties === undefined} @@ -624,7 +723,6 @@ {/await} {:else if Array.isArray(itemsType?.enum)} { dispatch('focus') @@ -633,7 +731,7 @@ dispatch('blur') }} {defaultValue} - {valid} + valid={valid ?? true} {disabled} {autofocus} bind:value={value[i]} @@ -680,7 +778,7 @@ transition:fade|local={{ duration: 100 }} class="rounded-full p-1 bg-surface-secondary duration-200 hover:bg-surface-hover ml-2" aria-label="Clear" - on:click={() => { + onclick={() => { value = value.filter((_, index) => index !== i) redraw += 1 }} @@ -691,7 +789,7 @@ {/if} {/each} {#if value.length > itemsLimit} - {/if} @@ -754,16 +852,16 @@ args={otherArgs} {helperScript} bind:value - entrypoint={format.substring('dynselect_'.length)} + entrypoint={format?.substring('dynselect_'.length) ?? ''} /> {:else if inputCat == 'resource-object' && resourceTypes == undefined} Loading resource types... - {:else if inputCat == 'resource-object' && (resourceTypes == undefined || (format.split('-').length > 1 && resourceTypes.includes(format.substring('resource-'.length))))} + {:else if inputCat == 'resource-object' && (resourceTypes == undefined || (format && format?.split('-').length > 1 && resourceTypes.includes(format?.substring('resource-'.length))))} { @@ -771,7 +869,7 @@ }} {showSchemaExplorer} /> - {:else if inputCat == 'resource-object' && format.split('-').length > 1 && format + {:else if inputCat == 'resource-object' && format && format.split('-').length > 1 && format .replace('resource-', '') .replace('_', '') .toLowerCase() == 's3object'} @@ -855,11 +953,12 @@ : 'label' value = { ...toKeep, [tagKey]: detail } }} - let:item > - {#each oneOf as obj} - - {/each} + {#snippet children({ item })} + {#each oneOf as obj} + + {/each} + {/snippet} {#if oneOfSelected} {@const objIdx = oneOf.findIndex((o) => o.title === oneOfSelected)} @@ -873,13 +972,18 @@ {onlyMaskPassword} {disablePortal} {disabled} - schema={{ - properties: obj.properties, - order: obj.order, - $schema: '', - required: obj.required ?? [], - type: 'object' - }} + bind:schema={ + () => ({ + properties: obj.properties ?? {}, + order: obj.order, + $schema: '', + required: obj.required ?? [], + type: 'object' + }), + () => { + dispatch('nestedChange') + } + } bind:args={value} dndType={`nested-${title}`} hiddenArgs={['label', 'kind']} @@ -889,9 +993,6 @@ oneOf[objIdx].order = keys } }} - on:change={() => { - dispatch('nestedChange') - }} on:nestedChange {shouldDispatchChanges} /> @@ -909,7 +1010,12 @@ required: obj.required ?? [], type: 'object' }} - bind:args={value} + bind:args={ + () => value, + (v) => { + value = { ...v, [tagKey]: oneOfSelected } + } + } {shouldDispatchChanges} on:change={() => { dispatch('nestedChange') @@ -971,22 +1077,24 @@ {onlyMaskPassword} {disablePortal} {disabled} - schema={{ - properties, - $schema: '', - required: nestedRequired ?? [], - type: 'object', - order - }} + bind:schema={ + () => ({ + properties, + $schema: '', + required: nestedRequired ?? [], + type: 'object', + order + }), + (newSchema) => { + dispatch('nestedChange') + } + } bind:args={value} dndType={`nested-${title}`} on:reorder={(e) => { const keys = e.detail order = keys }} - on:change={() => { - dispatch('nestedChange') - }} diff={diffStatus && typeof diffStatus.diff === 'object' ? diffStatus.diff : {}} on:acceptChange={(e) => { dispatch('acceptChange', e.detail) @@ -1065,10 +1173,9 @@ {:else if inputCat == 'enum'}
fileChanged(x, (val) => (value = val))} + onchange={(x) => fileChanged(x, (val) => (value = val))} multiple={false} /> {#if value?.length} @@ -1144,8 +1251,8 @@ {:else if inputCat == 'email'} { + onfocus={(e) => { dispatch('focus') }} - on:blur={(e) => { + onblur={(e) => { dispatch('blur') }} use:autosize - on:keydown={onKeyDown} + onkeydown={onKeyDown} {disabled} class={twMerge( 'w-full', @@ -1197,10 +1304,10 @@ > {/key} {#if !disabled && itemPicker && extra?.['disableVariablePicker'] != true} - + {/if} @@ -1224,7 +1331,7 @@ {/if}
{/if} - + {@render actions?.()}
{#if !compact || (error && error != '')} diff --git a/frontend/src/lib/components/Auth0Setting.svelte b/frontend/src/lib/components/Auth0Setting.svelte index e3d4eb46ef..5d8dc9ba5a 100644 --- a/frontend/src/lib/components/Auth0Setting.svelte +++ b/frontend/src/lib/components/Auth0Setting.svelte @@ -6,11 +6,11 @@ import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' - export let value: any + interface Props { + value: any + } - $: enabled = value != undefined - - $: changeDomain(value?.['domain'], value?.['custom']) + let { value = $bindable() }: Props = $props() function changeDomain(domain, custom) { if (value) { @@ -31,10 +31,14 @@ } } } + let enabled = $derived(value != undefined) + $effect(() => { + changeDomain(value?.['domain'], value?.['custom']) + })
- +
diff --git a/frontend/src/lib/components/AutoscalingConfigEditor.svelte b/frontend/src/lib/components/AutoscalingConfigEditor.svelte index e90f39a9ef..9afa7a004f 100644 --- a/frontend/src/lib/components/AutoscalingConfigEditor.svelte +++ b/frontend/src/lib/components/AutoscalingConfigEditor.svelte @@ -11,11 +11,15 @@ import Label from './Label.svelte' import MultiSelect from 'svelte-multiselect' - export let config: AutoscalingConfig | undefined - export let worker_tags: string[] | undefined + interface Props { + config: AutoscalingConfig | undefined + worker_tags: string[] | undefined + } + + let { config = $bindable(), worker_tags }: Props = $props() const dispatch = createEventDispatcher() - let test_input: number = 3 + let test_input: number = $state(3)
@@ -49,11 +53,11 @@ } }} /> - + - +
diff --git a/frontend/src/lib/components/ContextualVariableEditor.svelte b/frontend/src/lib/components/ContextualVariableEditor.svelte index 7a6243ea25..a511c3c6b2 100644 --- a/frontend/src/lib/components/ContextualVariableEditor.svelte +++ b/frontend/src/lib/components/ContextualVariableEditor.svelte @@ -69,7 +69,7 @@
- + {#snippet actions()} - + {/snippet} diff --git a/frontend/src/lib/components/DBManagerDrawerButton.svelte b/frontend/src/lib/components/DBManagerDrawerButton.svelte index d0a1b68624..583d5a71ac 100644 --- a/frontend/src/lib/components/DBManagerDrawerButton.svelte +++ b/frontend/src/lib/components/DBManagerDrawerButton.svelte @@ -249,7 +249,7 @@ {/if} - + {#snippet actions()} - + {/snippet} diff --git a/frontend/src/lib/components/Dev.svelte b/frontend/src/lib/components/Dev.svelte index 101f576846..f226430d83 100644 --- a/frontend/src/lib/components/Dev.svelte +++ b/frontend/src/lib/components/Dev.svelte @@ -11,7 +11,6 @@ OpenAPI, type Preview, type OpenFlow, - type FlowModule, WorkspaceService, type InputTransform, type TriggersCount @@ -20,7 +19,7 @@ import { setCopilotInfo, userStore, workspaceStore } from '$lib/stores' import { emptySchema, sendUserToast } from '$lib/utils' import { Pane, Splitpanes } from 'svelte-splitpanes' - import { onDestroy, onMount, setContext } from 'svelte' + import { onDestroy, onMount, setContext, untrack } from 'svelte' import DarkModeToggle from '$lib/components/sidebar/DarkModeToggle.svelte' import { page } from '$app/stores' import { getUserExt } from '$lib/user' @@ -49,16 +48,6 @@ import type { FlowPropPickerConfig, PropPickerContext } from './prop_picker' import type { PickableProperties } from './flows/previousResults' import { Triggers } from './triggers/triggers.svelte' - $: token = $page.url.searchParams.get('wm_token') ?? undefined - $: workspace = $page.url.searchParams.get('workspace') ?? undefined - $: themeDarkRaw = $page.url.searchParams.get('activeColorTheme') - $: themeDark = themeDarkRaw == '2' || themeDarkRaw == '4' - - $: if (token) { - OpenAPI.WITH_CREDENTIALS = true - OpenAPI.TOKEN = token - loadUser() - } let flowCopilotContext: FlowCopilotContext = { shouldUpdatePropertyType: writable<{ @@ -87,14 +76,6 @@ } } } - $: if (workspace) { - $workspaceStore = workspace - setupCopilotInfo() - } - - $: if (workspace && token) { - loadUser() - } async function loadUser() { try { @@ -105,31 +86,26 @@ } } - let darkModeToggle: DarkModeToggle - let darkMode: boolean | undefined = undefined - let modeInitialized = false + let darkModeToggle: DarkModeToggle | undefined = $state() + let darkMode: boolean | undefined = $state(undefined) + let modeInitialized = $state(false) function initializeMode() { modeInitialized = true - darkModeToggle.toggle() + darkModeToggle?.toggle() } - $: darkModeToggle && - themeDark != darkMode && - darkMode != undefined && - !modeInitialized && - initializeMode() - let testJobLoader: TestJobLoader + let testJobLoader: TestJobLoader | undefined = $state() let socket: WebSocket | undefined = undefined // Test args input - let args: Record = {} - let isValid: boolean = true + let args: Record = $state({}) + let isValid: boolean = $state(true) // Test - let testIsLoading = false - let testJob: Job | undefined - let pastPreviews: CompletedJob[] = [] - let validCode = true + let testIsLoading = $state(false) + let testJob: Job | undefined = $state() + let pastPreviews: CompletedJob[] = $state([]) + let validCode = $state(true) type LastEditScript = { content: string @@ -140,9 +116,9 @@ tag?: string } - let currentScript: LastEditScript | undefined = undefined + let currentScript: LastEditScript | undefined = $state(undefined) - let schema = emptySchema() + let schema = $state(emptySchema()) const href = window.location.href const indexQ = href.indexOf('?') const searchParams = indexQ > -1 ? new URLSearchParams(href.substring(indexQ)) : undefined @@ -151,12 +127,12 @@ connectWs() } - let useLock = false + let useLock = $state(false) let lockChanges = false let timeout: NodeJS.Timeout | undefined = undefined - let loadingCodebaseButton = false + let loadingCodebaseButton = $state(false) let lastCommandId = '' const el = (event) => { @@ -174,7 +150,7 @@ } } else if (event.data.type == 'testPreviewBundle') { if (event.data.id == lastCommandId && currentScript) { - testJobLoader.runPreview( + testJobLoader?.runPreview( currentScript.path, event.data.file, currentScript.language, @@ -333,7 +309,7 @@ } } - let typescriptBundlePreviewMode = false + let typescriptBundlePreviewMode = $state(false) function runTest() { if (mode == 'script') { if (!currentScript) { @@ -391,7 +367,7 @@ } } - let relativePaths: any[] = [] + let relativePaths: any[] = $state([]) let lastPath: string | undefined = undefined async function replaceScript(lastEdit: LastEditScript) { mode = 'script' @@ -416,14 +392,16 @@ } } - let mode: 'script' | 'flow' = 'script' + let mode: 'script' | 'flow' = $state('script') - const flowStore = writable({ - summary: '', - value: { modules: [] }, - extra_perms: {}, - schema: emptySchema() - } as OpenFlow) + const flowStore = $state({ + val: { + summary: '', + value: { modules: [] }, + extra_perms: {}, + schema: emptySchema() + } as OpenFlow + }) type LastEditFlow = { flow: OpenFlow @@ -436,14 +414,14 @@ // sendUserToast(JSON.stringify(lastEdit.flow), true) // return try { - if (!deepEqual(lastEdit.flow, $flowStore)) { + if (!deepEqual(lastEdit.flow, flowStore)) { if (!lastEdit.flow.summary) { lastEdit.flow.summary = 'New flow' } if (!lastEdit.flow.value?.modules) { lastEdit.flow.value = { modules: [] } } - $flowStore = lastEdit.flow + flowStore.val = lastEdit.flow inferModuleArgs($selectedIdStore) } } catch (e) { @@ -453,10 +431,10 @@ const flowStateStore = writable({} as FlowState) - const previewArgsStore = writable>({}) + const previewArgsStore = $state({ val: {} }) const scriptEditorDrawer = writable(undefined) - const moving = writable<{ module: FlowModule; modules: FlowModule[] } | undefined>(undefined) - const history = initHistory($flowStore) + const moving = writable<{ id: string } | undefined>(undefined) + const history = initHistory(flowStore.val) const testStepStore = writable>({}) const selectedIdStore = writable('settings-metadata') @@ -496,7 +474,6 @@ flowPropPickerConfig: writable(undefined), pickablePropertiesFiltered: writable(undefined) }) - $: updateFlow($flowStore) let lastSent: OpenFlow | undefined = undefined function updateFlow(flow: OpenFlow) { @@ -509,17 +486,15 @@ } } - $: $selectedIdStore && inferModuleArgs($selectedIdStore) - - let flowPreviewButtons: FlowPreviewButtons - let reload = 0 + let flowPreviewButtons: FlowPreviewButtons | undefined = $state() + let reload = $state(0) async function inferModuleArgs(selectedIdStore: string) { if (selectedIdStore == '') { return } //@ts-ignore - dfs($flowStore.value.modules, async (mod) => { + dfs(flowStore.value.modules, async (mod) => { if (mod.id == selectedIdStore) { if ( mod.value.type == 'rawscript' || @@ -544,9 +519,44 @@ } }) } + let token = $derived($page.url.searchParams.get('wm_token') ?? undefined) + let workspace = $derived($page.url.searchParams.get('workspace') ?? undefined) + let themeDarkRaw = $derived($page.url.searchParams.get('activeColorTheme')) + let themeDark = $derived(themeDarkRaw == '2' || themeDarkRaw == '4') + $effect(() => { + if (token) { + OpenAPI.WITH_CREDENTIALS = true + OpenAPI.TOKEN = token + untrack(() => loadUser()) + } + }) + $effect(() => { + if (workspace) { + $workspaceStore = workspace + untrack(() => setupCopilotInfo()) + } + }) + $effect(() => { + if (workspace && token) { + untrack(() => loadUser()) + } + }) + $effect(() => { + darkModeToggle && + themeDark != darkMode && + darkMode != undefined && + !modeInitialized && + untrack(() => initializeMode()) + }) + $effect(() => { + flowStore.val && untrack(() => updateFlow(flowStore.val)) + }) + $effect(() => { + $selectedIdStore && untrack(() => inferModuleArgs($selectedIdStore)) + }) - + - {#if $flowStore?.value?.modules} + {#if flowStore.val?.value?.modules}
{/if}
- + {#snippet actions()} {#if data?.button} {/if} - + {/snippet} diff --git a/frontend/src/lib/components/DisplayResult.svelte b/frontend/src/lib/components/DisplayResult.svelte index c03cfbf597..d27de74a5b 100644 --- a/frontend/src/lib/components/DisplayResult.svelte +++ b/frontend/src/lib/components/DisplayResult.svelte @@ -1,4 +1,6 @@ @@ -442,17 +472,18 @@ on:selected={(ev) => { globalForceJson = ev.detail === 'json' }} - let:item > - + {#snippet children({ item })} + - + + {/snippet} {/if}
{#each result['render_all'] as res} - { forceJson = ev.detail === 'json' }} > - {#if ['table-col', 'table-row', 'table-row-object'].includes(resultKind ?? '')} - - {:else} - - {/if} - + {#snippet children({ item })} + {#if ['table-col', 'table-row', 'table-row-object'].includes(resultKind ?? '')} + + {:else} + + {/if} + + {/snippet} {/if}
{#if customUi?.disableAiFix !== true} - + {@render copilot_fix?.()} {/if} {#if toolbarLocation === 'self'} @@ -647,7 +679,7 @@ >{JSON.stringify(result.error.extra, null, 4)} {/if} - + {@render children?.()}
{#if !isTest && language === 'bun'}
@@ -723,7 +755,7 @@ /> - - + import { createBubbler, preventDefault, stopPropagation } from 'svelte/legacy' + + const bubble = createBubbler() import type { Schema } from '$lib/common' import { VariableService } from '$lib/gen' import { workspaceStore } from '$lib/stores' @@ -11,7 +14,7 @@ import FlowPropertyEditor from './schema/FlowPropertyEditor.svelte' import PropertyEditor from './schema/PropertyEditor.svelte' import SimpleEditor from './SimpleEditor.svelte' - import { createEventDispatcher, tick } from 'svelte' + import { createEventDispatcher, tick, untrack } from 'svelte' import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' import Label from './Label.svelte' @@ -22,72 +25,112 @@ import SchemaFormDnd from './schema/SchemaFormDND.svelte' import { deepEqual } from 'fast-equals' import { tweened } from 'svelte/motion' - import type { SchemaDiff } from '$lib/components/schema/schemaUtils' + import type { SchemaDiff } from '$lib/components/schema/schemaUtils.svelte' import type { EditableSchemaFormUi } from '$lib/components/custom_ui' import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted' - export let schema: Schema | any - export let hiddenArgs: string[] = [] - export let args: Record = {} - export let shouldHideNoInputs: boolean = false - export let noVariablePicker = false - export let flexWrap = false - export let uiOnly: boolean = false - export let isFlowInput: boolean = false - export let noPreview: boolean = false - export let jsonEnabled: boolean = true - export let isAppInput: boolean = false - export let displayWebhookWarning: boolean = false - export let onlyMaskPassword: boolean = false - export let dndType: string | undefined = undefined - export let editTab: - | 'inputEditor' - | 'history' - | 'savedInputs' - | 'json' - | 'captures' - | 'firstStepInputs' - | undefined - export let previewSchema: Record | undefined = undefined - export let editPanelInitialSize: number | undefined = undefined - export let editPanelSize = 0 - export let diff: Record = {} - export let disableDnd: boolean = false - export let shouldDispatchChanges: boolean = false - export let isValid: boolean = true - export let customUi: EditableSchemaFormUi | undefined = undefined - + // export let openEditTab: () => void = () => {} const dispatch = createEventDispatcher() const dispatchIfMounted = createDispatcherIfMounted(dispatch) - let clazz: string = '' - export { clazz as class } - - $: if (args == undefined || typeof args !== 'object') { - args = {} + interface Props { + schema: Schema | any + hiddenArgs?: string[] + args?: Record + shouldHideNoInputs?: boolean + noVariablePicker?: boolean + flexWrap?: boolean + uiOnly?: boolean + isFlowInput?: boolean + noPreview?: boolean + jsonEnabled?: boolean + isAppInput?: boolean + displayWebhookWarning?: boolean + onlyMaskPassword?: boolean + dndType?: string | undefined + editTab: + | 'inputEditor' + | 'history' + | 'savedInputs' + | 'json' + | 'captures' + | 'firstStepInputs' + | undefined + previewSchema?: Record | undefined + editPanelInitialSize?: number | undefined + editPanelSize?: number + diff?: Record + disableDnd?: boolean + shouldDispatchChanges?: boolean + isValid?: boolean + customUi?: EditableSchemaFormUi | undefined + pannelExtraButtonWidth?: number + class?: string + openEditTab?: import('svelte').Snippet + addProperty?: import('svelte').Snippet + runButton?: import('svelte').Snippet + extraTab?: import('svelte').Snippet } + let { + schema = $bindable(), + hiddenArgs = [], + args = $bindable(undefined), + shouldHideNoInputs = false, + noVariablePicker = false, + flexWrap = false, + uiOnly = false, + isFlowInput = false, + noPreview = false, + jsonEnabled = true, + isAppInput = false, + displayWebhookWarning = false, + onlyMaskPassword = false, + dndType = undefined, + editTab, + previewSchema = undefined, + editPanelInitialSize = undefined, + editPanelSize = $bindable(0), + diff = {}, + disableDnd = false, + shouldDispatchChanges = false, + isValid = $bindable(true), + customUi = undefined, + pannelExtraButtonWidth = 0, + class: clazz = '', + openEditTab, + addProperty, + runButton, + extraTab + }: Props = $props() + + $effect.pre(() => { + if (args == undefined) { + args = {} + } + }) + export function setDefaults() { const nargs = {} Object.keys(schema?.properties ?? {}).forEach((key) => { - if (schema?.properties[key].default != undefined && args[key] == undefined) { + if (schema?.properties[key].default != undefined && args?.[key] == undefined) { let value = schema?.properties[key].default - nargs[key] = value === 'object' ? structuredClone(value) : value + nargs[key] = value === 'object' ? structuredClone($state.snapshot(value)) : value } }) args = nargs } let pickForField: string | undefined - let itemPicker: ItemPicker | undefined = undefined - let variableEditor: VariableEditor | undefined = undefined + let itemPicker: ItemPicker | undefined = $state(undefined) + let variableEditor: VariableEditor | undefined = $state(undefined) - let keys: string[] = Array.isArray(schema?.order) - ? [...schema.order] - : (Object.keys(schema?.properties ?? {}) ?? Object.keys(schema?.properties ?? {})) - - $: schema && onSchemaChange() + let keys: string[] = $state( + Array.isArray(schema?.order) + ? [...schema.order] + : (Object.keys(schema?.properties ?? {}) ?? Object.keys(schema?.properties ?? {})) + ) function alignOrderWithProperties(schema: { properties: Record @@ -134,15 +177,25 @@ } } - let opened: string | undefined = keys[0] - let selected = '' + let opened: string | undefined = $state(untrack(() => keys[0])) + + function computeSelected(property: any) { + if (!opened) return '' + return property.type !== 'object' + ? property.type + : property.format === 'resource-s3_object' + ? 'S3' + : property.oneOf && property.oneOf.length >= 2 + ? 'oneOf' + : 'object' + } export function openField(key: string) { opened = key } export function deleteField(key: string) { - delete args[key] + delete args?.[key] delete schema.properties[key] if (schema.required?.includes(key)) { schema.required = schema.required?.filter((x) => x !== key) @@ -152,20 +205,6 @@ } } - $: opened && updateSelected(schema.properties[opened]) - function updateSelected(property: any) { - if (!property) return - selected = opened - ? property.type !== 'object' - ? property.type - : property.format === 'resource-s3_object' - ? 'S3' - : property.oneOf && property.oneOf.length >= 2 - ? 'oneOf' - : 'object' - : '' - } - function renameProperty(oldName: string, key: string) { const el = document.getElementById(key) as HTMLInputElement const newName = el.value @@ -179,8 +218,10 @@ // clear the input el.value = oldName } else { - args[newName] = args[oldName] - delete args[oldName] + if (args) { + args[newName] = args[oldName] + delete args[oldName] + } schema.properties[newName] = schema.properties[oldName] delete schema.properties[oldName] @@ -199,26 +240,34 @@ opened = newName - schema = schema + schema = $state.snapshot(schema) dispatch('change', schema) - sendUserToast('Argument renamed successfully') + sendUserToast('Argument renamed') } } - let jsonView: boolean = customUi?.jsonOnly == true - let schemaString: string = JSON.stringify(schema, null, '\t') - let error: string | undefined = undefined - let editor: SimpleEditor | undefined = undefined + let jsonView: boolean = $state(customUi?.jsonOnly == true) + let schemaString: string = $state(JSON.stringify(schema, null, '\t')) + let error: string | undefined = $state(undefined) + let editor: SimpleEditor | undefined = $state(undefined) + + export function updateJson() { + schemaString = JSON.stringify(schema, null, '\t') + editor?.setCode(schemaString) + } const editTabDefaultSize = noPreview ? 100 : 50 editPanelSize = editTab ? (editPanelInitialSize ?? editTabDefaultSize) : 0 - let inputPanelSize = 100 - editPanelSize + let inputPanelSize = $state(100 - editPanelSize) let editPanelSizeSmooth = tweened(editPanelSize, { duration: 150 }) - let inputPanelSizeSmooth = tweened(inputPanelSize, { duration: 150 }) + let inputPanelSizeSmooth = tweened( + untrack(() => inputPanelSize), + { duration: 150 } + ) - function openEditTab() { + function openEditTabFn() { if (editPanelSize > 0) return editPanelSizeSmooth.set(editTabDefaultSize) inputPanelSizeSmooth.set(100 - editTabDefaultSize) @@ -234,17 +283,22 @@ inputPanelSize = inputSize dispatchIfMounted('editPanelSizeChanged', editSize) } - $: updatePanelSizes($editPanelSizeSmooth, $inputPanelSizeSmooth) - $: !!editTab ? openEditTab() : closeEditTab() - - let panelButtonWidth: number = 0 - export let pannelExtraButtonWidth: number = 0 - - export function updateJson() { - schemaString = JSON.stringify(schema, null, '\t') - editor?.setCode(schemaString) - } + let panelButtonWidth: number = $state(0) + $effect(() => { + if (args == undefined || typeof args !== 'object') { + args = {} + } + }) + $effect(() => { + schema && untrack(() => onSchemaChange()) + }) + $effect(() => { + updatePanelSizes($editPanelSizeSmooth, $inputPanelSizeSmooth) + }) + $effect(() => { + !!editTab ? openEditTabFn() : closeEditTab() + })
@@ -254,37 +308,42 @@ style="right: calc({editPanelSize}% - 1px - {pannelExtraButtonWidth}px); top: 0px;" bind:clientWidth={panelButtonWidth} > - + {@render openEditTab?.()}
{#if !noPreview}
0 ? 'pr-[38px]' : 'pr-2'}" > - {#if $$slots.addProperty} + {#if addProperty}
0 ? `width: 100%;` : `width: calc(100% - ${panelButtonWidth - pannelExtraButtonWidth}px);`} > - + {@render addProperty?.()}
{/if}
(previewSchema ? previewSchema : schema), + (newSchema) => { + console.log('schemaChange set', $state.snapshot(newSchema)) + schema = newSchema + tick().then(() => dispatch('change', schema)) + } + } {dndType} {disableDnd} {onlyMaskPassword} @@ -293,12 +352,10 @@ opened = e.detail }} on:reorder={(e) => { - schema.order = e.detail - schema = schema - tick().then(() => dispatch('change', schema)) - }} - on:change={() => { - schema = schema + schema = { + ...schema, + order: e.detail + } tick().then(() => dispatch('change', schema)) }} prettifyHeader={isAppInput} @@ -314,7 +371,7 @@ noVariablePicker={noVariablePicker || customUi?.disableVariablePicker === true} /> - + {@render runButton?.()}
@@ -327,7 +384,7 @@ class={twMerge('border rounded-md', panelButtonWidth > 0 ? 'rounded-tl-none' : '')} > {#if editTab !== 'inputEditor'} - + {@render extraTab?.()} {:else} {#if jsonEnabled && customUi?.jsonOnly != true} @@ -357,14 +414,14 @@ {#if keys.length > 0} {#each keys as argName, i (argName)}
- - + +
{ + onclick={() => { if (opened === argName) { opened = undefined } else { @@ -375,9 +432,9 @@
{argName} {#if !uiOnly} -
+
- + {#snippet trigger()}
- + {/snippet}
{/if} @@ -429,7 +486,7 @@ class="rounded-full p-1 text-gray-500 bg-white duration-200 hover:bg-gray-600 focus:bg-gray-600 hover:text-white dark:bg-gray-700 dark:text-white dark:hover:bg-gray-800" aria-label="Clear" - on:click={() => { + onclick={() => { dispatch('delete', argName) }} > @@ -461,103 +518,103 @@ {isFlowInput} {isAppInput} on:change={() => { - schema = schema + schema = $state.snapshot(schema) dispatch('change', schema) }} > - + {#snippet typeeditor()} {#if isFlowInput || isAppInput} {/if} - + {/snippet} {#if isFlowInput || isAppInput} { - schema = schema + schema = $state.snapshot(schema) dispatch('change', schema) dispatch('schemaChange') }} @@ -651,7 +708,9 @@ bind:this={itemPicker} pickCallback={(path, _) => { if (pickForField) { - args[pickForField] = '$var:' + path + if (args) { + args[pickForField] = '$var:' + path + } } }} itemName="Variable" @@ -664,17 +723,19 @@ ...x }))} > -
- -
+ {#snippet submission()} +
+ +
+ {/snippet} diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index 16dedf01c6..08ce87d57e 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -206,7 +206,7 @@ let websocketInterval: NodeJS.Timeout | undefined let lastWsAttempt: Date = new Date() let nbWsAttempt = 0 - let disposeMethod: () => void | undefined + let disposeMethod: (() => void) | undefined const dispatch = createEventDispatcher() let graphqlService: MonacoGraphQLAPI | undefined = undefined @@ -1267,15 +1267,20 @@ onFileChanges() - editor = meditor.create(divEl as HTMLDivElement, { - ...editorConfig(code, lang, automaticLayout, fixedOverflowWidgets), - model, - fontSize: !small ? 14 : 12, - lineNumbersMinChars, - // overflowWidgetsDomNode: widgets, - tabSize: lang == 'python' ? 4 : 2, - folding - }) + try { + editor = meditor.create(divEl as HTMLDivElement, { + ...editorConfig(code, lang, automaticLayout, fixedOverflowWidgets), + model, + fontSize: !small ? 14 : 12, + lineNumbersMinChars, + // overflowWidgetsDomNode: widgets, + tabSize: lang == 'python' ? 4 : 2, + folding + }) + } catch (e) { + console.error('Error loading monaco:', e) + return + } keepModelAroundToAvoidDisposalOfWorkers() diff --git a/frontend/src/lib/components/EditorBar.svelte b/frontend/src/lib/components/EditorBar.svelte index c2ae98599c..5b5681b3b3 100644 --- a/frontend/src/lib/components/EditorBar.svelte +++ b/frontend/src/lib/components/EditorBar.svelte @@ -1,8 +1,10 @@ - - +
{#if schema && mod} @@ -77,10 +81,10 @@ selected ? 'bg-surface-selected' : 'hover:bg-surface-hover' )} disabled={!schema} - on:click={handleClick} + onclick={handleClick} > -
- {#if mod.value.type === 'rawscript'} - - {:else if mod.value.type === 'script'} - {#if mod.value.path.startsWith('hub/')} -
- -
- {:else} - + {#snippet icon()} +
+ {#if mod.value.type === 'rawscript'} + + {:else if mod.value.type === 'script'} + {#if mod.value.path.startsWith('hub/')} +
+ +
+ {:else} + + {/if} {/if} - {/if} -
+
+ {/snippet}
{:else} diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index 17c1f04818..4c1f766c77 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -1,8 +1,9 @@ - + - +{@render children?.()} { - const currentModules = $flowStore?.value?.modules + const currentModules = flowStore.val?.value?.modules - $flowStore = undo(history, $flowStore) + flowStore.val = undo(history, flowStore.val) - const newModules = $flowStore?.value?.modules + const newModules = flowStore.val?.value?.modules const restoredModules = newModules?.filter( (node) => !currentModules?.some((currentNode) => currentNode?.id === node?.id) ) @@ -820,7 +861,7 @@ $selectedIdStore = 'Input' }} on:redo={() => { - $flowStore = redo(history) + flowStore.val = redo(history) }} />
@@ -853,7 +894,7 @@
{/if}
- + {/snippet}
diff --git a/frontend/src/lib/components/FolderEditor.svelte b/frontend/src/lib/components/FolderEditor.svelte index 22229d8f8e..c12aecf9c7 100644 --- a/frontend/src/lib/components/FolderEditor.svelte +++ b/frontend/src/lib/components/FolderEditor.svelte @@ -184,13 +184,11 @@
- (ownerItem = '')} - let:item - > - - + (ownerItem = '')}> + {#snippet children({ item })} + + + {/snippet}
@@ -278,7 +276,6 @@ { const role = e.detail // const wasInFolder = (folder?.owners ?? []).includes(folder) @@ -315,28 +312,30 @@ loadFolder() }} > - + {#snippet children({ item })} + - + - + + {/snippet}
{:else} diff --git a/frontend/src/lib/components/GroupEditor.svelte b/frontend/src/lib/components/GroupEditor.svelte index fbe03bd9f0..1f66cabfec 100644 --- a/frontend/src/lib/components/GroupEditor.svelte +++ b/frontend/src/lib/components/GroupEditor.svelte @@ -150,7 +150,6 @@
{ const role = e.detail // const wasInGroup = (group?.members ?? []).includes(group) @@ -211,31 +210,33 @@ loadGroup() }} > - - - {#if role === 'manager'} + {#snippet children({ item })} - {/if} + + {#if role === 'manager'} + + {/if} + {/snippet}
{:else} diff --git a/frontend/src/lib/components/HistoricInputs.svelte b/frontend/src/lib/components/HistoricInputs.svelte index 1acd9a9e28..ac79c0479b 100644 --- a/frontend/src/lib/components/HistoricInputs.svelte +++ b/frontend/src/lib/components/HistoricInputs.svelte @@ -8,21 +8,34 @@ import HistoricList from './HistoricList.svelte' import { Loader2 } from 'lucide-svelte' - export let runnableId: string | undefined = undefined - export let runnableType: RunnableType | undefined = undefined - export let loading: boolean = false - export let selected: string | undefined = undefined - export let showAuthor = false - export let placement: 'bottom-start' | 'top-start' | 'bottom-end' | 'top-end' = 'top-end' - export let limitPayloadSize = false - export let searchArgs: Record | undefined = undefined + interface Props { + runnableId?: string | undefined + runnableType?: RunnableType | undefined + loading?: boolean + selected?: string | undefined + showAuthor?: boolean + placement?: 'bottom-start' | 'top-start' | 'bottom-end' | 'top-end' + limitPayloadSize?: boolean + searchArgs?: Record | undefined + } - let historicList: HistoricList | undefined = undefined + let { + runnableId = undefined, + runnableType = undefined, + loading = $bindable(false), + selected = $bindable(undefined), + showAuthor = false, + placement = 'top-end', + limitPayloadSize = false, + searchArgs = undefined + }: Props = $props() + + let historicList: HistoricList | undefined = $state(undefined) const dispatch = createEventDispatcher() - let jobs: Job[] = [] + let jobs: Job[] = $state([]) let hasMoreCurrentRuns = false - let page = 1 + let page = $state(1) async function handleSelected(data: any) { if (selected === data.id) { @@ -34,7 +47,10 @@ const fullPayload = await data.getFullPayload?.() dispatch('select', { args: fullPayload, jobId: data.id }) } else { - dispatch('select', { args: structuredClone(data.payloadData), jobId: data.id }) + dispatch('select', { + args: structuredClone($state.snapshot(data.payloadData)), + jobId: data.id + }) } } @@ -77,11 +93,11 @@ } else if (runnableType === 'ScriptHash') { return 'script,preview' } - return 'all' + return '' } - + {#if runnableId} import { InputService, type RunnableType } from '$lib/gen' import { workspaceStore } from '$lib/stores' - import { onDestroy } from 'svelte' + import { onDestroy, untrack } from 'svelte' import InfiniteList from './InfiniteList.svelte' import JobSchemaPicker from './schema/JobSchemaPicker.svelte' - export let runnableId: string | undefined - export let runnableType: RunnableType | undefined - export let selected: string | undefined = undefined - export let showAuthor = false - export let placement: 'bottom-start' | 'top-start' | 'bottom-end' | 'top-end' = 'bottom-start' - export let limitPayloadSize = false - export let searchArgs: Record | undefined = undefined + interface Props { + runnableId: string | undefined + runnableType: RunnableType | undefined + selected?: string | undefined + showAuthor?: boolean + placement?: 'bottom-start' | 'top-start' | 'bottom-end' | 'top-end' + limitPayloadSize?: boolean + searchArgs?: Record | undefined + } - let infiniteList: InfiniteList | undefined = undefined + let { + runnableId, + runnableType, + selected = undefined, + showAuthor = false, + placement = 'bottom-start', + limitPayloadSize = false, + searchArgs = undefined + }: Props = $props() + + let infiniteList: InfiniteList | undefined = $state(undefined) let loadInputsPageFn: ((page: number, perPage: number) => Promise) | undefined = undefined - let viewerOpen = false + let viewerOpen = $state(false) let openStates: Record = {} // Track open state for each item export async function refresh(clearCurrentRuns: boolean = false) { @@ -101,18 +113,20 @@ viewerOpen = Object.values(openStates).some((state) => state) } - $: $workspaceStore && runnableId && runnableType && infiniteList && initLoadInputs() + $effect(() => { + $workspaceStore && runnableId && runnableType && infiniteList && untrack(() => initLoadInputs()) + }) - + {#snippet columns()} - - + {/snippet} + {#snippet children({ item, hover })} - - + {/snippet} + {#snippet empty()}
{runnableId ? 'No previous inputs' : 'Save draft to see previous runs'}
-
+ {/snippet}
diff --git a/frontend/src/lib/components/IconedResourceType.svelte b/frontend/src/lib/components/IconedResourceType.svelte index 5daea14d17..7375c64cd7 100644 --- a/frontend/src/lib/components/IconedResourceType.svelte +++ b/frontend/src/lib/components/IconedResourceType.svelte @@ -1,18 +1,35 @@
@@ -20,12 +37,13 @@ {name} {/if} {#if iconComponent} + {@const SvelteComponent = iconComponent} - + {:else if formatExtension} - + {:else} @@ -34,4 +52,3 @@ {name} {/if}
- diff --git a/frontend/src/lib/components/InfiniteList.svelte b/frontend/src/lib/components/InfiniteList.svelte index 6457d5ea60..b4afaf41ba 100644 --- a/frontend/src/lib/components/InfiniteList.svelte +++ b/frontend/src/lib/components/InfiniteList.svelte @@ -6,26 +6,48 @@ const dispatch = createEventDispatcher() - export let loading = false - export let items: any[] | undefined = undefined - export let selectedItemId: any | undefined = undefined - export let isEmpty: boolean = true - export let length: number = 0 - export let rounded: boolean = true - export let noBorder: boolean = false - export let extraRowClasses: { bgSelected: string; bgHover: string; class: string } = { - bgSelected: '', - bgHover: '', - class: '' + interface Props { + loading?: boolean + items?: any[] | undefined + selectedItemId?: any | undefined + isEmpty?: boolean + length?: number + rounded?: boolean + noBorder?: boolean + extraRowClasses?: { bgSelected: string; bgHover: string; class: string } + neverShowLoader?: boolean + columns?: import('svelte').Snippet + extra_row?: import('svelte').Snippet<[any]> + children?: import('svelte').Snippet<[any]> + empty?: import('svelte').Snippet<[any]> } - export let neverShowLoader = false + + let { + loading = $bindable(false), + items = $bindable(undefined), + selectedItemId = $bindable(undefined), + isEmpty = $bindable(true), + length = $bindable(0), + rounded = true, + noBorder = false, + extraRowClasses = { + bgSelected: '', + bgHover: '', + class: '' + }, + neverShowLoader = false, + columns, + extra_row, + children, + empty + }: Props = $props() const perPage = 20 - let hasMore = false + let hasMore = $state(false) let page = 1 let hasAlreadyFailed = false - let hovered: any | undefined = undefined + let hovered: any | undefined = $state(undefined) let initLoad = false let loadInputs: ((page: number, perPage: number) => Promise) | undefined = undefined let deleteItemFn: ((id: any) => Promise) | undefined = undefined @@ -39,7 +61,7 @@ page = 1 } - let loadingMore = false + let loadingMore = $state(false) export async function loadData(loadOption: 'refresh' | 'forceRefresh' | 'loadMore' = 'loadMore') { // console.log('loadData', loadOption, length, items?.length) @@ -146,7 +168,7 @@ {noBorder} {neverShowLoader} > - + {@render columns?.()} (hovered = e.detail ? 'extraRow' : undefined)} > - + {@render extra_row?.({ hover: hovered === 'extraRow' })} {#each items ?? [] as item, index} {@const hover = item.id === hovered} @@ -171,16 +193,16 @@ )} on:hover={(e) => (hovered = e.detail ? item.id : undefined)} > - + {@render children?.({ item, hover })} {/each} - + {#snippet emptyMessage()} {#if (!items || items?.length === 0) && (!loading || neverShowLoader)} - + {@render empty?.({ items })} {/if} - + {/snippet}