'use client'; import { ChatMessage as ChatMessageRow, ChatMessageBubble, } from '@astryxdesign/core/Chat'; import {VStack} from '@astryxdesign/core/Layout'; import {MessageContent} from './MessageContent'; import {TypingIndicator} from './TypingIndicator'; import {BrandMark} from '@/shared/components/brand/BrandLogo'; import type {ChatMessage} from '@/features/loyaly-ai/types/chat'; /** * One turn. * * User turns are a filled bubble on the right; assistant turns are `ghost` — * transparent, full-width, no container. That asymmetry is the single biggest * reason ChatGPT and Claude read as conversation rather than as messaging: a * long analytical answer inside a chat bubble reads as a quote, while the same * text set flush against the panel reads as prose written for you. * * It also solves a practical problem. Assistant answers contain tables and * code blocks, and a bubble with a max-width would force both to scroll * horizontally inside a 380px panel. * * Timestamps are deliberately absent. They are metadata about a chat log, and * this is a working session — ChatGPT, Claude and Gemini all omit them for the * same reason. */ export function MessageBubble({message}: {message: ChatMessage}) { const isAssistant = message.role === 'assistant'; // Nothing has arrived yet: the dots stand in for the answer, and are // replaced by it rather than joined to it. const isThinking = isAssistant && message.isStreaming && message.parts.length === 0; return ( ) : undefined } > {isThinking ? ( ) : ( {message.parts.map((part, index) => ( ))} )} ); }