All notable changes to this project are documented here. Format follows Keep a Changelog; this project follows Semantic Versioning.
endnotes section no longer skips its own location — previously it was suppressed there unconditionally (since a noteref already speaks it inline), leaving the section’s list silent.noteref no longer merges its Start of the footnote./End of the footnote. contextualization into the same utterance as its content — the note’s real content is now its own separate utterance, consistent with every other block role, instead of being incorrectly flagged synthetic. With inlineContextualization: true, the note is still spoken inline as one merged utterance.biblioref and glossary added to skippableRoles and to skippedAtVerbosity’s none/few/some presets — both are now skippable and, under a preset, extracted only at most.ExtractUtterancesOptions.contextualizationLocale — pick which shipped catalog locale to use.ExtractUtterancesOptions.contextualization — for a custom or overridden catalog: override wording, shape, and params per call.ReadiumSpeechNavigatorConfiguration.contextualizationOverrides — the same overrides, set once for a navigator.ReadiumSpeechUtterance.locate — options for createLocator() to build a locator for the utterance’s source.ReadiumSpeechUtterance.synthetic — true when the utterance’s text isn’t copied from the source, so word-level highlighting can be skipped (element-level highlighting via locate still works).makeGnd()/parseMarkup() can generate textrefs from a live document. See GuidedNavigation.md.cell/rowheader/row contextualizations carry positional context (column header, row number).extractUtterances(), extractUtterancesWithSources(), loadGndContent(), and submitPreferences() are now async.LocatorOptions restructured around text/cssSelector/domRange/fragment.skippableAtVerbosity renamed to skippedAtVerbosity.setRate()/setPitch()/setVolume() now restart the current utterance so changes apply immediately, not on the next one.<th> resolves to columnheader/rowheader instead of always cell.setRate()/setPitch()/setVolume() and playback-rate clamping no longer propagate NaN/±Infinity.defaultAnnouncements, Announcement, AnnouncementKey, AnnouncementPair, Announcements, RoleAnnouncement, isAnnouncementPair — superseded by the JSON contextualization catalog (defaultContextualizations, Contextualizations, ContextualizationEntry).ExtractUtterancesOptions.announcements — superseded by contextualization.contextualizations.FallbackSpeechEngine no longer picks a fallback voice that requires network access unless navigator.onLine is confirmed true, defaulting to offline-only when connectivity is unknown — previously it could pick an online voice while swapping away from a recoverable failure, risking the same failure again.FallbackEngineProvider / FallbackSpeechEngine, wrapping a primary and a fallback ReadiumSpeechEngineProvider as one. onFailure (default "fallback") controls the behavior: "fallback" swaps to the fallback on a recoverable playback failure (mid-playback included) and stays there; "fallbackAndRecover" additionally polls the primary and swaps back once it’s reachable again; "error" never swaps, surfacing failures as-is. No registry involved. See FallbackEngine.md."enginefallback" / "enginerecovered" events on ReadiumSpeechPlaybackEvent, emitted by FallbackSpeechEngine when it swaps engines in either direction.ReadiumSpeechPlaybackEngine.loadUtterances() takes an optional startIndex hint, so an engine that pre-buffers ahead of playback (e.g. SpeechServerEngine) warms the utterances playback will actually resume at instead of always index 0.ReadiumSpeechEngineProvider.getVoices() takes an optional forceRefresh parameter to bypass a provider’s own voice cache — used by FallbackSpeechEngine’s health check so a stale cache can’t report the primary reachable when it isn’t.SpeechServerEngineOptions.timeoutMs, a grace period tolerated after the playback buffer is projected to run dry before a still-pending /synthesize chunk is declared stalled (SpeechServerStallError, 408) and aborted. Default undefined: a stalled request waits forever, same as before this option existed. See Stall detection.SpeechServerNetworkError, thrown when a request to the server never reaches the network at all — distinguishes an actual connectivity failure from an unrelated TypeError thrown later while reading a response that did arrive.ErrorEventDetail, the { message, recoverable, ... } shape an engine’s "error" event detail is expected to follow.SpeechServerAudioDecodeError, thrown when a /synthesize response arrives and parses fine but its audio payload fails to decode.recoverable on every SpeechServerEngine error event’s detail: true when the server never responded at all (network failure or a stall), false when it responded but rejected the request or the audio payload failed to decode. Used by FallbackSpeechEngine to decide whether swapping engines could help.SpeechServerEngine’s "error" event detail always has at least { message, recoverable } now; { status, type, title, instance } is only present for a Problem Details response from the server.SpeechServerEngine catches exceptions thrown by its own event listeners instead of letting one throw out of emitEvent() and abort the others.WebSpeechEngine.loadUtterances() called back-to-back while already "ready" now emits "ready" each time — it previously went through the same diff-based state setter as everything else, which swallowed a same-state transition.SpeechServerEngine/SpeechServerEngineProvider no longer double-apply speed on voices whose provider already applies it server-side (e.g. ElevenLabs). speech-server stopped sending controls per voice on GET /voices, moving it to GET /service’s providers[] only — voice mapping now merges each voice’s controls from there instead of a field the server no longer sends.autoPause preference ("none" | "utterance" | "block", default "none") — fully pauses playback between utterances or blocks (fires pause, navigator.getState() becomes "paused") instead of continuing on its own; playback resumes only when play() is called. See Preferences.md.SpeechDefaults.language default is now "block-level", was "always".ReadiumSpeechUtterance drops startsNewBlock. Block-boundary info is now internal to extraction — autoPause: "block" only has effect on content loaded via loadGndContent(); content loaded via loadContent() never triggers it.pauseDuration always delays the next automatic continuation now that pauseScope is gone — it’s no longer scoped to a subset of transitions.SpeechPreferences.pauseScope / SpeechDefaults.pauseScope, added in 0.6.0. It was meant to implement automatic pausing between utterances/paragraphs but only ever delayed an automatic continuation rather than actually pausing — replaced by autoPause.ReadiumSpeechNavigator (submitPreferences(), settings, preferencesEditor), covering verbosity presets and prosody (pauseDuration, pauseScope, rate, pitch, volume). See Preferences.md.ReadiumSpeechNavigator.loadGndContent(), retaining a Guided Navigation tree so preferences can re-run extraction on it.ReadiumSpeechUtterance.startsNewBlock, marking utterances that begin a new block-level element — used by pauseScope: "block".ExtractUtterancesOptions.contextualize is now GndRole[] (which roles announce), replacing the previous on/off boolean; default is now nothing contextualized.ExtractUtterancesOptions.interruptSentence renamed to inlineContextualization.ReadiumSpeechNavigatorContract drops setRate/getRate/setPitch/getPitch/setVolume/getVolume — use the Preferences API instead.utterances.json now pairs one result with every option-set that produces it (options is an array), instead of repeating identical results per option-set — see fixtures/README.md.SpeechServerEngine / SpeechServerEngineProvider, a second ReadiumSpeechPlaybackEngine implementation backed by a Readium Speech Server HTTP service — with utterance prefetching, chunk-streaming for long/over-limit utterances, gapless Web Audio API scheduling, format/bitrate selection, and RFC 9457 error handling. See SpeechServerEngine.md.ReadiumSpeechProviderRegistry, for registering multiple ReadiumSpeechEngineProviders (e.g. WebSpeech and speech-server) side by side and querying voices/creating engines across all of them. See ProviderRegistry.md.ReadiumSpeechVoice.identifier and ReadiumSpeechVoice.controls (which playback controls a server-sourced voice actually honors), and "server" added to TSource.ReadiumSpeechNavigator (renamed from WebSpeechReadAloudNavigator) is now engine-agnostic: its constructor requires an explicit ReadiumSpeechPlaybackEngine (e.g. new ReadiumSpeechNavigator(new WebSpeechEngine())), instead of defaulting to WebSpeech.WebSpeechEngineProvider.getVoices() no longer requires an engine to have been created first.setSpeakInContentLanguage(enabled) / getSpeakInContentLanguage() on ReadiumSpeechNavigator and ReadiumSpeechPlaybackEngine: when enabled, each utterance is spoken in the best available voice for its own language, falling back to the selected/default voice when none matches (never racing ahead of voice loading and picking the wrong-language voice)."languagefallback" playback event, fired when no voice matches an utterance’s content language (detail: { language, reason: "no-matching-voice" }).WebSpeechVoiceManager.initialize({ languages }) can now be called again on an already-initialized singleton to broaden it to additional languages, instead of being silently ignored.makeGnd, extracting a Guided Navigation (GND) JSON tree from HTML/XHTML.extractUtterances, walking a GND tree into a flat, ordered list of ReadiumSpeechUtterances.@readium/decorator for highlighting, via a new wrapper API that simplifies common usage (including Locator handling).ReadiumSpeechVoice is now fully typed, dropping the any fallback (#48).textContent coercion in demos; README quickstart example.Initial release.