Skip to content

Commit 5ffbbd8

Browse files
committed
make reliability and UI improvements to the understand page experience
e.g., don't throw an error if abstract not found. don't show loading skeletons when streaming messages. improve type checking for streamed errors.
1 parent 659c867 commit 5ffbbd8

File tree

6 files changed

+130
-54
lines changed

6 files changed

+130
-54
lines changed

client/src/app/(main)/understand/page.tsx

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ import {
3333
import { useAuth } from '@/lib/auth';
3434
import { PaperItem } from "@/lib/schema";
3535
import ReferencePaperCards from '@/components/ReferencePaperCards';
36-
import { Skeleton } from '@/components/ui/skeleton';
3736
import Link from 'next/link';
3837
import { TopicBubbles } from '@/components/TopicBubbles';
3938
import { AnimatedGradientText } from "@/components/magicui/animated-gradient-text";
39+
import { ChatHistorySkeleton } from "@/components/ChatHistorySkeleton";
4040

4141
interface ChatRequestBody {
4242
user_query: string;
@@ -73,6 +73,7 @@ function UnderstandPageContent() {
7373
const [statusMessage, setStatusMessage] = useState('');
7474

7575
const [isConversationLoading, setIsConversationLoading] = useState(false);
76+
const [isSessionLoading, setIsSessionLoading] = useState(true);
7677

7778
const messagesEndRef = useRef<HTMLDivElement>(null);
7879
const inputMessageRef = useRef<HTMLTextAreaElement>(null);
@@ -129,6 +130,7 @@ function UnderstandPageContent() {
129130
toast.error("Failed to load conversation history.");
130131
} finally {
131132
setIsConversationLoading(false);
133+
setIsSessionLoading(false); // New line
132134
}
133135
}, []);
134136

@@ -143,6 +145,7 @@ function UnderstandPageContent() {
143145
setMessages([]);
144146
setConversationId(null);
145147
setIsCentered(true);
148+
setIsSessionLoading(false);
146149
}
147150
}, [searchParams, user, fetchMessages, isStreaming]);
148151

