feat(coding): complete PI conversation UI

This commit is contained in:
brother7 committed 2026-08-24 08:59:16 +08:00
1 parent 2613d6530b
commit 863f005203
21 files changed
+2040 -137

No files matched your search

@@ -0,0 +1,66 @@
import { memo, useEffect, useState } from 'react';
import { FileImage } from 'lucide-react';
import { loadCodingAttachmentPreview } from '@/lib/coding-attachments';
export const CodingAttachmentPreview = memo(function CodingAttachmentPreview({
attachmentId,
mime,
caption = '对话图片附件',
}: {
attachmentId: string;
mime: string;
caption?: string;
}) {
const [state, setState] = useState<{
attachmentId: string;
previewUrl: string | null;
failed: boolean;
}>({ attachmentId, previewUrl: null, failed: false });
useEffect(() => {
let active = true;
let objectUrl: string | null = null;
void loadCodingAttachmentPreview(attachmentId)
.then((preview) => {
if (!active) return;
const bytes = Uint8Array.from(preview.bytes);
objectUrl = URL.createObjectURL(new Blob([bytes], { type: preview.mime }));
setState({ attachmentId, previewUrl: objectUrl, failed: false });
})
.catch(() => {
if (active) setState({ attachmentId, previewUrl: null, failed: true });
});
return () => {
active = false;
if (objectUrl) URL.revokeObjectURL(objectUrl);
};
}, [attachmentId]);
const previewUrl = state.attachmentId === attachmentId ? state.previewUrl : null;
const failed = state.attachmentId === attachmentId && state.failed;
return (
<figure
className="max-w-sm overflow-hidden rounded-2xl bg-foreground/[0.04] shadow-[0_0_0_1px_rgba(0,0,0,0.06)]"
data-attachment-id={attachmentId}
>
{previewUrl
? (
<img
src={previewUrl}
alt={caption}
className="max-h-80 w-full object-contain outline outline-1 -outline-offset-1 outline-black/10"
/>
)
: (
<div className="flex min-h-28 items-center justify-center gap-2 px-4 text-sm text-muted-foreground">
<FileImage className="h-5 w-5" aria-hidden="true" />
{failed ? '附件预览不可用' : '正在加载附件…'}
</div>
)}
<figcaption className="truncate px-3 py-2 font-mono text-[11px] text-muted-foreground">
{mime} · {attachmentId}
</figcaption>
</figure>
);
});