chore: tidy repository layout and consolidate hardware docs

Root had grown to 30 entries, most of it generated output and one-off
scripts. It now holds 12.

Docs:
- Merge HARDWARE_SETUP, HARDWARE_PINOUT, GPIO_BUTTONS and
  ACCELEROMETER_PAGE_FLIP into a single docs/HARDWARE.md
- Move ARCHITECTURE, REQUIREMENTS and HAL_IMPLEMENTATION_SPEC to docs/,
  leaving only README.md in the root
- Update cross-references in README, setup_rpi.py and
  install_hardware_drivers.sh, and re-base ARCHITECTURE's source links

Merging surfaced three errors, reconciled against hardware_config.json:
- The power button was documented as GPIO 21 to GND with a pull-up. It is
  active high (pull_up: false); wiring it as documented reads as
  permanently pressed.
- GPIO_BUTTONS used GPIO 23 for next-page; it is GPIO 27.
- The FT5316 INT pin was routed to GPIO 27, which collides with the
  next-page button. Now documented as a conflict.

Scripts:
- Move debug_overlay_links.py and debug_previous_page.py to scripts/debug/
- Rename test_pagination_visual.py to scripts/debug/visualize_pagination.py;
  it renders output and asserts nothing, so the test_ prefix was misleading
- Fix the __file__-relative paths these three relied on
- Track update_pyweblayout.sh under scripts/

