Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 88 additions & 82 deletions apps/web/src/components/chat/MessagesTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1374,13 +1374,21 @@ const ConversationTimeline = memo(function ConversationTimeline({
contentInsetEndAdjustment,
});

// Each row carries its own provider. LegendList moves rows into their
// containers one commit after this component renders, so a provider around
// the list would re-render the outgoing thread's rows with the incoming
// thread's context just before they unmount. extraData hands a changed
// sharedState to the rows that stay.
const renderItem = ({ item }: { item: MessagesTimelineRow }) => (
<div className="messages-timeline-row-frame">
<div className="chat-content-lane overflow-x-clip" data-timeline-root="true">
<TimelineRowContent row={item} />
<TimelineRowCtx value={sharedState}>
<div className="messages-timeline-row-frame">
<div className="chat-content-lane overflow-x-clip" data-timeline-root="true">
<TimelineRowContent row={item} />
</div>
</div>
</div>
</TimelineRowCtx>
);
const listExtraData = { identity: listIdentityKey, rowCount: rows.length, sharedState };

const forwardMinimapWheel = (event: WheelEvent) => {
const scrollNode = listRef.current?.getScrollableNode();
Expand Down Expand Up @@ -1411,85 +1419,83 @@ const ConversationTimeline = memo(function ConversationTimeline({

return (
<MarkdownFindContext value={findActive}>
<TimelineRowCtx value={sharedState}>
<TimelineRowActivityCtx value={activityState}>
<TooltipScrollDismissArea
ref={setTimelineViewportElement}
className="relative h-full min-h-0"
data-assistant-citation-viewport="true"
>
{onCiteAssistantText && citationThreadRef ? (
<AssistantSelectionToolbar
viewport={timelineViewportElement}
threadRef={citationThreadRef}
onCite={onCiteAssistantText}
/>
) : null}
<LegendList<MessagesTimelineRow>
ref={setTimelineList}
data={rows}
extraData={`${listIdentityKey}:${rows.length}`}
keyExtractor={keyExtractor}
getItemType={getItemType}
renderItem={renderItem}
estimatedItemSize={90}
initialScrollAtEnd={
!findActive && citationRequest === null && rememberedPosition?.atEnd !== false
}
// Legend needs a data refresh to mount new pins without a scroll event.
dataVersion={readyCitationRequest?.key ?? listIdentityKey}
{...(alwaysRender ? { alwaysRender } : {})}
onLoad={handleListLoad}
{...(anchoredEndSpace ? { anchoredEndSpace } : {})}
contentInsetEndAdjustment={anchoredEndSpace ? contentInsetEndAdjustment : 0}
maintainScrollAtEnd={
citationPositioning ||
(restoringThreadPosition && rememberedPosition?.atEnd === false) ||
anchoredEndSpace ||
!liveFollowEnabled ||
fullscreenAppRowId !== null ||
disclosureToggleSettling
? false
: isWorking && !prefersReducedMotion && settlingListIdentity === null
? TIMELINE_MAINTAIN_SCROLL_AT_END_SMOOTH
: TIMELINE_MAINTAIN_SCROLL_AT_END
}
maintainVisibleContentPosition={
findActive ||
citationPositioning ||
(restoringThreadPosition && rememberedPosition?.atEnd === false)
? false
: maintainVisibleContentPosition
}
maintainScrollAtEndThreshold={1}
onScroll={handleScroll}
onItemSizeChanged={handleItemSizeChanged}
className={cn(
"messages-timeline-scroll scrollbar-gutter-both h-full min-h-0 overflow-x-hidden overscroll-y-contain [overflow-anchor:none]",
topFadeEnabled && "topbar-scroll-fade",
)}
ListHeaderComponent={listHeader}
ListFooterComponent={timelineListFooter}
/>
<TimelineMinimap
items={minimapItems}
hasPersistentGutter={minimapHasPersistentGutter}
hitStripWidth={minimapHitStripWidth}
currentIndex={minimapCurrentIndex}
stripMap={minimapStripMap}
onWheel={forwardMinimapWheel}
onSelect={(item) => {
onManualNavigation();
void listRef.current?.scrollToIndex({
index: item.rowIndex,
animated: true,
viewOffset: 24,
});
}}
<TimelineRowActivityCtx value={activityState}>
<TooltipScrollDismissArea
ref={setTimelineViewportElement}
className="relative h-full min-h-0"
data-assistant-citation-viewport="true"
>
{onCiteAssistantText && citationThreadRef ? (
<AssistantSelectionToolbar
viewport={timelineViewportElement}
threadRef={citationThreadRef}
onCite={onCiteAssistantText}
/>
</TooltipScrollDismissArea>
</TimelineRowActivityCtx>
</TimelineRowCtx>
) : null}
<LegendList<MessagesTimelineRow>
ref={setTimelineList}
data={rows}
extraData={listExtraData}
keyExtractor={keyExtractor}
getItemType={getItemType}
renderItem={renderItem}
estimatedItemSize={90}
initialScrollAtEnd={
!findActive && citationRequest === null && rememberedPosition?.atEnd !== false
}
// Legend needs a data refresh to mount new pins without a scroll event.
dataVersion={readyCitationRequest?.key ?? listIdentityKey}
{...(alwaysRender ? { alwaysRender } : {})}
onLoad={handleListLoad}
{...(anchoredEndSpace ? { anchoredEndSpace } : {})}
contentInsetEndAdjustment={anchoredEndSpace ? contentInsetEndAdjustment : 0}
maintainScrollAtEnd={
citationPositioning ||
(restoringThreadPosition && rememberedPosition?.atEnd === false) ||
anchoredEndSpace ||
!liveFollowEnabled ||
fullscreenAppRowId !== null ||
disclosureToggleSettling
? false
: isWorking && !prefersReducedMotion && settlingListIdentity === null
? TIMELINE_MAINTAIN_SCROLL_AT_END_SMOOTH
: TIMELINE_MAINTAIN_SCROLL_AT_END
}
maintainVisibleContentPosition={
findActive ||
citationPositioning ||
(restoringThreadPosition && rememberedPosition?.atEnd === false)
? false
: maintainVisibleContentPosition
}
maintainScrollAtEndThreshold={1}
onScroll={handleScroll}
onItemSizeChanged={handleItemSizeChanged}
className={cn(
"messages-timeline-scroll scrollbar-gutter-both h-full min-h-0 overflow-x-hidden overscroll-y-contain [overflow-anchor:none]",
topFadeEnabled && "topbar-scroll-fade",
)}
ListHeaderComponent={listHeader}
ListFooterComponent={timelineListFooter}
/>
<TimelineMinimap
items={minimapItems}
hasPersistentGutter={minimapHasPersistentGutter}
hitStripWidth={minimapHitStripWidth}
currentIndex={minimapCurrentIndex}
stripMap={minimapStripMap}
onWheel={forwardMinimapWheel}
onSelect={(item) => {
onManualNavigation();
void listRef.current?.scrollToIndex({
index: item.rowIndex,
animated: true,
viewOffset: 24,
});
}}
/>
</TooltipScrollDismissArea>
</TimelineRowActivityCtx>
</MarkdownFindContext>
);
});
Expand Down
Loading