navidocs/AGENT_5_KEYBOARD_SHORTCUTS_IMPLEMENTATION.md

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

  1. Cross-Platform Detection: Automatically uses Cmd on Mac, Ctrl on Windows/Linux
  2. Prevents Default Browser Find: Cmd/Ctrl+F won't open browser's native find dialog
  3. Context-Aware: Enter key performs search when input is focused, navigates results otherwise
  4. Global Shortcuts: Work anywhere when the document is in focus
  5. 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

  1. Focus Search Box

    • Press Cmd+F (Mac) or Ctrl+F (Windows/Linux)
    • Search input should receive focus
    • Existing text should be selected
    • Browser's native find dialog should NOT open
  2. Navigate Results

    • Type a search query and press Enter
    • Press Enter or Cmd/Ctrl+G → Should go to next result
    • Press Shift+Enter or Cmd/Ctrl+Shift+G → Should go to previous result
    • Current result should be highlighted with pink background
    • Page should scroll to show current result
  3. Clear Search

    • With search active, press Escape
    • Search should clear
    • Highlights should be removed
    • If search input was focused, it should blur
  4. Toggle Jump List

    • Perform a search with multiple results
    • Press Cmd/Ctrl+Alt+F
    • Jump list (search sidebar) should toggle open/closed
  5. Context Awareness

    • With search input focused, Enter should execute search
    • With search input NOT focused, Enter should navigate to next result
    • Shortcuts should not interfere with other inputs or modals

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:

  1. Reactive ref: searchInputRef for accessing the DOM element
  2. Event handler function: handleKeyboardShortcuts for centralized shortcut logic
  3. Lifecycle hooks: onMounted to register, onBeforeUnmount to clean up
  4. 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:

  1. Add keyboard shortcut help modal (press ? to show all shortcuts)
  2. Implement Cmd/Ctrl + A to select all results
  3. Add Cmd/Ctrl + C to copy current result context
  4. Support arrow keys for result navigation
  5. Add visual indicator when keyboard shortcuts are active
  6. Persist user's keyboard shortcut preferences
  • 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