Skip to content

feat: EPUB image tap detection#159

Open
ddfreiling wants to merge 12 commits into
Notalib:mainfrom
ddfreiling:feat/epub-image-tap
Open

feat: EPUB image tap detection#159
ddfreiling wants to merge 12 commits into
Notalib:mainfrom
ddfreiling:feat/epub-image-tap

Conversation

@ddfreiling

@ddfreiling ddfreiling commented Jun 21, 2026

Copy link
Copy Markdown
Member

Adds tap-to-open support for images inside EPUBs across all three platforms,
plus a resource-bytes API for lazy image loading.

  • Tapping an image now fires onImageTapped with an ImageTapEvent carrying the
    publication-relative href, optional alt/caption, on-screen rect, natural
    pixel dimensions, and (on Web) a direct srcUrl.
  • The event fires for every tapped image — deciding whether to act (e.g. may want to ignore taps inside a comic) is the consumer's responsibility.

ddfreiling and others added 11 commits June 20, 2026 21:45
…ustrated fixture

Follow-ups to the EPUB image-tap feature:

- web: resolve the tapped element via the event's cssSelector instead of
  elementFromPoint(e.x, e.y). Upstream Peripherals multiplies tap coordinates
  by devicePixelRatio, so on HiDPI/Retina displays they overshot and no image
  was ever detected. elementFromPoint kept as a dpr-corrected fallback.
- android: implement getResourceBytes via publication.get(url).read(); only
  image-tap *detection* remains the documented Android gap.
- comics: image-tap is no longer gated plugin-side. onImageTapped fires for
  every tapped image; deciding whether to act on it (e.g. ignoring comic
  books) is the consumer's responsibility. Documented on ImageTapEvent.
- test: replace the moby_dick stand-in with peter_rabbit.epub (Project
  Gutenberg #14838, ~29 images) for the image-tap / getResourceBytes
  integration tests.
- rebuild web bundle (lib/helpers + example copy).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- FullScreenImageView: replace FutureBuilder with Image+imageProvider;
  ReadiumResourceImageProvider already handles caching and avoids
  re-fetching on every rebuild (drops no-op .then and Uint8List.fromList)
- ImageTapDetector: replace manual DOM walk with el.closest('img')
- FlutterEpubNavigator: extract shared handleImageTapEvent for tap/click
  (was identical copy-paste)
- FlutterReadiumPlugin: align getResourceBytes task priority to .high
  (was inconsistently .userInitiated vs .high everywhere else)
- example pubspec.lock: reflect v0.1.0 from main rebase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…y escaping

- EPUBReaderView: use image.accessibilityLabel (HTML alt="") instead of
  image.caption (<figcaption>, always nil — TODO in swift-toolkit); caption
  and alt are semantically distinct fields
- ReadiumReaderChannel: Log.reader.warn (matches the logger API; .warning
  was a non-existent method)
- reader_image_interaction: ImageTapEvent.rect is now dart:ui Rect instead
  of Map<String, double>; callers use .left/.top/.width/.height
- ReadiumReaderWidget: fix viewTypeChannelName → VIEW_TYPE_CHANNEL_NAME
  (screaming snake case for const, and restore correct value
  dk.nota.flutter_readium/ReadiumReaderWidget — without-underscore typo
  introduced in the WIP commit broke Android platform view registration)
- ReadiumReaderWidget: wrap syncPolicy in JSONObject.quote() before JS
  injection to prevent breakage if the value contains quotes or backslashes
- FlutterReadiumPlugin: restore Task priority to .userInitiated (.high is
  an alias with identical raw value; .userInitiated is more descriptive for
  plugin work where the user is actively waiting)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement EPUB image-tap on Android via a @JavascriptInterface bridge
(EpubImageTapBridge) registered on each resource WebView and a capture-phase
click listener injected on page load. The bridge resolves the publication-
relative href by suffix-matching img.src against the manifest and emits
onImageTapped through ReadiumReaderChannel, matching the iOS/Web contract.

Add Jest coverage for the web hit-testing logic (tryBuildImageTapPayload):
frame resolution, cssSelector vs dpr-corrected elementFromPoint fallback,
the <img> ancestor walk, href derivation, and the null paths.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…upport

v0.1.0 is already tagged/published without these features, so the image-tap
and getResourceBytes entries belong under Unreleased, not the released 0.1.0
section. Also update the image-tap entry to reflect Android detection now
being implemented (was "iOS and Web, Android follow-up tracked").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add Unreleased entries for the ImageTapEvent model and getResourceBytes API.
Also correct the getResourceBytes doc comment, which wrongly claimed it was
unimplemented on Android — it is implemented on iOS, Android, and Web.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* main: (54 commits)
  chore: clamp EPUBPreferences.fontSize to supported ratios
  chore(deps): bump actions/cache from 5 to 6
  chore(deps): bump actions/checkout from 6 to 7
  update CLAUDE.md require compilation check before marking something as done
  fix: broke PDF rendering on iOS
  minor update to pagebreak css
  label ebooks with media overlays or guided navigation as 'Ebook with audio'
  init web support for page-break
  TTSPreferences.skipPageBreaks is now TTSPreferences.pageBreakBehavior
  ci: fix gitignore for pub.dev publishing
  ci: fix gitignore for pub.dev publishing
  start iOS skip page number TTS
  feat(TTS): Skip page breaks
  chore(release): v0.1.1
  WIP: wrap html resource iterator
  feat: total progression duration (Notalib#163)
  ci: use temurin distribution for java builds
  ci(web): build the web reader bundle (/readiumReader.js) in CI
  ci: build webview helpers in all native pipelines via shared action
  ci(ios): align build-ios DerivedData cache key with integration test
  ...
* origin:
  feat: add CBZ and DiViNa comic support (Notalib#155)
@ddfreiling ddfreiling marked this pull request as ready for review June 30, 2026 12:54
* origin/main:
  refactor(ios): remove duplicated inline MO code
  ci(ios): pre-select used simulator
  chore: use fontSize ratio in tests
  chore: simplify android unit test packages
  fix(android): respect preventMOColumnBreaks on page-change reinjection
  fix(web): decoration colors applied wrong because of DecorationLayout.Bounds
  fix(web): inject MO column-break CSS directly into iframe DOM
  fix(android,web): prevent MO paragraph column-breaks for audio/visual sync
  fix(ios): prevent MO paragraph column-breaks for audio/visual sync

# Conflicts:
#	flutter_readium/web/src/ReadiumReader.ts
#	flutter_readium/web/src/navigators/FlutterEpubNavigator.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant