966 lines
35 KiB
Markdown
966 lines
35 KiB
Markdown
# E-Reader Application Requirements
|
|
|
|
## Overview
|
|
This document defines the requirements for a full-featured e-reader application built on the dreader/pyWebLayout framework. The application will support multiple modes, state persistence, and a complete user interaction model.
|
|
|
|
## Implementation Status Summary
|
|
|
|
### Legend
|
|
- ✅ **Complete**: Fully implemented and tested
|
|
- 📖 **Example Ready**: Working examples exist, needs production controller
|
|
- 🔜 **Planned**: Designed but not yet implemented
|
|
- ⏸️ **Future**: Deferred to later phase
|
|
|
|
### Core Features Status
|
|
| Feature | Status | Location |
|
|
|---------|--------|----------|
|
|
| EbookReader (page rendering, navigation) | ✅ Complete | [application.py](dreader/application.py) |
|
|
| Gesture handling (tap, swipe, pinch) | ✅ Complete | [gesture.py](dreader/gesture.py) |
|
|
| Navigation Overlay (unified TOC + Bookmarks) | ✅ Complete | [application.py](dreader/application.py), [examples/navigation_overlay_example.py](examples/navigation_overlay_example.py) |
|
|
| Settings Overlay | ✅ Complete | [application.py](dreader/application.py#L1318-L1349) |
|
|
| Highlighting system | ✅ Complete | [application.py](dreader/application.py#L1086-L1288) |
|
|
| Library management | ✅ Complete | [library.py](dreader/library.py) |
|
|
| State persistence | ✅ Complete | [state.py](dreader/state.py) |
|
|
| Persistent rendering settings | ✅ Complete | [examples/persistent_settings_example.py](examples/persistent_settings_example.py) |
|
|
| Book scanning & metadata | ✅ Complete | [book_utils.py](dreader/book_utils.py) |
|
|
| Mode transitions (LIBRARY ↔ READING) | 📖 Example Ready | [examples/library_reading_integration.py](examples/library_reading_integration.py) |
|
|
| Full application integration | 📖 Example Ready | All components working, needs main controller |
|
|
| Word Lookup Overlay | 🔜 Spec complete | Phase 2 |
|
|
| X-Ray feature | 🔜 Spec complete | Phase 2 |
|
|
|
|
### Phase Completion
|
|
- **Phase 1 (MVP)**: **~95% complete** - All core features working with examples, needs production main controller
|
|
- **Phase 2**: ~60% complete - Unified navigation overlay done, settings persistence complete, word lookup planned
|
|
- **Phase 3**: 0% complete - Advanced features deferred
|
|
|
|
### Working Examples
|
|
The following complete, runnable examples demonstrate full integration:
|
|
|
|
| Example | Demonstrates | Status |
|
|
|---------|-------------|--------|
|
|
| [simple_ereader_example.py](examples/simple_ereader_example.py) | Basic reading, page navigation, bookmarks | ✅ Working |
|
|
| [library_reading_integration.py](examples/library_reading_integration.py) | **Full LIBRARY ↔ READING workflow** | ✅ Working |
|
|
| [persistent_settings_example.py](examples/persistent_settings_example.py) | Settings persistence across sessions | ✅ Working |
|
|
| [navigation_overlay_example.py](examples/navigation_overlay_example.py) | Unified navigation with tabs | ✅ Working |
|
|
| [demo_settings_overlay.py](examples/demo_settings_overlay.py) | Settings overlay interactions | ✅ Working |
|
|
| [word_selection_highlighting.py](examples/word_selection_highlighting.py) | Text highlighting | ✅ Working |
|
|
|
|
**Key Integration Example**: [library_reading_integration.py](examples/library_reading_integration.py) demonstrates the complete user flow:
|
|
1. Display library of EPUB files
|
|
2. Select book by clicking/tapping
|
|
3. Open and read selected book
|
|
4. Access settings overlay
|
|
5. Return to library from settings
|
|
6. Select another book
|
|
7. Auto-resume from saved position
|
|
|
|
---
|
|
|
|
## 1. Application Modes
|
|
|
|
### 1.1 LIBRARY Mode ✅ (Implemented in library.py)
|
|
**Purpose**: Browse and select books from the user's library
|
|
|
|
**Features**:
|
|
- Display grid/table of available books with cover images
|
|
- Show book metadata (title, author)
|
|
- Book selection via touch/click
|
|
- Visual feedback on selection (highlight, hover state)
|
|
- Support for scrolling/pagination if library is large
|
|
- Search/filter books (future enhancement)
|
|
- Sort options (by title, author, recent, etc.) (future enhancement)
|
|
|
|
**Interactions**:
|
|
- **Tap/Click on book**: Transition to READING mode with selected book
|
|
- **Long-press on book**: Show book context menu (delete, info, etc.) (future)
|
|
- **Swipe**: Scroll library view
|
|
|
|
**Display Requirements**:
|
|
- Cover images: 150-300px wide thumbnails
|
|
- Title/Author text below or beside cover
|
|
- Responsive layout for different screen sizes
|
|
- Loading indicator while scanning library
|
|
|
|
---
|
|
|
|
### 1.2 READING Mode ✅ (Implemented in application.py)
|
|
**Purpose**: Read the current book with page navigation
|
|
|
|
**Features**:
|
|
- Display current page rendered by pyWebLayout
|
|
- Page navigation (next/previous)
|
|
- Access to overlays via buttons/gestures
|
|
- Visual progress indicator (page numbers, percentage)
|
|
- Header showing book title/author
|
|
- Footer with navigation controls
|
|
|
|
**Interactions**:
|
|
- **Tap left/right edge**: Previous/Next page
|
|
- **Swipe left/right**: Previous/Next page
|
|
- **Tap center**: Toggle controls visibility (future)
|
|
- **Tap header buttons**: Open overlays (TOC, Settings, etc.)
|
|
- **Pinch in/out**: Decrease/Increase font size
|
|
- **Long-press on word**: Show definition/highlight menu (future)
|
|
|
|
**Sub-states within READING mode**:
|
|
1. **Base reading state**: Just the page content
|
|
2. **TOC_OVERLAY**: Table of contents over page
|
|
3. **SETTINGS_OVERLAY**: Settings panel over page
|
|
4. **BOOKMARKS_OVERLAY**: Bookmarks list over page
|
|
|
|
---
|
|
|
|
### 1.3 Overlay States
|
|
|
|
#### 1.3.1 TOC_OVERLAY ✅ (Implemented)
|
|
**Purpose**: Navigate to different chapters
|
|
|
|
**Features**:
|
|
- Scrollable list of chapters
|
|
- Hierarchical chapter display (if available)
|
|
- Current chapter indication
|
|
- Click to jump to chapter
|
|
|
|
**Interactions**:
|
|
- **Tap chapter**: Jump to chapter, close overlay, return to READING
|
|
- **Tap close/back**: Return to READING mode
|
|
- **Tap outside overlay**: Close overlay (optional)
|
|
|
|
#### 1.3.2 SETTINGS_OVERLAY ✅ (Implemented)
|
|
**Purpose**: Adjust reading preferences
|
|
|
|
**Features**:
|
|
- Font size controls (A-, A+)
|
|
- Line spacing controls
|
|
- Brightness controls (if hardware supports)
|
|
- Theme selection (day/night/sepia) (future)
|
|
- WiFi configuration (future)
|
|
|
|
**Interactions**:
|
|
- **Tap buttons**: Adjust settings in real-time
|
|
- **Tap close**: Return to READING mode
|
|
- Changes persist across sessions
|
|
|
|
#### 1.3.3 BOOKMARKS_OVERLAY ✅ (Implemented)
|
|
**Purpose**: Manage and navigate bookmarks
|
|
|
|
**Features**:
|
|
- List of saved bookmarks with names
|
|
- Show bookmark position info (chapter, page)
|
|
- Delete bookmark option
|
|
- Add new bookmark option
|
|
|
|
**Interactions**:
|
|
- **Tap bookmark**: Jump to bookmark, close overlay, return to READING
|
|
- **Tap delete**: Remove bookmark from list
|
|
- **Tap close**: Return to READING mode
|
|
|
|
#### 1.3.4 WORD_LOOKUP_OVERLAY 🔜 (Planned for Phase 2)
|
|
**Purpose**: Provide word definitions and contextual analysis
|
|
|
|
**Features**:
|
|
- **Dictionary Lookup**: Show word definition, pronunciation, etymology
|
|
- **X-Ray Feature**: Display LLM-generated contextual information about characters, places, and concepts
|
|
- Show **spoiler-free** summary of what has been revealed about this entity up to current reading position
|
|
- Character information: relationships, motivations, key events involving them (up to current page only)
|
|
- Place information: descriptions, significance, events that occurred there (up to current page only)
|
|
- Concept information: explanations, thematic importance (up to current page only)
|
|
- Pre-generated per book via LLM analysis (offline process), stored in cache
|
|
- Never reveals information from future chapters
|
|
- **Highlight Options**: Add permanent highlight to selected word with color choice
|
|
- **Copy to Clipboard**: Copy word or selection
|
|
|
|
**Interactions**:
|
|
- **Tap "Dictionary"**: Show dictionary definition panel
|
|
- **Tap "X-Ray"**: Show spoiler-free contextual summary (if available for this entity)
|
|
- **Tap "Highlight"**: Add colored highlight, show color picker
|
|
- **Tap "Copy"**: Copy text to clipboard
|
|
- **Tap close/outside**: Return to READING mode
|
|
|
|
**Display Layout**:
|
|
```
|
|
┌─────────────────────────────────────┐
|
|
│ Selected: "Sherlock Holmes" │
|
|
├─────────────────────────────────────┤
|
|
│ [Dictionary] [X-Ray] [Highlight] │
|
|
├─────────────────────────────────────┤
|
|
│ (Content area based on active tab) │
|
|
│ │
|
|
│ Dictionary Tab: │
|
|
│ Definition, pronunciation, etc. │
|
|
│ │
|
|
│ X-Ray Tab: │
|
|
│ SHERLOCK HOLMES (Character) │
|
|
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
|
|
│ A consulting detective living in │
|
|
│ London at 221B Baker Street. Known │
|
|
│ for exceptional deductive reasoning│
|
|
│ and observational skills. │
|
|
│ │
|
|
│ Key Relationships (so far): │
|
|
│ • Dr. Watson - Companion, narrator │
|
|
│ • Mrs. Hudson - Landlady │
|
|
│ │
|
|
│ Mentioned: 23 times (Chapters 1-3) │
|
|
│ [Info current as of your position] │
|
|
└─────────────────────────────────────┘
|
|
```
|
|
|
|
**Data Requirements**:
|
|
- X-Ray data file per book (JSON format)
|
|
- Generated via LLM chapter-by-chapter analysis (offline process)
|
|
- Structure: `{entity_name: {chapter_N: {summary, relationships, ...}}}`
|
|
- Lookup uses current reading position to filter out future content
|
|
- Dictionary API integration (local or online)
|
|
- Entity recognition (match selected word to X-Ray entities)
|
|
|
|
---
|
|
|
|
## 2. State Management & Persistence ✅ (Implemented in state.py)
|
|
|
|
### 2.1 State Definition
|
|
The application must track and persist the following state:
|
|
|
|
```python
|
|
class EreaderMode(Enum):
|
|
LIBRARY = "library"
|
|
READING = "reading"
|
|
|
|
class OverlayState(Enum):
|
|
NONE = "none"
|
|
TOC = "toc"
|
|
SETTINGS = "settings"
|
|
BOOKMARKS = "bookmarks"
|
|
WORD_LOOKUP = "word_lookup"
|
|
```
|
|
|
|
### 2.2 Persistent State Data Structure
|
|
|
|
**Key Insight**: EbookReader already manages bookmarks and positions via `bookmarks_dir`. Our state only needs to track high-level application state, not page positions.
|
|
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"mode": "reading",
|
|
"overlay": "none",
|
|
"current_book": {
|
|
"path": "/path/to/book.epub",
|
|
"title": "Book Title",
|
|
"author": "Author Name",
|
|
"last_read_timestamp": "2025-11-07T10:30:00Z"
|
|
},
|
|
"library": {
|
|
"books_path": "/path/to/library",
|
|
"last_selected_index": 3,
|
|
"scan_cache": [
|
|
{
|
|
"path": "/path/to/book.epub",
|
|
"title": "Book Title",
|
|
"author": "Author",
|
|
"cover_cached": true,
|
|
"last_modified": "2025-11-01T12:00:00Z"
|
|
}
|
|
]
|
|
},
|
|
"settings": {
|
|
"font_scale": 1.0,
|
|
"line_spacing": 5,
|
|
"inter_block_spacing": 15,
|
|
"brightness": 8,
|
|
"theme": "day"
|
|
}
|
|
}
|
|
```
|
|
|
|
**What's NOT in state (handled by EbookReader)**:
|
|
- Current page position → `EreaderLayoutManager.current_position`
|
|
- Bookmarks → `bookmarks_dir/{document_id}_{bookmark_name}.json`
|
|
- Reading progress → Calculated on-demand by `reader.get_reading_progress()`
|
|
|
|
**Auto-Resume Strategy**:
|
|
- Use special bookmark name `"__auto_resume__"` for last position
|
|
- Bookmark files are per-book: `{document_id}___auto_resume__.json`
|
|
- On shutdown: `reader.save_position("__auto_resume__")`
|
|
- On startup: `reader.load_position("__auto_resume__")`
|
|
- No mixing between books (each has its own document_id)
|
|
|
|
### 2.3 State Persistence Requirements
|
|
- **Location**:
|
|
- Linux/Mac: `~/.config/dreader/state.json`
|
|
- Windows: `%APPDATA%/dreader/state.json`
|
|
- **Save Triggers**:
|
|
- On mode change
|
|
- On page turn (debounced to avoid excessive writes)
|
|
- On settings change
|
|
- On bookmark add/delete
|
|
- On application close
|
|
- **Load Triggers**:
|
|
- On application startup
|
|
- After crash/power loss (automatic recovery)
|
|
- **Safety**:
|
|
- Atomic writes (write to temp file, then rename)
|
|
- Validation on load (schema check, corrupt data handling)
|
|
- Backup previous state file
|
|
- Handle missing/corrupt state gracefully
|
|
|
|
### 2.4 Boot/Resume Behavior
|
|
1. **Cold Start** (no state file):
|
|
- Start in LIBRARY mode
|
|
- Default settings applied
|
|
|
|
2. **Resume from State**:
|
|
- If last mode was LIBRARY: Return to library view
|
|
- If last mode was READING: Reopen last book at last page
|
|
- Restore all settings (font size, spacing, etc.)
|
|
- Clear overlay state (always start without overlay)
|
|
|
|
3. **Error Handling**:
|
|
- If last book no longer exists: Return to LIBRARY mode
|
|
- If state file corrupt: Start fresh, backup corrupt file
|
|
- Log all state errors for debugging
|
|
|
|
---
|
|
|
|
## 3. Interaction Model ✅ (Implemented via gesture.py + application.handle_touch)
|
|
|
|
### 3.1 Touch/Click Callbacks
|
|
The application must support callbacks/handlers for:
|
|
|
|
**LIBRARY Mode**:
|
|
- `on_book_selected(book_path)` - Open book in READING mode
|
|
- `on_library_scroll(direction)` - Scroll library view
|
|
|
|
**READING Mode**:
|
|
- `on_page_turn(direction)` - Next/Previous page
|
|
- `on_tap_region(x, y, region)` - Handle tap on specific region
|
|
- Regions: left-edge, right-edge, center, header, footer
|
|
- `on_button_press(button_id)` - Handle button clicks
|
|
- Buttons: toc, settings, bookmarks, library
|
|
- `on_zoom_gesture(direction)` - Pinch in/out for font size
|
|
- `on_word_tap(x, y)` - Single tap on word for quick highlight
|
|
- `on_word_long_press(x, y)` - Long press on word for lookup overlay
|
|
|
|
**Overlay Mode**:
|
|
- `on_chapter_selected(chapter_index)` - Jump to chapter
|
|
- `on_setting_changed(setting_name, value)` - Update settings
|
|
- `on_bookmark_selected(bookmark_name)` - Jump to bookmark
|
|
- `on_word_lookup_action(action, word)` - Handle word lookup actions
|
|
- Actions: dictionary, xray, highlight, copy
|
|
- `on_xray_occurrence_selected(position)` - Jump to word occurrence
|
|
- `on_overlay_close()` - Return to READING mode
|
|
|
|
### 3.2 Interactive Elements in Rendered Content
|
|
For the library view rendered via pyWebLayout:
|
|
- Each book cover/title should be a clickable element
|
|
- Use pyWebLayout's link/interactive features to make covers tappable
|
|
- Query pixel coordinates to determine which book was clicked
|
|
- Map clicks to book indices
|
|
|
|
**Implementation Options**:
|
|
1. **Bounding Box Approach**: Store bounding boxes for each book element, detect clicks
|
|
2. **HTML Links**: Embed book paths as links in HTML, use link query API
|
|
3. **Table Cell IDs**: Use table cell metadata to identify clicked books
|
|
|
|
---
|
|
|
|
## 4. Library Management ✅ (Implemented in book_utils.py + library.py)
|
|
|
|
### 4.1 Book Scanning
|
|
- Scan specified directory for EPUB files
|
|
- Extract metadata (title, author) using pyWebLayout's EPUB reader
|
|
- Extract cover images (or use first page as fallback)
|
|
- Cache metadata to avoid re-scanning on every boot
|
|
- Support for incremental updates (only scan new/modified files)
|
|
|
|
### 4.2 Cover Image Handling
|
|
**Options**:
|
|
1. **Base64 Embedded**: Encode covers as base64 in HTML (current approach)
|
|
2. **File-based**: Save covers to cache directory, reference by path
|
|
3. **Hybrid**: Cache on disk, fall back to base64 for generation
|
|
|
|
**Recommendation**: File-based caching
|
|
- Faster page generation
|
|
- Less memory usage
|
|
- Reusable across sessions
|
|
|
|
### 4.3 Library Cache Structure
|
|
```
|
|
~/.config/dreader/
|
|
├── state.json # Application state
|
|
├── covers/ # Cached cover images
|
|
│ ├── book1_cover.png
|
|
│ ├── book2_cover.png
|
|
│ └── ...
|
|
├── bookmarks/ # Bookmark data per book
|
|
│ ├── book1.json
|
|
│ └── book2.json
|
|
├── highlights/ # Highlight data per book
|
|
│ ├── book1.json
|
|
│ └── book2.json
|
|
└── xray/ # X-Ray data per book (LLM-generated)
|
|
├── book1_xray.json
|
|
└── book2_xray.json
|
|
```
|
|
|
|
**X-Ray Data Format Example**:
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"book_id": "sherlock_holmes_adventures",
|
|
"generated_date": "2025-11-08",
|
|
"entities": {
|
|
"Sherlock Holmes": {
|
|
"type": "character",
|
|
"chapters": {
|
|
"1": {
|
|
"summary": "Introduced as a consulting detective...",
|
|
"relationships": ["Dr. Watson"],
|
|
"traits": ["observant", "logical"],
|
|
"key_events": ["Met Watson", "Moved to Baker Street"]
|
|
},
|
|
"2": {
|
|
"summary": "Demonstrates deductive method...",
|
|
"relationships": ["Dr. Watson", "Inspector Lestrade"],
|
|
"traits": ["observant", "logical", "unconventional"],
|
|
"key_events": ["Solved first case"]
|
|
}
|
|
}
|
|
},
|
|
"221B Baker Street": {
|
|
"type": "place",
|
|
"chapters": {
|
|
"1": {
|
|
"summary": "Holmes and Watson's shared lodgings",
|
|
"significance": "Home base for investigations"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Technical Architecture ✅ (Core modules implemented)
|
|
|
|
### 5.1 Module Structure
|
|
```
|
|
dreader/
|
|
├── __init__.py # ✅ Exports
|
|
├── application.py # ✅ EbookReader class with gesture handling
|
|
├── state.py # ✅ State management with asyncio auto-save
|
|
├── library.py # ✅ Library mode logic with interactive selection
|
|
├── book_utils.py # ✅ Book scanning utilities
|
|
├── html_generator.py # ✅ HTML generation for UI
|
|
├── gesture.py # ✅ Touch/gesture event handling
|
|
└── overlay.py # ✅ Overlay rendering and compositing
|
|
```
|
|
**Note**: No separate callbacks.py - gesture handling integrated into application.py via handle_touch() method
|
|
|
|
### 5.2 State Manager API (`dreader/state.py`)
|
|
```python
|
|
class StateManager:
|
|
def __init__(self, state_file_path: str)
|
|
def load_state() -> AppState
|
|
def save_state(state: AppState)
|
|
def get_current_mode() -> EreaderMode
|
|
def set_mode(mode: EreaderMode)
|
|
def get_current_book() -> Optional[BookState]
|
|
def set_current_book(book: BookState)
|
|
def get_settings() -> Settings
|
|
def update_setting(key: str, value: Any)
|
|
```
|
|
|
|
### 5.3 Library Manager API (`dreader/library.py`)
|
|
```python
|
|
class LibraryManager:
|
|
def __init__(self, library_path: str, cache_dir: str)
|
|
def scan_library() -> List[Book]
|
|
def get_cached_metadata() -> List[Book]
|
|
def refresh_metadata(book_path: str)
|
|
def get_book_at_index(index: int) -> Optional[Book]
|
|
def render_library_view() -> Image.Image
|
|
def handle_library_click(x: int, y: int) -> Optional[str] # Returns book path
|
|
```
|
|
|
|
### 5.4 Callback Manager API (`dreader/callbacks.py`)
|
|
```python
|
|
class CallbackManager:
|
|
def __init__(self, reader: EbookReader, state_manager: StateManager, library_manager: LibraryManager)
|
|
def handle_interaction(event: InteractionEvent) -> Response
|
|
def on_book_selected(book_path: str)
|
|
def on_page_turn(direction: str)
|
|
def on_overlay_open(overlay_type: OverlayState)
|
|
def on_overlay_close()
|
|
def on_settings_change(setting: str, value: Any)
|
|
```
|
|
|
|
---
|
|
|
|
## 6. HAL (Hardware Abstraction Layer) Integration
|
|
|
|
### 6.1 HAL Responsibilities
|
|
The HAL will handle:
|
|
- Rendering images to the display
|
|
- Capturing touch/click input
|
|
- Sending interaction events to the application
|
|
- Hardware-specific features (brightness control, WiFi, etc.)
|
|
|
|
### 6.2 Application ↔ HAL Interface
|
|
**From Application to HAL**:
|
|
- `render_image(image: PIL.Image, layer: str)` - Display image on specified layer
|
|
- Layers: "base" (page/library), "overlay" (settings/TOC/etc.)
|
|
- `set_brightness(level: int)` - Adjust screen brightness
|
|
- `show_loading(message: str)` - Display loading indicator
|
|
|
|
**From HAL to Application**:
|
|
- `on_touch(x: int, y: int, gesture: GestureType)` - Touch event
|
|
- `on_button_press(button_id: str)` - Hardware button press (if any)
|
|
- `on_power_event(event: PowerEvent)` - Sleep/wake/shutdown
|
|
|
|
### 6.3 Multi-Layer Rendering
|
|
To support overlays efficiently:
|
|
1. **Base Layer**: Main content (library view or reading page)
|
|
2. **Overlay Layer**: Semi-transparent overlays (TOC, settings, etc.)
|
|
3. **HAL Compositing**: HAL combines layers for display
|
|
|
|
**Alternative**: Application composites layers and sends single image to HAL
|
|
|
|
---
|
|
|
|
## 7. Future Enhancements
|
|
|
|
### 7.1 Phase 2 Features
|
|
- Full-text search within books
|
|
- ✅ Highlighting and annotations (basic implementation exists)
|
|
- Word lookup overlay with dictionary and X-Ray features
|
|
- Night/sepia reading themes
|
|
- WiFi configuration UI
|
|
- OTA (over-the-air) updates
|
|
- Cloud sync for bookmarks/progress
|
|
- PDF support
|
|
|
|
### 7.2 Phase 3 Features
|
|
- Multiple library folders
|
|
- Collections/categories
|
|
- Reading statistics
|
|
- Social features (sharing quotes, etc.)
|
|
- Text-to-speech
|
|
- Translation support
|
|
|
|
---
|
|
|
|
## 8. Testing Requirements
|
|
|
|
### 8.1 Unit Tests
|
|
- State serialization/deserialization
|
|
- State file corruption handling
|
|
- Library scanning with various EPUB formats
|
|
- Callback routing logic
|
|
- Click detection and bounding box calculations
|
|
|
|
### 8.2 Integration Tests
|
|
- Full mode transitions (LIBRARY → READING → OVERLAY → READING → LIBRARY)
|
|
- State persistence across "reboots" (save → load → verify)
|
|
- Multi-book workflow (open book A, switch to book B, resume book A)
|
|
- Settings changes reflected in rendering
|
|
|
|
### 8.3 Manual/UI Tests
|
|
- Touch responsiveness on target hardware
|
|
- Overlay appearance and dismissal
|
|
- Page turn performance
|
|
- Library scrolling smoothness
|
|
- Boot time from power-on to usable state
|
|
|
|
---
|
|
|
|
## 9. Performance Requirements
|
|
|
|
- **Boot Time**: < 3 seconds to display library or resume reading
|
|
- **Page Turn**: < 200ms from gesture to new page display
|
|
- **Library Load**: < 1 second for libraries up to 100 books
|
|
- **State Save**: < 50ms (non-blocking if possible)
|
|
- **Memory**: < 100MB RAM for typical book (depends on images)
|
|
|
|
---
|
|
|
|
## 10. Implementation Priority
|
|
|
|
### Phase 1 (MVP):
|
|
1. ✅ Basic EbookReader class (application.py - complete)
|
|
2. ✅ HTML generation for reading view (html_generator.py - complete)
|
|
3. ✅ Book scanning and metadata extraction (book_utils.py - complete)
|
|
4. ✅ State management module with persistence (state.py - complete)
|
|
5. ✅ Library manager with clickable book selection (library.py - complete)
|
|
6. ✅ Mode transitions (LIBRARY ↔ READING) - **FULLY DEMONSTRATED** in [examples/library_reading_integration.py](examples/library_reading_integration.py)
|
|
7. ✅ Basic overlays (TOC, Settings) - overlay.py + application.py methods complete
|
|
8. ✅ Gesture/touch handling (gesture.py - complete with TouchEvent, GestureType, GestureResponse)
|
|
9. ✅ Full application integration - **WORKING EXAMPLES EXIST**, needs production main.py controller
|
|
|
|
### Phase 2:
|
|
1. ✅ Bookmarks overlay with management (application.py:1351-1377 - complete)
|
|
2. ✅ Settings persistence and real-time updates (FULLY COMPLETE)
|
|
- ✅ Settings overlay with real-time preview (application.py:1318-1349)
|
|
- ✅ Settings saved to state.json (state.py:79-103)
|
|
- ✅ Settings restored on app startup via apply_settings() (application.py:725-765)
|
|
- ✅ Includes: font_scale, line_spacing, inter_block_spacing, word_spacing
|
|
- ✅ Example: examples/persistent_settings_example.py
|
|
3. ✅ Boot recovery and resume functionality (FULLY TESTED)
|
|
- ✅ Position restoration via __auto_resume__ bookmark (tests/test_boot_recovery.py:49-98)
|
|
- ✅ Settings restoration across sessions (tests/test_boot_recovery.py:100-153)
|
|
- ✅ Bookmark persistence (tests/test_boot_recovery.py:155-200)
|
|
- ✅ Full state workflow (tests/test_boot_recovery.py:202-277)
|
|
- ✅ Multiple books with separate state (tests/test_boot_recovery.py:279-343)
|
|
- ✅ Corrupt state file recovery (tests/test_boot_recovery.py:345-362)
|
|
- ✅ Missing book handling (tests/test_boot_recovery.py:364-393)
|
|
- ✅ Cold start with no state (tests/test_boot_recovery.py:395-414)
|
|
- ✅ Async auto-save functionality (tests/test_boot_recovery.py:516-586)
|
|
4. 🚧 Error handling and user feedback
|
|
5. 🚧 Performance optimization
|
|
6. 🔜 Word lookup overlay (Dictionary + X-Ray feature) - spec complete, not implemented
|
|
7. ✅ Unified navigation overlay (TOC + Bookmarks tabs) - COMPLETE
|
|
- ✅ Tabbed interface combining Contents and Bookmarks (html_generator.py:507-637)
|
|
- ✅ Tab switching without closing overlay (overlay.py:459-481, application.py:1172-1186)
|
|
- ✅ Gesture handling for all interactions (application.py:828-904)
|
|
- ✅ New OverlayState.NAVIGATION state (state.py:33)
|
|
- ✅ Tests and example provided (tests/test_navigation_overlay.py, examples/navigation_overlay_example.py)
|
|
- ✅ Documentation: NAVIGATION_OVERLAY_IMPLEMENTATION.md
|
|
|
|
### Phase 3:
|
|
1. Advanced library features (search, sort, filter)
|
|
2. Advanced highlighting (color picker, annotation notes)
|
|
3. Enhanced X-Ray (character relationships, concept maps)
|
|
4. Themes and customization
|
|
5. Offline dictionary database
|
|
6. Word history and vocabulary tracking
|
|
|
|
---
|
|
|
|
## Resolved Design Decisions
|
|
|
|
### 1. Click Detection in Library View ✓
|
|
**Decision**: Use pyWebLayout's built-in `is_interactive` and `link_target` properties
|
|
- Each book element is marked as interactive with book path as link_target
|
|
- Query pixel on tap to get clicked book
|
|
- Same pattern as in-book link handling (application.py:748-769)
|
|
|
|
### 2. Multi-Layer Rendering ✓
|
|
**Decision**: Application-side compositing
|
|
- Application renders base layer (library or reading page)
|
|
- For overlays: Composite smaller overlay image onto base layer
|
|
- Remove overlay by re-rendering the existing base page
|
|
- On settings change: Re-render background, then re-composite overlay if active
|
|
- Single final image sent to HAL for display
|
|
|
|
### 3. State Save Frequency ✓
|
|
**Decision**: Timer-based with asyncio
|
|
- Auto-save every 60 seconds (configurable)
|
|
- Immediate save on:
|
|
- Book closed
|
|
- Device shutdown
|
|
- Mode change
|
|
- Settings change
|
|
- Use asyncio timer loop for automatic saves (non-blocking)
|
|
|
|
### 4. Cover Image Strategy
|
|
**Decision**: File-based cache (preferred)
|
|
- Cache covers to `~/.config/dreader/covers/`
|
|
- Generate on first scan, reuse on subsequent boots
|
|
- Fallback to base64 if cache unavailable
|
|
|
|
### 5. HAL Integration
|
|
**Decision**: HAL provides gesture recognition
|
|
- HAL sends TouchEvent with GestureType (TAP, SWIPE, etc.)
|
|
- Application handles business logic via gesture handlers
|
|
- Application returns GestureResponse with action type
|
|
|
|
---
|
|
|
|
## 11. Main Application Controller (Final Integration Piece)
|
|
|
|
### 11.1 Current Status
|
|
All core components are **fully functional and tested** with working integration examples. What's needed is a production-ready main application controller to orchestrate these components.
|
|
|
|
### 11.2 Reference Implementation
|
|
The file [examples/library_reading_integration.py](examples/library_reading_integration.py) demonstrates the complete integration pattern and serves as a reference for building the main controller.
|
|
|
|
### 11.3 Required Main Controller Components
|
|
|
|
#### A. Application Class Structure
|
|
```python
|
|
class DReaderApplication:
|
|
"""
|
|
Main application controller coordinating library and reading modes.
|
|
|
|
Responsibilities:
|
|
- Mode management (LIBRARY ↔ READING transitions)
|
|
- Component lifecycle (LibraryManager, EbookReader)
|
|
- State persistence integration
|
|
- Event routing to appropriate handlers
|
|
- Display updates
|
|
"""
|
|
|
|
def __init__(self, config: AppConfig):
|
|
# State management
|
|
self.state_manager = StateManager()
|
|
self.state = self.state_manager.load_state()
|
|
|
|
# Components (lazy-initialized)
|
|
self.library: Optional[LibraryManager] = None
|
|
self.reader: Optional[EbookReader] = None
|
|
|
|
# Display abstraction
|
|
self.display_hal = config.display_hal
|
|
self.current_image: Optional[Image.Image] = None
|
|
|
|
def start(self):
|
|
"""Initialize and show initial screen based on saved state"""
|
|
|
|
def handle_touch(self, event: TouchEvent):
|
|
"""Route touch events to library or reader based on mode"""
|
|
|
|
def shutdown(self):
|
|
"""Clean shutdown with state preservation"""
|
|
```
|
|
|
|
#### B. Mode Transition Logic
|
|
```python
|
|
def _enter_library_mode(self):
|
|
"""Switch to library browsing"""
|
|
# 1. Save and close reader if active
|
|
if self.reader:
|
|
self.reader.save_position("__auto_resume__")
|
|
self.reader.close()
|
|
self.reader = None
|
|
|
|
# 2. Initialize library
|
|
if not self.library:
|
|
self.library = LibraryManager(...)
|
|
|
|
# 3. Render library view
|
|
self.current_image = self.library.render_library()
|
|
self.state_manager.set_mode(EreaderMode.LIBRARY)
|
|
self._update_display()
|
|
|
|
def _enter_reading_mode(self, book_path: str):
|
|
"""Switch to reading mode"""
|
|
# 1. Initialize reader if needed
|
|
if not self.reader:
|
|
self.reader = EbookReader(...)
|
|
|
|
# 2. Load book
|
|
self.reader.load_epub(book_path)
|
|
|
|
# 3. Apply saved settings
|
|
self.reader.apply_settings(self.state.settings.to_dict())
|
|
|
|
# 4. Restore position
|
|
self.reader.load_position("__auto_resume__")
|
|
|
|
# 5. Update state
|
|
self.state_manager.set_current_book(BookState(...))
|
|
self.state_manager.set_mode(EreaderMode.READING)
|
|
|
|
# 6. Render page
|
|
self.current_image = self.reader.get_current_page()
|
|
self._update_display()
|
|
```
|
|
|
|
#### C. Event Handling Integration
|
|
```python
|
|
def handle_touch(self, event: TouchEvent):
|
|
"""Process touch based on current mode"""
|
|
if self.state.mode == EreaderMode.LIBRARY:
|
|
self._handle_library_touch(event)
|
|
elif self.state.mode == EreaderMode.READING:
|
|
self._handle_reading_touch(event)
|
|
|
|
self._update_display()
|
|
|
|
def _handle_library_touch(self, event: TouchEvent):
|
|
"""Library mode touch handling"""
|
|
if event.gesture == GestureType.TAP:
|
|
book_path = self.library.handle_library_tap(event.x, event.y)
|
|
if book_path:
|
|
self._enter_reading_mode(book_path)
|
|
|
|
def _handle_reading_touch(self, event: TouchEvent):
|
|
"""Reading mode touch handling"""
|
|
response = self.reader.handle_touch(event)
|
|
|
|
# Handle special actions
|
|
if response.action == ActionType.BACK_TO_LIBRARY:
|
|
self._enter_library_mode()
|
|
elif response.action == ActionType.PAGE_TURN:
|
|
self.current_image = self.reader.get_current_page()
|
|
elif response.action == ActionType.OVERLAY_OPENED:
|
|
self.current_image = self.reader.get_current_page()
|
|
elif response.action == ActionType.SETTING_CHANGED:
|
|
# Settings changed, update state
|
|
settings = self.reader.get_current_settings()
|
|
self.state_manager.update_settings(settings)
|
|
```
|
|
|
|
#### D. State Persistence Integration
|
|
```python
|
|
def start(self):
|
|
"""Boot sequence with state restoration"""
|
|
# 1. Start auto-save
|
|
self.state_manager.start_auto_save()
|
|
|
|
# 2. Restore previous mode
|
|
if self.state.mode == EreaderMode.READING and self.state.current_book:
|
|
# Resume reading last book
|
|
self._enter_reading_mode(self.state.current_book.path)
|
|
else:
|
|
# Show library
|
|
self._enter_library_mode()
|
|
|
|
# 3. Display initial screen
|
|
self._update_display()
|
|
|
|
def shutdown(self):
|
|
"""Graceful shutdown"""
|
|
# 1. Save current position if reading
|
|
if self.reader and self.reader.is_loaded():
|
|
self.reader.save_position("__auto_resume__")
|
|
self.reader.close()
|
|
|
|
# 2. Stop auto-save and save final state
|
|
asyncio.run(self.state_manager.stop_auto_save(save_final=True))
|
|
```
|
|
|
|
### 11.4 Display HAL Interface
|
|
|
|
The main controller needs a display abstraction layer (HAL). This is **platform-specific** and not part of the core dreader library.
|
|
|
|
**Required HAL Interface:**
|
|
```python
|
|
class DisplayHAL(ABC):
|
|
"""Abstract display interface for platform integration"""
|
|
|
|
@abstractmethod
|
|
def show_image(self, image: Image.Image):
|
|
"""Display a PIL Image on the screen"""
|
|
|
|
@abstractmethod
|
|
def get_touch_events(self) -> Iterator[TouchEvent]:
|
|
"""Get iterator of touch events from hardware"""
|
|
|
|
@abstractmethod
|
|
def set_brightness(self, level: int):
|
|
"""Set display brightness (0-10)"""
|
|
```
|
|
|
|
**Example Implementations:**
|
|
- **E-Ink Display**: Use device-specific SDK (e.g., IT8951, Remarkable)
|
|
- **Desktop Testing**: Use PIL + tkinter or pygame
|
|
- **Web Interface**: Use Flask + HTML canvas
|
|
- **Qt Application**: Use QPixmap + QTouchEvent
|
|
|
|
### 11.5 Implementation Checklist
|
|
|
|
To complete the main application controller:
|
|
|
|
- [ ] Create `dreader/main.py` with `DReaderApplication` class
|
|
- [ ] Implement mode transition methods (`_enter_library_mode`, `_enter_reading_mode`)
|
|
- [ ] Implement event routing (`handle_touch`)
|
|
- [ ] Implement boot/resume logic (`start`)
|
|
- [ ] Implement graceful shutdown (`shutdown`)
|
|
- [ ] Create platform-specific DisplayHAL implementation
|
|
- [ ] Create configuration system (AppConfig class)
|
|
- [ ] Add command-line argument parsing
|
|
- [ ] Create systemd service file (for embedded systems)
|
|
- [ ] Add logging throughout controller
|
|
- [ ] Write integration tests for main controller
|
|
- [ ] Create user documentation
|
|
|
|
### 11.6 Platform-Specific Entry Points
|
|
|
|
Once the main controller exists, create platform-specific entry points:
|
|
|
|
**Desktop (Pygame):**
|
|
```python
|
|
# desktop_app.py
|
|
from dreader.main import DReaderApplication
|
|
from hal.pygame_display import PygameDisplayHAL
|
|
|
|
def main():
|
|
config = AppConfig(
|
|
display_hal=PygameDisplayHAL(width=800, height=1200),
|
|
library_path="~/Books",
|
|
page_size=(800, 1200)
|
|
)
|
|
|
|
app = DReaderApplication(config)
|
|
app.start()
|
|
|
|
# Event loop
|
|
running = True
|
|
while running:
|
|
for event in app.display_hal.get_touch_events():
|
|
app.handle_touch(event)
|
|
|
|
app.shutdown()
|
|
```
|
|
|
|
**E-Ink Device:**
|
|
```python
|
|
# eink_app.py
|
|
from dreader.main import DReaderApplication
|
|
from hal.eink_display import EinkDisplayHAL
|
|
|
|
def main():
|
|
config = AppConfig(
|
|
display_hal=EinkDisplayHAL(device="/dev/epd"),
|
|
library_path="/mnt/books",
|
|
page_size=(1200, 1600)
|
|
)
|
|
|
|
app = DReaderApplication(config)
|
|
app.start()
|
|
|
|
# Hardware event loop
|
|
app.display_hal.run_event_loop(app.handle_touch)
|
|
|
|
app.shutdown()
|
|
```
|
|
|
|
**Web Interface:**
|
|
```python
|
|
# web_app.py
|
|
from flask import Flask, render_template, jsonify, request
|
|
from dreader.main import DReaderApplication
|
|
from hal.web_display import WebDisplayHAL
|
|
|
|
app = DReaderApplication(...)
|
|
|
|
@app.route('/touch', methods=['POST'])
|
|
def handle_touch():
|
|
x, y = request.json['x'], request.json['y']
|
|
gesture = request.json['gesture']
|
|
event = TouchEvent(gesture, x, y)
|
|
app.handle_touch(event)
|
|
return jsonify(success=True)
|
|
|
|
@app.route('/current_image')
|
|
def get_image():
|
|
return send_image(app.current_image)
|
|
```
|
|
|
|
---
|
|
|
|
## Acceptance Criteria
|
|
|
|
The implementation is complete when:
|
|
1. User can browse library and select a book with a single tap
|
|
2. Selected book opens at last read position (or first page if new)
|
|
3. User can turn pages forward/backward via tap or swipe
|
|
4. User can open TOC and jump to any chapter
|
|
5. User can adjust font size and see changes immediately
|
|
6. User can add/remove bookmarks and navigate to them
|
|
7. Application state persists across reboots
|
|
8. Application resumes exactly where user left off after power cycle
|
|
9. All features work on target hardware with acceptable performance
|
|
10. No data loss on unexpected shutdown/crash
|