Skip to content
Source
Sign inSign up
Open History to see the last commit

ThreadTimeline.tsx

58 lines2.5 KB

1import type { ScopedThreadRef } from "@t3tools/contracts";
2import { type ComponentProps, useLayoutEffect } from "react";
3import { useIncrementalTimelineEntries } from "../../hooks/useIncrementalTimelineEntries";
4import type { TimelineEntriesInput, TimelineEntry } from "../../session-logic";
5import { useTimelineVisibleTurnItems } from "../../state/entities";
6import {
7 peekRememberedThreadTimeline,
8 rememberReadyThreadTimeline,
9 resolveThreadSwitchTimeline,
10 timelineHasEphemeralPreviewUrls,
11} from "../ChatView.logic";
12import { MessagesTimeline } from "./MessagesTimeline";
13​
14/**
15 * The timeline with the streamed text. ChatView reads the thread through its quiet
16 * atoms, which hold through text deltas; this reads the live turn items, so a
17 * reasoning delta re-renders the timeline without re-rendering ChatView. Streamed
18 * assistant text reaches only its own row, through useAssistantMessageText.
19 */
20export function ThreadTimeline({
21 threadDetailRef,
22 entriesInput,
23 draftEntries,
24 activeThreadKey,
25 syncing,
26 detailLoading,
27 ...timelineProps
28}: Omit<ComponentProps<typeof MessagesTimeline>, "timelineEntries"> & {
29 /** Null until the thread's detail may load; see resolveThreadDetailRef. */
30 threadDetailRef: ScopedThreadRef | null;
31 entriesInput: Omit<TimelineEntriesInput, "visibleTurnItems">;
32 /** A local draft's optimistic rows; null for a server thread. */
33 draftEntries: ReadonlyArray<TimelineEntry> | null;
34 activeThreadKey: string | null;
35 syncing: boolean;
36 detailLoading: boolean;
37}) {
38 const visibleTurnItems = useTimelineVisibleTurnItems(threadDetailRef);
39 const serverEntries = useIncrementalTimelineEntries(activeThreadKey, {
40 ...entriesInput,
41 visibleTurnItems,
42 });
43 const entries = draftEntries ?? serverEntries;
44 const displayedEntries = resolveThreadSwitchTimeline({
45 loading: entries.length === 0 && syncing,
46 activeThreadKey,
47 nextEntries: entries,
48 rememberedForActive: peekRememberedThreadTimeline<typeof entries>(activeThreadKey),
49 });
50 // oxlint-disable-next-line t3code/no-unreasoned-state-sync -- stores the committed ready timeline in the module-level cache used during thread switches
51 useLayoutEffect(() => {
52 if (detailLoading || entries.length === 0 || timelineHasEphemeralPreviewUrls(entries)) {
53 return;
54 }
55 rememberReadyThreadTimeline({ threadKey: activeThreadKey, entries });
56 }, [activeThreadKey, detailLoading, entries]);
57 return <MessagesTimeline {...timelineProps} timelineEntries={displayedEntries} />;