12 KiB
12 KiB
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:
- Single tap verse → Opens details panel (existing behavior)
- Long-press or swipe verse → Highlights with default color (yellow)
- Shows mini toast: "Highlighted"
- Verse background changes color immediately
- 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
- Yellow:
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
syncStatusfor each highlight:pending: Created locally, not yet syncedsyncing: Currently pushing to serversynced: Successfully synced, in-sync with servererror: 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/