diff --git a/frontend/src/lib/components/VariableEditor.svelte b/frontend/src/lib/components/VariableEditor.svelte index 234798a68c..391949ee00 100644 --- a/frontend/src/lib/components/VariableEditor.svelte +++ b/frontend/src/lib/components/VariableEditor.svelte @@ -304,7 +304,9 @@ payloads.find((pl) => pl.ws === shownWs)?.s.path ?? payloads.find((pl) => pl.ws === fromWs)?.s.path ?? from - let actingCommitted = false + // Committed by the workspace the form is showing — the one `actingPath` follows, + // so the two cannot disagree about who this editor is tracking. + let shownCommitted = false // Every workspace written, with the path it wrote there. Reported once the loop // is done: per workspace, each carrying its own, so a linked workspace's rename // moves the tabs acting on it and no others. @@ -348,7 +350,7 @@ initialStates[ws] = s existedInitially[ws] = true } - if (ws === fromWs) actingCommitted = true + if (ws === (shownWs ?? fromWs)) shownCommitted = true // The just-saved state is the new deployed baseline; this resets the handle // to it via `discard` (not `remove` — blanking the cell to `undefined` reads // as dirty) and keeps an edit made mid-request. Each workspace settles @@ -385,7 +387,13 @@ // back the values this drawer opened on, which describe neither what the // committed workspace now has deployed nor the draft the failed one still // holds — that one stays at its own path, editable from its workspace. - if (actingCommitted && actingPath && editPath === from) editVariable(actingPath) + if (shownCommitted && actingPath && editPath === from) { + // Reopen on the workspace whose write this is following, not on the one the + // editor defaults to: `editVariable` selects `curWs`, which for a linked + // version would load a different workspace's item at this path. + editVariable(actingPath) + if (shownWs) selected = shownWs + } } } diff --git a/frontend/src/lib/components/copilot/chat/global/core.ts b/frontend/src/lib/components/copilot/chat/global/core.ts index 18dbf9c077..99943c8d2b 100644 --- a/frontend/src/lib/components/copilot/chat/global/core.ts +++ b/frontend/src/lib/components/copilot/chat/global/core.ts @@ -6182,9 +6182,16 @@ async function discardLocalDraft( // Published only now: the delete above can throw, and the marker is read by the // tool-completion listener, which a throw never reaches — leaving it to be // consumed by some later action on this item, which would send its editor away - // while the item is still there. + // while the item is still there. Nor is it published when a write is queued + // behind the delete: the form stays editable across it, and an edit made then + // recreates the draft, so the item the marker would report as gone is not. if (removesItem && discardedKind) { - UserDraft.recordDraftOnlyDiscard(discardedKind, storagePath, { workspace }) + const settled = + UserDraftDbSyncer.getState({ workspace, itemKind: discardedKind, path: storagePath }) + .state === 'none' + if (settled) { + UserDraft.recordDraftOnlyDiscard(discardedKind, storagePath, { workspace }) + } } // The chat's touch on the item is undone — drop it from the mask so a