import type React from "react";
import type { Message } from "@langchain/langgraph-sdk";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Loader2, Copy, CopyCheck } from "lucide-react";
import { InputForm } from "@/components/InputForm";
import { Button } from "@/components/ui/button";
import { useState, ReactNode } from "react";
import ReactMarkdown from "react-markdown";
import { cn } from "@/lib/utils";
import { Badge } from "@/components/ui/badge";
import {
ActivityTimeline,
ProcessedEvent,
} from "@/components/ActivityTimeline"; // Assuming ActivityTimeline is in the same dir or adjust path
// Markdown component props type from former ReportView
type MdComponentProps = {
className?: string;
children?: ReactNode;
[key: string]: any;
};
// Markdown components (from former ReportView.tsx)
const mdComponents = {
h1: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
h2: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
h3: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
p: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
a: ({ className, children, href, ...props }: MdComponentProps) => (
{children}
),
ul: ({ className, children, ...props }: MdComponentProps) => (
),
ol: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
li: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
blockquote: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
code: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
pre: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
hr: ({ className, ...props }: MdComponentProps) => (
),
table: ({ className, children, ...props }: MdComponentProps) => (
),
th: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
td: ({ className, children, ...props }: MdComponentProps) => (
{children}
),
};
// Props for HumanMessageBubble
interface HumanMessageBubbleProps {
message: Message;
mdComponents: typeof mdComponents;
}
// HumanMessageBubble Component
const HumanMessageBubble: React.FC = ({
message,
mdComponents,
}) => {
return (
{typeof message.content === "string"
? message.content
: JSON.stringify(message.content)}
);
};
// Props for AiMessageBubble
interface AiMessageBubbleProps {
message: Message;
historicalActivity: ProcessedEvent[] | undefined;
liveActivity: ProcessedEvent[] | undefined;
isLastMessage: boolean;
isOverallLoading: boolean;
mdComponents: typeof mdComponents;
handleCopy: (text: string, messageId: string) => void;
copiedMessageId: string | null;
}
// AiMessageBubble Component
const AiMessageBubble: React.FC = ({
message,
historicalActivity,
liveActivity,
isLastMessage,
isOverallLoading,
mdComponents,
handleCopy,
copiedMessageId,
}) => {
// Determine which activity events to show and if it's for a live loading message
const activityForThisBubble =
isLastMessage && isOverallLoading ? liveActivity : historicalActivity;
const isLiveActivityForThisBubble = isLastMessage && isOverallLoading;
return (
{activityForThisBubble && activityForThisBubble.length > 0 && (
)}
{typeof message.content === "string"
? message.content
: JSON.stringify(message.content)}
0 ? "visible" : "hidden"
}`}
onClick={() =>
handleCopy(
typeof message.content === "string"
? message.content
: JSON.stringify(message.content),
message.id!
)
}
>
{copiedMessageId === message.id ? "Copied" : "Copy"}
{copiedMessageId === message.id ? : }
);
};
interface ChatMessagesViewProps {
messages: Message[];
isLoading: boolean;
scrollAreaRef: React.RefObject;
onSubmit: (inputValue: string, effort: string, model: string) => void;
onCancel: () => void;
liveActivityEvents: ProcessedEvent[];
historicalActivities: Record;
}
export function ChatMessagesView({
messages,
isLoading,
scrollAreaRef,
onSubmit,
onCancel,
liveActivityEvents,
historicalActivities,
}: ChatMessagesViewProps) {
const [copiedMessageId, setCopiedMessageId] = useState(null);
const handleCopy = async (text: string, messageId: string) => {
try {
await navigator.clipboard.writeText(text);
setCopiedMessageId(messageId);
setTimeout(() => setCopiedMessageId(null), 2000); // Reset after 2 seconds
} catch (err) {
console.error("Failed to copy text: ", err);
}
};
console.log("liveActivityEvents", liveActivityEvents);
return (
{messages.map((message, index) => {
const isLast = index === messages.length - 1;
return (
{message.type === "human" ? (
) : (
)}
);
})}
{isLoading &&
(messages.length === 0 ||
messages[messages.length - 1].type === "human") && (
{" "}
{/* AI message row structure */}
{liveActivityEvents.length > 0 ? (
) : (
Processing...
)}
)}
0}
/>
);
}