"use client"; import { useEffect, useRef, useState } from "react"; import { askAI } from "../utils/askAI"; /** * GrasBot — refonte Stitch (étape 7.d). * * L'API publique ne change pas (`onClose` propagé par le parent). Le bouton * d'ouverture n'est plus dans ce composant : il est désormais porté par le FAB * global `GrasBotFab` monté dans `app/layout.tsx`. Ce composant se concentre * sur le panneau de conversation proprement dit. * * Détails notables : * - Fond `surface-container-lowest/95 backdrop-blur-vellum rounded-sheet shadow-ambient` * (plus de carte opaque `bg-white/70` + ombre lourde). * - Header primary avec Material Symbol `smart_toy` (`translate="no"`, règle § 4 quinquies). * - Bulles : user = `bg-primary text-white rounded-sheet` à droite, bot = * `bg-surface-container text-on-surface rounded-sheet` à gauche. Corps Manrope. * - Input `bg-surface-container-low focus-visible:ring-2 focus-visible:ring-primary`, * bouton envoyer jewel avec Material Symbol `send`. * - Auto-scroll en bas à chaque nouveau message, envoi à Enter. */ export default function ChatBot({ onClose }) { const [question, setQuestion] = useState(""); const [messages, setMessages] = useState([]); const [isWaiting, setIsWaiting] = useState(false); const scrollRef = useRef(null); const inputRef = useRef(null); useEffect(() => { if (scrollRef.current) { scrollRef.current.scrollTop = scrollRef.current.scrollHeight; } }, [messages, isWaiting]); useEffect(() => { inputRef.current?.focus(); }, []); const handleAsk = async () => { if (!question.trim() || isWaiting) return; const userMessage = { sender: "user", text: question }; setMessages((prev) => [...prev, userMessage]); setQuestion(""); setIsWaiting(true); try { const botResponse = await askAI(userMessage.text); setMessages((prev) => [...prev, { sender: "bot", text: botResponse }]); } catch (_error) { setMessages((prev) => [ ...prev, { sender: "bot", text: "Erreur de réponse. Réessayez plus tard." }, ]); } finally { setIsWaiting(false); } }; const handleKeyDown = (e) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); handleAsk(); } }; return (

GrasBot

Assistant IA locale

{messages.length === 0 && !isWaiting && (

Posez-moi une question sur le site, les projets ou les compétences.

)} {messages.map((msg, index) => (
{msg.text}
))} {isWaiting && (
GrasBot réfléchit . . .
)}
setQuestion(e.target.value)} onKeyDown={handleKeyDown} disabled={isWaiting} aria-label="Votre question pour GrasBot" />
); }