49 lines
1.9 KiB
Markdown
49 lines
1.9 KiB
Markdown
|
|
# Story 22: Smart Auto-Scroll (Respects User Scrolling)
|
||
|
|
|
||
|
|
## User Story
|
||
|
|
As a user, I want to be able to scroll up to review previous messages while the AI is streaming or adding new content, without being constantly dragged back to the bottom.
|
||
|
|
|
||
|
|
## Acceptance Criteria
|
||
|
|
- [ ] When I scroll up in the chat, auto-scroll is temporarily disabled
|
||
|
|
- [ ] Auto-scroll resumes when I scroll back to (or near) the bottom
|
||
|
|
- [ ] There's a visual indicator when auto-scroll is paused (optional)
|
||
|
|
- [ ] Clicking a "Jump to Bottom" button (if added) re-enables auto-scroll
|
||
|
|
- [ ] Auto-scroll works normally when I'm already at the bottom
|
||
|
|
- [ ] The detection works smoothly without flickering
|
||
|
|
- [ ] Works during both streaming responses and tool execution
|
||
|
|
|
||
|
|
## Out of Scope
|
||
|
|
- Manual scroll position restoration after page refresh
|
||
|
|
- Scroll position memory across sessions
|
||
|
|
- Keyboard shortcuts for scrolling
|
||
|
|
- Custom scroll speed or animation settings
|
||
|
|
|
||
|
|
## Technical Notes
|
||
|
|
- Detect if user is scrolled to bottom: `scrollHeight - scrollTop === clientHeight` (with small threshold)
|
||
|
|
- Only auto-scroll if user is at/near bottom (e.g., within 100px)
|
||
|
|
- Track scroll position in state or ref
|
||
|
|
- Add scroll event listener to detect when user manually scrolls
|
||
|
|
- Consider debouncing the scroll detection for performance
|
||
|
|
|
||
|
|
## Design Considerations
|
||
|
|
- Threshold for "near bottom": 100-150px is typical
|
||
|
|
- Optional: Show a "↓ New messages" badge when auto-scroll is paused
|
||
|
|
- Should feel natural and not interfere with reading
|
||
|
|
- Balance between auto-scroll convenience and user control
|
||
|
|
|
||
|
|
## Implementation Approach
|
||
|
|
```tsx
|
||
|
|
const isScrolledToBottom = () => {
|
||
|
|
const element = scrollContainerRef.current;
|
||
|
|
if (!element) return true;
|
||
|
|
const threshold = 150; // pixels from bottom
|
||
|
|
return element.scrollHeight - element.scrollTop - element.clientHeight < threshold;
|
||
|
|
};
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (isScrolledToBottom()) {
|
||
|
|
scrollToBottom();
|
||
|
|
}
|
||
|
|
}, [messages, streamingContent]);
|
||
|
|
```
|