From c0edbe431773f878201e96a79ce291d4b37a10bb Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Thu, 19 Mar 2026 06:23:38 +0000 Subject: [PATCH] fix: schema inference not updating on reset and language switch (#8446) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three root causes: 1. Editor.setCode() never dispatched `change` — it pre-set `code = ncode` before the Monaco edit, so the debounced updateCode() saw code == ncode and skipped dispatch. The Reset button, copilot accept, and other setCode callers never triggered schema inference. Fixed by capturing `changed` before the pre-set and dispatching directly when true. 2. EditableSchemaForm's $effect only tracked the schema reference, not its properties. Since inferArgs mutates schema in-place through the Svelte 5 proxy, the reference never changes and the effect never re-ran. Added schema?.order and Object.keys(schema?.properties ?? {}) reads to detect in-place mutations (matching SchemaForm's pattern). 3. ScriptEditor's $effect depended on both selectedTab and code, causing a redundant double inferSchema call on every code change (racing with the on:change handler and initContent's explicit call). Moved code into untrack() so the effect only fires on tab switches. Also removed the no-op `testPanelSchema = testPanelSchema` in inferModuleSchema. Co-authored-by: Claude Opus 4.6 (1M context) --- .../src/lib/components/EditableSchemaForm.svelte | 8 +++++++- frontend/src/lib/components/Editor.svelte | 12 ++++++++++-- frontend/src/lib/components/ScriptEditor.svelte | 7 +++++-- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/frontend/src/lib/components/EditableSchemaForm.svelte b/frontend/src/lib/components/EditableSchemaForm.svelte index 989ce6a2fe..f04bac5640 100644 --- a/frontend/src/lib/components/EditableSchemaForm.svelte +++ b/frontend/src/lib/components/EditableSchemaForm.svelte @@ -297,7 +297,9 @@ } const editTabDefaultSize = untrack(() => noPreview) ? 100 : 50 - editPanelSize = untrack(() => editTab) ? (untrack(() => editPanelInitialSize) ?? editTabDefaultSize) : 0 + editPanelSize = untrack(() => editTab) + ? (untrack(() => editPanelInitialSize) ?? editTabDefaultSize) + : 0 let inputPanelSize = $state(100 - editPanelSize) let editPanelSizeSmooth = tweened(editPanelSize, { duration: 150 @@ -331,6 +333,10 @@ } }) $effect(() => { + // Track schema properties and order so this effect re-runs when + // inferArgs rebuilds the schema (not just on reference change). + schema?.order + Object.keys(schema?.properties ?? {}) schema && untrack(() => onSchemaChange()) }) $effect(() => { diff --git a/frontend/src/lib/components/Editor.svelte b/frontend/src/lib/components/Editor.svelte index 709688f9a2..3a59b0d80c 100644 --- a/frontend/src/lib/components/Editor.svelte +++ b/frontend/src/lib/components/Editor.svelte @@ -347,7 +347,9 @@ } export function setCode(ncode: string, noHistory: boolean = false): void { - if (code != ncode) { + // Track whether the code actually changed before updating. + const changed = code != ncode + if (changed) { code = ncode } @@ -368,7 +370,13 @@ editor.pushUndoStop() } } - // Update lint diagnostics after code change + // Dispatch change immediately when code actually changed. This ensures + // callers like the Reset button and copilot trigger on:change handlers. + // The debounced onDidChangeModelContent handler will no-op since code + // will already match by the time it fires. + if (changed) { + dispatch('change', ncode) + } updateRawAppLintDiagnostics() } diff --git a/frontend/src/lib/components/ScriptEditor.svelte b/frontend/src/lib/components/ScriptEditor.svelte index 3da73fa132..6a4fd088e0 100644 --- a/frontend/src/lib/components/ScriptEditor.svelte +++ b/frontend/src/lib/components/ScriptEditor.svelte @@ -751,7 +751,6 @@ if (activeModuleTab === null) return try { await inferArgs(effectiveLang, editorCode, testPanelSchema) - testPanelSchema = testPanelSchema moduleTestState[activeModuleTab] = { args: testPanelArgs, schema: testPanelSchema } } catch (e) { // Module code may be in-progress; silently ignore @@ -1196,7 +1195,11 @@ !hasPreprocessor && (selectedTab = 'main') }) $effect(() => { - selectedTab && code && untrack(() => inferSchema(code)) + // Only depend on selectedTab (preprocessor ↔ main toggle). + // Code changes are handled by the editor on:change handler and + // explicit inferSchema calls (initContent, onMount), so we read + // `code` inside untrack to avoid a redundant double-inference race. + selectedTab && untrack(() => code && inferSchema(code)) }) let argsRender = $state(0)