Files
nexusAI/packages/chat-client/src/components/MessageBubble.jsx
2026-04-17 22:45:24 -07:00

70 lines
2.8 KiB
JavaScript

import React from 'react';
import ReactMarkdown from 'react-markdown';
export default function MessageBubble({ message }) {
const isUser = message.role === 'user';
return (
<div className="flex" style={{
justifyContent: isUser ? 'flex-end' : 'flex-start',
marginBottom: '12px',
padding: '0 16px',
}}>
{!isUser && (
<div className="flex items-center justify-center flex-shrink" style={{
width: '28px',
height: '28px',
borderRadius: '50%',
background: 'var(--accent)',
fontSize: '12px',
fontWeight: 600,
marginRight: '8px',
alignSelf: 'flex-end',
}}>N</div>
)}
<div style={{
maxWidth: '70%',
padding: '10px 14px',
borderRadius: isUser ? '18px 18px 4px 18px' : '18px 18px 18px 4px',
background: isUser ? 'var(--bubble-user)' : 'var(--bubble-ai)',
color: 'var(--text-primary)',
fontSize: '14px',
lineHeight: '1.6',
border: isUser ? 'none' : '1px solid var(--border)',
wordBreak: 'break-word',
}}>
<ReactMarkdown
components={{
// Tighten up default spacing so it fits the bubble style
p: ({ children }) => <p style={{ margin: '0 0 8px', lineHeight: 1.6 }}>{children}</p>,
ul: ({ children }) => <ul style={{ margin: '0 0 8px', paddingLeft: '20px' }}>{children}</ul>,
ol: ({ children }) => <ol style={{ margin: '0 0 8px', paddingLeft: '20px' }}>{children}</ol>,
li: ({ children }) => <li style={{ marginBottom: '2px' }}>{children}</li>,
code: ({ inline, children }) => inline
? <code style={{ background: 'var(--bg-elevated)', padding: '1px 5px', borderRadius: 'var(--radius-sm)', fontSize: '12px', fontFamily: 'monospace' }}>{children}</code>
: <pre style={{ background: 'var(--bg-elevated)', padding: '10px 12px', borderRadius: 'var(--radius-md)', overflowX: 'auto', fontSize: '12px', fontFamily: 'monospace' }}><code>{children}</code></pre>,
strong: ({ children }) => <strong style={{ fontWeight: 600, color: 'var(--text-primary)' }}>{children}</strong>,
}}
>{message.text}</ReactMarkdown>
{message.streaming && (
<span style={{
display: 'inline-block',
width: '8px',
height: '14px',
background: 'var(--text-secondary)',
marginLeft: '2px',
borderRadius: 'var(--radius-sm)',
animation: 'blink 1s step-end infinite',
}} />
)}
{message.error && (
<div className="text-xs" style={{ marginTop: '6px', color: '#ff6b6b' }}>
Failed to complete response
</div>
)}
</div>
</div>
);
}