import { TTSProvider } from '../types/types'; import { MarkdownToSpeechOptions } from '../utils/markdown-to-speech'; export type ReadAloudState = "idle" | "loading" | "speaking" | "paused"; export interface ReadAloudChunk { /** The text of the chunk currently being spoken. */ text: string; /** Zero-based position of this chunk in the queue. */ index: number; /** Total number of chunks queued for this utterance. */ total: number; } /** Turns a chunk of text into playable audio. Return `null` to defer to `speechSynthesis`. */ export type SynthesizeFn = (text: string, signal: AbortSignal) => Promise; export interface ReadAloudOptions { /** TTS provider passed to the endpoint. Default `kokoro`. */ provider?: TTSProvider; /** Provider-specific voice id. Default `af_heart`. */ voice?: string; /** HTTP route that synthesizes text. Default `/api/speech/tts`. */ endpoint?: string; /** * Target chunk size in characters. Smaller starts talking sooner but makes the * seams between chunks more audible. Default 240. */ maxChunkLength?: number; /** * How to read the text handed to `speak()`. `auto` (default) converts text * that looks like Markdown so "##" and "**" are not read out; `markdown` * always converts; `text` never does. */ format?: "auto" | "markdown" | "text"; /** Markdown conversion options, when the text is read as Markdown. */ markdown?: MarkdownToSpeechOptions; /** Override synthesis entirely (tests, a bring-your-own-TTS host app). */ synthesize?: SynthesizeFn; /** Called as each chunk starts playing. */ onChunk?: (chunk: ReadAloudChunk) => void; /** Called on every state transition. */ onStateChange?: (state: ReadAloudState) => void; /** Called when playback ends, either by running out of chunks or by `stop()`. */ onEnd?: (reason: "finished" | "stopped") => void; /** Called when synthesis or playback fails irrecoverably. */ onError?: (error: Error) => void; } export declare class ReadAloudController { private options; private state; private abortController; private audio; private objectUrl; /** Set once the endpoint has proved unreachable, so later chunks skip the retry. */ private endpointUnavailable; constructor(options?: ReadAloudOptions); /** Replace the options (voice, callbacks, …) without discarding playback state. */ setOptions(options: ReadAloudOptions): void; getState(): ReadAloudState; isActive(): boolean; /** * Speak `text`, cancelling anything already playing. Resolves when playback * finishes or is stopped — it never rejects; failures go to `onError`. */ speak(text: string): Promise; pause(): void; resume(): void; /** Stop playback and drop any queued chunks. Safe to call when already idle. */ stop(): void; /** * Converts Markdown to spoken words before chunking, so a document read out * of an editor does not have its syntax read back to the listener. */ private toSpeakableText; private setState; private cleanup; private synthesize; private fetchAudio; private playChunk; private playAudioBlob; private playWithSpeechSynthesis; }