mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-08 07:17:39 +00:00
87 lines
4.2 KiB
TypeScript
87 lines
4.2 KiB
TypeScript
import { MessagePrimitive, useAssistantState } from '@assistant-ui/react'
|
|
import { useHappyChatContext } from '@/components/AssistantChat/context'
|
|
import type { HappyChatMessageMetadata } from '@/lib/assistant-runtime'
|
|
import { MessageStatusIndicator } from '@/components/AssistantChat/messages/MessageStatusIndicator'
|
|
import { MessageAttachments } from '@/components/AssistantChat/messages/MessageAttachments'
|
|
import { UserBubbleContent, getUserBubbleClassName, shouldShowMessageStatus } from '@/components/AssistantChat/messages/user-bubble'
|
|
import { CliOutputBlock } from '@/components/CliOutputBlock'
|
|
import { getConversationMessageAnchorId } from '@/chat/outline'
|
|
import { MessageActions } from '@/components/AssistantChat/messages/MessageActions'
|
|
|
|
export function HappyUserMessage() {
|
|
const ctx = useHappyChatContext()
|
|
const role = useAssistantState(({ message }) => message.role)
|
|
const messageId = useAssistantState(({ message }) => message.id)
|
|
const text = useAssistantState(({ message }) => {
|
|
if (message.role !== 'user') return ''
|
|
return message.content.find((part) => part.type === 'text')?.text ?? ''
|
|
})
|
|
const status = useAssistantState(({ message }) => {
|
|
if (message.role !== 'user') return undefined
|
|
const custom = message.metadata.custom as Partial<HappyChatMessageMetadata> | undefined
|
|
return custom?.status
|
|
})
|
|
const localId = useAssistantState(({ message }) => {
|
|
if (message.role !== 'user') return null
|
|
const custom = message.metadata.custom as Partial<HappyChatMessageMetadata> | undefined
|
|
return custom?.localId ?? null
|
|
})
|
|
const attachments = useAssistantState(({ message }) => {
|
|
if (message.role !== 'user') return undefined
|
|
const custom = message.metadata.custom as Partial<HappyChatMessageMetadata> | undefined
|
|
return custom?.attachments
|
|
})
|
|
const isCliOutput = useAssistantState(({ message }) => {
|
|
const custom = message.metadata.custom as Partial<HappyChatMessageMetadata> | undefined
|
|
return custom?.kind === 'cli-output'
|
|
})
|
|
const cliText = useAssistantState(({ message }) => {
|
|
const custom = message.metadata.custom as Partial<HappyChatMessageMetadata> | undefined
|
|
if (custom?.kind !== 'cli-output') return ''
|
|
return message.content.find((part) => part.type === 'text')?.text ?? ''
|
|
})
|
|
if (role !== 'user') return null
|
|
const canRetry = status === 'failed' && typeof localId === 'string' && Boolean(ctx.onRetryMessage)
|
|
const onRetry = canRetry ? () => ctx.onRetryMessage!(localId) : undefined
|
|
const showStatus = shouldShowMessageStatus(status)
|
|
|
|
if (isCliOutput) {
|
|
return (
|
|
<MessagePrimitive.Root
|
|
id={getConversationMessageAnchorId(messageId)}
|
|
className="happy-message scroll-mt-4 px-1 min-w-0 max-w-full overflow-x-hidden"
|
|
>
|
|
<div className="ml-auto w-full max-w-[92%]">
|
|
<CliOutputBlock text={cliText} />
|
|
<MessageActions align="end" copyText={cliText} />
|
|
</div>
|
|
</MessagePrimitive.Root>
|
|
)
|
|
}
|
|
|
|
const hasText = text.length > 0
|
|
const hasAttachments = attachments && attachments.length > 0
|
|
|
|
return (
|
|
<MessagePrimitive.Root
|
|
id={getConversationMessageAnchorId(messageId)}
|
|
className="happy-message flex flex-col items-end scroll-mt-4"
|
|
>
|
|
<div className={getUserBubbleClassName(status)}>
|
|
<div className="flex items-start gap-2">
|
|
<div className="min-w-0 flex-1">
|
|
{hasText ? <UserBubbleContent text={text} /> : null}
|
|
{hasAttachments ? <MessageAttachments attachments={attachments} /> : null}
|
|
</div>
|
|
{showStatus && (
|
|
<div className="happy-message-actions-first-line flex shrink-0 items-center gap-1">
|
|
{showStatus ? <MessageStatusIndicator status={status} onRetry={onRetry} /> : null}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<MessageActions align="end" copyText={hasText ? text : undefined} />
|
|
</MessagePrimitive.Root>
|
|
)
|
|
}
|