From ba10d4e5be65dd9257be37ba73786136bf867460 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 21 Feb 2026 05:43:27 +0800 Subject: [PATCH] style: refine model badge typography to match competitor --- components/canvas/CanvasChatPanel.tsx | 7 +-- components/message.tsx | 61 +++++++++++++++++++-------- 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index fa9e17aa2..1e0fd3568 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -5,7 +5,7 @@ import { DefaultChatTransport } from "ai"; import { useEffect, useRef, useState, useCallback, useMemo } from "react"; import { createPortal } from "react-dom"; import useSWR, { useSWRConfig } from "swr"; -import { Send, Loader2, ImagePlus, Paperclip, X, Plus, History, LayoutGrid, Maximize2, RefreshCw, Globe, Box, ArrowUp, Video, Sparkles, Check, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Brain, ThumbsUp, ThumbsDown } from "lucide-react"; +import { Send, Loader2, ImagePlus, Paperclip, X, Plus, History, LayoutGrid, Maximize2, RefreshCw, Globe, Box, ArrowUp, Video, Sparkles, Check, ChevronDown, ChevronUp, AlertCircle, CheckCircle2, Brain, ThumbsUp, ThumbsDown, Eye } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { @@ -5492,8 +5492,9 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
{showModelMeta ? ( - - ◈ {assistantModelName} + + + {assistantModelName} ) : null} {showVoteActions ? ( diff --git a/components/message.tsx b/components/message.tsx index a346590d5..77a96e231 100644 --- a/components/message.tsx +++ b/components/message.tsx @@ -1,6 +1,7 @@ "use client"; import type { UseChatHelpers } from "@ai-sdk/react"; import equal from "fast-deep-equal"; +import { Eye } from "lucide-react"; import { memo, useState } from "react"; import type { Vote } from "@/lib/db/schema"; import type { ChatMessage } from "@/lib/types"; @@ -174,18 +175,22 @@ function filterAgentRoutingNoise(text: string): string { } const IMAGE_DONE_SUMMARY = "已完成本次生成,如需微调可继续告诉我。"; -const MODEL_BADGE_REGEX = - /^\s*[◈◇◆🔹⬡✦•·]\s*[A-Za-z][A-Za-z0-9 .:+\-_/()]{2,}\s*$/im; +const MODEL_BADGE_LINE_REGEX = + /^\s*[◈◇◆🔹⬡✦•·]\s*([A-Za-z][A-Za-z0-9 .:+\-_/()]{2,})\s*$/im; +const MODEL_BADGE_LINE_GLOBAL_REGEX = + /^\s*[◈◇◆🔹⬡✦•·]\s*[A-Za-z][A-Za-z0-9 .:+\-_/()]{2,}\s*$/gim; -function ensureImageModelBadge(text: string, fallbackModelName?: string): string { - if (!text) return text; - const normalized = String(text || "").trim(); - if (!normalized) return normalized; - const modelName = String(fallbackModelName || "").trim(); - if (!modelName) return normalized; - if (!/!\[[^\]]*\]\((https?:\/\/[^\s)]+)\)/i.test(normalized)) return normalized; - if (MODEL_BADGE_REGEX.test(normalized)) return normalized; - return `◈ ${modelName}\n\n${normalized}`; +function extractModelBadgeName(text: string): string { + if (!text) return ""; + const match = String(text || "").match(MODEL_BADGE_LINE_REGEX); + return match?.[1]?.trim() || ""; +} + +function stripModelBadgeLines(text: string): string { + return String(text || "") + .replace(MODEL_BADGE_LINE_GLOBAL_REGEX, "") + .replace(/\n{3,}/g, "\n\n") + .trim(); } function appendImageDoneSummary(text: string): string { @@ -342,19 +347,39 @@ const PurePreviewMessage = ({ } // 应用视频内容过滤 + const routedFilteredText = + message.role === "assistant" + ? filterAgentRoutingNoise(filterVideoContent(part.text)) + : part.text; + const extractedModelName = + message.role === "assistant" + ? extractModelBadgeName(routedFilteredText) + : ""; + const contentWithoutBadge = + message.role === "assistant" + ? stripModelBadgeLines(routedFilteredText) + : routedFilteredText; + const inferredFallbackModelName = + message.role === "assistant" && + !extractedModelName && + extractImageUrlsFromText(contentWithoutBadge).length > 0 + ? String(fallbackImageModelName || "").trim() + : ""; + const displayModelName = extractedModelName || inferredFallbackModelName; const filteredText = message.role === "assistant" - ? appendImageDoneSummary( - ensureImageModelBadge( - filterAgentRoutingNoise(filterVideoContent(part.text)), - fallbackImageModelName - ) - ) - : part.text; + ? appendImageDoneSummary(contentWithoutBadge) + : contentWithoutBadge; const textParts = parseAnnotations(filteredText); return (
+ {message.role === "assistant" && displayModelName ? ( +
+ + {displayModelName} +
+ ) : null}