Skip to content

@responsivevoice/features

Dashboard feature plugins for ResponsiveVoice. Provides the FeatureManager lifecycle manager plus nine speech features: welcome message, speak- selected-text, speak-links, paragraph and tab navigation, inactivity message, end-of-page message, exit intent, and web player. Consumed primarily by @responsivevoice/core, which re-exports the full surface.

Defined in: src/features/end-of-page-message.ts:9

Speaks a configured message when the user scrolls to (or near) the bottom of the page. Extends SpeakOnceFeature.

  • SpeakOnceFeature
new EndOfPageMessageFeature(): EndOfPageMessageFeature;

EndOfPageMessageFeature

SpeakOnceFeature.constructor
readonly name: "speakEndPage" = 'speakEndPage';

Defined in: src/features/end-of-page-message.ts:11

Unique feature name matching the WebsiteFeatures key

SpeakOnceFeature.name
get active(): boolean;

Defined in: src/features/base-speak-once.ts:20

Whether this feature is currently active

boolean

SpeakOnceFeature.active
cleanup(): void;

Defined in: src/features/end-of-page-message.ts:39

Tear down the feature (remove event listeners, timers, etc.)

void

SpeakOnceFeature.cleanup
init(
config,
speak,
voice): void;

Defined in: src/features/end-of-page-message.ts:15

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

SpeakOnceFeature.init

Defined in: src/features/exit-intent.ts:8

Speaks a configured message when the pointer moves toward the top of the viewport — a signal the user may be about to leave the page.

new ExitIntentFeature(): ExitIntentFeature;

ExitIntentFeature

readonly name: "exitIntent" = 'exitIntent';

Defined in: src/features/exit-intent.ts:10

Unique feature name matching the WebsiteFeatures key

Feature.name

get active(): boolean;

Defined in: src/features/exit-intent.ts:16

Whether this feature is currently active

boolean

Whether this feature is currently active

Feature.active

cleanup(): void;

Defined in: src/features/exit-intent.ts:41

Tear down the feature (remove event listeners, timers, etc.)

void

Feature.cleanup

init(
config,
speak,
voice): void;

Defined in: src/features/exit-intent.ts:21

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

Feature.init


Defined in: src/manager.ts:10

Registers and orchestrates dashboard Feature plugins. Features are registered once, then activated together with a resolved website config + speak function; calling activate() again cleans up the previous batch before starting the new one, so re-config is idempotent.

new FeatureManager(): FeatureManager;

FeatureManager

activate(
config,
speak,
voice,
apiKey?): void;

Defined in: src/manager.ts:23

