import { View, Text, Pressable, Animated } from 'react-native'; import { useState, useEffect, useRef } from 'react'; import Markdown from 'react-native-markdown-display'; import { Circle, Info, CheckCircle, XCircle, FileText, ChevronRight, ChevronDown, RefreshCw } from 'lucide-react-native'; interface UserMessageProps { message: string; timestamp: number; } export function UserMessage({ message, timestamp }: UserMessageProps) { return ( {message} ); } interface AssistantMessageProps { message: string; timestamp: number; isStreaming?: boolean; } export function AssistantMessage({ message, timestamp, isStreaming = false }: AssistantMessageProps) { const fadeAnim = useRef(new Animated.Value(0.3)).current; useEffect(() => { if (isStreaming) { Animated.loop( Animated.sequence([ Animated.timing(fadeAnim, { toValue: 1, duration: 600, useNativeDriver: true, }), Animated.timing(fadeAnim, { toValue: 0.3, duration: 600, useNativeDriver: true, }), ]) ).start(); } else { fadeAnim.stopAnimation(); fadeAnim.setValue(1); } }, [isStreaming, fadeAnim]); const markdownStyles = { body: { color: '#f0fdfa', fontSize: 16, lineHeight: 24, }, paragraph: { marginTop: 0, marginBottom: 8, }, strong: { fontWeight: '700', }, em: { fontStyle: 'italic', }, code_inline: { backgroundColor: '#134e4a', color: '#ccfbf1', paddingHorizontal: 4, paddingVertical: 2, borderRadius: 3, fontFamily: 'monospace', }, code_block: { backgroundColor: '#134e4a', color: '#ccfbf1', padding: 12, borderRadius: 6, fontFamily: 'monospace', fontSize: 14, }, fence: { backgroundColor: '#134e4a', color: '#ccfbf1', padding: 12, borderRadius: 6, fontFamily: 'monospace', fontSize: 14, }, link: { color: '#5eead4', textDecorationLine: 'underline', }, bullet_list: { marginBottom: 8, }, ordered_list: { marginBottom: 8, }, list_item: { marginBottom: 4, }, }; return ( {message} {isStreaming && ( ... )} ); } interface ActivityLogProps { type: 'system' | 'info' | 'success' | 'error' | 'artifact'; message: string; timestamp: number; metadata?: Record; artifactId?: string; artifactType?: string; title?: string; onArtifactClick?: (artifactId: string) => void; } export function ActivityLog({ type, message, timestamp, metadata, artifactId, artifactType, title, onArtifactClick }: ActivityLogProps) { const [isExpanded, setIsExpanded] = useState(false); const typeConfig = { system: { bg: 'bg-zinc-800/50', color: '#a1a1aa', Icon: Circle }, info: { bg: 'bg-blue-900/30', color: '#60a5fa', Icon: Info }, success: { bg: 'bg-green-900/30', color: '#4ade80', Icon: CheckCircle }, error: { bg: 'bg-red-900/30', color: '#f87171', Icon: XCircle }, artifact: { bg: 'bg-blue-900/40', color: '#93c5fd', Icon: FileText }, }; const config = typeConfig[type]; const IconComponent = config.Icon; const handlePress = () => { if (type === 'artifact' && artifactId && onArtifactClick) { onArtifactClick(artifactId); } else if (metadata) { setIsExpanded(!isExpanded); } }; const displayMessage = type === 'artifact' && artifactType && title ? `${artifactType}: ${title}` : message; return ( {displayMessage} {metadata && ( Details {isExpanded ? ( ) : ( )} )} {isExpanded && metadata && ( {JSON.stringify(metadata, null, 2)} )} ); } interface ToolCallProps { toolName: string; args: any; result?: any; error?: any; status: 'executing' | 'completed' | 'failed'; } export function ToolCall({ toolName, args, result, error, status }: ToolCallProps) { const [isExpanded, setIsExpanded] = useState(false); const spinAnim = useRef(new Animated.Value(0)).current; useEffect(() => { if (status === 'executing') { Animated.loop( Animated.timing(spinAnim, { toValue: 1, duration: 1000, useNativeDriver: true, }) ).start(); } else { spinAnim.stopAnimation(); spinAnim.setValue(0); } }, [status, spinAnim]); const spin = spinAnim.interpolate({ inputRange: [0, 1], outputRange: ['0deg', '360deg'], }); const statusConfig = { executing: { border: 'border-amber-500', bg: 'bg-amber-500/20', color: '#fcd34d', label: 'executing', }, completed: { border: 'border-green-500', bg: 'bg-green-500/20', color: '#86efac', label: 'completed', }, failed: { border: 'border-red-500', bg: 'bg-red-500/20', color: '#fca5a5', label: 'failed', }, }; const config = statusConfig[status]; return ( setIsExpanded(!isExpanded)} className={`mx-2 mb-2 bg-zinc-900 rounded-lg border ${config.border} overflow-hidden active:opacity-80`} > {isExpanded ? ( ) : ( )} {toolName} {status === 'executing' ? ( ) : status === 'completed' ? ( ) : ( )} {config.label} {isExpanded && ( Arguments {JSON.stringify(args, null, 2)} {result !== undefined && ( Result {JSON.stringify(result, null, 2)} )} {error !== undefined && ( Error {JSON.stringify(error, null, 2)} )} )} ); }