"use client"; import { useRef, useCallback } from "react"; import { useChatStore } from "@/lib/store"; import { consumeSSEChunk } from "@/lib/sse"; export function InputBar() { const textareaRef = useRef(null); const isStreaming = useChatStore((s) => s.isStreaming); const addUserMessage = useChatStore((s) => s.addUserMessage); const startStreaming = useChatStore((s) => s.startStreaming); const appendToken = useChatStore((s) => s.appendToken); const finishStreaming = useChatStore((s) => s.finishStreaming); const setError = useChatStore((s) => s.setError); const messages = useChatStore((s) => s.messages); const activeConversationId = useChatStore((s) => s.activeConversationId); const personId = useChatStore((s) => s.personId); const createConversation = useChatStore((s) => s.createConversation); const send = useCallback(async () => { const textarea = textareaRef.current; if (!textarea) return; const content = textarea.value.trim(); if (!content || isStreaming) return; textarea.value = ""; // Auto-create conversation if none active let conversationId = activeConversationId; if (!conversationId) { conversationId = createConversation(); } addUserMessage(content); const history = [ ...messages.map((m) => ({ role: m.role, content: m.content })), { role: "user" as const, content }, ]; const assistantId = startStreaming(); try { const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: history, conversationId, personId }), }); if (!res.ok) { throw new Error(`Server returned ${res.status}`); } const reader = res.body!.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; const { jsonLines, buffer: next } = consumeSSEChunk( buffer, decoder.decode(value, { stream: true }) ); buffer = next; for (const jsonStr of jsonLines) { try { const data = JSON.parse(jsonStr); if (data.error) { setError(data.error); return; } if (data.token) { appendToken(assistantId, data.token); } } catch { // skip malformed } } } finishStreaming(assistantId); } catch (err) { const msg = err instanceof Error ? err.message : "Something went wrong"; setError(msg); } textarea.focus(); }, [ isStreaming, messages, activeConversationId, personId, createConversation, addUserMessage, startStreaming, appendToken, finishStreaming, setError, ]); const handleKeyDown = useCallback( (e: React.KeyboardEvent) => { if (e.key === "Enter" && !e.shiftKey) { e.preventDefault(); send(); } }, [send] ); return (