fix: resolve scroll flickering on message send and improve thinking state animation (#1333)
This commit is contained in:
parent
d0b6f37aee
commit
a3802348fa
14 changed files with 225 additions and 174 deletions
|
|
@ -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}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue