Add search term highlighting in PDF viewer
Search Results Enhancement: - Pass search query to document viewer via URL parameter - Search results already show highlights via Meilisearch <mark> tags PDF Document Viewer: - Accept search query from URL (?q=search+term) - Highlight matching text in PDF text layer - Case-insensitive search term matching - Auto-scroll to first match with smooth behavior - Yellow highlight with pulsing animation for visibility Highlighting Features: - Uses regex to find all instances of search term - Preserves PDF.js text layer positioning - Highlights visible immediately after page render - Text remains fully selectable - Works with digitized/text-based PDFs Styling: - Yellow background (rgba(255, 215, 0, 0.6)) - Black text for contrast - Pulsing animation on initial load - Rounded corners for polish User Flow: 1. User searches in SearchView 2. Clicks on search result 3. Navigates to DocumentView with ?q=term&page=X 4. PDF page renders with matching text highlighted 5. Page auto-scrolls to first match This completes the search highlighting feature requested by the user, making it easy to find searched terms within PDF documents. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e0ae22cf63
commit
a2c0eee572
2 changed files with 65 additions and 1 deletions
|
|
@ -157,6 +157,7 @@ const route = useRoute()
|
|||
const documentId = ref(route.params.id)
|
||||
const currentPage = ref(parseInt(route.query.page, 10) || 1)
|
||||
const pageInput = ref(currentPage.value)
|
||||
const searchQuery = ref(route.query.q || '')
|
||||
const totalPages = ref(0)
|
||||
const documentTitle = ref('Loading...')
|
||||
const boatInfo = ref('')
|
||||
|
|
@ -216,6 +217,41 @@ async function loadDocument() {
|
|||
}
|
||||
}
|
||||
|
||||
function highlightSearchTerms() {
|
||||
if (!textLayer.value || !searchQuery.value) return
|
||||
|
||||
const spans = textLayer.value.querySelectorAll('span')
|
||||
const query = searchQuery.value.toLowerCase().trim()
|
||||
let firstMatch = null
|
||||
|
||||
spans.forEach(span => {
|
||||
const text = span.textContent
|
||||
if (!text) return
|
||||
|
||||
const lowerText = text.toLowerCase()
|
||||
if (lowerText.includes(query)) {
|
||||
// Create a highlighted version
|
||||
const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi')
|
||||
const highlightedText = text.replace(regex, '<mark class="search-highlight">$1</mark>')
|
||||
|
||||
// Wrap in a container to preserve PDF.js positioning
|
||||
span.innerHTML = highlightedText
|
||||
|
||||
// Track first match for scrolling
|
||||
if (!firstMatch) {
|
||||
firstMatch = span
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Scroll to first match
|
||||
if (firstMatch) {
|
||||
setTimeout(() => {
|
||||
firstMatch.scrollIntoView({ behavior: 'smooth', block: 'center' })
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
async function renderPage(pageNum) {
|
||||
if (!pdfDoc || componentIsUnmounting) return
|
||||
|
||||
|
|
@ -280,6 +316,12 @@ async function renderPage(pageNum) {
|
|||
viewport: viewport,
|
||||
textDivs: []
|
||||
})
|
||||
|
||||
// Highlight search terms if query exists
|
||||
if (searchQuery.value) {
|
||||
await nextTick()
|
||||
highlightSearchTerms()
|
||||
}
|
||||
} catch (textErr) {
|
||||
console.warn('Failed to render text layer:', textErr)
|
||||
}
|
||||
|
|
@ -477,4 +519,23 @@ onBeforeUnmount(() => {
|
|||
.textLayer ::-moz-selection {
|
||||
background: rgba(255, 92, 178, 0.3);
|
||||
}
|
||||
|
||||
/* Search highlighting */
|
||||
.search-highlight {
|
||||
background-color: rgba(255, 215, 0, 0.6);
|
||||
color: #000;
|
||||
padding: 2px 0;
|
||||
border-radius: 2px;
|
||||
font-weight: 600;
|
||||
animation: highlight-pulse 1.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes highlight-pulse {
|
||||
0%, 100% {
|
||||
background-color: rgba(255, 215, 0, 0.6);
|
||||
}
|
||||
50% {
|
||||
background-color: rgba(255, 215, 0, 0.9);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -187,7 +187,10 @@ function viewDocument(result) {
|
|||
router.push({
|
||||
name: 'document',
|
||||
params: { id: result.docId },
|
||||
query: { page: result.pageNumber }
|
||||
query: {
|
||||
page: result.pageNumber,
|
||||
q: searchQuery.value // Pass search query for highlighting
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue