From 9a0268a6b4b8ee4328b9de030841960230bc4422 Mon Sep 17 00:00:00 2001 From: Jaime Idolpx Date: Sun, 14 Jun 2026 01:31:47 -0400 Subject: [PATCH] feat(SearchOverlay): add scroll position restoration and mounted paths tracking --- src/app/components/SearchOverlay.tsx | 38 +++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/app/components/SearchOverlay.tsx b/src/app/components/SearchOverlay.tsx index 36a3976..f0f540c 100644 --- a/src/app/components/SearchOverlay.tsx +++ b/src/app/components/SearchOverlay.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { flushSync } from 'react-dom'; import { X, Search, Loader2, RefreshCw, FolderSearch, SlidersHorizontal, ArrowUp, ArrowDown, ArrowUpDown, HardDrive, FolderOpen } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; @@ -138,6 +138,7 @@ const _store = { filterLanguage: null as string | null, sortField: 'name' as SortField, sortDir: 'asc' as SortDir, + scrollTop: 0, }; function FilterChips({ @@ -166,6 +167,7 @@ function FilterChips({ } export default function SearchOverlay({ config, setConfig, onClose, onOpenFolder }: SearchOverlayProps) { + const scrollRef = useRef(null); const [query, setQuery] = useState(() => _store.query); const [isSearching, setIsSearching] = useState(false); const [isScanning, setIsScanning] = useState(false); @@ -202,6 +204,26 @@ export default function SearchOverlay({ config, setConfig, onClose, onOpenFolder _store.sortDir = sortDir; }, [query, results, hasSearched, showFilter, filterSystem, filterVideo, filterLanguage, sortField, sortDir]); + // Restore scroll position after results render. + useEffect(() => { + if (_store.scrollTop > 0 && scrollRef.current) { + scrollRef.current.scrollTop = _store.scrollTop; + } + }, []); + + // Build a set of fully-resolved paths currently mounted on any IEC device. + const mountedPaths = useMemo(() => { + const paths = new Set(); + for (const d of Object.values(config?.devices?.iec ?? {})) { + const dev = d as any; + if (!dev?.url) continue; + const base = (dev.base_url ?? '').replace(/\/$/, ''); + const url = dev.url.startsWith('/') ? dev.url : '/' + dev.url; + paths.add(base ? base + url : dev.url); + } + return paths; + }, [config]); + const handleSearch = async () => { if (!query.trim()) { toast.error('Please enter a search term'); return; } setIsSearching(true); @@ -426,17 +448,14 @@ export default function SearchOverlay({ config, setConfig, onClose, onOpenFolder - {/* DB status chip */} - {dbPhase === 'ready' && !busy && ( -

- - Database ready -

- )} {/* Body */} -
+
{ _store.scrollTop = (e.currentTarget as HTMLDivElement).scrollTop; }} + > {/* Scanning progress */} {isScanning && scanPhase && (
@@ -477,6 +496,7 @@ export default function SearchOverlay({ config, setConfig, onClose, onOpenFolder entry={resultToEntry(result)} onPrimaryClick={() => setMountEntry(result)} onActionsClick={e => { e.stopPropagation(); setActionEntry(result); }} + selected={mountedPaths.has(result.path)} nameSlot={ <>