import { Box, Text, Textarea, useUiHost } from "../../ui"; import { type ComponentType, type RefObject } from "react"; import { type TextareaRenderable } from "../../ui"; import { colors } from "../../theme/colors"; import { useRemoteUiNode } from "../../remote/semantic-tree"; import { remoteStringValue } from "../../remote/semantic-helpers"; export interface MessageComposerProps { inputRef?: RefObject; initialValue?: string; focused?: boolean; placeholder?: string; width?: number | string; height?: number | string; terminalPrefix?: string; terminalDivider?: boolean; terminalBottomInset?: number; onFocusRequest?: () => void; onInput?: (value: string) => void; onCursorChange?: () => void; onSubmit?: () => void; keyBindings?: Array>; wrapText?: boolean; } export interface MessageComposerBlockHeightOptions { height?: MessageComposerProps["height"]; nativePaneChrome?: boolean; terminalDivider?: boolean; terminalBottomInset?: number; } export function getMessageComposerBlockHeight({ height = 1, nativePaneChrome = false, terminalDivider = true, terminalBottomInset = 0, }: MessageComposerBlockHeightOptions = {}) { const inputHeight = typeof height === "number" ? height : 1; if (nativePaneChrome) return inputHeight; return inputHeight + (terminalDivider ? 1 : 0) + Math.max(0, Math.floor(terminalBottomInset)); } export function MessageComposer({ inputRef, initialValue = "", focused = false, placeholder = "", width, height = 1, terminalPrefix = " > ", terminalDivider = true, terminalBottomInset = 0, onFocusRequest, onInput, onCursorChange, onSubmit, keyBindings, wrapText = false, }: MessageComposerProps) { useRemoteUiNode({ role: "message-composer", label: placeholder || "Message composer", actions: { setValue: (input) => { const value = remoteStringValue(input); onFocusRequest?.(); onInput?.(value); }, submit: () => { onFocusRequest?.(); onSubmit?.(); }, focus: () => { onFocusRequest?.(); inputRef?.current?.focus?.(); }, }, metadata: { focused, placeholder, height }, }); const HostMessageComposer = useUiHost().MessageComposer as ComponentType | undefined; if (HostMessageComposer) { return ( ); } const prefixWidth = terminalPrefix.length; const textWidth = typeof width === "number" ? Math.max(width - prefixWidth, 1) : undefined; const bottomInsetHeight = Math.max(0, Math.floor(terminalBottomInset)); const totalHeight = typeof height === "number" ? getMessageComposerBlockHeight({ height, terminalDivider, terminalBottomInset }) : height; const requestFocus = () => { onFocusRequest?.(); inputRef?.current?.focus?.(); }; const handleInput = (value: string) => { if (!focused) onFocusRequest?.(); onInput?.(value); }; return ( {terminalDivider && ( {"-".repeat(typeof width === "number" ? width : 0)} )} {terminalPrefix.length > 0 && ( {terminalPrefix} )}