import { useState, useRef, useEffect } from "react"; import type { MessageType } from "@/types/MessageType"; import { postMessage } from "@/api"; import { cn } from "@/lib/utils"; interface Props { channelId: string; replyTo: string | null; onClearReply: () => void; onMessageSent: () => void; } const TYPE_META: Record = { text: { prefix: ">", color: "text-muted-foreground", key: "1" }, code: { prefix: "//", color: "text-[var(--color-msg-code)]", key: "2" }, result: { prefix: "=>", color: "text-[var(--color-msg-result)]", key: "3" }, error: { prefix: "!!", color: "text-[var(--color-msg-error)]", key: "4" }, plan: { prefix: "::", color: "text-[var(--color-msg-plan)]", key: "5" }, }; export function ComposeBox({ channelId, replyTo, onClearReply, onMessageSent, }: Props) { const [content, setContent] = useState(""); const [msgType, setMsgType] = useState("text"); const [sending, setSending] = useState(false); const [focused, setFocused] = useState(false); const inputRef = useRef(null); const meta = TYPE_META[msgType]; // Auto-resize textarea useEffect(() => { if (inputRef.current) { inputRef.current.style.height = "0"; inputRef.current.style.height = `${Math.min(inputRef.current.scrollHeight, 120)}px`; } }, [content]); // Auto-focus on mount useEffect(() => { inputRef.current?.focus(); }, []); async function handleSend() { if (!content.trim() || sending) return; setSending(true); try { await postMessage(channelId, { content: content.trim(), type: msgType, reply_to: replyTo ?? undefined, }); setContent(""); setMsgType("text"); onClearReply(); onMessageSent(); } finally { setSending(false); } } function cycleType(direction: 1 | -1) { const types: MessageType[] = ["text", "code", "result", "error", "plan"]; const idx = types.indexOf(msgType); const next = (idx + direction + types.length) % types.length; setMsgType(types[next]); } return (
{/* Reply chip */} {replyTo && (
^ #{replyTo.slice(0, 8)}
)}
{/* Type prefix — click to cycle */} {/* Auto-growing textarea — no send button */}