style: refine model badge typography to match competitor
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-02-21 05:43:27 +08:00
parent 8cd3fa2954
commit ba10d4e5be
2 changed files with 47 additions and 21 deletions

View File

@ -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
</div>
<div className="mt-1 flex items-center gap-2 text-[#8f96a3]">
{showModelMeta ? (
<span className="text-[12px] font-medium tracking-tight text-[#8d94a2]">
◈ {assistantModelName}
<span className="inline-flex items-center gap-1.5 text-[11px] font-medium tracking-tight text-[#a2a9b5]">
<Eye className="h-3.5 w-3.5 opacity-80" />
<span>{assistantModelName}</span>
</span>
) : null}
{showVoteActions ? (

View File

@ -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 (
<div key={key}>
{message.role === "assistant" && displayModelName ? (
<div className="mb-2 inline-flex items-center gap-1.5 text-[12px] font-medium leading-none text-[#a2a9b5]">
<Eye className="h-3.5 w-3.5 opacity-80" />
<span className="tracking-tight">{displayModelName}</span>
</div>
) : null}
<MessageContent
className={cn({
"w-fit break-words rounded-2xl px-3 py-2 text-right text-white":