feat: enhance message display

This commit is contained in:
Henry Li
2026-01-18 11:25:46 +08:00
parent 59683fc12e
commit 23dc64fab1
3 changed files with 116 additions and 67 deletions

View File

@@ -1,5 +1,5 @@
import type { Message } from "@langchain/langgraph-sdk";
import { memo } from "react";
import { memo, useMemo } from "react";
import {
Message as AIElementMessage,
@@ -9,25 +9,20 @@ import {
} from "@/components/ai-elements/message";
import {
extractContentFromMessage,
hasReasoning,
hasToolCalls,
extractReasoningContentFromMessage,
} from "@/core/messages/utils";
import { useRehypeSplitWordsIntoSpans } from "@/core/rehype";
import { cn } from "@/lib/utils";
import { CopyButton } from "../copy-button";
import { MessageGroup } from "./message-group";
export function MessageListItem({
className,
message,
messagesInGroup,
isLoading,
}: {
className?: string;
message: Message;
messagesInGroup: Message[];
isLoading?: boolean;
}) {
return (
@@ -38,7 +33,6 @@ export function MessageListItem({
<MessageContent
className={message.type === "human" ? "w-fit" : "w-full"}
message={message}
messagesInGroup={messagesInGroup}
isLoading={isLoading}
/>
<MessageToolbar
@@ -49,7 +43,13 @@ export function MessageListItem({
)}
>
<div className="flex gap-1">
<CopyButton clipboardData={extractContentFromMessage(message)} />
<CopyButton
clipboardData={
extractContentFromMessage(message)
? extractContentFromMessage(message)
: (extractReasoningContentFromMessage(message) ?? "")
}
/>
</div>
</MessageToolbar>
</AIElementMessage>
@@ -59,26 +59,26 @@ export function MessageListItem({
function MessageContent_({
className,
message,
messagesInGroup,
isLoading = false,
}: {
className?: string;
message: Message;
messagesInGroup: Message[];
isLoading?: boolean;
}) {
const rehypePlugins = useRehypeSplitWordsIntoSpans(isLoading);
const content = useMemo(() => {
const reasoningContent = extractReasoningContentFromMessage(message);
const content = extractContentFromMessage(message);
if (!isLoading && reasoningContent && !content) {
return reasoningContent;
}
return content;
}, [isLoading, message]);
return (
<AIElementMessageContent className={className}>
{hasReasoning(message) && (
<MessageGroup messages={messagesInGroup} isLoading={isLoading} />
)}
<AIElementMessageResponse rehypePlugins={rehypePlugins}>
{extractContentFromMessage(message)}
{content}
</AIElementMessageResponse>
{hasToolCalls(message) && (
<MessageGroup messages={messagesInGroup} isLoading={isLoading} />
)}
</AIElementMessageContent>
);
}

View File

@@ -7,7 +7,6 @@ import {
import {
extractPresentFilesFromMessage,
groupMessages,
hasContent,
hasPresentFiles,
} from "@/core/messages/utils";
import type { AgentThreadState } from "@/core/threads";
@@ -39,28 +38,26 @@ export function MessageList({
<ConversationContent className="mx-auto w-full max-w-(--container-width-md) gap-10 pt-12">
{groupMessages(
thread.messages,
(groupedMessages) => {
if (groupedMessages[0] && hasContent(groupedMessages[0])) {
const message = groupedMessages[0];
(group) => {
if (group.type === "human" || group.type === "assistant") {
return (
<MessageListItem
key={message.id}
message={message}
messagesInGroup={groupedMessages}
key={group.id}
message={group.messages[0]!}
isLoading={thread.isLoading}
/>
);
}
if (groupedMessages[0] && hasPresentFiles(groupedMessages[0])) {
if (group.type === "assistant:present-files") {
const files = [];
for (const message of groupedMessages) {
for (const message of group.messages) {
if (hasPresentFiles(message)) {
files.push(...extractPresentFilesFromMessage(message));
}
}
return (
<ArtifactFileList
key={groupedMessages[0].id}
key={group.id}
files={files}
threadId={threadId}
/>
@@ -68,8 +65,8 @@ export function MessageList({
}
return (
<MessageGroup
key={groupedMessages[0]!.id}
messages={groupedMessages}
key={group.id}
messages={group.messages}
isLoading={thread.isLoading}
/>
);