import { Box, Text, TextAttributes, useCommandBarShortcut, type TextareaRenderable } from "../../../../ui"; import { getMessageComposerBlockHeight, MessageComposer } from "../../../../components/ui"; import { colors } from "../../../../theme/colors"; import { t, tf } from "../../../../i18n"; import { truncateToDisplayWidth } from "../../../../utils/format"; import type { ChatMessage } from "../../../../api-client"; import { InlineAuthActions } from "../../cloud/auth-actions"; import { Button, ListView } from "../../../../components/ui"; import { COMPOSER_ACTION_WIDTH } from "../layout"; import type { ChatMentionSuggestion } from "./mentions"; const DESKTOP_CHAT_INPUT_TOP_MARGIN_PX = 6; interface MutableRef { current: T; } interface ChatComposerAreaProps { canSend: boolean; cancelEditMessage: () => void; clearReplyTarget: () => void; commitLocalDraft: (draft: string) => void; composerHeight: number; composerWidth: number; contentWidth: number; focused: boolean; focusComposer: () => void; hasSavedSession: boolean; inputFocused: boolean; inputPlaceholder: string; inputRef: MutableRef; inputValueRef: MutableRef; nativePaneChrome: boolean | undefined; editingMessage: ChatMessage | null; editingPreview: string; replyPreview: string; replyTo: ChatMessage | null; sendMessage: () => void; mentionSuggestions: ChatMentionSuggestion[]; mentionSelectedIndex: number; onMentionCursorChange: () => void; onMentionSelect: (index?: number) => boolean; user: { id: string; username: string; emailVerified: boolean } | null; } function ChatMentionSuggestions({ contentWidth, onSelect, selectedIndex, suggestions, }: { contentWidth: number; onSelect: (index?: number) => boolean; selectedIndex: number; suggestions: ChatMentionSuggestion[]; }) { if (suggestions.length === 0) return null; const width = Math.max(18, Math.min(contentWidth, 44)); return ( ({ id: suggestion.username, label: `@${suggestion.username}` }))} selectedIndex={selectedIndex} height={suggestions.length} rowGap={0} surface="framed" bgColor={colors.panel} onActivate={(_, index) => { onSelect(index); }} /> ); } export function ChatComposerArea({ canSend, cancelEditMessage, clearReplyTarget, commitLocalDraft, composerHeight, composerWidth, contentWidth, focused, focusComposer, hasSavedSession, inputFocused, inputPlaceholder, inputRef, inputValueRef, nativePaneChrome, editingMessage, editingPreview, replyPreview, replyTo, sendMessage, mentionSuggestions, mentionSelectedIndex, onMentionCursorChange, onMentionSelect, user, }: ChatComposerAreaProps) { const commandBarShortcut = useCommandBarShortcut(); if (!canSend) { const fitText = (value: string) => truncateToDisplayWidth(value, contentWidth); const fitLine = (value: string) => fitText(t(value)); return ( // Inset like the transcript above it, so the prompt lines up with the messages. {!user && !hasSavedSession ? ( <> {fitLine("Read-only chat. Log in or sign up to send.")} ) : !user ? ( <> {fitLine("Saved login found. Log in again to send.")} ) : ( <> {fitLine("Verify your email to send messages.")} {fitText(tf("{shortcut}, then Resend Verification Email", { shortcut: commandBarShortcut }))} )} ); } return ( <> {nativePaneChrome && ( )} {editingMessage && ( {` ${t("editing")} `} {editingPreview ? `: ${editingPreview}` : ""}