Programmer-RD-AI
feat: add Paragraph component and types for typography
a8aec61
import type { PlaygroundChatMessage } from '@/types/playground'
import { AgentMessage, UserMessage } from './MessageItem'
import Tooltip from '@/components/ui/tooltip'
import { memo } from 'react'
import {
ToolCallProps,
ReasoningStepProps,
ReasoningProps,
ReferenceData,
Reference
} from '@/types/playground'
import React, { type FC } from 'react'
import ChatBlankState from './ChatBlankState'
import Icon from '@/components/ui/icon'
interface MessageListProps {
messages: PlaygroundChatMessage[]
}
interface MessageWrapperProps {
message: PlaygroundChatMessage
isLastMessage: boolean
}
interface ReferenceProps {
references: ReferenceData[]
}
interface ReferenceItemProps {
reference: Reference
}
const ReferenceItem: FC<ReferenceItemProps> = ({ reference }) => (
<div className="relative flex h-[63px] w-[190px] cursor-default flex-col justify-between overflow-hidden rounded-md bg-background-secondary p-3 transition-colors hover:bg-background-secondary/80">
<p className="text-sm font-medium text-primary">{reference.name}</p>
<p className="truncate text-xs text-primary/40">{reference.content}</p>
</div>
)
const References: FC<ReferenceProps> = ({ references }) => (
<div className="flex flex-col gap-4">
{references.map((referenceData, index) => (
<div
key={`${referenceData.query}-${index}`}
className="flex flex-col gap-3"
>
<div className="flex flex-wrap gap-3">
{referenceData.references.map((reference, refIndex) => (
<ReferenceItem
key={`${reference.name}-${reference.meta_data.chunk}-${refIndex}`}
reference={reference}
/>
))}
</div>
</div>
))}
</div>
)
const AgentMessageWrapper = ({ message }: MessageWrapperProps) => {
return (
<div className="flex flex-col gap-y-9">
{message.extra_data?.reasoning_steps &&
message.extra_data.reasoning_steps.length > 0 && (
<div className="flex items-start gap-4">
<Tooltip
delayDuration={0}
content={<p className="text-accent">Reasoning</p>}
side="top"
>
<Icon type="reasoning" size="sm" />
</Tooltip>
<div className="flex flex-col gap-3">
<p className="text-xs uppercase">Reasoning</p>
<Reasonings reasoning={message.extra_data.reasoning_steps} />
</div>
</div>
)}
{message.extra_data?.references &&
message.extra_data.references.length > 0 && (
<div className="flex items-start gap-4">
<Tooltip
delayDuration={0}
content={<p className="text-accent">References</p>}
side="top"
>
<Icon type="references" size="sm" />
</Tooltip>
<div className="flex flex-col gap-3">
<References references={message.extra_data.references} />
</div>
</div>
)}
{message.tool_calls && message.tool_calls.length > 0 && (
<div className="flex items-center gap-3">
<Tooltip
delayDuration={0}
content={<p className="text-accent">Tool Calls</p>}
side="top"
>
<Icon
type="hammer"
className="rounded-lg bg-background-secondary p-1"
size="sm"
color="secondary"
/>
</Tooltip>
<div className="flex flex-wrap gap-2">
{message.tool_calls.map((toolCall, index) => (
<ToolComponent
key={
toolCall.tool_call_id ||
`${toolCall.tool_name}-${toolCall.created_at}-${index}`
}
tools={toolCall}
/>
))}
</div>
</div>
)}
<AgentMessage message={message} />
</div>
)
}
const Reasoning: FC<ReasoningStepProps> = ({ index, stepTitle }) => (
<div className="flex items-center gap-2 text-secondary">
<div className="flex h-[20px] items-center rounded-md bg-background-secondary p-2">
<p className="text-xs">STEP {index + 1}</p>
</div>
<p className="text-xs">{stepTitle}</p>
</div>
)
const Reasonings: FC<ReasoningProps> = ({ reasoning }) => (
<div className="flex flex-col items-start justify-center gap-2">
{reasoning.map((title, index) => (
<Reasoning
key={`${title.title}-${title.action}-${index}`}
stepTitle={title.title}
index={index}
/>
))}
</div>
)
const ToolComponent = memo(({ tools }: ToolCallProps) => (
<div className="cursor-default rounded-full bg-accent px-2 py-1.5 text-xs">
<p className="font-dmmono uppercase text-primary/80">{tools.tool_name}</p>
</div>
))
ToolComponent.displayName = 'ToolComponent'
const Messages = ({ messages }: MessageListProps) => {
if (messages.length === 0) {
return <ChatBlankState />
}
return (
<>
{messages.map((message, index) => {
const key = `${message.role}-${message.created_at}-${index}`
const isLastMessage = index === messages.length - 1
if (message.role === 'agent') {
return (
<AgentMessageWrapper
key={key}
message={message}
isLastMessage={isLastMessage}
/>
)
}
return <UserMessage key={key} message={message} />
})}
</>
)
}
export default Messages