Fix npm install errors and make ML/CV libraries truly optional.
Changes:
- Move dependencies from "dependencies" to "optionalDependencies"
- Fix @techstark/opencv-js version from "^4.12.0" to "4.12.0-release.1"
- The caret syntax (^) doesn't work with suffix versions like -release.1
- Use exact version match instead
Why optionalDependencies:
- Installation won't fail if these packages fail to install
- Users can opt-out with: npm install foliate-js --omit=optional
- Maintains the library's "no hard dependencies" philosophy
- Graceful degradation: panel detection falls back to grid-only mode
Behavior:
- Default: npm attempts to install OpenCV, TensorFlow, COCO-SSD (~4.5MB)
- With --omit=optional: skips heavy ML/CV libraries
- Either way: library works, panel detection gracefully adapts
This resolves npm install errors where "^4.12.0" couldn't be found
because the actual version is "4.12.0-release.1".
Document the new panel detection feature for manga and comics,
including installation options and usage examples.
Changes to README.md:
- Added "Optional panel detection for manga and comics" to Features list
- Added new "Panel Detection" section after "PDF and Other Fixed-Layout Formats"
- Documented the optionalDependencies approach and --omit=optional flag
- Explained the multi-tier fallback system (OpenCV → ML → Grid)
- Provided code examples for enabling panel mode and navigation
- Documented keyboard shortcuts (P, Arrow keys, Escape)
- Clarified graceful degradation when dependencies are missing
The documentation follows existing README patterns:
- Concise technical style
- Code examples with proper syntax
- Clear installation instructions
- Explanation of feature behavior and fallbacks
This helps users understand:
1. What panel detection is and when it's useful
2. How to install with or without the feature
3. How to use the API in their applications
4. What happens when optional dependencies aren't installed
Add a checkbox menu item to the demo reader's settings menu that
allows users to toggle panel detection mode on or off.
Implementation details:
- Adds "Panel Mode" checkbox to existing settings menu
- Uses the createMenu API with type="checkbox"
- Safely checks for togglePanelMode method availability
- Checks current panel-mode state before toggling
- Properly enables/disables panel mode via attribute API
User-facing behavior:
- Click gear icon → settings menu appears
- Check/uncheck "Panel Mode" to enable/disable
- Works seamlessly with existing layout options
- Only active when viewing fixed-layout content
This change only affects the demo reader (reader.html + reader.js),
not the foliate-js library itself. The library's panel detection
API in fixed-layout.js remains unchanged.
The panel mode toggle provides easy access to the new panel
navigation features for testing and demonstration purposes.
Implement comprehensive panel-aware navigation for manga, comics,
and other fixed-layout content with automatic panel detection.
Core features:
- Panel mode toggle via 'panel-mode' attribute
- Touch gesture support (swipe, tap)
- Visual panel overlay with SVG highlighting
- Auto-zoom to center and fit each panel
- Seamless integration with existing page navigation
Panel detection integration:
- Initialize PanelDetector on component construction
- Detect panels when entering panel mode
- Cache detection results per page
- Support re-detection via force flag
Panel navigation:
- nextPanel(): advance to next panel, wrapping to next page
- prevPanel(): go to previous panel, wrapping to previous page
- Auto-enter panel mode on first panel navigation
- Visual feedback with current panel highlighting
Touch support (previously missing from fixed-layout):
- touchstart: record initial position and timestamp
- touchmove: prevent default for significant movement
- touchend: velocity-based swipe detection
- Panel mode: tap to toggle overlay, swipe to navigate
Visual overlay:
- SVG rectangles showing detected panels
- Highlight current panel in orange (#ff6b35)
- Dim other panels in semi-transparent white
- Pointer-events none for non-blocking overlay
Zoom behavior:
- Calculate scale to fit panel within viewport
- Center panel with scroll positioning
- Apply via existing zoom attribute mechanism
- Re-render overlay after zoom
Internal methods:
- #nextPage() / #prevPage(): bypass panel-mode check
- #enterPanelMode() / #exitPanelMode(): mode management
- #showPanelOverlay() / #hidePanelOverlay(): overlay management
- #zoomToPanel(): auto-center and scale to panel
- #addTouchSupport(): attach touch event listeners
- #onTouchStart/Move/End: touch gesture handling
- #handlePanelTouch: panel-mode specific touch logic
Public API:
- panelCount: number of detected panels
- currentPanelIndex: current panel index
- togglePanelMode(): enter/exit panel mode
Observer attributes:
- 'zoom': existing zoom support
- 'panel-mode': new panel mode attribute
This implementation brings fixed-layout renderer to feature parity
with paginator.js regarding touch support while adding unique
panel-aware navigation capabilities.
Extend keyboard navigation to support panel mode when enabled.
Changes:
- Check for panel-mode attribute before routing key events
- Route Arrow keys and Vim keys (h/l) to panel navigation in panel mode
- Add 'P' key to toggle panel mode
- Add 'Escape' key to exit panel mode
- Maintain backward compatibility with existing page navigation
When panel mode is active:
- ArrowRight/l: next panel
- ArrowLeft/h: previous panel
- Escape/P: exit panel mode
When panel mode is inactive:
- ArrowRight/l: next page (existing behavior)
- ArrowLeft/h: previous page (existing behavior)
- P: enter panel mode (new feature)
Implement a comprehensive panel detection system for manga and comics
with automatic fallback chain for maximum compatibility.
Core detector (detector.js):
- PanelDetector class with in-memory caching
- Lazy-loading of OpenCV and TensorFlow.js
- Validation logic to filter poor detections
- Cache management to avoid re-detection
OpenCV edge detection (opencv.js):
- Canny edge detection for panel boundaries
- Contour finding with bounding box extraction
- Size and aspect ratio filtering
- Reading order sorting (top-to-bottom, left-to-right)
ML-based detection (coco-ssd.js):
- COCO-SSD pre-trained model integration
- Object detection for irregular panel layouts
- Rectangular filtering for panel-like regions
- Handles edge cases where edge detection fails
Grid-based fallback (grid.js):
- Lightweight 3x3 grid detection
- Empty cell detection via alpha channel analysis
- Adjacent panel merging algorithm
- Always works as final fallback
The detection pipeline tries OpenCV first (fast, accurate),
falls back to ML detection if validation fails,
and uses grid detection as ultimate baseline.
The MOBI6.init() method builds #fileposList (the map of all filepos byte-offset references, used to insert <a id="fileposXXXX"> anchors into section HTML) after calling getGuide() and the TOC-section loader — both of which call createDocument() → loadText(), which caches the section HTML. Since #fileposList is still empty at that point, the cached HTML has no anchor elements.
Later, when Foliate calls getElementById('filepos12345') on the cached (anchor-less) document, it returns null, so the paginator falls back to ?? 0 — i.e. scroll to position 0 = first page.
For EPUB this doesn't happen because chapters are separate files; navigation loads a new section by index, not by searching for an anchor element within one section.
Co-authored-by: Lars E. <lars@ermert.es>
If the viewer is running in an RTL context (this is not the same as the
reading direction of the Book itself), and a fixed-layout rendered view
is scaled (which is often the case), anchoring the transform to the
left breaks page positioning.
In Safari, clicking on links triggers `selectionchange`. Since we do not
move the caret when flipping through pages, when you have flipped to the
next or previous pages and click on any link, it would take you back to
the caret instead of activating the link.
Note that the bug occurs in Safari 17 but not in WebKitGTK 2.46.
Not sure if there's a way to fix this and still retain the "set anchor
to caret on click" behavior. But I guess this behavior isn't really that
essential, anyway. So removing it for now.
Fixes#44