HomePilot / frontend /components /ChatWindow.jsx
HomePilot Deploy Bot
chore(hf): sync HomePilot to HF Space
921d377
Raw
History Blame Contribute Delete
4.13 kB
"use client";
import { useEffect, useRef, useState } from "react";
import MessageList from "./MessageList";
import Composer from "./Composer";
function ExpertActivity({ stage }) {
if (stage === "idle")
return null;
const isParsing = stage === "parsing";
return (<div style={{
background: "#080808",
borderRadius: 16,
padding: "16px 18px",
marginBottom: 14,
border: "1px solid rgba(60,60,70,0.35)",
color: "#ddd",
}}>
<style>{`
@keyframes hpDotBounce {
0%, 80%, 100% { transform: translateY(0); opacity: 0.7; }
40% { transform: translateY(-6px); opacity: 1; }
}
@keyframes hpGrokPulse {
0% { background: #222; transform: scale(1); opacity: .75; box-shadow: 0 0 2px rgba(255,255,255,.1); }
8% { background: #fff; transform: scale(1.55); opacity: 1; box-shadow: 0 0 8px rgba(255,255,255,.9), 0 0 14px rgba(180,220,255,.6); }
18% { background: #aaa; transform: scale(1.15); opacity: .9; box-shadow: 0 0 4px rgba(255,255,255,.4); }
28%, 100% { background: #222; transform: scale(1); opacity: .75; box-shadow: 0 0 2px rgba(255,255,255,.08); }
}
`}</style>
<div style={{ display: "flex", alignItems: "center", gap: 14, marginBottom: 10 }}>
{isParsing ? (<div style={{ display: "grid", gridTemplateColumns: "repeat(3, 5px)", gridTemplateRows: "repeat(3, 5px)", gap: 7 }}>
{Array.from({ length: 9 }).map((_, i) => (<span key={i} style={{
width: 5,
height: 5,
borderRadius: "50%",
animation: "hpGrokPulse 2.4s infinite ease-in-out",
animationDelay: `${i * 0.2}s`,
}}/>))}
</div>) : (<div style={{ display: "flex", alignItems: "center", gap: 5 }}>
{[0, 1, 2].map((i) => (<span key={i} style={{
width: 6,
height: 6,
borderRadius: "50%",
background: "#888",
animation: "hpDotBounce 1.3s infinite ease-in-out",
animationDelay: `${i * 0.15}s`,
}}/>))}
</div>)}
<div style={{ color: "#7aaae0", fontSize: 15 }}>
{isParsing ? "Parsing question · sending to backend" : "Thinking"}
</div>
</div>
<div style={{ color: "#8e8e9a", fontSize: 13 }}>
{isParsing
? "Analyzing query structure and preparing tool context."
: "Processing reasoning step-by-step."}
</div>
</div>);
}
export default function ChatWindow() {
const [messages, setMessages] = useState([]);
const [stage, setStage] = useState("idle");
const parseTimer = useRef(null);
useEffect(() => {
return () => {
if (parseTimer.current)
clearTimeout(parseTimer.current);
};
}, []);
async function sendMessage(text) {
setStage("parsing");
parseTimer.current = setTimeout(() => setStage("thinking"), 1200);
try {
const resp = await fetch("http://localhost:8000/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ session_id: "default", message: text }),
});
const data = await resp.json();
if (parseTimer.current)
clearTimeout(parseTimer.current);
setStage("thinking");
setMessages((prev) => [...prev, `User: ${text}`, `Expert: ${data.answer}`]);
}
catch (err) {
if (parseTimer.current)
clearTimeout(parseTimer.current);
setMessages((prev) => [...prev, `User: ${text}`, `Expert: Failed to get response: ${String(err)}`]);
}
finally {
setStage("idle");
}
}
return (<div style={{ padding: 24 }}>
<MessageList messages={messages}/>
<ExpertActivity stage={stage}/>
<Composer onSend={sendMessage}/>
</div>);
}