Improve chat composer typing performance
This commit is contained in:
+26
-58
@@ -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<Record<string, PendingChatState>>({});
|
||||
const [runningSearchStates, setRunningSearchStates] = useState<Record<string, SearchDetail>>({});
|
||||
const [activeRuns, setActiveRuns] = useState<ActiveRunsState>(EMPTY_ACTIVE_RUNS);
|
||||
const [composer, setComposer] = useState("");
|
||||
const [composerDraftRevision, setComposerDraftRevision] = useState(0);
|
||||
const [pendingAttachments, setPendingAttachments] = useState<ChatAttachment[]>([]);
|
||||
const [isComposerDropActive, setIsComposerDropActive] = useState(false);
|
||||
const [provider, setProvider] = useState<Provider>("openai");
|
||||
@@ -977,6 +977,7 @@ export default function App() {
|
||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||
const dragDepthRef = useRef(0);
|
||||
const pendingAttachmentsRef = useRef<ChatAttachment[]>([]);
|
||||
const composerDraftRef = useRef("");
|
||||
const selectedItemRef = useRef<SidebarSelection | null>(null);
|
||||
const pendingTitleGenerationRef = useRef<Set<string>>(new Set());
|
||||
const chatStreamAbortRefs = useRef<Map<string, AbortController>>(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() {
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<Textarea
|
||||
id="composer-input"
|
||||
rows={1}
|
||||
value={composer}
|
||||
onInput={(event) => {
|
||||
const textarea = event.currentTarget;
|
||||
textarea.style.height = "0px";
|
||||
textarea.style.height = `${textarea.scrollHeight}px`;
|
||||
setComposer(textarea.value);
|
||||
}}
|
||||
<ChatComposer
|
||||
draftRef={composerDraftRef}
|
||||
draftRevision={composerDraftRevision}
|
||||
error={error}
|
||||
isSearchMode={isSearchMode}
|
||||
isSending={isActiveSelectionSending}
|
||||
pendingAttachmentCount={pendingAttachments.length}
|
||||
attachmentButtonDisabled={
|
||||
isActiveSelectionSending || pendingAttachments.length >= MAX_CHAT_ATTACHMENTS
|
||||
}
|
||||
onOpenAttachmentPicker={handleOpenAttachmentPicker}
|
||||
onPaste={(event) => {
|
||||
void handleComposerPaste(event);
|
||||
}}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
void handleSend();
|
||||
}
|
||||
}}
|
||||
placeholder={isSearchMode ? "Search the web" : "Enter prompt..."}
|
||||
className="max-h-40 min-h-0 resize-none overflow-y-auto border-0 bg-transparent px-3 py-3 text-base text-violet-50 shadow-none placeholder:text-violet-200/45 focus-visible:ring-0"
|
||||
disabled={isActiveSelectionSending}
|
||||
onSend={handleSend}
|
||||
/>
|
||||
<div className={cn("flex items-center gap-3 px-2 pb-1", error ? "justify-between" : "justify-end")}>
|
||||
{error ? <p className="min-w-0 truncate text-xs text-rose-300">{error}</p> : null}
|
||||
{!isSearchMode ? (
|
||||
<Button
|
||||
className="h-10 w-10 rounded-lg"
|
||||
onClick={handleOpenAttachmentPicker}
|
||||
size="icon"
|
||||
variant="secondary"
|
||||
disabled={isActiveSelectionSending || pendingAttachments.length >= MAX_CHAT_ATTACHMENTS}
|
||||
aria-label="Attach files"
|
||||
>
|
||||
<Paperclip className="h-4 w-4" />
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
className="h-10 w-10 rounded-lg"
|
||||
onClick={() => void handleSend()}
|
||||
size="icon"
|
||||
disabled={isActiveSelectionSending || (!composer.trim() && !pendingAttachments.length)}
|
||||
>
|
||||
{isSearchMode ? <Search className="h-4 w-4" /> : <SendHorizontal className="h-4 w-4" />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user