32 lines
1.7 KiB
TypeScript
32 lines
1.7 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
|
|
export interface Msg { id: number; role: "user" | "bot" | "sys"; text: string }
|
|
|
|
/** Chat popup. Typed messages go to the model via /api/chat; position/size/colours come from the theme. */
|
|
export function Chat({ open, setOpen, mode, msgs, onSend }: {
|
|
open: boolean; setOpen: (o: boolean) => void; mode: string; msgs: Msg[]; onSend: (text: string) => void;
|
|
}) {
|
|
const [value, setValue] = useState("");
|
|
const logRef = useRef<HTMLDivElement>(null);
|
|
useEffect(() => { if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight; }, [msgs, open]);
|
|
return (
|
|
<>
|
|
<button id="chatLauncher" className="launcher" aria-label="Open the market agent" style={{ display: open ? "none" : undefined }}
|
|
onClick={() => setOpen(true)}>💬 Ask the agent</button>
|
|
<aside id="chat" className={"chat" + (open ? "" : " hidden")} aria-label="Market agent chat">
|
|
<div className="chat-head">
|
|
<div><b>Market Guide</b><div className="sub" id="chatMode">{mode}</div></div>
|
|
<button id="chatClose" aria-label="Close" onClick={() => setOpen(false)}>✕</button>
|
|
</div>
|
|
<div id="log" className="log" ref={logRef}>
|
|
{msgs.map((m) => <div key={m.id} className={"msg " + m.role}>{m.text}</div>)}
|
|
</div>
|
|
<form id="chatForm" className="chat-form" onSubmit={(e) => { e.preventDefault(); const v = value.trim(); if (!v) return; setValue(""); onSend(v); }}>
|
|
<input id="chatInput" autoComplete="off" placeholder="e.g. find a bike, or: make it look retro" value={value} onChange={(e) => setValue(e.target.value)} />
|
|
<button>Send</button>
|
|
</form>
|
|
</aside>
|
|
</>
|
|
);
|
|
}
|