navidocs/agent_7_code_changes.txt

226 lines
7.3 KiB
Text

================================================================================
AGENT 7 - THUMBNAIL GENERATION CODE CHANGES
File: /home/setup/navidocs/client/src/views/DocumentView.vue
================================================================================
CHANGE 1: Add State Variables (Insert after line ~380, after searchStats computed)
================================================================================
// Thumbnail cache and state for search results
const thumbnailCache = new Map() // pageNum -> dataURL
const thumbnailLoading = ref(new Set()) // Track which thumbnails are currently loading
================================================================================
CHANGE 2: Add Thumbnail Functions (Insert after makeTocEntriesClickable(), before renderPage())
================================================================================
/**
* Generate thumbnail for a specific page
* @param {number} pageNum - Page number to generate thumbnail for
* @returns {Promise<string>} Data URL of the thumbnail image
*/
async function generateThumbnail(pageNum) {
// Check cache first
if (thumbnailCache.has(pageNum)) {
return thumbnailCache.get(pageNum)
}
// Check if already loading
if (thumbnailLoading.value.has(pageNum)) {
// Wait for the thumbnail to be generated
return new Promise((resolve) => {
const checkInterval = setInterval(() => {
if (thumbnailCache.has(pageNum)) {
clearInterval(checkInterval)
resolve(thumbnailCache.get(pageNum))
}
}, 100)
})
}
// Mark as loading
thumbnailLoading.value.add(pageNum)
try {
if (!pdfDoc) {
throw new Error('PDF document not loaded')
}
const page = await pdfDoc.getPage(pageNum)
// Use small scale for thumbnail (0.2 = 20% of original size)
// This produces roughly 80x100px thumbnails for standard letter-sized pages
const viewport = page.getViewport({ scale: 0.2 })
// Create canvas for thumbnail rendering
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d', { alpha: false })
if (!context) {
throw new Error('Failed to get canvas context for thumbnail')
}
canvas.width = viewport.width
canvas.height = viewport.height
// Render page to canvas
await page.render({
canvasContext: context,
viewport: viewport
}).promise
// Convert canvas to data URL
const dataURL = canvas.toDataURL('image/png', 0.8) // 0.8 quality for smaller file size
// Cache the thumbnail
thumbnailCache.set(pageNum, dataURL)
return dataURL
} catch (err) {
console.error(`Failed to generate thumbnail for page ${pageNum}:`, err)
// Return a placeholder or empty data URL
return ''
} finally {
// Remove from loading set
thumbnailLoading.value.delete(pageNum)
}
}
/**
* Check if a thumbnail is currently being generated
* @param {number} pageNum - Page number to check
* @returns {boolean} True if thumbnail is loading
*/
function isThumbnailLoading(pageNum) {
return thumbnailLoading.value.has(pageNum)
}
/**
* Get thumbnail for a page, generating if needed
* @param {number} pageNum - Page number
* @returns {Promise<string>} Data URL of thumbnail
*/
async function getThumbnail(pageNum) {
return await generateThumbnail(pageNum)
}
/**
* Clear thumbnail cache (useful when document changes)
*/
function clearThumbnailCache() {
thumbnailCache.clear()
thumbnailLoading.value.clear()
}
================================================================================
CHANGE 3: Update resetDocumentState() Function
================================================================================
Find the resetDocumentState() function and add this line at the beginning:
async function resetDocumentState() {
clearImages()
clearThumbnailCache() // ADD THIS LINE
// ... rest of existing code
}
================================================================================
TEMPLATE EXAMPLE: Search Results with Thumbnails
================================================================================
<!-- Replace or enhance existing Jump List template -->
<div v-if="jumpListOpen && hitList.length > 0" class="absolute right-6 top-full mt-2 w-96 bg-dark-900/95 backdrop-blur-lg border border-white/10 rounded-lg p-3 shadow-2xl z-50">
<div class="grid gap-2 max-h-96 overflow-y-auto">
<button
v-for="(hit, idx) in hitList.slice(0, 10)"
:key="idx"
@click="jumpToHit(idx)"
class="text-left flex gap-3 px-3 py-2 bg-white/5 hover:bg-white/10 rounded transition-colors border border-white/10"
:class="{ 'ring-2 ring-pink-400': idx === currentHitIndex }"
>
<!-- Thumbnail -->
<div class="flex-shrink-0">
<!-- Loading Placeholder -->
<div
v-if="isThumbnailLoading(hit.page)"
class="w-20 h-25 bg-white/10 rounded flex items-center justify-center"
>
<div class="w-4 h-4 border-2 border-white/30 border-t-pink-400 rounded-full animate-spin"></div>
</div>
<!-- Thumbnail Image -->
<img
v-else
:src="getThumbnail(hit.page)"
:alt="`Page ${hit.page} thumbnail`"
class="w-20 h-auto rounded shadow-md border border-white/20"
loading="lazy"
/>
</div>
<!-- Match Info -->
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between gap-2 mb-1">
<span class="text-white/70 text-xs font-mono">Match {{ idx + 1 }}</span>
<span class="text-white/50 text-xs">Page {{ hit.page }}</span>
</div>
<p class="text-white text-sm line-clamp-2">{{ hit.snippet }}</p>
</div>
</button>
<div v-if="hitList.length > 10" class="text-white/50 text-xs text-center py-2">
+ {{ hitList.length - 10 }} more matches
</div>
</div>
</div>
================================================================================
CSS ADDITIONS (Add to <style> section if needed)
================================================================================
.thumbnail-container {
width: 80px;
min-width: 80px;
}
.thumbnail-loading {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
================================================================================
USAGE NOTES
================================================================================
1. Thumbnails are cached after first generation
2. Multiple requests for same page wait for first to complete
3. Scale of 0.2 produces ~80x100px thumbnails
4. PNG quality of 0.8 balances size and quality
5. Call getThumbnail(pageNum) from template (returns Promise<string>)
6. Check isThumbnailLoading(pageNum) to show loading state
7. clearThumbnailCache() clears all cached thumbnails
================================================================================
INTEGRATION STATUS
================================================================================
✓ State variables defined
✓ Core functions implemented
✓ Caching logic complete
✓ Loading state management
✓ Error handling
✓ Cleanup integration
□ Template integration (Agent 8)
□ UI styling (Agent 8)
□ Testing (Agent 10)
================================================================================