@@ -297,19 +300,24 @@ function UnderstandPageContent() {
297300

298301
try {
299302
const parsedChunk = JSON.parse(event.trim());
300-
const chunkType = parsedChunk.type;
301-
const chunkContent = parsedChunk.content;
302-
303-
if (chunkType === 'content') {
304-
accumulatedContent += chunkContent;
305-
setStreamingChunks(prev => [...prev, chunkContent]);
306-
} else if (chunkType === 'references') {
307-
references = chunkContent;
308-
setStreamingReferences(chunkContent);
309-
} else if (chunkType === 'status') {
310-
setStatusMessage(chunkContent);
311-
} else {
312-
console.warn(`Unknown chunk type: ${chunkType}`);
303+
304+
if (parsedChunk && typeof parsedChunk === 'object' && 'type' in parsedChunk) {
305+
const chunkType = parsedChunk.type;
306+
const chunkContent = parsedChunk.content;
307+
308+
if (chunkType === 'content') {
309+
accumulatedContent += chunkContent;
310+
setStreamingChunks(prev => [...prev, chunkContent]);
311+
} else if (chunkType === 'references') {
312+
references = chunkContent;
313+
setStreamingReferences(chunkContent);
314+
} else if (chunkType === 'status') {
315+
setStatusMessage(chunkContent);
316+
} else {
317+
console.warn(`Unknown chunk type: ${chunkType}`);
318+
}
319+
} else if (parsedChunk) {
320+
console.warn('Received unexpected chunk:', parsedChunk);
313321
}
314322
} catch (error) {
315323
console.error('Error processing event:', error, 'Raw event:', event);
@@ -351,6 +359,22 @@ function UnderstandPageContent() {
351359
setCurrentMessage(e.target.value);
352360
}, []);
353361

362+
useEffect(() => {
363+
const focusInput = () => {
364+
if (inputMessageRef.current &&
365+
!isStreaming &&
366+
papers.length > 0 &&
367+
!chatCreditLimitReached) {
368+
// Small delay to ensure DOM is ready
369+
setTimeout(() => {
370+
inputMessageRef.current?.focus();
371+
}, 100);
372+
}
373+
};
374+
375+
focusInput();
376+
}, [papers.length, isStreaming, chatCreditLimitReached]); // Dependencies that affect focusability
377+
354378
const memoizedMessages = useMemo(() => {
355379
return messages.map((msg, index) => (
356380
<div
@@ -417,7 +441,7 @@ function UnderstandPageContent() {
417441
</div>
418442
</div>
419443
));
420-
}, [messages, user, handleCitationClick]);
444+
}, [messages, user, handleCitationClick, papers]);
421445

422446
const [isCentered, setIsCentered] = useState(true);
423447

@@ -435,23 +459,23 @@ function UnderstandPageContent() {
435459
<div className="flex flex-col w-full h-[calc(100vh-64px)]">
436460
<div className={`${isCentered ? 'flex-0' : 'flex-1'} w-full overflow-y-auto`} ref={messagesContainerRef}>
437461
<div className="mx-auto max-w-3xl space-y-4 p-4 w-full">
438-
{papers.length === 0 && messages.length === 0 && !authLoading && (
439-
<div className="text-center p-8">
440-
<h2 className="text-xl font-semibold mb-2">No Papers Found</h2>
441-
<p className="text-gray-600 dark:text-gray-400 mb-4">
442-
You need to have at least one paper indexed to ask questions.
443-
</p>
444-
<Button onClick={() => window.location.href = '/'}>Index a Paper</Button>
445-
</div>
446-
)}
447-
{isConversationLoading && (
448-
<div className="space-y-4">
449-
<Skeleton className="h-16 w-full" />
450-
<Skeleton className="h-16 w-full" />
451-
<Skeleton className="h-16 w-full" />
452-
</div>
462+
{isSessionLoading ? (
463+
<ChatHistorySkeleton />
464+
) : (
465+
<>
466+
{papers.length === 0 && messages.length === 0 && !authLoading && (
467+
<div className="text-center p-8">
468+
<h2 className="text-xl font-semibold mb-2">No Papers Found</h2>
469+
<p className="text-gray-600 dark:text-gray-400 mb-4">
470+
You need to have at least one paper indexed to ask questions.
471+
</p>
472+
<Button onClick={() => window.location.href = '/'}>Index a Paper</Button>
473+
</div>
474+
)}
475+
476+
{messages.length > 0 && memoizedMessages}
477+
</>
453478
)}
454-
{messages.length > 0 && memoizedMessages}
455479
{
456480
isStreaming && streamingChunks.length > 0 && (
457481
<div className="relative group prose dark:prose-invert !max-w-full rounded-lg w-full text-primary dark:text-primary-foreground">
@@ -559,9 +583,9 @@ function UnderstandPageContent() {
559583
)}
560584
</form>
561585
{
562-
isCentered && !currentMessage && (
586+
isCentered && (
563587
<div className="absolute bottom-0 left-0 w-full">
564-
<TopicBubbles />
588+
<TopicBubbles isVisible={currentMessage.length === 0} />
565589
</div>
566590
)
567591
}

client/src/app/globals.css

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,17 +191,6 @@
191191
animation: float 3s ease-in-out infinite;
192192
}
193193

194-
.horizontal-scroll-container {
195-
overflow-x: auto;
196-
white-space: nowrap;
197-
-ms-overflow-style: none; /* IE and Edge */
198-
scrollbar-width: none; /* Firefox */
199-
}
200-
201-
.horizontal-scroll-container::-webkit-scrollbar {
202-
display: none; /* Chrome, Safari, and Opera */
203-
}
204-
205194
.relative.w-full.max-w-2xl::after {
206195
content: '';
207196
position: absolute;
@@ -212,3 +201,37 @@
212201
background: linear-gradient(to left, var(--background), transparent);
213202
pointer-events: none;
214203
}
204+
205+
.animate-marquee {
206+
animation: marquee var(--duration) linear infinite;
207+
}
208+
209+
.animate-marquee-vertical {
210+
animation: marquee-vertical var(--duration) linear infinite;
211+
}
212+
213+
.animation-direction-reverse {
214+
animation-direction: reverse;
215+
}
216+
217+
.group:hover .group-hover-paused {
218+
animation-play-state: paused;
219+
}
220+
221+
@keyframes marquee {
222+
from {
223+
transform: translateX(0);
224+
}
225+
to {
226+
transform: translateX(calc(-100% - var(--gap)));
227+
}
228+
}
229+
230+
@keyframes marquee-vertical {
231+
from {
232+
transform: translateY(0);
233+
}
234+
to {
235+
transform: translateY(calc(-100% - var(--gap)));
236+
}
237+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
2+
import { Skeleton } from "@/components/ui/skeleton";
3+
4+
export function ChatHistorySkeleton() {
5+
return (
6+
<div className="space-y-4 p-4">
7+
<div className="flex items-end gap-2">
8+
<div className="flex-1 space-y-2">
9+
<Skeleton className="h-6 w-1/4" />
10+
<Skeleton className="h-4 w-3/4" />
11+
</div>
12+
</div>
13+
<div className="flex items-end gap-2 justify-end">
14+
<div className="flex-1 space-y-2 max-w-md">
15+
<Skeleton className="h-6 w-1/4 ml-auto" />
16+
<Skeleton className="h-4 w-full" />
17+
</div>
18+
</div>
19+
<div className="flex items-end gap-2">
20+
<div className="flex-1 space-y-2">
21+
<Skeleton className="h-6 w-1/4" />
22+
<Skeleton className="h-4 w-1/2" />
23+
<Skeleton className="h-4 w-5/6" />
24+
</div>
25+
</div>
26+
</div>
27+
);
28+
}

client/src/components/TopicBubbles.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const TopicCard = ({ topic }: { topic: string }) => {
2020
);
2121
};
2222

23-
export function TopicBubbles() {
23+
export function TopicBubbles({ isVisible }: { isVisible: boolean }) {
2424
const [topics, setTopics] = useState<string[]>([]);
2525

2626
useEffect(() => {
@@ -40,13 +40,16 @@ export function TopicBubbles() {
4040
const secondRow = topics.slice(topics.length / 2);
4141

4242
return (
43-
<div className="relative flex h-full flex-col items-center justify-center overflow-hidden rounded-lg bg-background py-2">
44-
<Marquee pauseOnHover className="[--duration:20s]">
43+
<div className={cn(
44+
"relative flex h-full flex-col items-center justify-center overflow-hidden rounded-lg bg-background py-2 transition-opacity duration-500",
45+
isVisible ? "opacity-100" : "opacity-0"
46+
)}>
47+
<Marquee pauseOnHover className="[--duration:80s]">
4548
{firstRow.map((topic) => (
4649
<TopicCard topic={topic} key={topic} />
4750
))}
4851
</Marquee>
49-
<Marquee reverse pauseOnHover className="[--duration:20s]">
52+
<Marquee pauseOnHover reverse className="[--duration:80s]">
5053
{secondRow.map((topic) => (
5154
<TopicCard topic={topic} key={topic} />
5255
))}

client/src/components/magicui/marquee.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ export function Marquee({
6161
className={cn("flex shrink-0 justify-around [gap:var(--gap)]", {
6262
"animate-marquee flex-row": !vertical,
6363
"animate-marquee-vertical flex-col": vertical,
64-
"group-hover:[animation-play-state:paused]": pauseOnHover,
65-
"[animation-direction:reverse]": reverse,
64+
"group-hover-paused": pauseOnHover,
65+
"animation-direction-reverse": reverse,
6666
})}
6767
>
6868
{children}

server/app/llm/tools/file_tools.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,6 @@ def read_abstract(paper_id: str, current_user: CurrentUser, db: Session) -> str:
224224

225225
abstract = paper.abstract
226226
if not abstract:
227-
raise ValueError("Abstract not found")
227+
return f"Abstract for {paper.title} not found"
228228

229-
return (
230-
f"Abstract:\n\n{abstract.strip()}\n\n" if abstract else "No abstract available."
231-
)
229+
return f"Abstract:\n\n{abstract.strip()}\n\n"

0 commit comments

Comments
 (0)