diff --git a/apps/desktop/src/app/chat/composer/attachments.tsx b/apps/desktop/src/app/chat/composer/attachments.tsx index 0c154a8a4b1..6229c9da8bd 100644 --- a/apps/desktop/src/app/chat/composer/attachments.tsx +++ b/apps/desktop/src/app/chat/composer/attachments.tsx @@ -3,8 +3,9 @@ import { useStore } from '@nanostores/react' import { Codicon } from '@/components/ui/codicon' import { Tip } from '@/components/ui/tooltip' import { useI18n } from '@/i18n' -import { FileText, FolderOpen, ImageIcon, Link, Terminal } from '@/lib/icons' +import { AlertCircle, FileText, FolderOpen, ImageIcon, Link, Loader2, Terminal } from '@/lib/icons' import { normalizeOrLocalPreviewTarget } from '@/lib/local-preview' +import { cn } from '@/lib/utils' import type { ComposerAttachment } from '@/store/composer' import { notifyError } from '@/store/notifications' import { setCurrentSessionPreviewTarget } from '@/store/preview' @@ -31,7 +32,9 @@ function AttachmentPill({ attachment, onRemove }: { attachment: ComposerAttachme const c = t.composer const Icon = { folder: FolderOpen, url: Link, image: ImageIcon, file: FileText, terminal: Terminal }[attachment.kind] const cwd = useStore($currentCwd) - const canPreview = attachment.kind !== 'folder' && attachment.kind !== 'terminal' + const isUploading = attachment.uploadState === 'uploading' + const hasUploadError = attachment.uploadState === 'error' + const canPreview = attachment.kind !== 'folder' && attachment.kind !== 'terminal' && !isUploading const detail = attachment.detail && attachment.detail !== attachment.label ? attachment.detail : undefined async function openPreview() { @@ -59,7 +62,15 @@ function AttachmentPill({ attachment, onRemove }: { attachment: ComposerAttachme throw new Error(c.couldNotPreview(attachment.label)) } - setCurrentSessionPreviewTarget(preview, 'manual', target) + // We already hold the image bytes (the card thumbnail) — render those + // directly so a screenshot/clipboard image previews even when its only + // on-disk copy is a transient path the renderer can't re-read. + const withBytes = + attachment.kind === 'image' && attachment.previewUrl + ? { ...preview, dataUrl: attachment.previewUrl, previewKind: 'image' as const } + : preview + + setCurrentSessionPreviewTarget(withBytes, 'manual', target) } catch (error) { notifyError(error, c.previewUnavailable) } @@ -69,30 +80,51 @@ function AttachmentPill({ attachment, onRemove }: { attachment: ComposerAttachme