Skip to content

Commit e483d23

Browse files
committed
fix: anchor saved scroll position by timestamp instead of index
Store the first visible entry's timestamp (stable across buffer eviction) rather than its array index. On restore, resolve the timestamp back to the current index via findEntryIndexByTime so the scroll position survives head eviction while minimized.
1 parent 4fd7c6b commit e483d23

1 file changed

Lines changed: 23 additions & 11 deletions

File tree

  • ui/providers/BottomDrawer/containers/LogViewer

ui/providers/BottomDrawer/containers/LogViewer/index.tsx

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,13 @@ const LogViewerContainer: React.FC<Props> = ({ sessionId, source, toolbarPrefix,
163163
followRef.current = follow;
164164
const filteredLineCountRef = useRef(filteredLineCount);
165165
filteredLineCountRef.current = filteredLineCount;
166+
const filteredEntriesRef = useRef(filteredEntries);
167+
filteredEntriesRef.current = filteredEntries;
166168

167169
// Re-pin to bottom (or restore position) when the scroll container resizes.
168170
// This covers drawer drag-resize, minimize/re-expand, and fullscreen toggle.
169-
const savedFirstVisibleRef = useRef<number | null>(null);
171+
// We store a timestamp (not an index) so the anchor survives buffer eviction.
172+
const savedVisibleTimestampRef = useRef<string | null>(null);
170173
const lastContainerHeightRef = useRef(0);
171174

172175
React.useEffect(() => {
@@ -188,24 +191,33 @@ const LogViewerContainer: React.FC<Props> = ({ sessionId, source, toolbarPrefix,
188191
isAutoScrolling.current = false;
189192
});
190193
});
191-
} else if (prevHeight === 0 && savedFirstVisibleRef.current !== null) {
192-
// Restoring from minimized without follow: jump back to saved position
193-
isAutoScrolling.current = true;
194-
rowVirtualizer.scrollToIndex(savedFirstVisibleRef.current, { align: 'start' });
195-
savedFirstVisibleRef.current = null;
196-
requestAnimationFrame(() => {
194+
} else if (prevHeight === 0 && savedVisibleTimestampRef.current !== null) {
195+
// Restoring from minimized without follow: resolve saved timestamp to current index
196+
const idx = findEntryIndexByTime(
197+
filteredEntriesRef.current,
198+
new Date(savedVisibleTimestampRef.current),
199+
);
200+
savedVisibleTimestampRef.current = null;
201+
if (idx >= 0) {
202+
isAutoScrolling.current = true;
203+
rowVirtualizer.scrollToIndex(idx, { align: 'start' });
197204
requestAnimationFrame(() => {
198-
isAutoScrolling.current = false;
205+
requestAnimationFrame(() => {
206+
isAutoScrolling.current = false;
207+
});
199208
});
200-
});
209+
}
201210
}
202211
}
203212

204-
// Save the first visible index before the container collapses
213+
// Save the first visible entry's timestamp before the container collapses
205214
if (prevHeight > 0 && newHeight === 0 && !followRef.current) {
206215
const range = rowVirtualizer.range;
207216
if (range) {
208-
savedFirstVisibleRef.current = range.startIndex;
217+
const entry = filteredEntriesRef.current[range.startIndex];
218+
if (entry?.timestamp) {
219+
savedVisibleTimestampRef.current = entry.timestamp;
220+
}
209221
}
210222
}
211223

0 commit comments

Comments
 (0)