Initialize every registered feature with the given website config. If a previous activation is still live, cleans it up first.

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabled?boolean-
config.exitIntent.text?string | null-
config.paragraphNavigation?{ enabled: boolean; }-
config.paragraphNavigation.enabled?boolean-
config.speakEndPage?{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabled?boolean-
config.speakEndPage.text?string | null-
config.speakInactivity?{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabled?boolean-
config.speakInactivity.text?string | null-
config.speakLinks?{ enabled: boolean; }-
config.speakLinks.enabled?boolean-
config.speakSelectedText?{ enabled: boolean; }-
config.speakSelectedText.enabled?boolean-
config.webPlayer?{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls?{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brand?booleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progress?booleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skip?booleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speed?booleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.time?booleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabled?booleanTurn the player on.
config.webPlayer.layout?{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display?"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode?"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer?{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation?"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabled?booleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position?| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation?{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClick?booleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlight?booleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelector?stringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position?| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize?{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabled?booleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.exclude?string[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selector?stringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme?| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage?{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabled?boolean-
config.welcomeMessage.text?string | null-
config.welcomeMessageOnce?boolean-
speak?SpeakFn-
voice?{ name: string; pitch: number; rate: number; volume: number; }-
voice.name?string-
voice.pitch?number-
voice.rate?number-
voice.volume?number-
apiKey?string-

void

cleanup(): void;

Defined in: src/manager.ts:36

Tear down every currently-active feature (event listeners, timers, etc.).

void

get<T>(name): T | undefined;

Defined in: src/manager.ts:55

Returns the registered feature with the given name, or undefined if none is registered. Useful for callers that need to invoke feature- specific public methods (e.g. webPlayer.mount(...)).

Type ParameterDefault type
T extends FeatureFeature
ParameterType
namestring

T | undefined

getActiveFeatures(): string[];

Defined in: src/manager.ts:46

Names of features currently reporting active === true.

string[]

isActivated(): boolean;

Defined in: src/manager.ts:60

Whether activate() has been called (and not yet cleaned up).

boolean

register(feature): void;

Defined in: src/manager.ts:15

Register a feature plugin. Safe to call before or after activate().

ParameterType
featureFeature

void


Defined in: src/features/inactivity-message.ts:11

Speaks a configured message after a period of user inactivity on the page. Extends SpeakOnceFeature for per-page-load idempotency.

  • SpeakOnceFeature
new InactivityMessageFeature(): InactivityMessageFeature;

InactivityMessageFeature

SpeakOnceFeature.constructor
readonly name: "speakInactivity" = 'speakInactivity';

Defined in: src/features/inactivity-message.ts:13

Unique feature name matching the WebsiteFeatures key

SpeakOnceFeature.name
get active(): boolean;

Defined in: src/features/base-speak-once.ts:20

Whether this feature is currently active

boolean

SpeakOnceFeature.active
cleanup(): void;

Defined in: src/features/inactivity-message.ts:49

Tear down the feature (remove event listeners, timers, etc.)

void

SpeakOnceFeature.cleanup
init(
config,
speak,
voice): void;

Defined in: src/features/inactivity-message.ts:18

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

SpeakOnceFeature.init

Defined in: src/features/paragraph-navigation.ts:8

Provides keyboard navigation across paragraphs with speech feedback on the focused block.

new ParagraphNavigationFeature(): ParagraphNavigationFeature;

ParagraphNavigationFeature

readonly name: "paragraphNavigation" = 'paragraphNavigation';

Defined in: src/features/paragraph-navigation.ts:10

Unique feature name matching the WebsiteFeatures key

Feature.name

get active(): boolean;

Defined in: src/features/paragraph-navigation.ts:16

Whether this feature is currently active

boolean

Whether this feature is currently active

Feature.active

cleanup(): void;

Defined in: src/features/paragraph-navigation.ts:52

Tear down the feature (remove event listeners, timers, etc.)

void

Feature.cleanup

init(
config,
speak,
voice): void;

Defined in: src/features/paragraph-navigation.ts:21

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

Feature.init


Defined in: src/features/speak-links.ts:9

Speaks link text when the pointer hovers long enough to register hover intent. Uses a short dwell timer to avoid firing on incidental cursor movement.

new SpeakLinksFeature(): SpeakLinksFeature;

SpeakLinksFeature

readonly name: "speakLinks" = 'speakLinks';

Defined in: src/features/speak-links.ts:11

Unique feature name matching the WebsiteFeatures key

Feature.name

get active(): boolean;

Defined in: src/features/speak-links.ts:17

Whether this feature is currently active

boolean

Whether this feature is currently active

Feature.active

cleanup(): void;

Defined in: src/features/speak-links.ts:61

Tear down the feature (remove event listeners, timers, etc.)

void

Feature.cleanup

init(
config,
speak,
voice): void;

Defined in: src/features/speak-links.ts:22

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

Feature.init


Defined in: src/features/speak-selected-text.ts:7

Listens for text selection on the page and speaks the highlighted text. Activates on mouseup selection events.

new SpeakSelectedTextFeature(): SpeakSelectedTextFeature;

SpeakSelectedTextFeature

readonly name: "speakSelectedText" = 'speakSelectedText';

Defined in: src/features/speak-selected-text.ts:9

Unique feature name matching the WebsiteFeatures key

Feature.name

get active(): boolean;

Defined in: src/features/speak-selected-text.ts:14

Whether this feature is currently active

boolean

Whether this feature is currently active

Feature.active

cleanup(): void;

Defined in: src/features/speak-selected-text.ts:35

Tear down the feature (remove event listeners, timers, etc.)

void

Feature.cleanup

init(
config,
speak,
voice): void;

Defined in: src/features/speak-selected-text.ts:19

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

Feature.init


Defined in: src/features/tab-navigation.ts:7

Provides keyboard tab navigation with speech feedback on focused elements — the accessibility navigation mode.

new TabNavigationFeature(): TabNavigationFeature;

TabNavigationFeature

readonly name: "accessibilityNavigation" = 'accessibilityNavigation';

Defined in: src/features/tab-navigation.ts:9

Unique feature name matching the WebsiteFeatures key

Feature.name

get active(): boolean;

Defined in: src/features/tab-navigation.ts:14

Whether this feature is currently active

boolean

Whether this feature is currently active

Feature.active

cleanup(): void;

Defined in: src/features/tab-navigation.ts:62

Tear down the feature (remove event listeners, timers, etc.)

void

Feature.cleanup

init(
config,
speak,
voice): void;

Defined in: src/features/tab-navigation.ts:19

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

Feature.init


Defined in: src/features/web-player.ts:49

Web player orchestrator. Discovers articles matching selector at init time and binds an independent WebPlayerInstance to each one. Exposes mount for runtime mounts on dynamically-added elements (SPAs, lazy-loaded sections).

Coordinates single-active-narrator behaviour: when one instance starts speaking, it calls back via the constructor-injected onActivate so the orchestrator can preempt every other live instance.

new WebPlayerFeature(): WebPlayerFeature;

WebPlayerFeature

readonly name: "webPlayer" = 'webPlayer';

Defined in: src/features/web-player.ts:51

Unique feature name matching the WebsiteFeatures key

Feature.name

get active(): boolean;

Defined in: src/features/web-player.ts:60

Whether this feature is currently active

boolean

Whether this feature is currently active

Feature.active

cleanup(): void;

Defined in: src/features/web-player.ts:91

Tear down the feature (remove event listeners, timers, etc.)

void

Feature.cleanup

init(
config,
speak,
voice): void;

Defined in: src/features/web-player.ts:65

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabledboolean-
config.exitIntent.textstring | null-
config.paragraphNavigation{ enabled: boolean; }-
config.paragraphNavigation.enabledboolean-
config.speakEndPage{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabledboolean-
config.speakEndPage.textstring | null-
config.speakInactivity{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabledboolean-
config.speakInactivity.textstring | null-
config.speakLinks{ enabled: boolean; }-
config.speakLinks.enabledboolean-
config.speakSelectedText{ enabled: boolean; }-
config.speakSelectedText.enabledboolean-
config.webPlayer{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brandbooleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progressbooleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skipbooleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speedbooleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.timebooleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabledbooleanTurn the player on.
config.webPlayer.layout{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabledbooleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClickbooleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlightbooleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelectorstringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabledbooleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.excludestring[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selectorstringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabledboolean-
config.welcomeMessage.textstring | null-
config.welcomeMessageOnceboolean-
speakSpeakFn-
voice{ name: string; pitch: number; rate: number; volume: number; }-
voice.namestring-
voice.pitchnumber-
voice.ratenumber-
voice.volumenumber-

void

Feature.init

mount(selectorOrElement, overrides?):
| WebPlayerMountHandle
| null;

Defined in: src/features/web-player.ts:112

Imperatively mount a player on a dynamically-added element. Used by SPAs and any case where the target element wasn't in the DOM at rv.init() time.

overrides accepts the same shape as the init webPlayer config; leaf-merged over the init config (missing fields fall through to init defaults). Returns a handle whose unmount() tears this instance down.

No-op when the feature isn't active (init not called or enabled: false) or when the target element can't be resolved.

ParameterType
selectorOrElementstring | HTMLElement
overrides?WebPlayerMountOverrides

| WebPlayerMountHandle | null


Defined in: src/features/welcome-message.ts:12

Speaks a configured welcome message shortly after page load. Respects the welcomeMessageOnce flag via sessionStorage (keyed by API key hash) so repeat visits stay silent for the same text.

new WelcomeMessageFeature(): WelcomeMessageFeature;

WelcomeMessageFeature

readonly name: "welcomeMessage" = 'welcomeMessage';

Defined in: src/features/welcome-message.ts:14

Unique feature name matching the WebsiteFeatures key

Feature.name

get active(): boolean;

Defined in: src/features/welcome-message.ts:19

Whether this feature is currently active

boolean

Whether this feature is currently active

Feature.active

cleanup(): void;

Defined in: src/features/welcome-message.ts:66

Tear down the feature (remove event listeners, timers, etc.)

void

Feature.cleanup

init(
config,
speak,
voice,
apiKey?): void;

Defined in: src/features/welcome-message.ts:24

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabled?boolean-
config.exitIntent.text?string | null-
config.paragraphNavigation?{ enabled: boolean; }-
config.paragraphNavigation.enabled?boolean-
config.speakEndPage?{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabled?boolean-
config.speakEndPage.text?string | null-
config.speakInactivity?{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabled?boolean-
config.speakInactivity.text?string | null-
config.speakLinks?{ enabled: boolean; }-
config.speakLinks.enabled?boolean-
config.speakSelectedText?{ enabled: boolean; }-
config.speakSelectedText.enabled?boolean-
config.webPlayer?{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls?{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brand?booleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progress?booleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skip?booleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speed?booleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.time?booleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabled?booleanTurn the player on.
config.webPlayer.layout?{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display?"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode?"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer?{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation?"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabled?booleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position?| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation?{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClick?booleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlight?booleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelector?stringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position?| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize?{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabled?booleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.exclude?string[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selector?stringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme?| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage?{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabled?boolean-
config.welcomeMessage.text?string | null-
config.welcomeMessageOnce?boolean-
speak?SpeakFn-
voice?{ name: string; pitch: number; rate: number; volume: number; }-
voice.name?string-
voice.pitch?number-
voice.rate?number-
voice.volume?number-
apiKey?string-

void

Feature.init

Defined in: src/types.ts:64

Feature plugin interface. Each dashboard feature implements this.

readonly active: boolean;

Defined in: src/types.ts:68

Whether this feature is currently active

readonly name: string;

Defined in: src/types.ts:66

Unique feature name matching the WebsiteFeatures key

cleanup(): void;

Defined in: src/types.ts:72

Tear down the feature (remove event listeners, timers, etc.)

void

init(
config,
speak,
voice,
apiKey?): void;

Defined in: src/types.ts:70

Initialize and activate the feature

ParameterTypeDescription
config{ accessibilityNavigation: { enabled: boolean; }; exitIntent: { enabled: boolean; text: string | null; }; paragraphNavigation: { enabled: boolean; }; speakEndPage: { enabled: boolean; text: string | null; }; speakInactivity: { enabled: boolean; text: string | null; }; speakLinks: { enabled: boolean; }; speakSelectedText: { enabled: boolean; }; webPlayer: { controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }; welcomeMessage: { enabled: boolean; text: string | null; }; welcomeMessageOnce: boolean; }-
config.accessibilityNavigation{ enabled: boolean; }-
config.accessibilityNavigation.enabledboolean-
config.exitIntent{ enabled: boolean; text: string | null; }-
config.exitIntent.enabled?boolean-
config.exitIntent.text?string | null-
config.paragraphNavigation?{ enabled: boolean; }-
config.paragraphNavigation.enabled?boolean-
config.speakEndPage?{ enabled: boolean; text: string | null; }-
config.speakEndPage.enabled?boolean-
config.speakEndPage.text?string | null-
config.speakInactivity?{ enabled: boolean; text: string | null; }-
config.speakInactivity.enabled?boolean-
config.speakInactivity.text?string | null-
config.speakLinks?{ enabled: boolean; }-
config.speakLinks.enabled?boolean-
config.speakSelectedText?{ enabled: boolean; }-
config.speakSelectedText.enabled?boolean-
config.webPlayer?{ controls: { brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }; enabled: boolean; layout: { display: "inline" | "block"; mode: "fill" | "shrink"; }; miniPlayer: { animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }; navigation: { paragraphClick: boolean; paragraphHighlight: boolean; }; paragraphSelector: string; pitch?: number; position: | "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }; rate?: number; sanitize: { enabled: boolean; exclude: string[]; }; selector: string; theme: | "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }; voice?: | string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }; volume?: number; }-
config.webPlayer.controls?{ brand: boolean; progress: boolean; skip: boolean; speed: boolean; time: boolean; }Show or hide individual controls (progress bar, time, skip, speed, brand).
config.webPlayer.controls.brand?booleanResponsiveVoice brand icon (links to responsivevoice.org).
config.webPlayer.controls.progress?booleanProgress bar showing how much of the article has been narrated.
config.webPlayer.controls.skip?booleanSkip-back and skip-forward buttons (jump one paragraph at a time).
config.webPlayer.controls.speed?booleanPlayback speed button — click to cycle 0.5× through 3×.
config.webPlayer.controls.time?booleanElapsed / total time label (e.g. 0:42 / 3:15).
config.webPlayer.enabled?booleanTurn the player on.
config.webPlayer.layout?{ display: "inline" | "block"; mode: "fill" | "shrink"; }Main-pill width and page flow.
config.webPlayer.layout.display?"inline" | "block"How the player sits on the page: block takes its own line, inline flows with surrounding text.
config.webPlayer.layout.mode?"fill" | "shrink"Player width: shrink sizes to its content, fill stretches to fill its container.
config.webPlayer.miniPlayer?{ animation: "none" | "fade" | "slide" | "pop"; enabled: boolean; position: | "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }; }Floating mini-player. Boolean shorthand maps to { enabled, position } with the default 'bottom-left' corner.
config.webPlayer.miniPlayer.animation?"none" | "fade" | "slide" | "pop"Entrance/exit animation. none is instant; fade opacity-only; slide fades and slides from the docked corner; pop scales in with a bounce. Forced instant under prefers-reduced-motion: reduce.
config.webPlayer.miniPlayer.enabled?booleanShow the floating mini-player when the main player scrolls out of view.
config.webPlayer.miniPlayer.position?| "top-left" | "top-right" | "bottom-left" | "bottom-right" | { bottom?: string; left?: string; right?: string; top?: string; }Viewport placement — a corner keyword or a CSS-offset object.
config.webPlayer.navigation?{ paragraphClick: boolean; paragraphHighlight: boolean; }Toggle paragraph highlighting and click-to-jump navigation.
config.webPlayer.navigation.paragraphClick?booleanClick-to-jump: clicking any paragraph starts narration from that point.
config.webPlayer.navigation.paragraphHighlight?booleanVisual "currently playing" indicator on each narrated paragraph.
config.webPlayer.paragraphSelector?stringCSS selector for the elements inside the article to narrate (paragraphs, headings, list items).
config.webPlayer.pitch?numberVoice pitch (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.position?| "inline" | "before" | "after" | { at: "before" | "after" | "inside"; target: string; }Where the player is placed. Either a keyword (relative to selector) or an object specifying a custom mount target. - `'before'
config.webPlayer.rate?numberSpeech rate (0–2, default 1 = normal). Providers may normalize to their own scale.
config.webPlayer.sanitize?{ enabled: boolean; exclude: string[]; }Exclude non-narratable content (scripts, styles, controls, media) from narration.
config.webPlayer.sanitize.enabled?booleanStrip non-narratable nodes from narrated text.
config.webPlayer.sanitize.exclude?string[]Extra CSS selectors to exclude, composed with the built-in list.
config.webPlayer.selector?stringCSS selector for the article element to attach the player to (default: the first <article> on the page).
config.webPlayer.theme?| "neutral" | "responsivevoice" | { accent?: string; accentSoft?: string; bg?: string; border?: string; fg?: string; fill?: string; hover?: string; muted?: string; track?: string; }Colour theme — a preset name or a partial set of overrides merged over the 'neutral' defaults.
config.webPlayer.voice?| string | { flags?: string; regex: string; } | { gender?: "m" | "male" | "f" | "female"; isByok?: boolean; lang?: string; name?: string; provider?: string; }Voice to narrate this player. Same VoiceSelector grammar as core.speak()'s second argument — name string, regex literal, or structured query. Inherits the website default voice when omitted.
config.webPlayer.volume?numberVolume (0–1, default 1 = full).
config.welcomeMessage?{ enabled: boolean; text: string | null; }-
config.welcomeMessage.enabled?boolean-
config.welcomeMessage.text?string | null-
config.welcomeMessageOnce?boolean-
speak?SpeakFn-
voice?{ name: string; pitch: number; rate: number; volume: number; }-
voice.name?string-
voice.pitch?number-
voice.rate?number-
voice.volume?number-
apiKey?string-

void


Defined in: src/types.ts:15

Handle returned by SpeakFn. Lets features control the active utterance (pause / resume / cancel) without needing a direct reference to the core client.

Use pause / resume for "stop and continue from this exact point" — implemented via speechSynthesis.pause() / resume(), which reliably suspend the engine mid-word. Use cancel when abandoning the current utterance entirely (e.g. to jump to a different paragraph); that routes through speechSynthesis.cancel(), which is more aggressive but is the right move when followed immediately by a fresh speak() call.

cancel(): void;

Defined in: src/types.ts:17

Abort the active utterance and clear the queue. Does NOT fire onend.

void

pause(): void;

Defined in: src/types.ts:22

Suspend the active utterance in place. The engine keeps the utterance alive; SpeakHandle.resume will continue from the same word.

void

resume(): void;

Defined in: src/types.ts:24

Resume a paused utterance from the word it was suspended on.

void


Defined in: src/types.ts:46

Minimal parameter shape accepted by the injected SpeakFn. Matches the subset of SpeakParams from @responsivevoice/types that feature plugins actually need — keeping this local avoids a circular dependency on the full core type.

optional onend?: () => void;

Defined in: src/types.ts:56

Called when playback finishes naturally.

void

optional onerror?: (error) => void;

Defined in: src/types.ts:58

Called on engine errors.

ParameterType
errorError

void

optional onstart?: () => void;

Defined in: src/types.ts:54

Called when playback starts.

void

optional pitch?: number;

Defined in: src/types.ts:48

Speech pitch (0–2 scale, 1 = normal).

optional rate?: number;

Defined in: src/types.ts:50

Speech rate (0.1–1.5 scale, 1 = normal).

optional volume?: number;

Defined in: src/types.ts:52

Speech volume (0–1 scale).


Defined in: src/features/web-player.ts:19

Handle returned by WebPlayerFeature.mount. Caller invokes unmount() when the mounted element is removed from the DOM, or to tear the player down imperatively.

unmount(): void;

Defined in: src/features/web-player.ts:21

Tear down this mounted instance. Idempotent.

void

type SpeakFn = (text, voice?, params?) => SpeakHandle;

Defined in: src/types.ts:34

A speak function injected by core — features don't depend on core directly. This avoids the circular dependency corefeaturescore.

Returns a SpeakHandle the caller can use to cancel mid-utterance. Features that only need "fire and forget" playback may ignore the return.

ParameterType
textstring
voice?VoiceSelectorInput
params?SpeakParams

SpeakHandle


type WebPlayerMountOverrides = Omit<Partial<WebPlayerFeature>, "controls" | "navigation" | "layout"> & {
controls?: Partial<WebPlayerFeature["controls"]>;
layout?: Partial<WebPlayerFeature["layout"]>;
navigation?: Partial<WebPlayerFeature["navigation"]>;
};

Defined in: src/features/web-player.ts:30

Partial-override shape for WebPlayerFeature.mount. Top-level fields are optional and the three grouped sets (controls, navigation, layout) accept partial leaves so { controls: { brand: false } } keeps the rest of controls from the init config.

optional controls?: Partial<WebPlayerFeature["controls"]>;
optional layout?: Partial<WebPlayerFeature["layout"]>;
optional navigation?: Partial<WebPlayerFeature["navigation"]>;
function createFeatureManager(): FeatureManager;

Defined in: src/factory.ts:15

Creates a FeatureManager with all 9 built-in dashboard features registered.

FeatureManager