5.7 KiB
5.7 KiB
Agent 2: Search-as-You-Type Implementation
Task
Implement Apple Preview-style search with debouncing in DocumentView.vue
File Modified
/home/setup/navidocs/client/src/views/DocumentView.vue
Changes Implemented
1. Added Reactive State (Line 353)
const isSearching = ref(false)
- Tracks loading state during search operations
- Used to show/hide loading spinner
2. Added Control Variables (Lines 418-419)
let searchDebounceTimer = null
let searchAbortController = null
searchDebounceTimer: Manages 300ms debounce delaysearchAbortController: Allows cancellation of in-flight searches
3. Enhanced performSearch() Function (Lines 707-767)
Key improvements:
- Made async to support cancellable operations
- Added minimum query length check (2 characters)
- Implements AbortController for cancellation
- Sets
isSearchingstate for loading indicator - Validates search hasn't been aborted before proceeding
- Wraps search in try-catch for error handling
- Cleans up abort controller in finally block
Flow:
- Validate query (not empty, min 2 chars)
- Cancel any previous search
- Create new AbortController
- Set loading state
- Highlight current page immediately
- Start background search across all pages
- Clean up and reset loading state
4. Enhanced clearSearch() Function (Lines 769-799)
Key improvements:
- Clears pending debounce timer
- Aborts any ongoing search operation
- Resets
isSearchingstate - Cleans up all search-related state
Added cleanup for:
searchDebounceTimersearchAbortControllerisSearchingstate
5. Implemented handleSearchInput() Function (Lines 801-840)
Core debouncing logic:
function handleSearchInput() {
// Clear previous debounce timer
if (searchDebounceTimer) {
clearTimeout(searchDebounceTimer)
}
// Cancel previous search if new input arrives
if (searchAbortController) {
searchAbortController.abort()
searchAbortController = null
}
const query = searchInput.value.trim()
// Clear results immediately if search input is cleared
if (!query) {
clearSearch()
return
}
// Don't search if query is less than 2 characters
if (query.length < 2) {
// Clear previous results but don't perform new search
searchQuery.value = ''
totalHits.value = 0
hitList.value = []
allPagesHitList.value = []
currentHitIndex.value = 0
return
}
// Set searching state to show loading indicator
isSearching.value = true
// Debounce search - wait 300ms after user stops typing
searchDebounceTimer = setTimeout(() => {
performSearch()
searchDebounceTimer = null
}, 300)
}
Features:
- Clears previous timer on each keystroke
- Cancels in-flight searches when new input arrives
- Immediately clears results when input is cleared
- Doesn't search for queries < 2 characters
- Shows loading state immediately
- Waits 300ms after typing stops before searching
6. Added Loading Indicator UI (Lines 69-99)
Before:
<button @click="performSearch">
<svg><!-- Search icon --></svg>
</button>
After:
<button
@click="performSearch"
:class="{ 'pointer-events-none': isSearching }"
:title="isSearching ? 'Searching...' : 'Search'"
>
<!-- Loading spinner when searching -->
<svg v-if="isSearching" class="animate-spin">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<!-- Search icon when not searching -->
<svg v-else>
<path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</button>
Features:
- Shows spinning loader during search
- Disables button clicks while searching
- Updates tooltip text dynamically
Requirements Met
✅ Debounced search (300ms delay): Implemented with setTimeout in handleSearchInput()
✅ Loading indicator: Spinning icon shown via isSearching reactive state
✅ Cancel previous search: Uses AbortController to cancel in-flight operations
✅ Real-time results: Triggers search automatically as user types
✅ Clear results: Immediately clears when input is cleared
✅ Minimum query length: Won't search if query < 2 characters
User Experience
Typing Flow
- User types "eng" (3 characters)
- Timer starts counting 300ms
- User continues typing "ine"
- Timer resets, counts another 300ms
- User stops typing
- After 300ms, search executes
- Loading spinner shows during search
- Results appear when complete
Backspace Flow
- User backspaces to 1 character
- Previous results clear immediately
- No search is performed (< 2 chars)
Clear Flow
- User clicks clear button or empties input
- All results clear immediately
- Any pending search is cancelled
Technical Implementation
Custom Debounce
- No external libraries required (VueUse not installed)
- Simple
setTimeoutpattern - Properly cleans up timers
Cancellation Pattern
// Create controller
searchAbortController = new AbortController()
// Check if aborted
if (searchAbortController.signal.aborted) {
return
}
// Clean up
searchAbortController = null
State Management
isSearching: Boolean for loading statesearchDebounceTimer: Timer ID for cleanupsearchAbortController: AbortController instance
Build Status
✅ Build successful with no errors ✅ All syntax validated ✅ No TypeScript/linting issues
Integration Notes
- Works seamlessly with existing search functionality
- Preserves cross-page search capability
- Maintains highlight and navigation features
- No breaking changes to public API