Skip to content

Feat: web platform feature parity#136

Draft
ddfreiling wants to merge 32 commits into
Notalib:mainfrom
ddfreiling:feat/web-feature-parity
Draft

Feat: web platform feature parity#136
ddfreiling wants to merge 32 commits into
Notalib:mainfrom
ddfreiling:feat/web-feature-parity

Conversation

@ddfreiling
Copy link
Copy Markdown
Member

Attempting to bring web platform up-to-speed.
Initial skeleton impl. done with help by AI, but tested and corrected from there.

It needs testing and further work:

  • Use new AudioNavigator by readium.
  • Implement TTS Navigator using WebSpeech API and mapping via the readium/speech repo.
  • Implement MediaOverlay Navigator similarly to how we did on native platforms.
  • Add Guided Navigation to support to above navigator
  • Fix TTS, it does not work in practice (traced to bug in @readium/shared, made upstream PR)
  • Implement goToProgression in all modals.
  • Validate that tocHref enricher and ToC skip/goTo works
  • Validate that disableSynchronization works for TTS and Overlays publications

ddfreiling and others added 30 commits May 23, 2026 16:58
Bumps the three ts-toolkit packages to latest:
  @readium/navigator             2.2.4 → 2.5.5
  @readium/navigator-html-injectables  2.2.1 → 2.4.2
  @readium/shared                2.1.1 → 2.2.0

Adapter changes required by the new API surface:
- EpubNavigator/WebPubNavigator listeners: add stub handlers for
  contentProtection, peripheral, and contextMenu (now required fields).
- EpubPreferences: add scrollPaddingLeft / scrollPaddingRight fields
  introduced in 2.5.x.
- helpers.ts: note that highlightSelection() is experimental and will
  be superseded once ts-toolkit PR #209 (Decorator API) merges.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Brings the web platform to parity with iOS and Android for timebased
playback features.  All three share the same Dart API and event streams
(onTimebasedPlayerStateChanged, onTextLocatorChanged) already wired up
for audiobooks on native.

AudioNavigator (audiobooks)
- New Audio/audioNavigator.ts wraps @readium/navigator AudioNavigator.
- Introduces AudioLocatorMapper — an optional hook applied by every
  state-emitting listener (play, pause, positionChanged, trackEnded,
  error, stalled) so all state transitions carry correctly-mapped
  locators, not just periodic poll events.
- openPublication now initialises AudioNavigator for audiobook profiles
  (was a TODO stub).

TTS (Audio/ttsNavigator.ts + Audio/ttsPreferences.ts)
- WebTTSEngine walks EPUB text via PublicationContentIterator +
  HTMLResourceContentIterator and speaks each TextElement via the
  browser SpeechSynthesis API.
- Sub-utterance onboundary granularity (word/sentence) with 100 ms
  throttle; degrades silently when unavailable (Firefox, some mobile).
- Voice list: { identifier, name, language, networkRequired } —
  gender/quality enriched by ReaderTTSVoiceUtils in the platform
  interface via readium/speech voice data.
- Per-language voice map and global voice override.
- TODO(#209): visual word/sentence highlight deferred until ts-toolkit
  Decorator API (PR #209) merges.

Media Overlay / Sync Narration
  (Audio/syncNarration.ts + Audio/mediaOverlayNavigator.ts)
- Parses Readium Sync Narration JSON alternates
  (application/vnd.readium.narration+json) into SyncNarrationItem[].
- Builds a synthetic audiobook reading order (one Link per unique audio
  file) and reuses AudioNavigator via AudioLocatorMapper to emit
  text-based locators on every state event — matching iOS/Android.
- audioSeekBy wired through AudioNavigator.jump(seconds).

Dart wiring
- js_publication_channel.dart: JS interop + static wrappers for the
  full timebased playback API (play, pause, resume, stop, next,
  previous, seekBy, setAudioPreferences, ttsEnable,
  ttsGetAvailableVoices, ttsSetVoice, ttsSetPreferences, audioEnable).
- flutter_readium_web.dart: replaces UnimplementedError stubs for all
  playback methods with real JsPublicationChannel calls.
- readium_webview.dart: registers updateTimebasedPlayerState JS export
  so AudioNavigator/TTS state events reach the Dart stream.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Webpack 5's default splitChunks optimisation, combined with the
pre-split @readium/* packages (locale and ReadiumCSS variant files),
was generating 30+ chunk files alongside the main bundle.  These had
to be committed and served as separate assets.

Fix: add splitChunks: false and output.asyncChunks: false to
webpack.config.js.  asyncChunks: false (Webpack 5.84+) inlines all
dynamic imports — including the ReadiumCSS injection files that
@readium/navigator loads lazily — into the single bundle.  The
trade-off is a slightly larger readiumReader.js (~1.2 MiB dev build)
with no practical penalty since the webview loads everything up front.

Deleted all stale chunk files from lib/helpers/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Link.fromJsonArray: guard against non-Map elements (e.g. JSON-LD
  @context strings embedded in link arrays) instead of hard-casting,
  preventing a TypeError on manifests that follow the JSON-LD convention.
- MediaType: add readiumNarration constant for
  application/vnd.readium.narration+json (Readium Sync Narration format).
- Publication.containsMediaOverlays: extend to recognise both
  vnd.syncnarr+json and vnd.readium.narration+json alternates, so
  isAudioBook returns true for Sync Narration EPUBs and the example app's
  play-button dispatch routes them to audioEnable correctly.
- FlutterReadiumWebPlugin: override setLogLevel (was throwing
  UnimplementedError; the web layer needs only ReadiumLog.setLevel).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PlayerControlsBloc: add SeekRelative event dispatching audioSeekBy so
  ±10 s relative seeks can be triggered from the UI.
- PlayerControlsWidget: show replay-10 / forward-10 buttons when audio
  (audiobook or Media Overlay) is active; buttons carry ValueKeys for
  marionette automation.
- webManifestList.json: replace placeholder URLs with a Nota EPUB and
  audio-only webpub for smoke-testing TTS and Media Overlay on web.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add a 'build_web' task that runs bin/update_web_example (builds the TS
bundle and copies readiumReader.js into example/web/) and an
'example (web)' launch configuration that targets Chrome, running the
build task as a pre-launch step.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
browser scrolling in publication emits updates with 60hz
timing between calls during init was all wrong before, now uses a ready promise for when first track is loaded. Should avoid observed race-conditions
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