navidocs/AGENT_7_ARCHITECTURE.md

380 lines
19 KiB
Markdown

# Agent 7 - Thumbnail Generation Architecture
## System Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ THUMBNAIL GENERATION SYSTEM │
└─────────────────────────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Template │────────▶│ getThumbnail │────────▶│ Cache │
│ (Vue View) │ │ (pageNum) │ │ Check First │
└──────────────┘ └──────────────┘ └──────┬───────┘
┌───────────────────────────┼───────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌─────────┐
│ Cached │ │ Loading? │ │Generate │
│ Return │ │ Wait │ │ New │
└──────────┘ └──────────┘ └────┬────┘
┌─────────────────┐
│ PDF.js API │
│ getPage(num) │
└────────┬────────┘
┌─────────────────┐
│ getViewport() │
│ scale: 0.2 │
└────────┬────────┘
┌─────────────────┐
│ Create Canvas │
│ 80x100px (≈) │
└────────┬────────┘
┌─────────────────┐
│ page.render() │
│ to Canvas │
└────────┬────────┘
┌─────────────────┐
│ toDataURL() │
│ PNG @ 0.8 qual │
└────────┬────────┘
┌─────────────────┐
│ Store in Cache │
│ Return Data URL│
└─────────────────┘
```
## Component Interaction Flow
```
USER SEARCHES
┌────────────────────────────────────────────────────┐
│ performSearch() → highlightSearchTerms() │
│ Creates hitList with page numbers and snippets │
└────────────────┬───────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ Template renders search results │
│ For each hit in hitList: │
│ - Show thumbnail (getThumbnail(hit.page)) │
│ - Show snippet │
│ - Show page number │
└────────────────┬───────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ getThumbnail(pageNum) │
│ ├─▶ Check thumbnailCache Map │
│ ├─▶ Check thumbnailLoading Set │
│ └─▶ Call generateThumbnail(pageNum) │
└────────────────┬───────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ generateThumbnail(pageNum) │
│ 1. Mark as loading │
│ 2. Get page from PDF │
│ 3. Create viewport at 0.2 scale │
│ 4. Render to off-screen canvas │
│ 5. Convert to PNG data URL │
│ 6. Cache result │
│ 7. Unmark loading │
└────────────────┬───────────────────────────────────┘
┌────────────────────────────────────────────────────┐
│ Template receives data URL │
│ <img :src="dataURL" /> │
└────────────────────────────────────────────────────┘
```
## State Management
```
┌─────────────────────────────────────────────────────┐
│ STATE VARIABLES │
├─────────────────────────────────────────────────────┤
│ │
│ thumbnailCache (Map) │
│ ├─ Key: Page Number (integer) │
│ └─ Value: Data URL (string) │
│ │
│ Example: │
│ Map { │
│ 1 => "data:image/png;base64,iVBORw0KG...", │
│ 3 => "data:image/png;base64,iVBORw0KG...", │
│ 5 => "data:image/png;base64,iVBORw0KG..." │
│ } │
│ │
│ thumbnailLoading (Set) │
│ └─ Contains page numbers currently generating │
│ │
│ Example: │
│ Set { 2, 4, 7 } │
│ │
└─────────────────────────────────────────────────────┘
```
## Cache Lifecycle
```
Document Load
┌─────────────────┐
│ Cache: Empty │
│ Loading: Empty │
└────────┬────────┘
Search Performed
┌─────────────────────────────────────┐
│ User sees results with thumbnails │
└────────┬────────────────────────────┘
First thumbnail request (page 5)
┌─────────────────┐
│ Cache: Empty │──▶ Generate thumbnail
│ Loading: {5} │
└────────┬────────┘
▼ (thumbnail rendered)
┌─────────────────┐
│ Cache: {5} │
│ Loading: Empty │
└────────┬────────┘
Second request for same page (page 5)
┌─────────────────┐
│ Cache: {5} ✓ │──▶ Return immediately
│ Loading: Empty │ (no regeneration)
└────────┬────────┘
Document change
clearThumbnailCache()
┌─────────────────┐
│ Cache: Empty │
│ Loading: Empty │
└─────────────────┘
```
## Performance Characteristics
```
┌────────────────────────────────────────────────────┐
│ PERFORMANCE METRICS │
├────────────────────────────────────────────────────┤
│ │
│ Thumbnail Size (typical): │
│ ├─ Dimensions: 80x100px (approx) │
│ ├─ File size: 5-10 KB per thumbnail │
│ └─ Format: PNG (0.8 quality) │
│ │
│ Generation Time: │
│ ├─ First generation: 50-150ms │
│ └─ Cached retrieval: <1ms │
│ │
│ Memory Usage: │
│ ├─ Per thumbnail: ~10 KB │
│ ├─ 50 pages cached: ~500 KB │
│ └─ 200 pages cached: ~2 MB │
│ │
│ Concurrent Generation: │
│ ├─ Multiple pages can generate simultaneously │
│ ├─ No race conditions (loading Set prevents) │
│ └─ Duplicates wait for first to complete │
│ │
└────────────────────────────────────────────────────┘
```
## Error Handling
```
generateThumbnail(pageNum)
├──▶ pdfDoc null? ──▶ Return ''
├──▶ getPage() fails? ──▶ Catch, log, return ''
├──▶ Canvas context fail? ──▶ Throw error, return ''
└──▶ render() fails? ──▶ Catch, log, return ''
┌────────────────┐
│ Finally block │
│ - Unmark loading│
└────────────────┘
```
## Integration Points
```
┌────────────────────────────────────────────────────┐
│ DocumentView.vue Structure │
├────────────────────────────────────────────────────┤
│ │
│ <template> │
│ └─ Search Results │
│ └─ Thumbnail Images ──▶ getThumbnail() │
│ │
│ <script setup> │
│ ├─ State Variables │
│ │ ├─ thumbnailCache │
│ │ └─ thumbnailLoading │
│ │ │
│ ├─ Functions │
│ │ ├─ generateThumbnail() │
│ │ ├─ getThumbnail() │
│ │ ├─ isThumbnailLoading() │
│ │ └─ clearThumbnailCache() │
│ │ │
│ └─ Lifecycle │
│ └─ resetDocumentState() │
│ └─ clearThumbnailCache() │
│ │
└────────────────────────────────────────────────────┘
```
## Data Flow Example
```
User searches for "engine"
Results found on pages: 3, 7, 15, 22
┌────────────────────────────────────────────────────┐
│ hitList = [ │
│ { page: 3, snippet: "...engine maintenance..." },│
│ { page: 7, snippet: "...engine oil..." }, │
│ { page: 15, snippet: "...engine specs..." }, │
│ { page: 22, snippet: "...engine diagram..." } │
│ ] │
└────────────────┬───────────────────────────────────┘
Template renders 4 results
├──▶ getThumbnail(3) ──▶ Generate ──▶ Cache ──▶ Display
├──▶ getThumbnail(7) ──▶ Generate ──▶ Cache ──▶ Display
├──▶ getThumbnail(15) ──▶ Generate ──▶ Cache ──▶ Display
└──▶ getThumbnail(22) ──▶ Generate ──▶ Cache ──▶ Display
User clicks result #2 (page 7)
Navigates to page 7 (full render)
User searches again for same term
Same results, same pages
┌────────────────────────────────────────────────────┐
│ getThumbnail(3) ──▶ Cache Hit ──▶ Instant Display│
│ getThumbnail(7) ──▶ Cache Hit ──▶ Instant Display│
│ getThumbnail(15) ──▶ Cache Hit ──▶ Instant Display│
│ getThumbnail(22) ──▶ Cache Hit ──▶ Instant Display│
└────────────────────────────────────────────────────┘
```
## Key Design Decisions
1. **Map for Cache**
- Fast O(1) lookup
- Easy to check existence
- Simple to clear
2. **Set for Loading State**
- Prevents duplicate requests
- O(1) add/delete/has operations
- Reactive for UI updates
3. **Data URL Storage**
- Self-contained (no separate file requests)
- Works with Vue reactivity
- Easy to use in <img> tags
4. **Scale Factor 0.2**
- Produces ~80x100px thumbnails
- Small enough for performance
- Large enough to be recognizable
5. **PNG Format @ 0.8 Quality**
- Good clarity for text/diagrams
- Reasonable file size
- Better than JPEG for sharp text
6. **Lazy Generation**
- Only generate when needed
- Don't preload all pages
- Better initial load time
## Testing Scenarios
```
1. First Search
└─ All thumbnails generate fresh
└─ Loading spinners shown during generation
└─ Thumbnails appear when ready
2. Repeat Search
└─ All thumbnails from cache
└─ Instant display (no spinners)
3. Different Search
└─ New pages generate fresh
└─ Previously seen pages from cache
4. Document Switch
└─ Cache cleared
└─ Fresh thumbnails for new document
5. Concurrent Requests
└─ Same page requested multiple times
└─ Only one generation occurs
└─ Other requests wait for first
6. Error Handling
└─ Page generation fails
└─ Empty string returned
└─ No crash, graceful fallback
```