@@ -212,64 +212,24 @@ export function Sidebar() {
212212 // Combined loading state
213213 const isLoading = workflowsLoading || sessionLoading
214214
215- // Ref to track active timeout IDs for cleanup
216- const scrollTimeoutRef = useRef<number | null>(null)
217-
218215 /**
219- * Scrolls an element into view if it's not already visible in the scroll container.
220- * Uses a retry mechanism with cleanup to wait for the element to be rendered in the DOM.
221- *
222- * @param elementId - The ID of the element to scroll to
223- * @param maxRetries - Maximum number of retry attempts (default: 10)
216+ * Scrolls a newly created element into view if completely off-screen.
217+ * Uses requestAnimationFrame to sync with render, then scrolls.
224218 */
225- const scrollToElement = useCallback(
226- (elementId: string, maxRetries = 10) => {
227- // Clear any existing timeout
228- if (scrollTimeoutRef.current !== null) {
229- clearTimeout(scrollTimeoutRef.current)
230- scrollTimeoutRef.current = null
231- }
232-
233- let attempts = 0
234-
235- const tryScroll = () => {
236- attempts++
237- const element = document.querySelector(`[data-item-id="${elementId}"]`)
238- const container = scrollContainerRef.current
239-
240- if (element && container) {
241- const elementRect = element.getBoundingClientRect()
242- const containerRect = container.getBoundingClientRect()
243-
244- // Check if element is not fully visible in the container
245- const isAboveView = elementRect.top < containerRect.top
246- const isBelowView = elementRect.bottom > containerRect.bottom
247-
248- if (isAboveView || isBelowView) {
249- element.scrollIntoView({ behavior: 'smooth', block: 'nearest' })
250- }
251- scrollTimeoutRef.current = null
252- } else if (attempts < maxRetries) {
253- // Element not in DOM yet, retry after a short delay
254- scrollTimeoutRef.current = window.setTimeout(tryScroll, 50)
255- } else {
256- scrollTimeoutRef.current = null
257- }
258- }
259-
260- // Start the scroll attempt after a small delay to ensure rendering.
261- scrollTimeoutRef.current = window.setTimeout(tryScroll, 50)
262- },
263- [scrollContainerRef]
264- )
265-
266- // Cleanup timeouts on unmount
267- useEffect(() => {
268- return () => {
269- if (scrollTimeoutRef.current !== null) {
270- clearTimeout(scrollTimeoutRef.current)
219+ const scrollToElement = useCallback((elementId: string) => {
220+ requestAnimationFrame(() => {
221+ const element = document.querySelector(`[data-item-id="${elementId}"]`)
222+ const container = scrollContainerRef.current
223+ if (!element || !container) return
224+
225+ const { top: elTop, bottom: elBottom } = element.getBoundingClientRect()
226+ const { top: ctTop, bottom: ctBottom } = container.getBoundingClientRect()
227+
228+ // Only scroll if element is completely off-screen
229+ if (elBottom <= ctTop || elTop >= ctBottom) {
230+ element.scrollIntoView({ behavior: 'smooth', block: 'center' })
271231 }
272- }
232+ })
273233 }, [])
274234
275235 /**
0 commit comments