iframe 页面优化

This commit is contained in:
ZyGuo committed 2026-06-01 17:27:57 +08:00
1 parent 16adb90653
commit e6af567206
3 files changed
+231 -47

No files matched your search

@@ -45,13 +45,53 @@ import { humanMessagePlugins } from "@/core/streamdown";
import { desensitize } from "@/lib/desensitize";
import { cn } from "@/lib/utils";
import { CitationLink } from "../citations/citation-link";
import { CopyButton } from "../copy-button";
import { SkillDisplayBlock } from "../skill-display-block";
import { ThreadContext } from "./context";
import { DownloadWordButton } from "./download-word-button";
import { MarkdownContent, type MarkdownContentProps } from "./markdown-content";
import { MessageTokenUsage } from "./message-token-usage";
import { CitationLink } from "../citations/citation-link";
import { CopyButton } from "../copy-button";
import { SkillDisplayBlock } from "../skill-display-block";
import { ThreadContext } from "./context";
import { DownloadWordButton } from "./download-word-button";
import { MarkdownContent, type MarkdownContentProps } from "./markdown-content";
import { MessageTokenUsage } from "./message-token-usage";
type EmbeddedPageContext = {
title?: string;
url?: string;
content?: string;
};
function extractTagContent(source: string, tagName: string) {
const match = new RegExp(`<${tagName}>\\s*([\\s\\S]*?)\\s*</${tagName}>`, "i").exec(
source,
);
return match?.[1]?.trim() ?? "";
}
function parseEmbeddedPageContext(source: string): {
content: string;
pageContext: EmbeddedPageContext | null;
} {
const match = /<page_context>\s*([\s\S]*?)\s*<\/page_context>/i.exec(source);
if (!match) {
return { content: source, pageContext: null };
}
const body = match[1] ?? "";
const pageContext = {
title: extractTagContent(body, "title") || undefined,
url: extractTagContent(body, "url") || undefined,
content: extractTagContent(body, "content") || undefined,
};
const cleanedContent = source
.replace(match[0], "")
.replace(/\n{3,}/g, "\n\n")
.trim();
if (!pageContext.title && !pageContext.url && !pageContext.content) {
return { content: cleanedContent, pageContext: null };
}
return { content: cleanedContent, pageContext };
}
/**
* Resolve an in-conversation file path to a fetchable URL.
@@ -165,7 +205,7 @@ function MessageImage({
);
}
function MessageContent_({
function MessageContent_({
className,
message,
isLoading = false,
@@ -239,17 +279,24 @@ function MessageContent_({
return files as FileInMessage[];
}, [message.additional_kwargs?.files, rawContent]);
const contentToDisplay = useMemo(() => {
if (isHuman) {
return rawContent ? stripArtifactTag(stripUploadedFilesTag(rawContent)) : "";
}
return applyInlineCitations(rawContent ?? "", displaySources);
}, [rawContent, isHuman, displaySources]);
const filesList =
files && files.length > 0 ? (
<RichFilesList files={files} threadId={threadId} />
) : null;
const contentToDisplay = useMemo(() => {
if (isHuman) {
return rawContent ? stripArtifactTag(stripUploadedFilesTag(rawContent)) : "";
}
return applyInlineCitations(rawContent ?? "", displaySources);
}, [rawContent, isHuman, displaySources]);
const parsedHumanContent = useMemo(
() =>
isHuman
? parseEmbeddedPageContext(contentToDisplay)
: { content: contentToDisplay, pageContext: null },
[contentToDisplay, isHuman],
);
const filesList =
files && files.length > 0 ? (
<RichFilesList files={files} threadId={threadId} />
) : null;
// Uploading state: mock AI message shown while files upload
if (message.additional_kwargs?.element === "task") {
@@ -284,25 +331,28 @@ function MessageContent_({
);
}
if (isHuman) {
const messageResponse = contentToDisplay ? (
<AIElementMessageResponse
remarkPlugins={humanMessagePlugins.remarkPlugins}
rehypePlugins={humanMessagePlugins.rehypePlugins}
components={components}
parseIncompleteMarkdown={false}
>
{desensitize(contentToDisplay)}
</AIElementMessageResponse>
) : null;
return (
<div className={cn("ml-auto flex flex-col gap-2", className)}>
{filesList}
{messageResponse && (
<AIElementMessageContent className="w-fit">
{messageResponse}
</AIElementMessageContent>
)}
if (isHuman) {
const messageResponse = parsedHumanContent.content ? (
<AIElementMessageResponse
remarkPlugins={humanMessagePlugins.remarkPlugins}
rehypePlugins={humanMessagePlugins.rehypePlugins}
components={components}
parseIncompleteMarkdown={false}
>
{desensitize(parsedHumanContent.content)}
</AIElementMessageResponse>
) : null;
return (
<div className={cn("ml-auto flex flex-col gap-2", className)}>
{filesList}
{parsedHumanContent.pageContext ? (
<EmbeddedPageContextCard pageContext={parsedHumanContent.pageContext} />
) : null}
{messageResponse && (
<AIElementMessageContent className="w-fit">
{messageResponse}
</AIElementMessageContent>
)}
<MessageTimeStamp time={messageTime} align="right" />
</div>
);
@@ -331,7 +381,39 @@ function MessageContent_({
) : null}
</AIElementMessageContent>
);
}
}
function EmbeddedPageContextCard({
pageContext,
}: {
pageContext: EmbeddedPageContext;
}) {
return (
<div className="bg-muted/70 ml-auto flex max-w-sm flex-col gap-2 rounded-2xl border px-3 py-2 text-sm shadow-sm">
{pageContext.title ? (
<div className="truncate font-medium" title={pageContext.title}>
{pageContext.title}
</div>
) : null}
{pageContext.url ? (
<a
className="text-muted-foreground block truncate text-xs underline underline-offset-2"
href={pageContext.url}
target="_blank"
rel="noopener noreferrer"
title={pageContext.url}
>
{pageContext.url}
</a>
) : null}
{pageContext.content ? (
<div className="text-muted-foreground truncate text-xs" title={pageContext.content}>
{pageContext.content}
</div>
) : null}
</div>
);
}
/**
* Format a wall-clock message time as ``YYYY-MM-DD HH:mm`` for display, plus a