fix(canvas): dedupe vote row and show model badge
This commit is contained in:
parent
7cc489a510
commit
1d293f180e
@ -158,6 +158,46 @@ function stripMediaMarkup(text: string): string {
|
||||
.trim();
|
||||
}
|
||||
|
||||
function extractCanvasModelName(text: string): string {
|
||||
const source = String(text || "");
|
||||
if (!source) return "";
|
||||
|
||||
const badgeMatch = source.match(
|
||||
/^\s*[◈◇◆🔹⬡✦•·]\s*([A-Za-z][A-Za-z0-9 .:+\-_/()]{2,})\s*$/im
|
||||
);
|
||||
if (badgeMatch?.[1]) {
|
||||
return badgeMatch[1].trim();
|
||||
}
|
||||
|
||||
const routedMatch = source.match(
|
||||
/^\s*(?:Routed to|Using model)\s*[::]\s*([^\n(]+).*$/im
|
||||
);
|
||||
if (routedMatch?.[1]) {
|
||||
return routedMatch[1].trim();
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function stripCanvasModelRoutingLines(text: string): string {
|
||||
return String(text || "")
|
||||
.replace(/^\s*(?:Routed to|Using model)\s*[::].*$/gim, "")
|
||||
.replace(/^\s*[◈◇◆🔹⬡✦•·]\s*[A-Za-z][A-Za-z0-9 .:+\-_/()]{2,}\s*$/gim, "")
|
||||
.replace(/\n{3,}/g, "\n\n")
|
||||
.trim();
|
||||
}
|
||||
|
||||
function messageHasAssistantMedia(message: ChatMessage | undefined): boolean {
|
||||
if (!message || message.role !== "assistant") return false;
|
||||
const textParts = (message.parts || []).filter(
|
||||
(part: any) => part.type === "text" && part.text
|
||||
);
|
||||
return textParts.some((part: any) => {
|
||||
const text = String(part.text || "");
|
||||
return extractImageUrls(text).length > 0 || extractVideoUrls(text).length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
function getMessageTimestamp(message: ChatMessage): number | null {
|
||||
const rawValue =
|
||||
(message as any).createdAt ??
|
||||
@ -4840,19 +4880,32 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
const assistantTextParts = message.role === "assistant"
|
||||
? (message.parts || []).filter((part: any) => part.type === "text" && part.text)
|
||||
: [];
|
||||
const assistantModelName =
|
||||
assistantTextParts
|
||||
.map((part: any) => extractCanvasModelName(part.text || ""))
|
||||
.find((name) => Boolean(name)) || "";
|
||||
const hasAssistantImages = assistantTextParts.some((part: any) =>
|
||||
extractImageUrls(part.text || "").length > 0
|
||||
);
|
||||
const hasAssistantVideos = assistantTextParts.some((part: any) =>
|
||||
extractVideoUrls(part.text || "").length > 0
|
||||
);
|
||||
const hasAssistantText = assistantTextParts.some((part: any) => {
|
||||
return stripMediaMarkup(part.text).length > 0;
|
||||
});
|
||||
const hasAssistantMedia = hasAssistantImages || hasAssistantVideos;
|
||||
const hasAssistantText = assistantTextParts.some((part: any) =>
|
||||
stripCanvasModelRoutingLines(stripMediaMarkup(part.text || "")).length > 0
|
||||
);
|
||||
const isImageOnlyAssistant =
|
||||
message.role === "assistant" &&
|
||||
(hasAssistantImages || hasAssistantVideos) &&
|
||||
hasAssistantMedia &&
|
||||
!hasAssistantText;
|
||||
const nextMessage = renderableVisibleMessages[index + 1] as ChatMessage | undefined;
|
||||
const nextAssistantHasMedia = messageHasAssistantMedia(nextMessage);
|
||||
const hideVoteActions =
|
||||
message.role === "assistant" &&
|
||||
!hasAssistantMedia &&
|
||||
nextAssistantHasMedia;
|
||||
const showVoteActions = message.role === "assistant" && !hideVoteActions;
|
||||
const showModelMeta = hideVoteActions && Boolean(assistantModelName);
|
||||
|
||||
const messageTimestamp = getMessageTimestamp(message);
|
||||
const prevMessageTimestamp =
|
||||
@ -4984,7 +5037,9 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
const rawText = part.text || '';
|
||||
const imageUrls = extractImageUrls(rawText);
|
||||
const videoUrls = extractVideoUrls(rawText);
|
||||
const textWithoutImages = stripMediaMarkup(rawText);
|
||||
const textWithoutImages = stripCanvasModelRoutingLines(
|
||||
stripMediaMarkup(rawText)
|
||||
);
|
||||
const analysisSection = extractCanvasImageAnalysisSection(textWithoutImages);
|
||||
const plainText = normalizeCanvasAgentText(textWithoutImages);
|
||||
const analysisToggleKey = `${message.id}-${i}-analysis`;
|
||||
@ -5109,31 +5164,40 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center gap-1.5 text-[#8f96a3]">
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-black/5",
|
||||
currentVote?.isUpvoted && "text-[#22a06b] bg-emerald-50/80"
|
||||
)}
|
||||
title="点赞"
|
||||
disabled={isVotingCurrentMessage}
|
||||
onClick={() => void handleAssistantVote(message.id, "up")}
|
||||
>
|
||||
<ThumbsUp className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-black/5",
|
||||
currentVote && !currentVote.isUpvoted && "text-[#d94848] bg-rose-50/80"
|
||||
)}
|
||||
title="点踩"
|
||||
disabled={isVotingCurrentMessage}
|
||||
onClick={() => void handleAssistantVote(message.id, "down")}
|
||||
>
|
||||
<ThumbsDown className="h-4 w-4" />
|
||||
</button>
|
||||
<div className="mt-1 flex items-center gap-2 text-[#8f96a3]">
|
||||
{showModelMeta ? (
|
||||
<span className="text-[12px] font-medium tracking-tight text-[#8d94a2]">
|
||||
◈ {assistantModelName}
|
||||
</span>
|
||||
) : null}
|
||||
{showVoteActions ? (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-black/5",
|
||||
currentVote?.isUpvoted && "text-[#22a06b] bg-emerald-50/80"
|
||||
)}
|
||||
title="点赞"
|
||||
disabled={isVotingCurrentMessage}
|
||||
onClick={() => void handleAssistantVote(message.id, "up")}
|
||||
>
|
||||
<ThumbsUp className="h-4 w-4" />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"inline-flex h-7 w-7 items-center justify-center rounded-md transition-colors hover:bg-black/5",
|
||||
currentVote && !currentVote.isUpvoted && "text-[#d94848] bg-rose-50/80"
|
||||
)}
|
||||
title="点踩"
|
||||
disabled={isVotingCurrentMessage}
|
||||
onClick={() => void handleAssistantVote(message.id, "down")}
|
||||
>
|
||||
<ThumbsDown className="h-4 w-4" />
|
||||
</button>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@ -5156,7 +5220,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
|
||||
<div className="flex w-full justify-start">
|
||||
<div className="flex items-center gap-2 text-[#8e97a5]">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
<span className="text-[16px]">思考中</span>
|
||||
<span className="text-[16px]">分析中</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user