Skip to content
Merged
Show file tree
Hide file tree
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
3 changes: 3 additions & 0 deletions apps/web/src/components/chat/MessagesTimeline.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ vi.mock("@legendapp/list/react", async () => {

const LegendList = (props: {
data: Array<{ id: string }>;
extraData?: unknown;
keyExtractor: (item: { id: string }) => string;
renderItem: (args: { item: { id: string } }) => ReactNode;
ListHeaderComponent?: ReactNode;
Expand Down Expand Up @@ -48,6 +49,7 @@ vi.mock("@legendapp/list/react", async () => {
return (
<div
data-testid={legendListTestId}
data-extra-data-matches-rows={props.extraData === props.data}
data-anchor-index={props.anchoredEndSpace?.anchorIndex}
data-anchor-max-size={props.anchoredEndSpace?.anchorMaxSize}
data-anchor-offset={props.anchoredEndSpace?.anchorOffset}
Expand Down Expand Up @@ -396,6 +398,7 @@ describe("MessagesTimeline", () => {
expect(markup).toContain('data-anchor-on-ready="true"');
expect(markup).not.toContain("data-anchor-max-size=");
expect(markup).toContain('data-content-inset-end="144"');
expect(markup).toContain('data-extra-data-matches-rows="true"');
expect(markup).toContain("[overflow-anchor:none]");
expect(markup).not.toContain('data-maintain-scroll-at-end="enabled"');
expect(markup).toContain('data-maintain-visible-content-position="object"');
Expand Down
12 changes: 11 additions & 1 deletion apps/web/src/components/chat/MessagesTimeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,11 @@ export const MessagesTimeline = memo(function MessagesTimeline({
// from TimelineRowCtx, which propagates through LegendList's memo.
const renderItem = useCallback(
({ item }: { item: MessagesTimelineRow }) => (
<div className="mx-auto w-full min-w-0 max-w-3xl overflow-x-clip" data-timeline-root="true">
<div
key={item.id}
className="mx-auto w-full min-w-0 max-w-3xl overflow-x-clip"
data-timeline-root="true"
>
<TimelineRowContent row={item} />
</div>
),
Expand Down Expand Up @@ -567,6 +571,12 @@ export const MessagesTimeline = memo(function MessagesTimeline({
<LegendList<MessagesTimelineRow>
ref={listRef}
data={rows}
// LegendList can retain a mounted container's previous child while
// anchored end-space is recomputed around a newly inserted turn.
// Re-running mounted renderers on each logical row-set change keeps
// the container content aligned with its current item key; memoized
// TimelineRowContent still skips unchanged rows.
extraData={rows}
keyExtractor={keyExtractor}
getItemType={getItemType}
renderItem={renderItem}
Expand Down
Loading