Files
biblical-guide.com/docs/plans/2025-01-11-phase-2-rich-annotations-design.md

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:
    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/