From b824fe3ffeb56829cd263f987e06c4791b2401ac Mon Sep 17 00:00:00 2001 From: Guilhem Lemouel Date: Wed, 7 Oct 2026 08:19:38 -0700 Subject: [PATCH] fix(sessions): hand back only what a form actually held A tab closed while the deployed item was still being fetched handed the card an empty object, wiping the proposal it was still parked on: the page registers its destroy hook long before `RunForm` gives `args` a value. The comment above it also claimed every way out destroys the page. The next call re-pointing the same tab does not, and that page's edits stop there. Co-Authored-By: Claude Opus 5 (1M context) --- .../src/lib/components/details/FlowDetail.svelte | 16 ++++++++++++---- .../lib/components/details/ScriptDetail.svelte | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/frontend/src/lib/components/details/FlowDetail.svelte b/frontend/src/lib/components/details/FlowDetail.svelte index bae35e2478..30f010f1ba 100644 --- a/frontend/src/lib/components/details/FlowDetail.svelte +++ b/frontend/src/lib/components/details/FlowDetail.svelte @@ -390,10 +390,18 @@ // The form here is a copy, so what the reader typed into it would be lost when the call // goes back to the card — the card would then offer, and run, the arguments this page - // opened with. Written on the way out rather than per keystroke: every way the page stops - // carrying a call and still holds edits destroys it, since the tab either closes or - // changes url, and `loadKey` remounts on that. - onDestroy(() => pendingRun?.handBack(args ?? {})) + // opened with. Written on the way out rather than per keystroke. + // + // Destroy covers the ways out that carry edits: the tab closes, or its url changes and + // `loadKey` remounts. Not the next call re-pointing this same tab, which swaps the form + // under a page that stays mounted; that page's edits are its own and go no further. + // + // Only once a form has held them. `args` is undefined until `RunForm` initialises, so a + // tab closed while the item is still being fetched would otherwise hand the card an empty + // object and wipe the proposal it is still parked on. + onDestroy(() => { + if (args !== undefined) pendingRun?.handBack(args) + }) // The dev workspace's editor is not one the session panel can host, so from a preview tab // it opens in a new browser tab, as the session editors' own entry does. diff --git a/frontend/src/lib/components/details/ScriptDetail.svelte b/frontend/src/lib/components/details/ScriptDetail.svelte index 6dd3a3a244..b7b4b6fa68 100644 --- a/frontend/src/lib/components/details/ScriptDetail.svelte +++ b/frontend/src/lib/components/details/ScriptDetail.svelte @@ -439,10 +439,18 @@ // The form here is a copy, so what the reader typed into it would be lost when the call // goes back to the card — the card would then offer, and run, the arguments this page - // opened with. Written on the way out rather than per keystroke: every way the page stops - // carrying a call and still holds edits destroys it, since the tab either closes or - // changes url, and `loadKey` remounts on that. - onDestroy(() => pendingRun?.handBack(args ?? {})) + // opened with. Written on the way out rather than per keystroke. + // + // Destroy covers the ways out that carry edits: the tab closes, or its url changes and + // `loadKey` remounts. Not the next call re-pointing this same tab, which swaps the form + // under a page that stays mounted; that page's edits are its own and go no further. + // + // Only once a form has held them. `args` is undefined until `RunForm` initialises, so a + // tab closed while the item is still being fetched would otherwise hand the card an empty + // object and wipe the proposal it is still parked on. + onDestroy(() => { + if (args !== undefined) pendingRun?.handBack(args) + }) // Read once on purpose: these args seed the form, so tracking the fragment would // overwrite what the user has typed whenever it changes.