File size: 1,777 Bytes
48eafa7 df790cc 48eafa7 df790cc 48eafa7 df790cc 48eafa7 df790cc 48eafa7 df790cc a686c43 48eafa7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | "use client";
import { useEffect, useRef } from "react";
import { useChatStore } from "@/lib/chatStore";
import type { SourceDoc } from "@/lib/types";
import { AssistantMessage, ErrorMessage, UserMessage } from "./Message";
import { Thinking } from "./Thinking";
export function Thread({
onOpenSource,
}: {
onOpenSource?: (doc: SourceDoc, index: number) => void;
}) {
const conv = useChatStore((s) =>
s.activeId ? s.conversations[s.activeId] : null
);
const ref = useRef<HTMLDivElement | null>(null);
// Auto-scroll to bottom on new turns / when streaming tokens arrive
const turns = conv?.turns ?? [];
const lastTurn = turns[turns.length - 1];
useEffect(() => {
const el = ref.current;
if (!el) return;
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
}, [
turns.length,
lastTurn?.pending,
lastTurn?.response?.answer?.length,
]);
if (!conv || conv.turns.length === 0) return null;
return (
<div ref={ref} className="flex-1 overflow-y-auto min-h-0">
<div className="min-h-full flex flex-col justify-end max-w-[920px] mx-auto px-4 sm:px-6 py-8 space-y-6">
{conv.turns.map((t) => {
const hasAnswer = Boolean(t.response?.answer);
const showThinking = t.pending && !hasAnswer && !t.error;
const showAnswer = !!t.response && (hasAnswer || !t.pending);
return (
<div key={t.id} className="space-y-4">
<UserMessage text={t.question} />
{showThinking && <Thinking />}
{t.error && <ErrorMessage turn={t} />}
{showAnswer && (
<AssistantMessage turn={t} onOpenSource={onOpenSource} />
)}
</div>
);
})}
</div>
</div>
);
}
|