# Phase 2.1 Design: Rich Annotations & Highlighting **Date**: 2025-01-11 **Status**: Approved Design **Objective**: Build a complete highlighting and annotation system that works offline-first with seamless sync. Users can color-code verses for study and reference management. --- ## Core Philosophy - **Instant feedback**: Highlights appear immediately when user acts - **Never lose work**: All highlights persist locally, sync when possible - **Distraction-free**: Visual indicators are subtle; details reveal on demand - **Cross-device sync**: Annotations follow the user across devices --- ## Feature Specifications ### 1. Highlighting System #### Colors & Interaction - **4 highlight colors**: Yellow (default), Orange, Pink, Blue - **Two-gesture interaction**: 1. Single tap verse β†’ Opens details panel (existing behavior) 2. Long-press or swipe verse β†’ Highlights with default color (yellow) - Shows mini toast: "Highlighted" - Verse background changes color immediately 3. Tap highlighted verse β†’ Details panel opens with Highlights tab active - Shows current color + ColorPicker - User can change color or delete highlight #### Visual Representation - **Colored background** on highlighted verses - **Opacity**: 0.3 (subtle, maintains text contrast) - **Colors**: - Yellow: `rgba(255, 193, 7, 0.3)` - Default, general marking - Orange: `rgba(255, 152, 0, 0.3)` - Important, needs attention - Pink: `rgba(233, 30, 99, 0.3)` - Devotional, personal significance - Blue: `rgba(33, 150, 243, 0.3)` - Reference, study focus #### Storage - **Database**: IndexedDB table `highlights` - **Schema**: ``` { id: string (UUID), verseId: string, userId: string (from localStorage auth), color: 'yellow' | 'orange' | 'pink' | 'blue', createdAt: timestamp, updatedAt: timestamp, syncStatus: 'pending' | 'syncing' | 'synced' | 'error', syncErrorMsg?: string } ``` ### 2. Cross-References #### Visual Indicator - **Small link icon** (πŸ”—) or dot next to verse number when cross-references exist - **Placement**: Subtle, doesn't interrupt reading - **Behavior**: Clicking verse opens details panel with Cross-References tab #### Cross-Reference Display - **Tab in VersDetailsPanel**: "Cross-References" - **Format**: Collapsible list showing: - Book name (e.g., "John") - Chapter:verse reference (e.g., "3:16") - 1-line preview of the verse text - Tap to jump to that verse #### Quick Jump Behavior - **Tap reference** β†’ Navigate to verse - **Add to history**: User can go back to original verse - **Smooth transition**: No page reload, updates reading view #### Data Source - **Endpoint**: `GET /api/bible/cross-references?verseId={verseId}` - **Lazy-loaded**: Only fetch when user opens Cross-References tab - **Cached**: Store in IndexedDB with 7-day expiration ### 3. Local-First Sync Strategy #### Immediate Local Storage - All highlights saved to IndexedDB instantly when user acts - Provides instant feedback, works offline - No waiting for network round-trip #### Automatic Sync Queue - **Background service** tracks `syncStatus` for each highlight: - `pending`: Created locally, not yet synced - `syncing`: Currently pushing to server - `synced`: Successfully synced, in-sync with server - `error`: Failed to sync, will retry #### Auto-Sync Timing - **Interval**: Every 30 seconds when online - **Batch operation**: POST all pending highlights in one request - **Smart batching**: Only send items with `syncStatus: 'pending'` or `'error'` - **Exponential backoff**: Failed syncs retry after 30s, 60s, 120s, then give up #### Conflict Resolution - **Strategy**: Last-modified timestamp wins - **Scenario**: User highlights same verse on two devices - Device 1: Highlights yellow at 10:00:00 - Device 2: Highlights pink at 10:00:05 - Result: Pink wins (newer timestamp), displayed on both devices after sync - **Safety**: No data lossβ€”version history kept server-side for audit #### Offline Fallback - All operations (highlight, change color, delete) queued locally - Sync indicator shows "Offline" state - When connection returns: `syncStatus: 'pending'` items auto-sync #### Sync Status Indicator - **Location**: Footer bar (right side, near existing sync indicator) - **States**: - "Syncing..." (briefly while POST in flight) - "Synced βœ“" (green checkmark, 2 second display) - "Sync failed" (red icon, expandable for retry) - "Offline" (gray icon) - **Manual retry**: User can click "Retry" on failed syncs from settings ### 4. Component Architecture #### Enhanced Components **HighlightsTab** (NEW - in VersDetailsPanel) ``` HighlightsTab β”œβ”€β”€ HighlightToggle β”‚ └── "Highlight this verse" button (if not highlighted) β”‚ └── "Remove highlight" button (if highlighted) β”œβ”€β”€ ColorPicker (if highlighted) β”‚ β”œβ”€β”€ 4 color swatches (yellow, orange, pink, blue) β”‚ β”œβ”€β”€ Selected color indicator β”‚ └── OnColorChange β†’ Update highlight, queue sync └── HighlightMetadata β”œβ”€β”€ Created: [date/time] └── Last modified: [date/time] ``` **VerseRenderer** (enhanced in ReadingView) ``` VerseRenderer β”œβ”€β”€ HighlightBackground β”‚ └── Colored background if verse is highlighted β”œβ”€β”€ VerseNumber + CrossRefIndicator β”‚ └── Small icon if cross-references available └── VerseText └── Regular text, no inline linking ``` **HighlightSyncManager** (NEW - in BibleReaderApp) ``` HighlightSyncManager β”œβ”€β”€ IndexedDB operations β”‚ β”œβ”€β”€ addHighlight(verseId, color) β”‚ β”œβ”€β”€ updateHighlight(highlightId, color) β”‚ β”œβ”€β”€ deleteHighlight(highlightId) β”‚ └── getAllHighlights() β”œβ”€β”€ Sync queue logic β”‚ β”œβ”€β”€ getPendingHighlights() β”‚ β”œβ”€β”€ markSyncing(ids) β”‚ β”œβ”€β”€ markSynced(ids) β”‚ └── markError(ids, msg) └── Auto-sync interval └── Every 30s: fetch pending β†’ POST batch β†’ update status ``` ### 5. Data Flow #### Highlight Creation ``` 1. User long-presses verse 2. VerseRenderer detects long-press 3. Create highlight entry in IndexedDB { verseId, color: 'yellow', syncStatus: 'pending' } 4. VerseRenderer background changes color 5. Show toast "Highlighted" 6. SyncManager picks it up in next 30s cycle β†’ POST to backend ``` #### Highlight Color Change ``` 1. User tap verse β†’ Details panel opens 2. HighlightsTab shows current color + ColorPicker 3. User taps new color 4. Update highlight in IndexedDB with new color + new timestamp 5. VerseRenderer background updates immediately 6. syncStatus changed to 'pending' 7. SyncManager syncs in next cycle ``` #### Offline β†’ Reconnect Flow ``` 1. User highlights while offline β†’ Stored in IndexedDB with syncStatus: 'pending' 2. Connection returns 3. SyncManager detects online status change 4. Fetches all syncStatus: 'pending' or 'error' items 5. POSTs to /api/highlights/bulk 6. Updates syncStatus to 'synced' 7. Shows sync status indicator ``` #### Cross-Device Sync ``` 1. App loads on Device 2 2. Fetch /api/highlights/all from backend 3. For each highlight from server: - Check if exists locally (by verseId + userId) - If not: Add to IndexedDB - If exists: Compare timestamps, keep newer 4. Show user any conflicts (rare) 5. Render highlights with merged data ``` ### 6. Backend API Endpoints (NEW) #### POST /api/highlights Create a single highlight for authenticated user. ``` Request: { verseId: string, color: 'yellow' | 'orange' | 'pink' | 'blue', createdAt: timestamp } Response: { id: string (UUID), verseId: string, userId: string, color: string, createdAt: timestamp, updatedAt: timestamp, syncStatus: 'synced' } ``` #### POST /api/highlights/bulk Batch sync highlights (create or update). ``` Request: { highlights: [ { id?: string, verseId: string, color: string, createdAt: timestamp, updatedAt: timestamp } ] } Response: { synced: number, errors: [{ verseId, error }], serverTime: timestamp } ``` #### GET /api/highlights/all Fetch all highlights for authenticated user (for cross-device sync). ``` Response: { highlights: [ { id: string, verseId: string, color: string, createdAt: timestamp, updatedAt: timestamp } ], serverTime: timestamp } ``` #### GET /api/bible/cross-references Get cross-referenced verses for a given verse. ``` Request: GET /api/bible/cross-references?verseId={verseId} Response: { verseId: string, references: [ { refVerseId: string, bookName: string, chapter: number, verse: number, preview: string (first 60 chars) } ] } ``` ### 7. Error Handling & Resilience **Sync Failures** - Network timeout: Auto-retry after 30s with exponential backoff - 400/401 (invalid request): Remove from queue, log error - 5xx (server error): Keep in queue, retry next cycle - Display "Sync failed" in footer with manual retry button **Offline Highlighting** - All operations queue locally, appear immediately - When online: Auto-sync without user intervention - If sync fails: User notified, can manually retry from settings **IndexedDB Quota Exceeded** - Highlights table should never exceed reasonable size (< 1MB typical) - If quota warning: Suggest clearing old highlights from settings - Oldest highlights (by date) suggested for removal first **Cross-Device Conflicts** - Rare: User highlights same verse on two devices at same second - Resolution: Newer timestamp wins (automatic) - User sees no warning (conflict handled transparently) ### 8. Testing Strategy #### Unit Tests - Highlight color validation (only 4 valid colors) - Sync queue operations (add, remove, get pending) - Timestamp-based conflict resolution - IndexedDB CRUD operations - Batch sync request formatting #### Integration Tests - Highlight creation β†’ immediate display β†’ queued sync - Offline highlight β†’ reconnect β†’ verify sync success - Color change persistence across storage layers - Cross-device highlight fetch and merge - Sync conflict resolution (timestamp comparison) #### E2E Tests - User highlights verse β†’ sees background change β†’ goes offline β†’ comes back online β†’ highlight is synced - User highlights on Device 1 β†’ reads on Device 2 β†’ sees highlight immediately after fetch - User deletes highlight β†’ sync β†’ verify removal on all devices - Bulk operations: highlight multiple verses rapidly, verify all sync #### Manual Testing - Desktop browsers: Chrome, Firefox, Safari - Mobile: iOS Safari, Chrome Mobile, Android browsers - Network conditions: Fast 3G, slow 3G, offline - Sync conflict scenarios (use network throttling to trigger) --- ## Success Criteria - **Offline**: Can highlight and change colors without internet - **Sync**: Auto-syncs all highlights within 60 seconds of reconnection - **Performance**: Highlighting action responds in < 200ms - **Reliability**: No lost highlights after sync - **UX**: User never confused about sync state (status indicator clear) - **Accessibility**: All interactions keyboard-navigable --- ## Implementation Dependencies ### Already Available - βœ… IndexedDB infrastructure (cache-manager.ts) - βœ… Details panel infrastructure (VersDetailsPanel.tsx) - βœ… Verse rendering with click handlers - βœ… ReadingView component structure - βœ… Auth system (user identification) ### New Dependencies - API endpoints (backend implementation) - Highlight sync manager (new service) - Color picker component (can use Material-UI) --- ## Future Enhancements (Phase 3+) - **Highlight statistics**: "You've highlighted 47 verses across 12 books" - **Highlight search**: Find all yellow highlights, or search within highlights - **Highlight export**: Export all highlights as PDF or CSV with context - **Highlight sharing**: Share specific highlighted passages with study groups - **Highlight collections**: Group highlights into "studies" or "topics" --- ## References - Current reader: `/root/biblical-guide/components/bible/bible-reader-app.tsx` - Verse panel: `/root/biblical-guide/components/bible/verse-details-panel.tsx` - Cache manager: `/root/biblical-guide/lib/cache-manager.ts` - API Bible endpoints: `/root/biblical-guide/app/api/bible/`