From f154863061806bf9e3182c41f2bdca0179b8e76d Mon Sep 17 00:00:00 2001 From: Diego Imbert Date: Sat, 5 Sep 2026 19:49:33 +0200 Subject: [PATCH] fix(sessions): keep the tab put when a resource save fails Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KcyBRxE8j3ujCYkum9KrC4 --- .../src/lib/components/ResourceEditorDrawer.svelte | 10 +++++++--- .../src/lib/components/sessions/PreviewTabHost.svelte | 10 +++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frontend/src/lib/components/ResourceEditorDrawer.svelte b/frontend/src/lib/components/ResourceEditorDrawer.svelte index 4939a1ef20..795a413cbd 100644 --- a/frontend/src/lib/components/ResourceEditorDrawer.svelte +++ b/frontend/src/lib/components/ResourceEditorDrawer.svelte @@ -57,7 +57,8 @@ let resourceEditor: | { - save: () => void + /** False when the write failed; it toasts its own error. */ + save: () => Promise localDraftDeployed: () => unknown localDraftCurrent: () => unknown discardLocalDraft: () => void @@ -205,9 +206,12 @@ // Closed before the write is awaited, the way it always was: `save()` toasts its // own failures and never rejects, so waiting would only add visible lag to every // caller of this drawer. `onSaved` still fires after the write lands. - const saved = resourceEditor?.save() + const saving = resourceEditor?.save() drawer?.closeDrawer() - await saved + // Everything below moves this host onto the path that was written, so it + // must not run for a write that failed — a rejected rename (a name + // collision, say) would point the tab at a path this save never created. + if (!(await saving)) return // Rendered inline there is no drawer to close, so the mounted editor would // otherwise keep the pre-save baseline. Follow a rename before remounting, // or it comes back up on a path the save just moved the item off. diff --git a/frontend/src/lib/components/sessions/PreviewTabHost.svelte b/frontend/src/lib/components/sessions/PreviewTabHost.svelte index b0ae98ecf9..a44f64de1b 100644 --- a/frontend/src/lib/components/sessions/PreviewTabHost.svelte +++ b/frontend/src/lib/components/sessions/PreviewTabHost.svelte @@ -86,13 +86,9 @@ let frame: HTMLIFrameElement | undefined = $state() - // Bumped to remount a hosted entity editor, which is how it re-reads the item - // from the server: its own state is built at mount from the draft cell, so - // there is nothing to refresh in place once that cell has been dropped. The - // acting workspace joins it in the key for the same reason — these editors - // load and save against the workspace they were mounted with, so a session - // that rescopes (a staged fork materialising on first send) must not leave - // them bound to the previous one. + // A hosted entity editor builds its state at mount, from the draft cell and the + // workspace it was given, and can refresh neither in place — so both the bump + // below and a change of `workspaceId` key it, and remounting is how it re-reads. let entityNonce = $state(0) // Pages whose theme we mirror on live toggles. Regular apps are the only item