MessageBubble CC 19 -> below 10. The fence and indented code block rules were identical and are now one CodeBlock component; the Markdown style map lives in markdownStyles.ts. Adds characterization tests for the bubble (roles, actions visibility, edited badge, copy feedback).
74 lines
2.2 KiB
TypeScript
74 lines
2.2 KiB
TypeScript
import { useThemeColor } from '@/hooks/useThemeColor';
|
|
import { ChatMessage } from '@/store/useAppStore';
|
|
import Markdown from '@ronradtke/react-native-markdown-display';
|
|
import * as Clipboard from 'expo-clipboard';
|
|
import React, { useMemo, useState } from 'react';
|
|
import { StyleSheet, Text } from 'react-native';
|
|
import { CodeBlock } from './CodeBlock';
|
|
import { createMarkdownStyles } from './markdownStyles';
|
|
|
|
interface MessageContentProps {
|
|
message: ChatMessage;
|
|
/** True while this message is the answer currently being streamed. */
|
|
showingStreaming: boolean;
|
|
}
|
|
|
|
export function MessageContent({ message, showingStreaming }: MessageContentProps) {
|
|
const colors = useThemeColor();
|
|
const [copiedText, setCopiedText] = useState<string | null>(null);
|
|
|
|
const handleCopy = async (text: string) => {
|
|
await Clipboard.setStringAsync(text);
|
|
setCopiedText(text);
|
|
setTimeout(() => setCopiedText(null), 2000);
|
|
};
|
|
|
|
const markdownStyles = useMemo(() => createMarkdownStyles(colors), [colors]);
|
|
|
|
const markdownRules = useMemo(() => ({
|
|
fence: (node: any) => (
|
|
<CodeBlock
|
|
key={node.key}
|
|
language={node.sourceInfo || 'code'}
|
|
content={node.content || ''}
|
|
copied={copiedText === (node.content || '')}
|
|
onCopy={handleCopy}
|
|
/>
|
|
),
|
|
code_block: (node: any) => (
|
|
<CodeBlock
|
|
key={node.key}
|
|
language="code"
|
|
content={node.content || ''}
|
|
copied={copiedText === (node.content || '')}
|
|
onCopy={handleCopy}
|
|
/>
|
|
),
|
|
}), [copiedText]);
|
|
|
|
if (message.role === 'user') {
|
|
return (
|
|
<Text style={[styles.text, { color: colors.messageUserText }]}>
|
|
{message.content}
|
|
</Text>
|
|
);
|
|
}
|
|
|
|
if (!message.content && showingStreaming) {
|
|
return null; // TypingIndicator handled externally
|
|
}
|
|
|
|
return (
|
|
<Markdown style={markdownStyles} rules={markdownRules}>
|
|
{message.content}
|
|
</Markdown>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
text: {
|
|
fontSize: 15,
|
|
lineHeight: 22,
|
|
},
|
|
});
|