fix(desktop): revoke optimistic blob previews when the consumer is discarded or replaced

Completes the #63682 ownership handoff flagged in review on #66546: the
queued-prompt side revoked discarded blob: previews, but the optimistic
bubble's previews were never released. Revoke when the optimistic message
is dropped (unless a rejected-submit restore re-loaded the attachments
into the composer, which re-owns them) and when the post-sync rewrite
replaces blob: refs with workspace-resolvable ones.

Co-authored-by: stantheman0128 <stanshih888@gmail.com>
This commit is contained in:
Hermes Agent
2026-09-25 15:27:42 -05:00
committed by brooklyn!
parent 5de3c046b0
commit fe59dc2dac

View File

@@ -18,6 +18,7 @@ import {
$composerAttachments,
type ComposerAttachment,
mainComposerScope,
revokeDiscardedAttachmentPreviews,
terminalContextBlocksFromDraft
} from '@/store/composer'
import { $hudMode } from '@/store/hud'
@@ -487,7 +488,11 @@ export function useSubmitPrompt(deps: SubmitPromptDeps) {
// After sync rewrites refs, refresh the optimistic message in place so the
// transcript shows the resolved @file: ref rather than the local path.
const rewriteOptimistic = (sid: string) =>
// Sync replaces blob: previews with workspace-resolvable refs, so any
// blob: URL the rewritten refs no longer retain is this consumer's last
// reference — release it (#63682 ownership handoff).
const rewriteOptimistic = (sid: string, syncedAttachments: ComposerAttachment[] = attachments) => {
revokeDiscardedAttachmentPreviews(attachments, syncedAttachments)
updateSessionState(
sid,
state => ({
@@ -496,8 +501,13 @@ export function useSubmitPrompt(deps: SubmitPromptDeps) {
}),
targetStoredSessionId
)
}
const dropOptimistic = (sid: null | string) => {
// The optimistic bubble is gone, so its blob: previews die with it —
// unless a rejected-submit restore already re-loaded the attachments
// into the composer, which re-owns those URLs (#63682 handoff).
revokeDiscardedAttachmentPreviews(attachments, usingComposerAttachments ? $composerAttachments.get() : [])
if (!sid) {
if (targetIsCurrentView()) {
scope.setMessages(current => current.filter(m => m.id !== optimisticId))
@@ -811,7 +821,7 @@ export function useSubmitPrompt(deps: SubmitPromptDeps) {
// the gateway receives @file: paths that resolve in its workspace.
// Images keep their inline bounded thumbnail — see optimisticAttachmentRef.
attachmentRefs = syncedAttachments.map(optimisticAttachmentRef).filter((r): r is string => Boolean(r))
rewriteOptimistic(liveSessionId)
rewriteOptimistic(liveSessionId, syncedAttachments)
const text = buildContextText(syncedAttachments)
// Another Desktop window may own a newer transcript while this one