fix: resolve scroll flickering on message send and improve thinking state animation (#1333)

This commit is contained in:
josh 2025-11-29 13:02:24 +00:00 committed by GitHub
parent d0b6f37aee
commit a3802348fa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 225 additions and 174 deletions

View file

@ -21,7 +21,6 @@ import { useLocalStorage, useWindowSize } from "usehooks-ts";
import { saveChatModelAsCookie } from "@/app/(chat)/actions";
import { SelectItem } from "@/components/ui/select";
import { chatModels } from "@/lib/ai/models";
import { myProvider } from "@/lib/ai/providers";
import type { Attachment, ChatMessage } from "@/lib/types";
import type { AppUsage } from "@/lib/usage";
import { cn } from "@/lib/utils";
@ -195,10 +194,6 @@ function PureMultimodalInput({
}
}, []);
const _modelResolver = useMemo(() => {
return myProvider.languageModel(selectedModelId);
}, [selectedModelId]);
const contextProps = useMemo(
() => ({
usage,
@ -231,36 +226,39 @@ function PureMultimodalInput({
},
[setAttachments, uploadFile]
);
const handlePaste = useCallback(
async (event: ClipboardEvent) => {
const items = event.clipboardData?.items;
if (!items) return;
if (!items) {
return;
}
const imageItems = Array.from(items).filter((item) =>
item.type.startsWith('image/'),
item.type.startsWith("image/")
);
if (imageItems.length === 0) return;
if (imageItems.length === 0) {
return;
}
// Prevent default paste behavior for images
event.preventDefault();
setUploadQueue((prev) => [...prev, 'Pasted image']);
setUploadQueue((prev) => [...prev, "Pasted image"]);
try {
const uploadPromises = imageItems.map(async (item) => {
const file = item.getAsFile();
if (!file) return;
return uploadFile(file);
});
const uploadPromises = imageItems
.map((item) => item.getAsFile())
.filter((file): file is File => file !== null)
.map((file) => uploadFile(file));
const uploadedAttachments = await Promise.all(uploadPromises);
const successfullyUploadedAttachments = uploadedAttachments.filter(
(attachment) =>
attachment !== undefined &&
attachment.url !== undefined &&
attachment.contentType !== undefined,
attachment.contentType !== undefined
);
setAttachments((curr) => [
@ -268,22 +266,24 @@ function PureMultimodalInput({
...(successfullyUploadedAttachments as Attachment[]),
]);
} catch (error) {
console.error('Error uploading pasted images:', error);
toast.error('Failed to upload pasted image(s)');
console.error("Error uploading pasted images:", error);
toast.error("Failed to upload pasted image(s)");
} finally {
setUploadQueue([]);
}
},
[setAttachments],
[setAttachments, uploadFile]
);
// Add paste event listener to textarea
useEffect(() => {
const textarea = textareaRef.current;
if (!textarea) return;
if (!textarea) {
return;
}
textarea.addEventListener('paste', handlePaste);
return () => textarea.removeEventListener('paste', handlePaste);
textarea.addEventListener("paste", handlePaste);
return () => textarea.removeEventListener("paste", handlePaste);
}, [handlePaste]);
return (
@ -385,9 +385,9 @@ function PureMultimodalInput({
) : (
<PromptInputSubmit
className="size-8 rounded-full bg-primary text-primary-foreground transition-colors duration-200 hover:bg-primary/90 disabled:bg-muted disabled:text-muted-foreground"
data-testid="send-button"
disabled={!input.trim() || uploadQueue.length > 0}
status={status}
data-testid="send-button"
>
<ArrowUpIcon size={14} />
</PromptInputSubmit>
@ -482,7 +482,7 @@ function PureModelSelectorCompact({
value={selectedModel?.name}
>
<Trigger asChild>
<Button variant="ghost" className="h-8 px-2">
<Button className="h-8 px-2" variant="ghost">
<CpuIcon size={16} />
<span className="hidden font-medium text-xs sm:block">
{selectedModel?.name}