docs: Phase 2.1 Rich Annotations & Highlighting design specification
This commit is contained in:
405
docs/plans/2025-01-11-phase-2-rich-annotations-design.md
Normal file
405
docs/plans/2025-01-11-phase-2-rich-annotations-design.md
Normal file
@@ -0,0 +1,405 @@
|
||||
# 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/`
|
||||
Reference in New Issue
Block a user