8.7 KiB
Agent 5: Keyboard Shortcuts Implementation
Overview
Implementation of Apple Preview-style keyboard shortcuts for search functionality in DocumentView.vue.
Files Modified
/home/setup/navidocs/client/src/views/DocumentView.vue
Implementation Summary
Keyboard Shortcuts Implemented
| Shortcut | Action | Platform |
|---|---|---|
Cmd + F (Mac) / Ctrl + F (Win/Linux) |
Focus search box and select text | Cross-platform |
Enter |
Navigate to next search result (when not in input) | All |
Cmd/Ctrl + G |
Navigate to next search result | Cross-platform |
Shift + Enter |
Navigate to previous search result | All |
Cmd/Ctrl + Shift + G |
Navigate to previous search result | Cross-platform |
Escape |
Clear search and close jump list | All |
Cmd/Ctrl + Alt + F |
Toggle search jump list (sidebar) | Cross-platform |
Key Features
- Cross-Platform Detection: Automatically uses Cmd on Mac, Ctrl on Windows/Linux
- Prevents Default Browser Find: Cmd/Ctrl+F won't open browser's native find dialog
- Context-Aware: Enter key performs search when input is focused, navigates results otherwise
- Global Shortcuts: Work anywhere when the document is in focus
- Apple Preview-Style: Matches familiar keyboard navigation patterns from macOS Preview app
Changes Required
1. Template Changes (Line ~49)
Add ref="searchInputRef" to the search input:
<input
ref="searchInputRef"
v-model="searchInput"
@keydown.enter="performSearch"
@input="handleSearchInput"
type="text"
class="w-full px-6 pr-28 rounded-2xl border-2 border-white/20 bg-white/10 backdrop-blur-lg text-white placeholder-white/50 shadow-lg focus:outline-none focus:border-pink-400 focus:ring-4 focus:ring-pink-400/20"
:class="isHeaderCollapsed ? 'h-10 text-sm' : 'h-16 text-lg'"
placeholder="Search in document... (Cmd/Ctrl+F)"
/>
2. Script Setup - Add Ref Declaration (After line ~427)
// Use hysteresis to prevent flickering at threshold
const COLLAPSE_THRESHOLD = 120 // Collapse when scrolling down past 120px
const EXPAND_THRESHOLD = 80 // Expand when scrolling up past 80px
// Search input ref for keyboard shortcuts
const searchInputRef = ref(null)
// Computed property for selected image URL
const selectedImageUrl = computed(() => {
if (!selectedImage.value) return ''
return getImageUrl(documentId.value, selectedImage.value.id)
})
3. Add Keyboard Handler Function (Before onMounted, around line 1180)
/**
* Handles keyboard shortcuts for search functionality (Apple Preview-style)
*/
function handleKeyboardShortcuts(event) {
const isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0
const cmdOrCtrl = isMac ? event.metaKey : event.ctrlKey
const isInputFocused = document.activeElement === searchInputRef.value
// Cmd/Ctrl + F - Focus search box
if (cmdOrCtrl && event.key === 'f') {
event.preventDefault()
if (searchInputRef.value) {
searchInputRef.value.focus()
searchInputRef.value.select()
}
return
}
// Escape - Clear search and blur input
if (event.key === 'Escape') {
if (searchQuery.value || isInputFocused) {
event.preventDefault()
clearSearch()
if (isInputFocused && searchInputRef.value) {
searchInputRef.value.blur()
}
jumpListOpen.value = false
}
return
}
// Enter or Cmd/Ctrl + G - Next result
if (event.key === 'Enter' && !isInputFocused) {
if (totalHits.value > 0) {
event.preventDefault()
nextHit()
}
return
}
if (cmdOrCtrl && event.key === 'g' && !event.shiftKey) {
if (totalHits.value > 0) {
event.preventDefault()
nextHit()
}
return
}
// Shift + Enter or Cmd/Ctrl + Shift + G - Previous result
if (event.key === 'Enter' && event.shiftKey && !isInputFocused) {
if (totalHits.value > 0) {
event.preventDefault()
prevHit()
}
return
}
if (cmdOrCtrl && event.key === 'G' && event.shiftKey) {
if (totalHits.value > 0) {
event.preventDefault()
prevHit()
}
return
}
// Cmd/Ctrl + Option/Alt + F - Toggle jump list
if (cmdOrCtrl && event.altKey && event.key === 'f') {
if (hitList.value.length > 0) {
event.preventDefault()
jumpListOpen.value = !jumpListOpen.value
}
return
}
}
4. Register Listener in onMounted (Around line 1180)
onMounted(() => {
loadDocument()
// Register global keyboard shortcut handler
window.addEventListener('keydown', handleKeyboardShortcuts)
// Handle deep links (#p=12)
const hash = window.location.hash
// ... rest of existing code
})
5. Clean Up Listener in onBeforeUnmount (Around line 1246)
// Clean up listeners
onBeforeUnmount(() => {
if (rafId) {
cancelAnimationFrame(rafId)
}
if (scrollTimeout) {
clearTimeout(scrollTimeout)
}
window.removeEventListener('scroll', handleScroll)
window.removeEventListener('hashchange', handleHashChange)
window.removeEventListener('keydown', handleKeyboardShortcuts) // ADD THIS LINE
})
Testing Checklist
-
Focus Search Box
- Press
Cmd+F(Mac) orCtrl+F(Windows/Linux) - Search input should receive focus
- Existing text should be selected
- Browser's native find dialog should NOT open
- Press
-
Navigate Results
- Type a search query and press Enter
- Press
EnterorCmd/Ctrl+G→ Should go to next result - Press
Shift+EnterorCmd/Ctrl+Shift+G→ Should go to previous result - Current result should be highlighted with pink background
- Page should scroll to show current result
-
Clear Search
- With search active, press
Escape - Search should clear
- Highlights should be removed
- If search input was focused, it should blur
- With search active, press
-
Toggle Jump List
- Perform a search with multiple results
- Press
Cmd/Ctrl+Alt+F - Jump list (search sidebar) should toggle open/closed
-
Context Awareness
- With search input focused,
Entershould execute search - With search input NOT focused,
Entershould navigate to next result - Shortcuts should not interfere with other inputs or modals
- With search input focused,
Implementation Notes
Cross-Platform Detection
The implementation uses navigator.platform to detect macOS and automatically maps shortcuts:
- Mac: Uses
event.metaKey(Cmd key) - Windows/Linux: Uses
event.ctrlKey(Ctrl key)
Event Prevention
All shortcuts call event.preventDefault() to prevent default browser behavior, particularly important for Cmd/Ctrl+F which would normally open the browser's find dialog.
Context-Aware Enter Key
The Enter key behavior changes based on whether the search input is focused:
- Input focused: Executes search (existing behavior)
- Input not focused: Navigates to next result (new behavior)
Memory Management
Keyboard event listener is properly cleaned up in onBeforeUnmount to prevent memory leaks when the component is destroyed.
Code Structure
The implementation follows Vue 3 Composition API patterns:
- Reactive ref:
searchInputReffor accessing the DOM element - Event handler function:
handleKeyboardShortcutsfor centralized shortcut logic - Lifecycle hooks:
onMountedto register,onBeforeUnmountto clean up - Existing functions: Reuses
nextHit(),prevHit(),clearSearch(),performSearch()
UI Improvements
The search input placeholder now includes a hint:
"Search in document... (Cmd/Ctrl+F)"
This provides visual feedback to users that keyboard shortcuts are available.
Future Enhancements
Potential improvements for future iterations:
- Add keyboard shortcut help modal (press
?to show all shortcuts) - Implement
Cmd/Ctrl + Ato select all results - Add
Cmd/Ctrl + Cto copy current result context - Support arrow keys for result navigation
- Add visual indicator when keyboard shortcuts are active
- Persist user's keyboard shortcut preferences
Related Files
- Implementation Reference:
/home/setup/navidocs/KEYBOARD_SHORTCUTS_CODE.js - Detailed Patch Guide:
/home/setup/navidocs/KEYBOARD_SHORTCUTS_PATCH.md - Target File:
/home/setup/navidocs/client/src/views/DocumentView.vue
Agent Handoff
Status: Implementation code ready
Next Agent: Can proceed with integration testing and UI polish
Blockers: None - all existing search functionality preserved
Dependencies: Existing search functions (nextHit, prevHit, clearSearch, performSearch)
Implementation completed by Agent 5 of 10 Date: 2025-11-13 Task: Implement keyboard shortcuts for Apple Preview-style search