diff --git a/web/src/App.tsx b/web/src/App.tsx index cb42fc3..f86221e 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -7,7 +7,6 @@ import { LoaderCircle, Menu, MessageSquare, - Paperclip, Pencil, Plus, Rabbit, @@ -23,6 +22,7 @@ import { Textarea } from "@/components/ui/textarea"; import { Separator } from "@/components/ui/separator"; import { AuthScreen } from "@/components/auth/auth-screen"; import { ChatAttachmentList } from "@/components/chat/chat-attachment-list"; +import { ChatComposer } from "@/components/chat/chat-composer"; import { ChatMessagesPanel } from "@/components/chat/chat-messages-panel"; import { SearchResultsPanel } from "@/components/search/search-results-panel"; import { SybilCharacter } from "@/components/sybil-character"; @@ -925,7 +925,7 @@ export default function App() { const [pendingChatStates, setPendingChatStates] = useState>({}); const [runningSearchStates, setRunningSearchStates] = useState>({}); const [activeRuns, setActiveRuns] = useState(EMPTY_ACTIVE_RUNS); - const [composer, setComposer] = useState(""); + const [composerDraftRevision, setComposerDraftRevision] = useState(0); const [pendingAttachments, setPendingAttachments] = useState([]); const [isComposerDropActive, setIsComposerDropActive] = useState(false); const [provider, setProvider] = useState("openai"); @@ -977,6 +977,7 @@ export default function App() { const fileInputRef = useRef(null); const dragDepthRef = useRef(0); const pendingAttachmentsRef = useRef([]); + const composerDraftRef = useRef(""); const selectedItemRef = useRef(null); const pendingTitleGenerationRef = useRef>(new Set()); const chatStreamAbortRefs = useRef>(new Map()); @@ -1050,13 +1051,10 @@ export default function App() { }); }; - useEffect(() => { - if (typeof document === "undefined") return; - const textarea = document.getElementById("composer-input") as HTMLTextAreaElement | null; - if (!textarea) return; - textarea.style.height = "0px"; - textarea.style.height = `${textarea.scrollHeight}px`; - }, [composer]); + const replaceComposerDraft = (value: string) => { + composerDraftRef.current = value; + setComposerDraftRevision((current) => current + 1); + }; useEffect(() => { pendingAttachmentsRef.current = pendingAttachments; @@ -1096,7 +1094,7 @@ export default function App() { } searchRunAbortRefs.current.clear(); searchRunCountersRef.current.clear(); - setComposer(""); + replaceComposerDraft(""); setPendingAttachments([]); setIsChatSettingsOpen(false); setIsSavingChatSettings(false); @@ -2967,7 +2965,7 @@ export default function App() { try { const chat = await createChatFromSearch(sourceSearch.id); setDraftKind(null); - setComposer(""); + replaceComposerDraft(""); setPendingAttachments([]); setChats((current) => { const withoutExisting = current.filter((existing) => existing.id !== chat.id); @@ -3126,7 +3124,7 @@ export default function App() { }); setDraftKind(null); - setComposer(""); + replaceComposerDraft(""); setPendingAttachments([]); setIsQuickQuestionOpen(false); setProvider(selection.provider); @@ -3167,8 +3165,8 @@ export default function App() { } }; - const handleSend = async () => { - const content = composer.trim(); + const handleSend = async (draft: string) => { + const content = draft.trim(); const attachments = pendingAttachments; if ((!content && !attachments.length) || isActiveSelectionSending) return; if (isSearchMode && attachments.length) { @@ -3176,7 +3174,7 @@ export default function App() { return; } - setComposer(""); + replaceComposerDraft(""); setPendingAttachments([]); setError(null); @@ -3201,7 +3199,7 @@ export default function App() { } if (!sentAsSearch && (!sentTarget || isCurrentSelection(sentTarget))) { - setComposer(content); + replaceComposerDraft(content); setPendingAttachments(attachments); } @@ -3521,52 +3519,22 @@ export default function App() { ) : null} -