fix(desktop): render optimistic image thumbnails from in-hand base64
The in-flight user bubble seeded image attachment refs as `@image:<localpath>`.
In remote-gateway mode that path lives on the desktop, not the gateway, so the
inline thumbnail fetch hit /api/media and 403'd ("Path outside media roots"),
flashing a fallback chip until submit uploaded the bytes.
Seed (and keep) image refs as the raw base64 preview data URL instead. It
renders inline via extractEmbeddedImages with zero network, and survives the
post-sync rewrite (the agent gets the bytes through the attached-image pipeline,
not this display ref) so the thumbnail no longer remounts/flashes. Non-image
refs are unchanged.
Adds optimisticAttachmentRef + unit coverage.
This commit is contained in:
@@ -6,7 +6,7 @@ import { getProfiles, transcribeAudio } from '@/hermes'
|
||||
import { translateNow, type Translations, useI18n } from '@/i18n'
|
||||
import { branchGroupForUser, type ChatMessage, chatMessageText, textPart } from '@/lib/chat-messages'
|
||||
import {
|
||||
attachmentDisplayText,
|
||||
optimisticAttachmentRef,
|
||||
parseCommandDispatch,
|
||||
parseSlashCommand,
|
||||
pathLabel,
|
||||
@@ -418,7 +418,9 @@ export function usePromptActions({
|
||||
// Refs are recomputed after sync (file.attach rewrites @file: refs to
|
||||
// workspace-relative paths the remote gateway can resolve). Seed the
|
||||
// optimistic message with the pre-sync refs, then rewrite once synced.
|
||||
let attachmentRefs = attachments.map(attachmentDisplayText).filter((r): r is string => Boolean(r))
|
||||
// Images use their base64 preview so the thumbnail renders inline without
|
||||
// a (remote-mode 403-prone) /api/media fetch — see optimisticAttachmentRef.
|
||||
let attachmentRefs = attachments.map(optimisticAttachmentRef).filter((r): r is string => Boolean(r))
|
||||
const buildContextText = (atts: ComposerAttachment[]): string => {
|
||||
const contextRefs = atts
|
||||
.map(a => a.refText)
|
||||
@@ -550,7 +552,8 @@ export function usePromptActions({
|
||||
})
|
||||
// Rewrite the optimistic message + prompt text with the synced refs so
|
||||
// the gateway receives @file: paths that resolve in its workspace.
|
||||
attachmentRefs = syncedAttachments.map(attachmentDisplayText).filter((r): r is string => Boolean(r))
|
||||
// (Images keep their inline base64 preview — see optimisticAttachmentRef.)
|
||||
attachmentRefs = syncedAttachments.map(optimisticAttachmentRef).filter((r): r is string => Boolean(r))
|
||||
rewriteOptimistic(sessionId)
|
||||
const text = buildContextText(syncedAttachments)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user