refactor: streamline chat scroll manager

- Removed unused activeTurnAnchorId and activeTurnSpacerHeight from MemoryDebugPanel.
- Simplified useChatScrollManager by eliminating unnecessary state and functions related to active turn anchoring.
- Updated loadMessages function signatures to accept an optional limit parameter across message and session stores.
- Enhanced scroll behavior in useScrollEngine to support dynamic bottom tracking during animations.
- Improved event stream handling to allow for resyncing messages with a specified limit.
- Cleaned up session memory state to remove active turn properties, focusing on viewport anchoring.
This commit is contained in:
Bohdan Triapitsyn
2026-01-20 03:23:39 +02:00
parent 8fa16baf3b
commit 6544b12e78
15 changed files with 318 additions and 937 deletions
+15
View File
@@ -7,6 +7,7 @@ type ScrollEngineOptions = {
type ScrollOptions = {
instant?: boolean;
followBottom?: boolean; // Dynamically track bottom during animation
};
type ScrollEngineResult = {
@@ -33,6 +34,7 @@ export const useScrollEngine = ({
const animationStartRef = React.useRef<number | null>(null);
const animationFromRef = React.useRef(0);
const animationTargetRef = React.useRef(0);
const followBottomRef = React.useRef(false);
const cancelAnimation = React.useCallback(() => {
if (animationFrameRef.current !== null && typeof window !== 'undefined') {
@@ -41,6 +43,7 @@ export const useScrollEngine = ({
animationFrameRef.current = null;
animationStartRef.current = null;
followBottomRef.current = false;
}, []);
const runAnimationFrame = React.useCallback(
@@ -55,6 +58,11 @@ export const useScrollEngine = ({
animationStartRef.current = timestamp;
}
// If followBottom mode, dynamically update target to current bottom
if (followBottomRef.current) {
animationTargetRef.current = container.scrollHeight - container.clientHeight;
}
const progress = Math.min(1, (timestamp - animationStartRef.current) / ANIMATION_DURATION_MS);
const easedProgress = 1 - Math.pow(1 - progress, 3);
const from = animationFromRef.current;
@@ -86,6 +94,7 @@ export const useScrollEngine = ({
const target = Math.max(0, position);
const preferInstant = options?.instant ?? false;
const followBottom = options?.followBottom ?? false;
manualOverrideRef.current = false;
@@ -102,6 +111,11 @@ export const useScrollEngine = ({
return;
}
// If followBottom animation is already running, don't restart - let it continue
if (followBottom && followBottomRef.current && animationFrameRef.current !== null) {
return;
}
cancelAnimation();
const distance = Math.abs(target - container.scrollTop);
@@ -120,6 +134,7 @@ export const useScrollEngine = ({
animationFromRef.current = container.scrollTop;
animationTargetRef.current = target;
animationStartRef.current = null;
followBottomRef.current = followBottom;
animationFrameRef.current = window.requestAnimationFrame(runAnimationFrame);
},
[cancelAnimation, containerRef, runAnimationFrame, setIsAtTop]