fix(frontend): reset the editor content when the script language changes (#10560)

Picking a new language seeds the new template into `script.content`, which
`ScriptEditor` writes straight into Monaco. That write goes through
`onDidChangeModelContent`, which arms the keystroke debounce as if the user had
typed — so when the `{#key effectiveLang}` block then tears the editor down, the
unmount flush sees a pending timer, reads a stale `code`, and dispatches a change
that puts the previous language's template back. The editor kept showing the old
content under the new language.

`alignCodeWithEditor` now clears the timer its own write armed, restoring the
premise the unmount flush is guarded on: a pending timer means Monaco holds
something newer than `code`.

`ScriptEditor` also takes the change payload instead of re-reading `editorCode`.
A destroyed component's `bind:` writes no longer reach the parent, so the
re-read returned the value from before the change — which is what actually
wrote the old template back, and would equally have made the unmount flush
save stale text after real typing.

Fixes WIN-2330

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-08-06 09:49:20 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent c7ea530e1f
commit 56ea133366
2 changed files with 11 additions and 1 deletions
@@ -2203,6 +2203,12 @@
} else {
ed.setValue(next)
}
// The write above went through `onDidChangeModelContent`, which arms the
// keystroke debounce as if the user had typed. Monaco now holds exactly
// `code`, so there is nothing to flush — and leaving the timer armed makes
// every "is Monaco the newer side?" check (notably the unmount flush) answer
// yes on the strength of our own write.
cancelPendingChanges()
}
// External `code` prop changes should flow into the Monaco editor. Skip
@@ -2762,7 +2762,11 @@
awareness={wsProvider?.awareness}
on:change={(e) => {
if (activeModuleTab === null) {
code = editorCode
// The payload, not `editorCode`: the editor also fires this while it is
// being torn down (the unmount flush), and a destroyed component's
// `bind:` writes no longer reach us — re-reading would take the value
// from before the change and write it back over `code`.
code = e.detail
lastSyncedCode = code
inferSchema(e.detail)
} else {