import { useEffect, useRef, useState } from 'react' import { Send, X, ChefHat, Croissant } from 'lucide-react' import { Streamdown } from 'streamdown' import { Store } from '@tanstack/store' import { useConferenceChat } from '#/lib/conference-ai-hook' import type { ConferenceChatMessages } from '#/lib/conference-ai-hook' function Messages({ messages }: { messages: ConferenceChatMessages }) { const messagesContainerRef = useRef(null) useEffect(() => { if (messagesContainerRef.current) { messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight } }, [messages]) if (!messages.length) { return (

Bonjour! I'm Remy 👨‍🍳

Your culinary guide to Haute Pâtisserie 2026. Ask about speakers, sessions, or pastry techniques!

) } return (
{messages.map(({ id, role, parts }) => (
{parts.map((part, index) => { if (part.type === 'text' && part.content) { return (
{role === 'assistant' ? (
👨‍🍳
) : (
You
)}
{part.content}
) } return null })}
))}
) } interface RemyAssistantProps { speakerSlug?: string talkSlug?: string contextTitle?: string } // Export store for header control export const showRemyAssistant = new Store(false) export default function RemyAssistant({ speakerSlug, talkSlug, contextTitle, }: RemyAssistantProps) { const [isOpen, setIsOpen] = useState(false) const { messages, sendMessage, isLoading } = useConferenceChat(speakerSlug, talkSlug) const [input, setInput] = useState('') // Sync with store for header control useEffect(() => { return showRemyAssistant.subscribe(() => { setIsOpen(showRemyAssistant.state) }) }, []) const handleToggle = () => { const newState = !isOpen setIsOpen(newState) showRemyAssistant.setState(() => newState) } const handleSend = () => { if (input.trim()) { sendMessage(input) setInput('') } } if (!isOpen) return null return (
{/* Decorative top gradient */}
{/* Header */}
👨‍🍳

Remy

{contextTitle && (

🥐 {contextTitle}

)}
{/* Messages */} {/* Loading indicator */} {isLoading && (
Crafting a response...
)} {/* Input */}
{ e.preventDefault() handleSend() }} >