import { memo, useEffect, useMemo, useRef, useState, type KeyboardEvent, } from "react"; export type RemoteAgentChatMessage = { id: string; role: "user" | "system"; text: string; timestampMs: number; }; type RemoteAgentChatPanelProps = { agentName: string; canSend: boolean; sending: boolean; draft: string; error: string | null; messages: RemoteAgentChatMessage[]; disabledReason?: string | null; onDraftChange: (value: string) => void; onSend: (message: string) => void; }; const formatTimestamp = (timestampMs: number) => new Intl.DateTimeFormat(undefined, { hour: "2-digit", minute: "2-digit", hour12: true, }).format(new Date(timestampMs)); export const RemoteAgentChatPanel = memo(function RemoteAgentChatPanel({ agentName, canSend, sending, draft, error, messages, disabledReason, onDraftChange, onSend, }: RemoteAgentChatPanelProps) { const [draftValue, setDraftValue] = useState(draft); const feedRef = useRef(null); const sendDisabled = !canSend || sending || !draftValue.trim(); const helperText = useMemo(() => { if (disabledReason?.trim()) return disabledReason.trim(); if (sending) return "Forwarding your message to the remote gateway."; return "Text-only relay. Remote replies are not mirrored here yet."; }, [disabledReason, sending]); useEffect(() => { setDraftValue(draft); }, [draft]); useEffect(() => { if (!feedRef.current) return; feedRef.current.scrollTop = feedRef.current.scrollHeight; }, [messages, sending]); const handleSend = () => { const trimmed = draftValue.trim(); if (!trimmed || sendDisabled) return; onSend(trimmed); }; const handleKeyDown = (event: KeyboardEvent) => { if (event.key !== "Enter" || event.shiftKey) return; event.preventDefault(); handleSend(); }; return (
Remote Agent
{agentName}
{helperText}
{messages.length === 0 ? (
Send a plain-text note to this remote agent.
) : ( messages.map((message) => (
{message.text}
{formatTimestamp(message.timestampMs)}
)) )}
{error ? (
{error}
) : null}