From d15510effa420f22804f829e6fa5dbdb41e63df5 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Sat, 30 Aug 2025 02:32:35 +0000 Subject: [PATCH] fix: schema editor reactivity improvements (#6496) * all * all * all * all * all * nit * all * all * all * all --- frontend/package-lock.json | 3 +- frontend/package.json | 2 +- frontend/src/global.d.ts | 4 +- frontend/src/lib/components/ArgInput.svelte | 27 +- .../lib/components/EditableSchemaForm.svelte | 108 ++++---- frontend/src/lib/components/Portal.svelte | 18 +- frontend/src/lib/components/SchemaForm.svelte | 4 +- .../apps/components/layout/AppModal.svelte | 4 +- .../apps/editor/AppEditorHeader.svelte | 1 + .../component/ComponentNavigation.svelte | 8 +- .../component/componentCallbacks.svelte.ts | 2 +- .../ArrayStaticInputEditor.svelte | 5 +- .../editor/settingsPanel/GridCondition.svelte | 5 +- .../editor/settingsPanel/GridNavbar.svelte | 5 +- .../apps/editor/settingsPanel/GridTab.svelte | 5 +- .../editor/settingsPanel/TableActions.svelte | 5 +- .../common/drawer/Disposable.svelte | 101 +++++--- .../components/common/drawer/Drawer.svelte | 27 +- .../components/flows/content/FlowInput.svelte | 37 ++- .../flows/content/FlowModuleSuspend.svelte | 33 ++- .../flows/content/ScriptEditorDrawer.svelte | 21 +- .../propertyPicker/ObjectViewer.svelte | 16 +- .../components/schema/AddPropertyV2.svelte | 12 +- .../schema/EditableSchemaDrawer.svelte | 234 +++++++++--------- .../schema/EditableSchemaSdkWrapper.svelte | 22 +- .../schema/EditableSchemaWrapper.svelte | 12 +- .../schema/FlowPropertyEditor.svelte | 52 +--- .../components/schema/SchemaFormDND.svelte | 38 +-- .../schema/editable_schema_wrapper.ts | 1 - .../triggers/AddTriggersButton.svelte | 1 + .../routes/test_dev/sdk_schema/+page.svelte | 11 +- 31 files changed, 433 insertions(+), 391 deletions(-) diff --git a/frontend/package-lock.json b/frontend/package-lock.json index c37ee5d0f4..69d3d0a144 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -25,7 +25,7 @@ "@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", + "@windmill-labs/svelte-dnd-action": "^0.9.44", "@xterm/addon-fit": "^0.10.0", "@xyflow/svelte": "^1.0.0", "ag-charts-community": "^9.0.1", @@ -3898,6 +3898,7 @@ "version": "0.9.48", "resolved": "https://registry.npmjs.org/@windmill-labs/svelte-dnd-action/-/svelte-dnd-action-0.9.48.tgz", "integrity": "sha512-A6pWayH3nOi79DZohTscGj5t2PuNtHlKM5WIAj9WlVDx5pS1A+MsyfuNZi8oCpj2590wFmQl+58G4y4xZqDa1Q==", + "license": "MIT", "peerDependencies": { "svelte": ">=3.23.0 || ^5.0.0-next.0" } diff --git a/frontend/package.json b/frontend/package.json index 7174d7cafa..775d5f4ec0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -92,7 +92,7 @@ "@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", + "@windmill-labs/svelte-dnd-action": "^0.9.44", "@xterm/addon-fit": "^0.10.0", "@xyflow/svelte": "^1.0.0", "ag-charts-community": "^9.0.1", diff --git a/frontend/src/global.d.ts b/frontend/src/global.d.ts index 3cf0168db4..bd06974068 100644 --- a/frontend/src/global.d.ts +++ b/frontend/src/global.d.ts @@ -1,7 +1,7 @@ /// -declare type Item = import('svelte-dnd-action').Item -declare type DndEvent = import('svelte-dnd-action').DndEvent +declare type Item = import('@windmill-labs/svelte-dnd-action').Item +declare type DndEvent = import('@windmill-labs/svelte-dnd-action').DndEvent declare namespace svelte.JSX { interface HTMLAttributes { onconsider?: (event: CustomEvent> & { target: EventTarget & T }) => void diff --git a/frontend/src/lib/components/ArgInput.svelte b/frontend/src/lib/components/ArgInput.svelte index 5e8b189a09..7897832188 100644 --- a/frontend/src/lib/components/ArgInput.svelte +++ b/frontend/src/lib/components/ArgInput.svelte @@ -992,8 +992,11 @@ } } bind:args={value} - dndType={`nested-${title}`} - hiddenArgs={['label', 'kind']} + hiddenArgs={[ + oneOf?.find((o) => Object.keys(o.properties ?? {}).includes('kind')) + ? 'kind' + : 'label' + ]} on:reorder={(e) => { if (oneOf && oneOf[objIdx]) { const keys = e.detail @@ -1086,20 +1089,14 @@ {disablePortal} {disabled} {prettifyHeader} - bind:schema={ - () => ({ - properties, - $schema: '', - required: nestedRequired ?? [], - type: 'object', - order - }), - (newSchema) => { - dispatch('nestedChange') - } - } + schema={{ + properties, + $schema: '', + required: nestedRequired ?? [], + type: 'object', + order + }} bind:args={value} - dndType={`nested-${title}`} on:reorder={(e) => { const keys = e.detail order = keys diff --git a/frontend/src/lib/components/EditableSchemaForm.svelte b/frontend/src/lib/components/EditableSchemaForm.svelte index a3dfc5a168..529e99a1f9 100644 --- a/frontend/src/lib/components/EditableSchemaForm.svelte +++ b/frontend/src/lib/components/EditableSchemaForm.svelte @@ -14,13 +14,18 @@ import FlowPropertyEditor from './schema/FlowPropertyEditor.svelte' import PropertyEditor from './schema/PropertyEditor.svelte' import SimpleEditor from './SimpleEditor.svelte' - import { createEventDispatcher, tick, untrack } from 'svelte' + import { createEventDispatcher, untrack } from 'svelte' import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' import Label from './Label.svelte' import { sendUserToast } from '$lib/toast' import Toggle from './Toggle.svelte' - import { DynamicSelect, emptyString } from '$lib/utils' + import { + DynamicSelect, + emptyString, + generateRandomString, + readFieldsRecursively + } from '$lib/utils' import Popover from './meltComponents/Popover.svelte' import SchemaFormDnd from './schema/SchemaFormDND.svelte' import { deepEqual } from 'fast-equals' @@ -48,7 +53,6 @@ isAppInput?: boolean displayWebhookWarning?: boolean onlyMaskPassword?: boolean - dndType?: string | undefined editTab: | 'inputEditor' | 'history' @@ -75,6 +79,8 @@ addProperty?: import('svelte').Snippet runButton?: import('svelte').Snippet extraTab?: import('svelte').Snippet + schemaFormClassName?: string + onChange?: (args: Record) => void } let { @@ -91,7 +97,6 @@ isAppInput = false, displayWebhookWarning = false, onlyMaskPassword = false, - dndType = undefined, editTab, previewSchema = undefined, editPanelInitialSize = undefined, @@ -110,7 +115,9 @@ openEditTab, addProperty, runButton, - extraTab + extraTab, + schemaFormClassName = undefined, + onChange = undefined }: Props = $props() $effect.pre(() => { @@ -125,6 +132,13 @@ } }) + $effect(() => { + if (onChange) { + readFieldsRecursively(args) + onChange(args ?? {}) + } + }) + $effect(() => { if (schema && dynSelectCode !== undefined && dynSelectLang !== undefined) { if (dynSelectCode && dynSelectCode.trim()) { @@ -154,9 +168,10 @@ let variableEditor: VariableEditor | undefined = $state(undefined) let keys: string[] = $state( - Array.isArray(schema?.order) + (Array.isArray(schema?.order) ? [...schema.order] : (Object.keys(schema?.properties ?? {}) ?? Object.keys(schema?.properties ?? {})) + ).filter((x) => !hiddenArgs?.includes(x)) ) function alignOrderWithProperties(schema: { @@ -187,22 +202,18 @@ return hasChanged } function onSchemaChange() { - let editSchema = false if (alignOrderWithProperties(schema)) { - console.log('alignOrderWithProperties', JSON.stringify(schema, null, 2)) - editSchema = true + // console.log('alignOrderWithProperties', JSON.stringify(schema, null, 2)) } - let lkeys = schema?.order ?? Object.keys(schema?.properties ?? {}) + let lkeys = (schema?.order ?? Object.keys(schema?.properties ?? {})).filter( + (x) => !hiddenArgs?.includes(x) + ) if (schema?.properties && !deepEqual(lkeys, keys)) { keys = [...lkeys] - editSchema = true if (opened == undefined) { opened = keys[0] } } - if (editSchema) { - schema = schema - } } let opened: string | undefined = $state(untrack(() => keys[0])) @@ -248,30 +259,31 @@ // clear the input el.value = oldName } else { + let newSchema = $state.snapshot(schema) if (args) { args[newName] = args[oldName] delete args[oldName] } - schema.properties[newName] = schema.properties[oldName] - delete schema.properties[oldName] + newSchema.properties[newName] = newSchema.properties[oldName] + delete newSchema.properties[oldName] - if (schema.required?.includes(oldName)) { - schema.required = schema.required?.map((x) => (x === oldName ? newName : x)) + if (newSchema.required?.includes(oldName)) { + newSchema.required = newSchema.required?.map((x) => (x === oldName ? newName : x)) } // Replace the old name with the new name in the order array - if (schema.order) { - const index = schema.order.indexOf(oldName) + if (newSchema.order) { + const index = newSchema.order.indexOf(oldName) if (index !== -1) { - schema.order[index] = newName + newSchema.order[index] = newName } } opened = newName - schema = $state.snapshot(schema) - dispatch('change', schema) + schema = newSchema + sendUserToast('Argument renamed') } } @@ -368,6 +380,8 @@ const code = generateFn(functionName) dynSelectCode = dynSelectCode ? dynSelectCode.concat(code) : code } + + let dndType = $state(generateRandomString())
@@ -404,15 +418,15 @@ class="min-h-0 overflow-y-auto grow rounded-md {runButton ? 'flex flex-col gap-2' : ''}" > (previewSchema ? previewSchema : schema), (newSchema) => { schema = newSchema - tick().then(() => dispatch('change', schema)) } } - {dndType} + {hiddenArgs} {disableDnd} {onlyMaskPassword} bind:args @@ -420,11 +434,16 @@ opened = e.detail }} on:reorder={(e) => { + let order = e.detail + let newProperties = {} + for (let key of order) { + newProperties[key] = schema.properties[key] + } schema = { ...schema, + properties: newProperties, order: e.detail } - tick().then(() => dispatch('change', schema)) }} helperScript={{ type: 'inline', @@ -436,9 +455,6 @@ {diff} on:acceptChange on:rejectChange - on:nestedChange={() => { - dispatch('change', schema) - }} {shouldDispatchChanges} bind:isValid noVariablePicker={noVariablePicker || customUi?.disableVariablePicker === true} @@ -446,8 +462,8 @@ {@render runButton?.()} -
- {#if dynSelectFunctions.length > 0} + {#if dynSelectFunctions.length > 0} +
- {/if} -
+
+ {/if}
@@ -514,7 +530,7 @@ {#if jsonEnabled && customUi?.jsonOnly != true}
{#if addPropertyInEditorTab} - + {#snippet trigger()}
{#if opened === argName}
- {#if !hiddenArgs.includes(argName) && Object.keys(schema?.properties ?? {}).includes(argName)} + {#if Object.keys(schema?.properties ?? {}).includes(argName)} {#if typeof args == 'object' && schema?.properties[argName]} { - schema = $state.snapshot(schema) - dispatch('change', schema) - }} > {#snippet typeeditor()} {#if isFlowInput || isAppInput} @@ -742,13 +754,9 @@ type: v } } + schema.properties = schema.properties } } - on:selected={(e) => { - schema = schema - dispatch('change', schema) - dispatch('schemaChange') - }} > {#snippet children({ item })} {#each typeOptions as x} @@ -762,6 +770,9 @@ {#if isFlowInput || isAppInput} { + dndType = generateRandomString() + }} bind:defaultValue={schema.properties[argName].default} {variableEditor} {itemPicker} @@ -792,12 +803,6 @@ (x) => x !== argName ) } - dispatch('change', schema) - }} - on:schemaChange={(e) => { - schema = $state.snapshot(schema) - dispatch('change', schema) - dispatch('schemaChange') }} /> {/if} @@ -822,7 +827,6 @@ on:change={() => { try { schema = JSON.parse(schemaString) - dispatch('change', schema) error = '' } catch (err) { error = err.message diff --git a/frontend/src/lib/components/Portal.svelte b/frontend/src/lib/components/Portal.svelte index 5ea9c44e95..801188de2e 100644 --- a/frontend/src/lib/components/Portal.svelte +++ b/frontend/src/lib/components/Portal.svelte @@ -1,4 +1,4 @@ - -