iframe 页面优化
This commit is contained in:
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
|
||||
|
||||
Reference in new issue
Block a user