fix(ui): reasoning display glitches and scroll button persistence (#1206)
This commit is contained in:
parent
a1844c8294
commit
b229241854
3 changed files with 46 additions and 37 deletions
|
|
@ -1,5 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import {
|
||||
Reasoning,
|
||||
ReasoningTrigger,
|
||||
|
|
@ -15,10 +16,18 @@ export function MessageReasoning({
|
|||
isLoading,
|
||||
reasoning,
|
||||
}: MessageReasoningProps) {
|
||||
const [hasBeenStreaming, setHasBeenStreaming] = useState(isLoading);
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoading) {
|
||||
setHasBeenStreaming(true);
|
||||
}
|
||||
}, [isLoading]);
|
||||
|
||||
return (
|
||||
<Reasoning
|
||||
isStreaming={isLoading}
|
||||
defaultOpen={true}
|
||||
defaultOpen={hasBeenStreaming}
|
||||
data-testid="message-reasoning"
|
||||
>
|
||||
<ReasoningTrigger />
|
||||
|
|
|
|||
|
|
@ -38,9 +38,6 @@ import { SelectItem } from '@/components/ui/select';
|
|||
import * as SelectPrimitive from '@radix-ui/react-select';
|
||||
import equal from 'fast-deep-equal';
|
||||
import type { UseChatHelpers } from '@ai-sdk/react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { ArrowDown } from 'lucide-react';
|
||||
import { useScrollToBottom } from '@/hooks/use-scroll-to-bottom';
|
||||
import type { VisibilityType } from './visibility-selector';
|
||||
import type { Attachment, ChatMessage } from '@/lib/types';
|
||||
import type { AppUsage } from '@/lib/usage';
|
||||
|
|
@ -234,40 +231,8 @@ function PureMultimodalInput({
|
|||
[setAttachments],
|
||||
);
|
||||
|
||||
const { isAtBottom, scrollToBottom } = useScrollToBottom();
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'submitted') {
|
||||
scrollToBottom();
|
||||
}
|
||||
}, [status, scrollToBottom]);
|
||||
|
||||
return (
|
||||
<div className="flex relative flex-col gap-4 w-full">
|
||||
<AnimatePresence>
|
||||
{!isAtBottom && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: 10 }}
|
||||
transition={{ type: 'spring', stiffness: 300, damping: 20 }}
|
||||
className="absolute -top-12 left-1/2 z-50 -translate-x-1/2"
|
||||
>
|
||||
<Button
|
||||
data-testid="scroll-to-bottom-button"
|
||||
className="rounded-full"
|
||||
size="icon"
|
||||
variant="outline"
|
||||
onClick={(event) => {
|
||||
event.preventDefault();
|
||||
scrollToBottom();
|
||||
}}
|
||||
>
|
||||
<ArrowDown />
|
||||
</Button>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
{messages.length === 0 &&
|
||||
attachments.length === 0 &&
|
||||
|
|
|
|||
|
|
@ -17,7 +17,42 @@ export function useScrollToBottom() {
|
|||
|
||||
// Check if we are within 100px of the bottom (like v0 does)
|
||||
setIsAtBottom(scrollTop + clientHeight >= scrollHeight - 100);
|
||||
}, []);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
const container = containerRef.current;
|
||||
|
||||
const resizeObserver = new ResizeObserver(() => {
|
||||
requestAnimationFrame(() => {
|
||||
handleScroll();
|
||||
});
|
||||
});
|
||||
|
||||
const mutationObserver = new MutationObserver(() => {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
handleScroll();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
resizeObserver.observe(container);
|
||||
mutationObserver.observe(container, {
|
||||
childList: true,
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['style', 'class', 'data-state']
|
||||
});
|
||||
|
||||
handleScroll();
|
||||
|
||||
return () => {
|
||||
resizeObserver.disconnect();
|
||||
mutationObserver.disconnect();
|
||||
};
|
||||
}, [handleScroll]);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue