import { useEffect, useRef } from "react"; import { View, Text, Animated, Platform, StyleSheet } from "react-native"; import { Image } from "expo-image"; import { useAuiState, MessagePrimitive, ErrorPrimitive, type TextMessagePartComponent, } from "@assistant-ui/react-native"; import { useTheme } from "@/hooks/use-theme"; import { Radius } from "@/constants/theme"; import { MessageActionBar } from "./message-action-bar"; import { MessageBranchPicker } from "./message-branch-picker"; const UserText: TextMessagePartComponent = ({ text }) => { const { colors } = useTheme(); return ( {text} ); }; const AssistantText: TextMessagePartComponent = ({ text }) => { const { colors } = useTheme(); return ( {text} ); }; function TypingDot({ delay }: { delay: number }) { const { colors } = useTheme(); const opacity = useRef(new Animated.Value(0.3)).current; useEffect(() => { const anim = Animated.loop( Animated.sequence([ Animated.timing(opacity, { toValue: 1, duration: 400, delay, useNativeDriver: Platform.OS !== "web", }), Animated.timing(opacity, { toValue: 0.3, duration: 400, useNativeDriver: Platform.OS !== "web", }), ]), ); anim.start(); return () => anim.stop(); }, [opacity, delay]); return ( ); } function TypingIndicator() { const isRunning = useAuiState((s) => s.message.status?.type === "running"); if (!isRunning) return null; return ( ); } function MessageImageAttachment() { const { colors } = useTheme(); const attachment = useAuiState((s) => s.attachment); if (!attachment) return null; const imageContent = attachment.content?.find((c: any) => c.type === "image"); const uri = (imageContent as any)?.image; if (!uri) return null; return ( ); } function UserMessage() { const { colors } = useTheme(); return ( {() => } ); } function AssistantMessage() { const { colors } = useTheme(); return ( ); } export function MessageBubble() { const role = useAuiState((s) => s.message.role); if (role === "user") return ; return ; } const styles = StyleSheet.create({ userContainer: { alignItems: "flex-end", }, userAttachments: { flexDirection: "row", flexWrap: "wrap", justifyContent: "flex-end", gap: 6, }, userBubble: { maxWidth: "85%", paddingHorizontal: 16, paddingVertical: 10, borderRadius: Radius.bubble, }, assistantContainer: { alignItems: "flex-start", }, assistantContent: { paddingHorizontal: 2, }, userText: { fontSize: 16, lineHeight: 22, letterSpacing: -0.2, }, assistantText: { fontSize: 16, lineHeight: 25, letterSpacing: -0.2, }, typing: { flexDirection: "row", alignItems: "center", gap: 5, paddingVertical: 8, }, dot: { width: 7, height: 7, borderRadius: 3.5, }, messageImage: { width: 200, height: 200, borderRadius: Radius.card, marginBottom: 6, }, actionsRow: { flexDirection: "row", alignItems: "center", gap: 4, marginTop: 6, marginLeft: -4, }, error: { marginTop: 8, borderRadius: Radius.md, borderWidth: StyleSheet.hairlineWidth, paddingHorizontal: 12, paddingVertical: 10, }, errorText: { fontSize: 14, lineHeight: 20, }, });