From a9254048502ddb02f2ca9d7a06e66e096414ee37 Mon Sep 17 00:00:00 2001 From: Zane Staggs Date: Sun, 13 Jul 2025 16:01:38 -0700 Subject: [PATCH] fix search highlighting in sessions list view --- .../components/sessions/SessionListView.tsx | 181 +++++++----------- ui/desktop/src/utils/searchHighlighter.ts | 7 +- 2 files changed, 72 insertions(+), 116 deletions(-) diff --git a/ui/desktop/src/components/sessions/SessionListView.tsx b/ui/desktop/src/components/sessions/SessionListView.tsx index 46bdd7847c..f91719c79c 100644 --- a/ui/desktop/src/components/sessions/SessionListView.tsx +++ b/ui/desktop/src/components/sessions/SessionListView.tsx @@ -1,6 +1,5 @@ -import React, { useEffect, useState, useRef, useMemo } from 'react'; +import React, { useEffect, useState, useRef, useCallback } from 'react'; import { MessageSquareText, Target, AlertCircle, Calendar, Folder } from 'lucide-react'; -import { VariableSizeList as List } from 'react-window'; import { fetchSessions, type Session } from '../../sessions'; import { Card } from '../ui/card'; import { Button } from '../ui/button'; @@ -22,13 +21,6 @@ interface SessionListViewProps { onSelectSession: (sessionId: string) => void; } -// Flattened item structure for virtualization -interface FlattenedItem { - type: 'header' | 'session'; - data: DateGroup | Session; - index: number; -} - const SessionListView: React.FC = ({ onSelectSession }) => { const [sessions, setSessions] = useState([]); const [filteredSessions, setFilteredSessions] = useState([]); @@ -43,37 +35,29 @@ const SessionListView: React.FC = ({ onSelectSession }) => currentIndex: number; } | null>(null); const containerRef = useRef(null); - const listRef = useRef(null); - // Flatten date groups into a single array for virtualization - const flattenedItems = useMemo((): FlattenedItem[] => { - const items: FlattenedItem[] = []; - let index = 0; - - dateGroups.forEach((group) => { - // Add header - items.push({ - type: 'header', - data: group, - index: index++, - }); - - // Add sessions - group.sessions.forEach((session) => { - items.push({ - type: 'session', - data: session, - index: index++, - }); - }); - }); - - return items; - }, [dateGroups]); + const loadSessions = useCallback(async () => { + setIsLoading(true); + setShowSkeleton(true); + setShowContent(false); + setError(null); + try { + const sessions = await fetchSessions(); + setSessions(sessions); + setFilteredSessions(sessions); + } catch (err) { + console.error('Failed to load sessions:', err); + setError('Failed to load sessions. Please try again later.'); + setSessions([]); + setFilteredSessions([]); + } finally { + setIsLoading(false); + } + }, []); useEffect(() => { loadSessions(); - }, []); + }, [loadSessions]); // Timing logic to prevent flicker between skeleton and content on initial load useEffect(() => { @@ -100,13 +84,6 @@ const SessionListView: React.FC = ({ onSelectSession }) => } }, [filteredSessions]); - // Reset list cache when items change - useEffect(() => { - if (listRef.current) { - listRef.current.resetAfterIndex(0); - } - }, [flattenedItems]); - // Filter sessions when search term or case sensitivity changes const handleSearch = (term: string, caseSensitive: boolean) => { if (!term) { @@ -140,25 +117,6 @@ const SessionListView: React.FC = ({ onSelectSession }) => setSearchResults(filtered.length > 0 ? { count: filtered.length, currentIndex: 1 } : null); }; - const loadSessions = async () => { - setIsLoading(true); - setShowSkeleton(true); - setShowContent(false); - setError(null); - try { - const sessions = await fetchSessions(); - setSessions(sessions); - setFilteredSessions(sessions); - } catch (err) { - console.error('Failed to load sessions:', err); - setError('Failed to load sessions. Please try again later.'); - setSessions([]); - setFilteredSessions([]); - } finally { - setIsLoading(false); - } - }; - // Handle search result navigation const handleSearchNavigation = (direction: 'next' | 'prev') => { if (!searchResults || filteredSessions.length === 0) return; @@ -325,71 +283,68 @@ const SessionListView: React.FC = ({ onSelectSession }) =>
- - {/* Skeleton layer - always rendered but conditionally visible */} -
+ +
-
- {/* Today section */} -
- -
- - - - - + {/* Skeleton layer - always rendered but conditionally visible */} +
+
+ {/* Today section */} +
+ +
+ + + + + +
-
- {/* Yesterday section */} -
- -
- - - - - - + {/* Yesterday section */} +
+ +
+ + + + + + +
-
- {/* Additional section */} -
- -
- - - + {/* Additional section */} +
+ +
+ + + +
- -
- {/* Content layer - always rendered but conditionally visible */} -
- - {renderActualContent()} + {/* Content layer - always rendered but conditionally visible */} +
+ {renderActualContent()} +
diff --git a/ui/desktop/src/utils/searchHighlighter.ts b/ui/desktop/src/utils/searchHighlighter.ts index f479192c8e..ad3fa45e68 100644 --- a/ui/desktop/src/utils/searchHighlighter.ts +++ b/ui/desktop/src/utils/searchHighlighter.ts @@ -29,7 +29,7 @@ export class SearchHighlighter { left: 0; right: 0; bottom: 0; - z-index: 1; + z-index: 9999; `; // Find scroll container (look for our custom data attribute first, then fallback to radix) @@ -151,8 +151,9 @@ export class SearchHighlighter { const scrollLeft = this.scrollContainer?.scrollLeft || 0; // Calculate the highlight position relative to the scroll container - const top = rect.top + scrollTop - containerRect.top; - const left = rect.left + scrollLeft - containerRect.left; + // rect.top/left are already relative to viewport, so we need to adjust for scroll container position + const top = rect.top - containerRect.top + scrollTop; + const left = rect.left - containerRect.left + scrollLeft; highlightRect.style.cssText = ` position: absolute;