diff --git a/lib/features/chat/providers/text_to_speech_provider.dart b/lib/features/chat/providers/text_to_speech_provider.dart index 36bcb03..7992596 100644 --- a/lib/features/chat/providers/text_to_speech_provider.dart +++ b/lib/features/chat/providers/text_to_speech_provider.dart @@ -219,8 +219,8 @@ class TextToSpeechController extends Notifier { // Prepare sentence split for highlighting final cleanText = MarkdownToText.convert(text); - final sentences = _splitForTts(cleanText); - final offsets = _computeOffsets(sentences); + final sentences = _service.splitTextForSpeech(cleanText); + final offsets = _computeOffsets(cleanText, sentences); state = state.copyWith( status: TtsPlaybackStatus.loading, @@ -265,30 +265,24 @@ class TextToSpeechController extends Notifier { } } - List _splitForTts(String text) { - final normalized = text.replaceAll(RegExp(r"\s+"), ' ').trim(); - if (normalized.isEmpty) return const []; - final parts = []; - final sentenceRegex = RegExp(r"(.+?[\.!?]+)(\s+|\$)"); - int index = 0; - for (final match in sentenceRegex.allMatches('$normalized ')) { - final s = match.group(1) ?? ''; - if (s.trim().isNotEmpty) parts.add(s.trim()); - index = match.end; - } - if (index < normalized.length) { - final tail = normalized.substring(index).trim(); - if (tail.isNotEmpty) parts.add(tail); - } - return parts; - } - - List _computeOffsets(List sentences) { + List _computeOffsets(String source, List sentences) { + if (sentences.isEmpty) return const []; final offsets = []; - int acc = 0; - for (final s in sentences) { - offsets.add(acc); - acc += s.length + 1; // assume a space or punctuation between + var cursor = 0; + for (final sentence in sentences) { + final chunk = sentence.trim(); + if (chunk.isEmpty) { + offsets.add(cursor); + continue; + } + final index = source.indexOf(chunk, cursor); + if (index == -1) { + offsets.add(cursor); + cursor += chunk.length; + } else { + offsets.add(index); + cursor = index + chunk.length; + } } return offsets; } diff --git a/lib/features/chat/services/text_to_speech_service.dart b/lib/features/chat/services/text_to_speech_service.dart index c807475..7c8f4b4 100644 --- a/lib/features/chat/services/text_to_speech_service.dart +++ b/lib/features/chat/services/text_to_speech_service.dart @@ -657,6 +657,12 @@ class TextToSpeechService { ); } + /// Splits [text] into the chunks used for playback sequencing. + /// + /// This mirrors the server-side streaming behavior so UI consumers can stay + /// in sync with sentence indices reported during playback. + List splitTextForSpeech(String text) => _splitForTts(text); + Future _onAudioComplete() async { final session = _session; // If there are more expected chunks