import { useEffect, useRef, useState } from "react"; import { ImagePlus, Loader2 } from "lucide-react"; import { MarkdownEditor, type MarkdownEditorRef, type MentionOption, } from "@/components/MarkdownEditor"; import { Button } from "@/components/ui/button"; interface TaskChatRichInputProps { value: string; onChange: (value: string) => void; placeholder?: string; imageUploadHandler?: (file: File) => Promise; mentions?: MentionOption[]; disabled?: boolean; autoFocus?: boolean; onSubmit?: () => void; onUploadingChange?: (uploading: boolean) => void; ariaLabelledBy?: string; testId?: string; attachAriaLabel?: string; showImageAttachControls?: boolean; } /** Composer-grade rich text field for takeover answers and revision notes. */ export function TaskChatRichInput({ value, onChange, placeholder, imageUploadHandler, mentions, disabled = false, autoFocus = false, onSubmit, onUploadingChange, ariaLabelledBy, testId = "task-chat-rich-input", attachAriaLabel = "Attach image", showImageAttachControls = false, }: TaskChatRichInputProps) { const editorRef = useRef(null); const fileInputRef = useRef(null); const uploadCountRef = useRef(0); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(null); useEffect(() => { if (!autoFocus) return; const frame = requestAnimationFrame(() => editorRef.current?.focus()); return () => cancelAnimationFrame(frame); }, [autoFocus]); function updateUploading(next: boolean) { setUploading(next); onUploadingChange?.(next); } async function uploadImage(file: File): Promise { if (!imageUploadHandler) throw new Error("Image uploads are unavailable."); uploadCountRef.current += 1; updateUploading(true); setUploadError(null); try { return await imageUploadHandler(file); } catch (error) { setUploadError( error instanceof Error ? error.message : "The image could not be uploaded.", ); throw error; } finally { uploadCountRef.current = Math.max(0, uploadCountRef.current - 1); if (uploadCountRef.current === 0) updateUploading(false); } } async function chooseImage(file: File | null) { if (!file) return; try { const url = await uploadImage(file); const alt = (file.name || "image").replace(/[[\]]/g, "\\$&"); editorRef.current?.insertMarkdown(`\n\n![${alt}](${url})\n\n`); } catch { // uploadImage owns the visible error state. } } return (
{imageUploadHandler && showImageAttachControls ? (
{ void chooseImage(event.target.files?.[0] ?? null); event.target.value = ""; }} /> or drop/paste an image into the note
) : null} {uploadError ? (

{uploadError}

) : null}
); }