feat: add click-to-preview for image attachments in chat

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xintaofei
2026-04-02 15:23:48 +08:00
parent 2a3b4b1908
commit f23ed12650
3 changed files with 121 additions and 11 deletions

View File

@@ -29,6 +29,7 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { ImagePreviewDialog } from "@/components/ui/image-preview-dialog"
import { cn, randomUUID } from "@/lib/utils"
import { matchShortcutEvent } from "@/lib/keyboard-shortcuts"
import { useShortcutSettings } from "@/hooks/use-shortcut-settings"
@@ -303,6 +304,9 @@ export function MessageInput({
})
const [attachments, setAttachments] = useState<InputAttachment[]>([])
const [isDragActive, setIsDragActive] = useState(false)
const [previewAttachmentId, setPreviewAttachmentId] = useState<string | null>(
null
)
const containerRef = useRef<HTMLDivElement>(null)
const textareaRef = useRef<HTMLTextAreaElement>(null)
const lastDomDropAtRef = useRef(0)
@@ -394,6 +398,13 @@ export function MessageInput({
),
[attachments]
)
const previewAttachment = useMemo(
() =>
previewAttachmentId
? (imageAttachments.find((a) => a.id === previewAttachmentId) ?? null)
: null,
[previewAttachmentId, imageAttachments]
)
const resourceAttachments = useMemo(
() =>
attachments.filter(
@@ -1275,14 +1286,20 @@ export function MessageInput({
key={attachment.id}
className="relative shrink-0 overflow-hidden rounded-md border border-border/70 bg-muted/30"
>
<Image
src={`data:${attachment.mimeType};base64,${attachment.data}`}
alt={attachment.name}
width={56}
height={56}
unoptimized
className="h-14 w-14 object-cover"
/>
<button
type="button"
onClick={() => setPreviewAttachmentId(attachment.id)}
className="cursor-pointer transition-opacity hover:opacity-80"
>
<Image
src={`data:${attachment.mimeType};base64,${attachment.data}`}
alt={attachment.name}
width={56}
height={56}
unoptimized
className="h-14 w-14 object-cover"
/>
</button>
<button
type="button"
onClick={() => removeAttachment(attachment.id)}
@@ -1378,6 +1395,18 @@ export function MessageInput({
{t("dropFilesToAttach")}
</div>
)}
<ImagePreviewDialog
src={
previewAttachment
? `data:${previewAttachment.mimeType};base64,${previewAttachment.data}`
: ""
}
alt={previewAttachment?.name ?? ""}
open={previewAttachment !== null}
onOpenChange={(open) => {
if (!open) setPreviewAttachmentId(null)
}}
/>
</div>
)
}