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)}
)}
)}
);
}