Tests:
- Reword the backward-navigation tests, which described a pyWebLayout bug
  that is now fixed, as regression tests

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 22:27:53 +02:00
co-authored by Claude Opus 5
parent c62b8eff38
commit f7d59d025f
17 changed files with 942 additions and 1450 deletions
+207
View File
@@ -0,0 +1,207 @@
#!/usr/bin/env python3
"""
Debug script to visualize interactive elements in overlays.
Shows where clickable links are located.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parents[2]))
from dreader.application import EbookReader
from dreader.overlays.settings import SettingsOverlay
from dreader.overlays.navigation import NavigationOverlay
from PIL import Image, ImageDraw, ImageFont
def find_all_links(overlay_reader, panel_width, panel_height):
"""Scan overlay to find all interactive link positions."""
link_positions = {}
if not overlay_reader or not overlay_reader.manager:
print("No overlay reader available")
return link_positions
page = overlay_reader.manager.get_current_page()
if not page:
print("No page available")
return link_positions
print(f"Scanning {panel_width}x{panel_height} overlay for interactive elements...")
# Scan with moderate granularity (every 5 pixels)
for y in range(0, panel_height, 5):
for x in range(0, panel_width, 5):
result = page.query_point((x, y))
if result and result.link_target:
if result.link_target not in link_positions:
link_positions[result.link_target] = {
'first_pos': (x, y),
'bounds': result.bounds,
'text': result.text
}
return link_positions
def visualize_settings_overlay():
"""Visualize interactive elements in settings overlay."""
print("\n" + "="*70)
print("SETTINGS OVERLAY - Interactive Element Map")
print("="*70)
# Create reader
reader = EbookReader(page_size=(800, 1200))
# Load a test book
test_book = Path(__file__).parents[2] / "tests" / "data" / "library-epub" / "pg11-images-3.epub"
if not test_book.exists():
print(f"Test book not found: {test_book}")
return
reader.load_epub(str(test_book))
# Create settings overlay
settings_overlay = SettingsOverlay(reader)
base_page = reader.get_current_page()
# Open overlay
overlay_image = settings_overlay.open(
base_page,
font_scale=1.0,
line_spacing=5,
inter_block_spacing=15,
word_spacing=0
)
# Find all interactive elements
panel_width = 480 # 60% of 800
panel_height = 840 # 70% of 1200
link_positions = find_all_links(
settings_overlay._overlay_reader,
panel_width,
panel_height
)
print(f"\nFound {len(link_positions)} interactive elements:")
for link_target, info in sorted(link_positions.items()):
x, y = info['first_pos']
bounds = info['bounds']
text = info['text']
print(f" {link_target:30s} at ({x:3d}, {y:3d}) - \"{text}\"")
print(f" Bounds: {bounds}")
# Create visualization
print("\nCreating visualization...")
# Get just the overlay panel (not the composited image)
overlay_panel = settings_overlay._cached_overlay_image.copy()
draw = ImageDraw.Draw(overlay_panel)
# Draw markers on each interactive element
for link_target, info in link_positions.items():
x, y = info['first_pos']
# Draw red circle at first detected position
radius = 8
draw.ellipse(
[x - radius, y - radius, x + radius, y + radius],
outline=(255, 0, 0),
width=2
)
# Draw crosshair
draw.line([(x - 15, y), (x + 15, y)], fill=(255, 0, 0), width=1)
draw.line([(x, y - 15), (x, y + 15)], fill=(255, 0, 0), width=1)
# Save visualization
output_path = Path(__file__).parent / "overlay_links_debug.png"
overlay_panel.save(output_path)
print(f"\nVisualization saved to: {output_path}")
print("Red circles show clickable link positions")
reader.close()
def visualize_navigation_overlay():
"""Visualize interactive elements in navigation overlay."""
print("\n" + "="*70)
print("NAVIGATION OVERLAY - Interactive Element Map")
print("="*70)
# Create reader
reader = EbookReader(page_size=(800, 1200))
# Load a test book
test_book = Path(__file__).parents[2] / "tests" / "data" / "library-epub" / "pg11-images-3.epub"
if not test_book.exists():
print(f"Test book not found: {test_book}")
return
reader.load_epub(str(test_book))
# Create navigation overlay
nav_overlay = NavigationOverlay(reader)
base_page = reader.get_current_page()
# Get chapters
chapters = reader.get_chapters()
# Open overlay
overlay_image = nav_overlay.open(
base_page,
chapters=chapters,
bookmarks=[],
active_tab="contents"
)
# Find all interactive elements
panel_width = 480 # 60% of 800
panel_height = 840 # 70% of 1200
link_positions = find_all_links(
nav_overlay._overlay_reader,
panel_width,
panel_height
)
print(f"\nFound {len(link_positions)} interactive elements:")
for link_target, info in sorted(link_positions.items()):
x, y = info['first_pos']
text = info['text']
print(f" {link_target:30s} at ({x:3d}, {y:3d}) - \"{text}\"")
# Create visualization
print("\nCreating visualization...")
# Get just the overlay panel
overlay_panel = nav_overlay._cached_overlay_image.copy()
draw = ImageDraw.Draw(overlay_panel)
# Draw markers on each interactive element
for link_target, info in link_positions.items():
x, y = info['first_pos']
# Draw green circle
radius = 8
draw.ellipse(
[x - radius, y - radius, x + radius, y + radius],
outline=(0, 255, 0),
width=2
)
# Save visualization
output_path = Path(__file__).parent / "nav_overlay_links_debug.png"
overlay_panel.save(output_path)
print(f"\nVisualization saved to: {output_path}")
print("Green circles show clickable link positions")
reader.close()
if __name__ == "__main__":
visualize_settings_overlay()
visualize_navigation_overlay()
print("\n" + "="*70)
print("Debug complete! Check the generated PNG files.")
print("="*70)
+88
View File
@@ -0,0 +1,88 @@
#!/usr/bin/env python3
"""
Debug previous_page issue.
"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parents[2]))
from dreader.application import EbookReader
def debug_previous():
"""Debug previous_page functionality."""
epub_path = Path("tests/data/library-epub/pg11-images-3.epub")
print("=" * 70)
print("Debug Previous Page")
print("=" * 70)
reader = EbookReader(page_size=(800, 1200))
reader.load_epub(str(epub_path))
print(f"\nLoaded: {reader.book_title}")
print(f"Manager type: {type(reader.manager)}")
print(f"Manager has previous_page: {hasattr(reader.manager, 'previous_page')}")
# Check manager's state
if reader.manager:
print(f"\nManager state:")
print(f" current_position: {reader.manager.current_position}")
if hasattr(reader.manager, 'page_buffer'):
print(f" page_buffer length: {len(reader.manager.page_buffer)}")
if hasattr(reader.manager, 'buffer'):
print(f" buffer: {reader.manager.buffer}")
# Try going forward first
print("\n" + "-" * 70)
print("Going forward 3 pages...")
print("-" * 70)
for i in range(3):
page = reader.next_page()
if page:
print(f" Forward {i+1}: position = {reader.manager.current_position}")
else:
print(f" Forward {i+1}: FAILED")
if reader.manager:
print(f"\nAfter forward navigation:")
print(f" current_position: {reader.manager.current_position}")
if hasattr(reader.manager, 'page_buffer'):
print(f" page_buffer length: {len(reader.manager.page_buffer)}")
if len(reader.manager.page_buffer) > 0:
print(f" page_buffer[0]: {reader.manager.page_buffer[0].position if hasattr(reader.manager.page_buffer[0], 'position') else 'N/A'}")
# Now try going backward
print("\n" + "-" * 70)
print("Trying to go backward...")
print("-" * 70)
# Try calling previous_page directly on manager
if reader.manager:
print("\nCalling manager.previous_page() directly...")
result = reader.manager.previous_page()
print(f" Result: {type(result) if result else None}")
if result:
print(f" Result has render(): {hasattr(result, 'render')}")
print(f" Position after: {reader.manager.current_position}")
else:
print(f" Result is None")
print(f" Position still: {reader.manager.current_position}")
# Try via reader.previous_page()
print("\nCalling reader.previous_page()...")
page = reader.previous_page()
if page:
print(f" SUCCESS: Got page {page.size}")
print(f" Position: {reader.manager.current_position}")
else:
print(f" FAILED: Got None")
print(f" Position: {reader.manager.current_position}")
reader.close()
if __name__ == "__main__":
debug_previous()
+50
View File
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""
Test script to visualize library pagination.
"""
from pathlib import Path
from dreader import LibraryManager
def test_pagination():
"""Test pagination with actual library"""
library_path = Path(__file__).parents[2] / 'tests' / 'data' / 'library-epub'
# Create library manager (default books_per_page=6)
library = LibraryManager(
library_path=str(library_path),
page_size=(800, 1200)
)
# Scan library
books = library.scan_library()
print(f"\nFound {len(books)} books")
print(f"Books per page: {library.books_per_page}")
print(f"Total pages: {library.get_total_pages()}")
# Render all pages
for page_num in range(library.get_total_pages()):
library.set_page(page_num)
print(f"\n=== Rendering Page {page_num + 1}/{library.get_total_pages()} ===")
library.create_library_table()
img = library.render_library()
output_path = f'/tmp/library_pagination_page{page_num + 1}.png'
img.save(output_path)
print(f"Saved to {output_path}")
# Show which books are on this page
start_idx = page_num * library.books_per_page
end_idx = min(start_idx + library.books_per_page, len(books))
page_books = books[start_idx:end_idx]
print(f"Books on this page ({len(page_books)}):")
for book in page_books:
print(f" - {book['title']} by {book['author']}")
# Cleanup
library.cleanup()
print("\nPagination test complete!")
if __name__ == '__main__':
test_pagination()