From 01e79dfa4b1d936fd9816446e285578edb27ab98 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sun, 9 Nov 2025 17:47:34 +0100 Subject: [PATCH] Test appplication for offdevice testing --- ARCHITECTURE.md | 552 +++++++++++++++ HTML_GENERATION.md | 210 ------ REQUIREMENTS.md | 965 ++++++++++++++++++++++++++ debug_overlay_links.py | 207 ++++++ debug_previous_page.py | 88 +++ docs/images/settings_overlay_demo.gif | Bin 1971313 -> 386762 bytes docs/images/toc_overlay_demo.gif | Bin 556002 -> 417214 bytes dreader/__init__.py | 11 + dreader/application.py | 2 +- dreader/hal.py | 188 +++++ dreader/hal_pygame.py | 401 +++++++++++ dreader/handlers/gestures.py | 45 +- dreader/html_generator.py | 46 +- dreader/main.py | 428 ++++++++++++ dreader/overlays/base.py | 86 +++ dreader/overlays/navigation.py | 19 +- dreader/overlays/settings.py | 20 +- run_dreader.py | 188 +++++ test_debug_overlay.py | 106 +++ test_main_integration.py | 211 ++++++ test_page_navigation.py | 121 ++++ test_swipe_detection.py | 124 ++++ 22 files changed, 3749 insertions(+), 269 deletions(-) create mode 100644 ARCHITECTURE.md delete mode 100644 HTML_GENERATION.md create mode 100644 REQUIREMENTS.md create mode 100644 debug_overlay_links.py create mode 100644 debug_previous_page.py create mode 100644 dreader/hal.py create mode 100644 dreader/hal_pygame.py create mode 100644 dreader/main.py create mode 100755 run_dreader.py create mode 100644 test_debug_overlay.py create mode 100644 test_main_integration.py create mode 100644 test_page_navigation.py create mode 100644 test_swipe_detection.py diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 0000000..80343d5 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,552 @@ +# DReader Application Architecture + +## Overview + +DReader is a full-featured ebook reader application built on top of [pyWebLayout](https://gitea.tourolle.paris/dtourolle/pyWebLayout). It provides a complete reading experience with navigation, bookmarks, highlights, and customizable display settings. + +## System Architecture + +### High-Level Component Structure + +``` +dreader/ +├── application.py # Main EbookReader class (coordinator) +├── managers/ # Specialized management modules +│ ├── document.py # Document loading (EPUB/HTML) +│ ├── settings.py # Font and spacing controls +│ └── highlight_coordinator.py # Text highlighting +├── handlers/ +│ └── gestures.py # Touch event routing +├── overlays/ # UI overlay system +│ ├── base.py # Base overlay functionality +│ ├── navigation.py # TOC and bookmarks overlay +│ └── settings.py # Settings overlay +├── library.py # Library browsing and book selection +├── state.py # Application state persistence +├── html_generator.py # HTML generation for overlays +└── gesture.py # Gesture definitions and responses +``` + +### Relationship to pyWebLayout + +**pyWebLayout** provides low-level rendering primitives: +- Text layout and rendering algorithms +- Document structure and pagination +- Query systems for interactive content +- Core rendering infrastructure + +**DReader** is an application framework that: +- Combines pyWebLayout components into a complete reader +- Provides high-level APIs for common ereader tasks +- Manages application state (bookmarks, highlights, positions) +- Handles business logic for gestures and interactions + +Think of it as: +- **pyWebLayout** = React (library) +- **DReader** = Next.js (framework) + +## Core Components + +### 1. EbookReader (Main Coordinator) + +**Location**: [application.py](dreader/application.py) + +The central orchestrator that coordinates all subsystems: + +```python +class EbookReader: + """Main ebook reader application""" + + # Core dependencies + manager: EreaderLayoutManager # pyWebLayout layout engine + doc_manager: DocumentManager # Document loading + settings_manager: SettingsManager # Display settings + highlight_coordinator: HighlightCoordinator # Text highlighting + gesture_router: GestureRouter # Gesture handling + overlay_manager: OverlayManager # Overlay rendering +``` + +**Key Responsibilities**: +- Document lifecycle (load, close) +- Page navigation (next, previous, chapters) +- Bookmark management +- Position persistence +- Settings coordination +- Gesture event routing + +### 2. Document Manager + +**Location**: [managers/document.py](dreader/managers/document.py) + +Handles document loading and metadata extraction. + +**Responsibilities**: +- Load EPUB files via pyWebLayout +- Extract book metadata (title, author, etc.) +- Provide document info to other components + +### 3. Settings Manager + +**Location**: [managers/settings.py](dreader/managers/settings.py) + +Manages all display settings with persistence. + +**Settings**: +- Font scale (adjustable font size) +- Line spacing +- Inter-block spacing (paragraph spacing) +- Word spacing + +**Features**: +- Real-time preview in settings overlay +- Persistent across sessions +- Position preservation when settings change + +### 4. Gesture Router + +**Location**: [handlers/gestures.py](dreader/handlers/gestures.py) + +Routes touch events to appropriate handlers based on application state. + +**Gesture Types**: +- `TAP` - Word selection, link following, overlay interaction +- `SWIPE_LEFT` - Next page +- `SWIPE_RIGHT` - Previous page +- `SWIPE_UP` - Open navigation overlay (from bottom 20%) +- `SWIPE_DOWN` - Open settings overlay (from top) or close overlay +- `PINCH_IN/OUT` - Font size adjustment +- `DRAG` - Text selection (start, move, end) + +**Routing Logic**: +``` +Touch Event → GestureRouter + ├─ Is overlay open? + │ ├─ Yes → Route to overlay handler + │ └─ No → Route to reading mode handler + └─ Return GestureResponse +``` + +### 5. Overlay System + +**Location**: [overlays/](dreader/overlays/) + +The overlay system provides modal UI panels over the reading content. + +#### Overlay Manager + +**Location**: [overlays/base.py](dreader/overlays/base.py) + +Core overlay rendering and compositing infrastructure. + +**Responsibilities**: +- Render overlay HTML to images +- Composite overlays over base page +- Darken background for modal effect +- Handle coordinate translation for interaction +- Cache for performance + +#### Navigation Overlay + +**Location**: [overlays/navigation.py](dreader/overlays/navigation.py) + +Unified overlay with tabbed interface for: +- **Contents Tab**: Chapter navigation (TOC) +- **Bookmarks Tab**: Saved position management + +**Features**: +- Tab switching without closing overlay +- Chapter selection with jump +- Bookmark selection with jump +- Add/delete bookmarks + +#### Settings Overlay + +**Location**: [overlays/settings.py](dreader/overlays/settings.py) + +Interactive settings panel with real-time preview. + +**Controls**: +- Font size: A- / A+ buttons +- Line spacing: +/- buttons +- Block spacing: +/- buttons +- Word spacing: +/- buttons + +**Interaction**: Changes apply immediately, overlay refreshes to show updated values. + +### 6. Library Manager + +**Location**: [library.py](dreader/library.py) + +Manages the library browsing experience. + +**Features**: +- Scan directory for EPUB files +- Extract and cache metadata +- Render library grid view +- Handle book selection via tap +- Cache cover images for performance + +**Display**: Renders books in a grid with cover thumbnails and metadata. + +### 7. State Manager + +**Location**: [state.py](dreader/state.py) + +Persistent application state across sessions. + +**State Structure**: +```python +class AppState: + mode: EreaderMode # LIBRARY or READING + overlay: OverlayState # Current overlay type + current_book: BookState # Currently open book + library: LibraryState # Library scan cache + settings: SettingsState # Display settings +``` + +**Persistence**: +- Location: `~/.config/dreader/state.json` +- Auto-save every 60 seconds +- Immediate save on mode change, settings change, shutdown +- Atomic writes for safety + +**Boot Behavior**: +- Resume last book at last position +- Restore all settings +- Fall back to library if book missing + +## Data Flow Diagrams + +### Opening an Overlay + +``` +User Action + ↓ +EbookReader.open_navigation_overlay() + ├─ Get current page (base layer) + ├─ Get chapters and bookmarks + ↓ +OverlayManager.open_navigation_overlay() + ├─ Generate HTML + ├─ Render to image (using temp reader) + ├─ Composite over base page + │ ├─ Darken background + │ ├─ Add border + │ └─ Paste panel at center + └─ Cache base page, overlay, offset + ↓ +Return composited image +``` + +### Overlay Interaction + +``` +User Touch (x, y) + ↓ +GestureRouter.handle_touch() + ├─ Overlay open? YES + ↓ +EbookReader._handle_overlay_tap(x, y) + ↓ +OverlayManager.query_overlay_pixel(x, y) + ├─ Translate screen coords to overlay coords + ├─ Query pyWebLayout for link at position + └─ Return link_target (e.g., "chapter:5") + ↓ +Parse link_target and execute action: + ├─ "chapter:N" → jump_to_chapter(N), close overlay + ├─ "bookmark:name" → load_position(name), close overlay + ├─ "setting:action" → apply setting, refresh overlay + └─ "tab:name" → switch tab, keep overlay open + ↓ +Return GestureResponse +``` + +### State Persistence + +``` +Application Running + ↓ +StateManager auto-save timer (every 60s) + ├─ Gather current state + ├─ Serialize to JSON + └─ Atomic write to disk + +OR + +User performs action (page turn, setting change) + ├─ StateManager.save_state() + └─ Immediate write + +Application Shutdown + ├─ Save position: reader.save_position("__auto_resume__") + ├─ Stop auto-save + └─ Final state.json write +``` + +### Boot Sequence + +``` +Application Start + ↓ +StateManager.load_state() + ├─ Read state.json + ├─ Validate and parse + └─ Create AppState object + ↓ +Check previous mode: + ├─ READING mode? + │ ├─ Load last book + │ ├─ Apply saved settings + │ └─ Restore position ("__auto_resume__") + │ + └─ LIBRARY mode? + └─ Show library grid +``` + +## File Organization + +### Application State Files + +``` +~/.config/dreader/ +├── state.json # Application state +├── covers/ # Cached book covers +│ └── {book_id}.png +├── bookmarks/ # Per-book bookmarks +│ └── {document_id}_{bookmark_name}.json +└── highlights/ # Per-book highlights + └── {document_id}_highlights.json +``` + +### Bookmark Format + +Each book's position is stored separately using document ID: +```json +{ + "document_id": "book123", + "bookmark_name": "__auto_resume__", + "position": { + "offset": 1234, + "chapter": 5 + }, + "timestamp": "2025-11-09T10:30:00Z" +} +``` + +## Gesture Handling + +### Gesture Priority and Routing + +``` +Touch Event + ↓ +Is overlay open? +├─ YES → Overlay Mode +│ ├─ TAP → Handle overlay interaction +│ ├─ SWIPE_DOWN → Close overlay +│ └─ Other → Ignore (modal behavior) +│ +└─ NO → Reading Mode + ├─ TAP + │ ├─ On link → Follow link + │ ├─ On word → Select word + │ ├─ Left edge → Previous page + │ └─ Right edge → Next page + │ + ├─ SWIPE + │ ├─ LEFT → Next page + │ ├─ RIGHT → Previous page + │ ├─ UP (from bottom 20%) → Open navigation + │ └─ DOWN (from top 20%) → Open settings + │ + ├─ PINCH + │ ├─ IN → Decrease font size + │ └─ OUT → Increase font size + │ + └─ DRAG + ├─ START → Begin text selection + ├─ MOVE → Extend selection + └─ END → Complete selection +``` + +### Response Types + +```python +class ActionType(Enum): + NONE = "none" + PAGE_TURN = "page_turn" + WORD_SELECTED = "word_selected" + LINK_FOLLOWED = "link_followed" + CHAPTER_SELECTED = "chapter_selected" + BOOKMARK_SELECTED = "bookmark_selected" + SETTING_CHANGED = "setting_changed" + OVERLAY_OPENED = "overlay_opened" + OVERLAY_CLOSED = "overlay_closed" + TAB_SWITCHED = "tab_switched" +``` + +## Performance Characteristics + +### Rendering Performance + +- **Page Turn**: ~50-100ms (depends on page complexity) +- **Overlay Open**: ~200-250ms (includes HTML generation and rendering) +- **Tab Switch**: ~125ms (uses cached base page) +- **Setting Change**: ~150ms (re-render with new settings) +- **Tap Interaction**: ~5-10ms (coordinate query) + +### Memory Usage + +- **Base Application**: ~20-30MB +- **Per Book**: ~10-50MB (depends on images) +- **Overlay Cache**: ~5-10MB + +### Optimization Strategies + +1. **Caching**: + - Base page cached during overlay display + - Overlay panel cached for tab switching + - Cover images cached to disk + - Metadata cached between sessions + +2. **Lazy Loading**: + - Library covers loaded on-demand + - Book content loaded only when opened + - Overlays rendered only when needed + +3. **Efficient Updates**: + - Tab switching reuses base page + - Setting changes use incremental rendering + - Position saves are debounced + +## Extension Points + +### Adding New Overlays + +To add a new overlay type: + +1. Define new `OverlayState` enum value in [state.py](dreader/state.py#L27-L33) +2. Create HTML generator in [html_generator.py](dreader/html_generator.py) +3. Add overlay class in `overlays/` directory +4. Implement open/close methods in [overlay manager](dreader/overlays/base.py) +5. Add gesture handling in [application.py](dreader/application.py) + +### Custom Gesture Handlers + +To add custom gestures: + +1. Define gesture type in [gesture.py](dreader/gesture.py) +2. Add handler in [gestures.py](dreader/handlers/gestures.py) +3. Define action type for response +4. Update gesture router logic + +### HAL Integration + +To integrate with hardware: + +Create a display abstraction layer implementing: +```python +class DisplayHAL(ABC): + @abstractmethod + def show_image(self, image: Image.Image): + """Display image on hardware""" + + @abstractmethod + def get_touch_events(self) -> Iterator[TouchEvent]: + """Get touch input from hardware""" + + @abstractmethod + def set_brightness(self, level: int): + """Control display brightness""" +``` + +Examples: +- **E-ink**: IT8951, Remarkable device SDK +- **Desktop**: pygame, tkinter +- **Web**: Flask + HTML canvas +- **Qt**: QPixmap + QTouchEvent + +## Testing Strategy + +### Unit Tests + +- State serialization and persistence +- Gesture routing logic +- Coordinate translation +- HTML generation + +### Integration Tests + +- Mode transitions (LIBRARY ↔ READING) +- Overlay lifecycle (open → interact → close) +- Boot recovery and resume +- Settings persistence + +### Example-Based Testing + +Working examples demonstrate full integration: +- [simple_ereader_example.py](examples/simple_ereader_example.py) +- [library_reading_integration.py](examples/library_reading_integration.py) +- [navigation_overlay_example.py](examples/navigation_overlay_example.py) +- [demo_settings_overlay.py](examples/demo_settings_overlay.py) + +## Design Patterns + +### Component-Based Architecture + +- **Managers**: Single-responsibility modules for specific tasks +- **Handlers**: Event routing and processing +- **Overlays**: Self-contained UI components + +### Delegation Over Inheritance + +- EbookReader delegates to specialized managers +- No deep inheritance hierarchies +- Composition for flexibility + +### State Machine Pattern + +- Clear state transitions (modes, overlays) +- State persistence for resume +- Predictable behavior + +### Event-Driven Architecture + +- Touch events drive all interactions +- Response objects communicate results +- Decoupled components + +## Future Architecture Considerations + +### Sub-Application Pattern + +Current overlay handling uses a monolithic approach. Future refactoring could extract overlays into sub-applications: + +```python +class OverlaySubApplication(ABC): + def open(self, context: OverlayContext) -> Image.Image: ... + def handle_tap(self, x: int, y: int) -> GestureResponse: ... + def close(self) -> Image.Image: ... +``` + +Benefits: +- Self-contained overlay logic +- Easier testing +- Plugin support +- Composable overlays + +### Plugin System + +Enable third-party extensions: +- Custom overlay types +- Additional gestures +- Export formats +- Cloud sync providers + +## References + +- [pyWebLayout Documentation](https://gitea.tourolle.paris/dtourolle/pyWebLayout) +- [REQUIREMENTS.md](REQUIREMENTS.md) - Detailed feature specifications +- [README.md](README.md) - User-facing documentation +- [examples/](examples/) - Working code examples diff --git a/HTML_GENERATION.md b/HTML_GENERATION.md deleted file mode 100644 index a5d538e..0000000 --- a/HTML_GENERATION.md +++ /dev/null @@ -1,210 +0,0 @@ -# HTML Generation for dreader - -This document describes how to use the HTML generation features in dreader to create UI for e-reader applications. - -## Overview - -The dreader library now includes HTML generation capabilities that allow you to create complete user interfaces programmatically. This is designed to work with a Hardware Abstraction Layer (HAL) that handles the actual display rendering and input processing. - -## Architecture - -``` -┌─────────────────────────────────────┐ -│ dreader Library │ -│ ├─ EbookReader (book rendering) │ -│ ├─ html_generator (UI generation) │ -│ └─ book_utils (scanning/metadata) │ -└─────────────────────────────────────┘ - ↓ HTML strings -┌─────────────────────────────────────┐ -│ HAL (Hardware Abstraction Layer) │ -│ - Receives HTML strings │ -│ - Renders to display │ -│ - Captures touch/button input │ -│ - Calls back to dreader │ -└─────────────────────────────────────┘ -``` - -## Page and Overlay Concept - -The UI uses a **page/overlay** architecture: - -- **Page (background)**: The main book content rendered as an image -- **Overlay (foreground)**: UI elements like settings, table of contents, bookmarks, etc. - -## Available Modules - -### 1. html_generator - -Functions for generating HTML strings: - -- `generate_library_html(books)` - Grid view of all books with covers -- `generate_reader_html(title, author, page_data)` - Book reading view -- `generate_settings_overlay()` - Settings panel -- `generate_toc_overlay(chapters)` - Table of contents -- `generate_bookmarks_overlay(bookmarks)` - Bookmarks list - -### 2. book_utils - -Utilities for managing books: - -- `scan_book_directory(path)` - Scan directory for EPUB files -- `extract_book_metadata(epub_path)` - Get title, author, cover -- `get_chapter_list(reader)` - Format chapters for TOC -- `get_bookmark_list(reader)` - Format bookmarks -- `page_image_to_base64(image)` - Convert page image to base64 - -## Usage Example - -```python -from pathlib import Path -from dreader import create_ebook_reader -from dreader.html_generator import ( - generate_library_html, - generate_reader_html, - generate_toc_overlay -) -from dreader.book_utils import ( - scan_book_directory, - get_chapter_list, - page_image_to_base64 -) - -# 1. Show library view -books_dir = Path('books') -books = scan_book_directory(books_dir) -library_html = generate_library_html(books) -# Pass library_html to HAL for rendering - -# 2. User selects a book -selected_book = books[0] -reader = create_ebook_reader(page_size=(800, 1000)) -reader.load_epub(selected_book['path']) - -# 3. Show reader view -page_image = reader.get_current_page() -page_base64 = page_image_to_base64(page_image) -reader_html = generate_reader_html( - book_title=reader.book_title, - book_author=reader.book_author, - page_image_data=page_base64 -) -# Pass reader_html to HAL for rendering - -# 4. User presses "Contents" button - show TOC overlay -chapters = get_chapter_list(reader) -toc_html = generate_toc_overlay(chapters) -# Pass toc_html to HAL for rendering on top of page -``` - -## HTML Structure - -### Library View - -The library uses an HTML table for grid layout: - -```html - - - - - -
- - - - -
Book Title
Author Name
-
-``` - -### Reader View - -The reader view has three sections: - -- Header: Book info + buttons (Library, Contents, Settings) -- Page container: Centered book page image -- Footer: Navigation buttons (Previous, Next) - -### Overlays - -Overlays use: - -- Semi-transparent background (`rgba(0, 0, 0, 0.7)`) -- Centered white panel -- Close button -- Table-based layout for content - -## Button/Link Interaction - -All interactive elements have: - -- `id` attributes for buttons (e.g., `id="btn-next"`) -- `data-*` attributes for dynamic content (e.g., `data-chapter-index="5"`) -- CSS classes for styling (e.g., `class="nav-button"`) - -Your HAL should: - -1. Parse the HTML to identify interactive elements -2. Map touch/click coordinates to elements -3. Call appropriate dreader methods -4. Regenerate and render updated HTML - -## Demo - -Run the included demo to see all features: - -```bash -source venv/bin/activate -python examples/html_generation_demo.py -``` - -This will generate example HTML files in the `output/` directory that you can open in a browser to preview. - -## Integration with HAL - -Your HAL should implement: - -1. **HTML Rendering**: Parse and display HTML strings -2. **Touch Input**: Map touch coordinates to HTML elements -3. **State Management**: Maintain reader state between interactions -4. **Re-rendering**: Update display when state changes - -Example HAL flow: - -``` -User touches screen - ↓ -HAL identifies touched element (e.g., "btn-next") - ↓ -HAL calls reader.next_page() - ↓ -HAL regenerates reader_html with new page - ↓ -HAL renders updated HTML -``` - -## Styling - -All HTML includes inline CSS for complete styling. The design is: - -- Clean, minimal interface -- Dark theme for reader (reduces eye strain) -- Large touch targets for buttons -- Responsive layout using tables (widely supported) - -## Customization - -To customize the UI: - -1. Edit functions in `dreader/html_generator.py` -2. Modify CSS in the `