fix(canvas): render video-mode user messages when attachment parts use data field
This commit is contained in:
parent
d1518a7606
commit
0ac60e3c4e
@ -3179,7 +3179,13 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
hasContent = true;
|
||||
break;
|
||||
}
|
||||
if ((part.type === 'file' || part.type === 'image') && part.url) {
|
||||
const fileUrl =
|
||||
typeof part?.url === "string" && part.url.trim()
|
||||
? part.url
|
||||
: typeof part?.data === "string" && part.data.trim()
|
||||
? part.data
|
||||
: "";
|
||||
if ((part.type === 'file' || part.type === 'image') && fileUrl) {
|
||||
hasContent = true;
|
||||
break;
|
||||
}
|
||||
@ -3249,50 +3255,80 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
// 🆕 用户消息:使用图片标签样式(类似竞品,按 parts 原始顺序渲染)
|
||||
<div className="bg-card border rounded-2xl rounded-tr-sm px-3 py-2 shadow-sm">
|
||||
<div className="flex flex-wrap items-center gap-1.5">
|
||||
{/* 按 parts 原始顺序渲染图片标签和文字 */}
|
||||
{cleanedMessage.parts?.map((part: any, idx: number) => {
|
||||
if (part.type === 'file' && part.url) {
|
||||
// 图片标签
|
||||
return (
|
||||
<span
|
||||
key={`part-${idx}`}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-background border rounded-lg text-xs"
|
||||
>
|
||||
<img
|
||||
src={part.url}
|
||||
alt=""
|
||||
className="w-5 h-5 object-cover rounded"
|
||||
/>
|
||||
<span className="max-w-[80px] truncate text-foreground">{part.name || 'Image'}</span>
|
||||
{(() => {
|
||||
const renderedUserParts: React.ReactNode[] = [];
|
||||
|
||||
cleanedMessage.parts?.forEach((part: any, idx: number) => {
|
||||
const fileUrl =
|
||||
typeof part?.url === "string" && part.url.trim()
|
||||
? part.url
|
||||
: typeof part?.data === "string" && part.data.trim()
|
||||
? part.data
|
||||
: "";
|
||||
|
||||
if (part.type === 'file' && fileUrl) {
|
||||
renderedUserParts.push(
|
||||
<span
|
||||
key={`part-${idx}`}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-background border rounded-lg text-xs"
|
||||
>
|
||||
<img
|
||||
src={fileUrl}
|
||||
alt=""
|
||||
className="w-5 h-5 object-cover rounded"
|
||||
/>
|
||||
<span className="max-w-[80px] truncate text-foreground">{part.name || 'Image'}</span>
|
||||
</span>
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (part.type === 'text' && part.text) {
|
||||
const text = String(part.text)
|
||||
.replace(/【注意:图片中有标注(箭头、圆圈、文字等)标记的区域是需要重点关注或修改的部分,请根据标注进行处理】\n?/g, '')
|
||||
.trim();
|
||||
if (!text) return;
|
||||
renderedUserParts.push(
|
||||
<span key={`part-${idx}`} className="text-sm text-foreground">{text}</span>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (
|
||||
!cleanedMessage.parts?.some((p: any) => p.type === 'file') &&
|
||||
(message as any).attachments?.length > 0
|
||||
) {
|
||||
(message as any).attachments.forEach((att: any, idx: number) => {
|
||||
if (!att?.url) return;
|
||||
renderedUserParts.push(
|
||||
<span
|
||||
key={`att-${idx}`}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-background border rounded-lg text-xs"
|
||||
>
|
||||
<img
|
||||
src={att.url}
|
||||
alt=""
|
||||
className="w-5 h-5 object-cover rounded"
|
||||
/>
|
||||
<span className="max-w-[80px] truncate text-foreground">{att.name || 'Image'}</span>
|
||||
</span>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
if (renderedUserParts.length === 0) {
|
||||
const fallbackText =
|
||||
extractUserTextPrompt(cleanedMessage as any) ||
|
||||
(isVideoModel ? "已发送视频生成请求" : "已发送请求");
|
||||
renderedUserParts.push(
|
||||
<span key="fallback-text" className="text-sm text-foreground">
|
||||
{fallbackText}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (part.type === 'text' && part.text) {
|
||||
// 文字(去掉标注提示前缀)
|
||||
let text = part.text.replace(/【注意:图片中有标注(箭头、圆圈、文字等)标记的区域是需要重点关注或修改的部分,请根据标注进行处理】\n?/g, '').trim();
|
||||
if (!text) return null;
|
||||
return (
|
||||
<span key={`part-${idx}`} className="text-sm text-foreground">{text}</span>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
{/* 如果有 attachments 但没有 file parts,也显示 */}
|
||||
{(!cleanedMessage.parts?.some((p: any) => p.type === 'file') && (message as any).attachments?.length > 0) &&
|
||||
(message as any).attachments.map((att: any, idx: number) => (
|
||||
<span
|
||||
key={`att-${idx}`}
|
||||
className="inline-flex items-center gap-1 px-2 py-1 bg-background border rounded-lg text-xs"
|
||||
>
|
||||
<img
|
||||
src={att.url}
|
||||
alt=""
|
||||
className="w-5 h-5 object-cover rounded"
|
||||
/>
|
||||
<span className="max-w-[80px] truncate text-foreground">{att.name || 'Image'}</span>
|
||||
</span>
|
||||
))
|
||||
}
|
||||
|
||||
return renderedUserParts;
|
||||
})()}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user