@pie-players/pie-assessment-toolkit 0.3.51 → 0.3.53
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/PieAssessmentToolkit.custom-element.js +219 -24
- package/dist/context/assessment-toolkit-context.d.ts +2 -0
- package/dist/context/assessment-toolkit-context.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/services/HighlightCoordinator.d.ts +6 -0
- package/dist/services/HighlightCoordinator.js +20 -5
- package/dist/services/HighlightCoordinator.js.map +1 -1
- package/dist/services/TTSService.d.ts +17 -0
- package/dist/services/TTSService.js +238 -21
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/createDefaultToolRegistry.d.ts +2 -1
- package/dist/services/createDefaultToolRegistry.js +2 -1
- package/dist/services/createDefaultToolRegistry.js.map +1 -1
- package/dist/services/interfaces.d.ts +11 -0
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/tool-config-defaults.d.ts +11 -0
- package/dist/services/tool-config-defaults.js +18 -0
- package/dist/services/tool-config-defaults.js.map +1 -1
- package/dist/services/tts/highlight-target-resolver.d.ts +17 -0
- package/dist/services/tts/highlight-target-resolver.js +2 -0
- package/dist/services/tts/highlight-target-resolver.js.map +1 -0
- package/package.json +7 -7
|
@@ -74,6 +74,7 @@ export class TTSService {
|
|
|
74
74
|
currentProvider = null;
|
|
75
75
|
provider = null;
|
|
76
76
|
highlightCoordinator = null;
|
|
77
|
+
highlightTargetResolverProvider = null;
|
|
77
78
|
catalogResolver = null;
|
|
78
79
|
state = PlaybackState.IDLE;
|
|
79
80
|
ttsConfig = {};
|
|
@@ -86,6 +87,7 @@ export class TTSService {
|
|
|
86
87
|
currentBoundaryOffset = 0;
|
|
87
88
|
activeWordBoundaryOffset = 0;
|
|
88
89
|
seekSegments = [];
|
|
90
|
+
playbackChunks = [];
|
|
89
91
|
sentenceHighlightSegments = [];
|
|
90
92
|
currentSeekSegmentIndex = 0;
|
|
91
93
|
activeSentenceStartOffset = null;
|
|
@@ -210,6 +212,17 @@ export class TTSService {
|
|
|
210
212
|
setHighlightCoordinator(coordinator) {
|
|
211
213
|
this.highlightCoordinator = coordinator;
|
|
212
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Set a late-bound provider for optional host TTS highlight target remapping.
|
|
217
|
+
*/
|
|
218
|
+
setHighlightTargetResolverProvider(provider) {
|
|
219
|
+
this.highlightTargetResolverProvider = provider;
|
|
220
|
+
return () => {
|
|
221
|
+
if (this.highlightTargetResolverProvider === provider) {
|
|
222
|
+
this.highlightTargetResolverProvider = null;
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
213
226
|
/**
|
|
214
227
|
* Set accessibility catalog resolver for spoken content
|
|
215
228
|
*
|
|
@@ -221,6 +234,145 @@ export class TTSService {
|
|
|
221
234
|
setCatalogResolver(resolver) {
|
|
222
235
|
this.catalogResolver = resolver;
|
|
223
236
|
}
|
|
237
|
+
getHighlightResolverRuntime() {
|
|
238
|
+
let provided = null;
|
|
239
|
+
try {
|
|
240
|
+
provided = this.highlightTargetResolverProvider?.() ?? null;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
provided = null;
|
|
244
|
+
}
|
|
245
|
+
const providedContext = provided?.context ?? {};
|
|
246
|
+
const fallbackScope = typeof HTMLElement !== "undefined" &&
|
|
247
|
+
this.currentContentElement instanceof HTMLElement
|
|
248
|
+
? this.currentContentElement
|
|
249
|
+
: null;
|
|
250
|
+
return {
|
|
251
|
+
context: {
|
|
252
|
+
...providedContext,
|
|
253
|
+
scopeElement: providedContext.scopeElement ?? fallbackScope,
|
|
254
|
+
},
|
|
255
|
+
resolver: provided?.resolver ?? null,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
getNodeElement(node) {
|
|
259
|
+
if (!node)
|
|
260
|
+
return null;
|
|
261
|
+
return node.nodeType === Node.ELEMENT_NODE
|
|
262
|
+
? node
|
|
263
|
+
: node.parentElement;
|
|
264
|
+
}
|
|
265
|
+
isElementWithinScope(element, scope) {
|
|
266
|
+
if (!scope)
|
|
267
|
+
return true;
|
|
268
|
+
return element === scope || scope.contains(element);
|
|
269
|
+
}
|
|
270
|
+
isRangeWithinScope(range, scope) {
|
|
271
|
+
const startElement = this.getNodeElement(range.startContainer);
|
|
272
|
+
const endElement = this.getNodeElement(range.endContainer);
|
|
273
|
+
if (!startElement || !endElement)
|
|
274
|
+
return false;
|
|
275
|
+
return (this.isElementWithinScope(startElement, scope) &&
|
|
276
|
+
this.isElementWithinScope(endElement, scope));
|
|
277
|
+
}
|
|
278
|
+
resolveWordHighlightRange(nativeRange) {
|
|
279
|
+
const { context, resolver } = this.getHighlightResolverRuntime();
|
|
280
|
+
const scope = context.scopeElement ?? this.currentContentElement;
|
|
281
|
+
try {
|
|
282
|
+
const resolved = resolver?.resolveWordRange?.(nativeRange, context);
|
|
283
|
+
if (resolved && this.isRangeWithinScope(resolved, scope)) {
|
|
284
|
+
return { range: resolved, remapped: !sameRange(nativeRange, resolved) };
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
// Fail open: custom resolver failures must not interrupt TTS playback.
|
|
289
|
+
}
|
|
290
|
+
return { range: nativeRange, remapped: false };
|
|
291
|
+
}
|
|
292
|
+
resolveSentenceHighlightTargets(nativeRanges) {
|
|
293
|
+
const { context, resolver } = this.getHighlightResolverRuntime();
|
|
294
|
+
const scope = context.scopeElement ?? this.currentContentElement;
|
|
295
|
+
try {
|
|
296
|
+
const resolved = resolver?.resolveSentenceRanges?.(nativeRanges, context);
|
|
297
|
+
if (!resolved)
|
|
298
|
+
return { ranges: nativeRanges, elements: [] };
|
|
299
|
+
const ranges = [];
|
|
300
|
+
const elements = [];
|
|
301
|
+
for (const target of resolved) {
|
|
302
|
+
if (typeof Range !== "undefined" && target instanceof Range) {
|
|
303
|
+
if (!this.isRangeWithinScope(target, scope)) {
|
|
304
|
+
return { ranges: nativeRanges, elements: [] };
|
|
305
|
+
}
|
|
306
|
+
ranges.push(target);
|
|
307
|
+
continue;
|
|
308
|
+
}
|
|
309
|
+
if (typeof Element !== "undefined" && target instanceof Element) {
|
|
310
|
+
if (!this.isElementWithinScope(target, scope)) {
|
|
311
|
+
return { ranges: nativeRanges, elements: [] };
|
|
312
|
+
}
|
|
313
|
+
elements.push(target);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
return { ranges: nativeRanges, elements: [] };
|
|
317
|
+
}
|
|
318
|
+
return { ranges, elements };
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
// Fail open: custom resolver failures must not interrupt TTS playback.
|
|
322
|
+
return { ranges: nativeRanges, elements: [] };
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
createTextRange(node, startOffset, endOffset) {
|
|
326
|
+
if (typeof document === "undefined")
|
|
327
|
+
return null;
|
|
328
|
+
if (typeof document.createRange !== "function")
|
|
329
|
+
return null;
|
|
330
|
+
try {
|
|
331
|
+
const range = document.createRange();
|
|
332
|
+
range.setStart(node, startOffset);
|
|
333
|
+
range.setEnd(node, endOffset);
|
|
334
|
+
if (range.startContainer !== node || range.endContainer !== node) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
return range;
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
paintTTSWordRange(range) {
|
|
344
|
+
if (!this.highlightCoordinator)
|
|
345
|
+
return;
|
|
346
|
+
try {
|
|
347
|
+
if (range.startContainer === range.endContainer &&
|
|
348
|
+
range.startContainer.nodeType === Node.TEXT_NODE) {
|
|
349
|
+
this.highlightCoordinator.highlightTTSWord(range.startContainer, range.startOffset, range.endOffset);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
// Fall through to generic range painting for lightweight test doubles.
|
|
355
|
+
}
|
|
356
|
+
this.highlightCoordinator.highlightRange(range, HighlightType.TTS_WORD, HighlightColor.YELLOW);
|
|
357
|
+
}
|
|
358
|
+
paintTTSSentenceRanges(nativeRanges) {
|
|
359
|
+
if (!this.highlightCoordinator)
|
|
360
|
+
return;
|
|
361
|
+
const { ranges, elements } = this.resolveSentenceHighlightTargets(nativeRanges);
|
|
362
|
+
if (elements.length > 0 &&
|
|
363
|
+
ranges.length === 0 &&
|
|
364
|
+
this.highlightCoordinator.highlightTTSSentenceElements) {
|
|
365
|
+
this.highlightCoordinator.highlightTTSSentenceElements(elements);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
const paintedRanges = [...ranges];
|
|
369
|
+
for (const element of elements) {
|
|
370
|
+
const elementRange = document.createRange();
|
|
371
|
+
elementRange.selectNodeContents(element);
|
|
372
|
+
paintedRanges.push(elementRange);
|
|
373
|
+
}
|
|
374
|
+
this.highlightCoordinator.highlightTTSSentence(paintedRanges);
|
|
375
|
+
}
|
|
224
376
|
/**
|
|
225
377
|
* Get provider capabilities
|
|
226
378
|
*/
|
|
@@ -554,6 +706,24 @@ export class TTSService {
|
|
|
554
706
|
.filter((offset) => offset > 0);
|
|
555
707
|
return this.splitSegmentsAtBoundaries(grammaticalSegments, structuralBoundaries, args.contentToSpeak);
|
|
556
708
|
}
|
|
709
|
+
createSeekSegmentsFromSpeechChunks(chunks) {
|
|
710
|
+
let offset = 0;
|
|
711
|
+
return chunks.map((chunk) => {
|
|
712
|
+
const speechText = chunk.plainFallback?.speechText ||
|
|
713
|
+
(this.hasExplicitBreakSemantics(chunk.speechText)
|
|
714
|
+
? chunk.visibleText
|
|
715
|
+
: chunk.speechText) ||
|
|
716
|
+
chunk.visibleText;
|
|
717
|
+
const text = normalizeTextForSpeech(speechText) || chunk.visibleText || "";
|
|
718
|
+
const segment = {
|
|
719
|
+
text,
|
|
720
|
+
startOffset: offset,
|
|
721
|
+
pauseMsAfter: 0,
|
|
722
|
+
};
|
|
723
|
+
offset += Math.max(1, text.length + 1);
|
|
724
|
+
return segment;
|
|
725
|
+
});
|
|
726
|
+
}
|
|
557
727
|
getCurrentSeekSegmentIndex() {
|
|
558
728
|
if (this.seekSegments.length === 0)
|
|
559
729
|
return 0;
|
|
@@ -629,7 +799,7 @@ export class TTSService {
|
|
|
629
799
|
const range = document.createRange();
|
|
630
800
|
range.setStart(start.node, start.offset);
|
|
631
801
|
range.setEnd(end.node, end.offset + 1);
|
|
632
|
-
this.
|
|
802
|
+
this.paintTTSSentenceRanges([range]);
|
|
633
803
|
this.activeSentenceStartOffset = startOffset;
|
|
634
804
|
}
|
|
635
805
|
getSegmentIndexForOffset(segments, offset) {
|
|
@@ -743,8 +913,10 @@ export class TTSService {
|
|
|
743
913
|
!usedCatalogSpoken &&
|
|
744
914
|
!hasExplicitBreaks &&
|
|
745
915
|
speechMatchesVisibleText;
|
|
746
|
-
this.
|
|
747
|
-
|
|
916
|
+
this.playbackChunks = speechChunks?.length ? speechChunks : [];
|
|
917
|
+
this.seekSegments = this.playbackChunks.length
|
|
918
|
+
? this.createSeekSegmentsFromSpeechChunks(this.playbackChunks)
|
|
919
|
+
: hasExplicitBreaks || !speechMatchesVisibleText
|
|
748
920
|
? []
|
|
749
921
|
: shouldUsePlan && this.currentContentElement
|
|
750
922
|
? this.createSpeechPlan(this.currentContentElement, normalizedText)
|
|
@@ -1155,6 +1327,7 @@ export class TTSService {
|
|
|
1155
1327
|
this.currentContentElement = options?.contentElement || null;
|
|
1156
1328
|
this.lastError = null;
|
|
1157
1329
|
this.currentBoundaryOffset = 0;
|
|
1330
|
+
this.playbackChunks = [];
|
|
1158
1331
|
this.currentSeekSegmentIndex = 0;
|
|
1159
1332
|
this.activeSentenceStartOffset = null;
|
|
1160
1333
|
}
|
|
@@ -1174,7 +1347,7 @@ export class TTSService {
|
|
|
1174
1347
|
try {
|
|
1175
1348
|
const range = document.createRange();
|
|
1176
1349
|
range.selectNodeContents(this.currentContentElement);
|
|
1177
|
-
this.
|
|
1350
|
+
this.paintTTSSentenceRanges([range]);
|
|
1178
1351
|
}
|
|
1179
1352
|
catch {
|
|
1180
1353
|
// No-op fallback when DOM range creation fails.
|
|
@@ -1198,7 +1371,13 @@ export class TTSService {
|
|
|
1198
1371
|
if (highlightRange && this.highlightCoordinator) {
|
|
1199
1372
|
const highlightText = highlightRange.node.textContent?.substring(highlightRange.start, highlightRange.end) || "";
|
|
1200
1373
|
this.debugLog(`[TTSService] Highlighting "${highlightText}" (word: "${word}") at position ${globalIndex}`);
|
|
1201
|
-
this.
|
|
1374
|
+
const nativeRange = this.createTextRange(highlightRange.node, highlightRange.start, highlightRange.end);
|
|
1375
|
+
if (nativeRange) {
|
|
1376
|
+
this.paintTTSWordRange(this.resolveWordHighlightRange(nativeRange).range);
|
|
1377
|
+
}
|
|
1378
|
+
else {
|
|
1379
|
+
this.highlightCoordinator.highlightTTSWord(highlightRange.node, highlightRange.start, highlightRange.end);
|
|
1380
|
+
}
|
|
1202
1381
|
}
|
|
1203
1382
|
else {
|
|
1204
1383
|
console.warn(`[TTSService] Could not find highlight range for position ${globalIndex}, length ${wordLength}`);
|
|
@@ -1218,7 +1397,7 @@ export class TTSService {
|
|
|
1218
1397
|
this.lastRenderedRegionTarget = null;
|
|
1219
1398
|
if (chunk.regionRange) {
|
|
1220
1399
|
this.highlightCoordinator.clearHighlights?.(HighlightType.TTS_WORD);
|
|
1221
|
-
this.
|
|
1400
|
+
this.paintTTSSentenceRanges([chunk.regionRange]);
|
|
1222
1401
|
return;
|
|
1223
1402
|
}
|
|
1224
1403
|
if (!element)
|
|
@@ -1226,7 +1405,7 @@ export class TTSService {
|
|
|
1226
1405
|
const range = document.createRange();
|
|
1227
1406
|
range.selectNodeContents(element);
|
|
1228
1407
|
this.highlightCoordinator.clearHighlights?.(HighlightType.TTS_WORD);
|
|
1229
|
-
this.
|
|
1408
|
+
this.paintTTSSentenceRanges([range]);
|
|
1230
1409
|
}
|
|
1231
1410
|
catch {
|
|
1232
1411
|
// Continue playback even when a detached node cannot be highlighted.
|
|
@@ -1235,12 +1414,7 @@ export class TTSService {
|
|
|
1235
1414
|
highlightCatalogActiveRange(range) {
|
|
1236
1415
|
if (!this.highlightCoordinator)
|
|
1237
1416
|
return;
|
|
1238
|
-
|
|
1239
|
-
range.startContainer.nodeType === Node.TEXT_NODE) {
|
|
1240
|
-
this.highlightCoordinator.highlightTTSWord(range.startContainer, range.startOffset, range.endOffset);
|
|
1241
|
-
return;
|
|
1242
|
-
}
|
|
1243
|
-
this.highlightCoordinator.highlightRange(range, HighlightType.TTS_WORD, HighlightColor.YELLOW);
|
|
1417
|
+
this.paintTTSWordRange(this.resolveWordHighlightRange(range).range);
|
|
1244
1418
|
}
|
|
1245
1419
|
highlightRenderableRegionTarget(target) {
|
|
1246
1420
|
if (!this.highlightCoordinator ||
|
|
@@ -1248,7 +1422,8 @@ export class TTSService {
|
|
|
1248
1422
|
typeof document === "undefined") {
|
|
1249
1423
|
return;
|
|
1250
1424
|
}
|
|
1251
|
-
if (
|
|
1425
|
+
if (!this.highlightTargetResolverProvider &&
|
|
1426
|
+
sameRenderableHighlightTarget(this.lastRenderedRegionTarget, target)) {
|
|
1252
1427
|
return;
|
|
1253
1428
|
}
|
|
1254
1429
|
try {
|
|
@@ -1257,7 +1432,7 @@ export class TTSService {
|
|
|
1257
1432
|
range.selectNodeContents(target.element);
|
|
1258
1433
|
}
|
|
1259
1434
|
else if (target.type === "range") {
|
|
1260
|
-
this.
|
|
1435
|
+
this.paintTTSSentenceRanges([target.range]);
|
|
1261
1436
|
this.lastRenderedRegionTarget = target;
|
|
1262
1437
|
return;
|
|
1263
1438
|
}
|
|
@@ -1265,7 +1440,7 @@ export class TTSService {
|
|
|
1265
1440
|
range.setStart(target.node, target.startOffset);
|
|
1266
1441
|
range.setEnd(target.node, target.endOffset);
|
|
1267
1442
|
}
|
|
1268
|
-
this.
|
|
1443
|
+
this.paintTTSSentenceRanges([range]);
|
|
1269
1444
|
this.lastRenderedRegionTarget = target;
|
|
1270
1445
|
}
|
|
1271
1446
|
catch {
|
|
@@ -1286,13 +1461,26 @@ export class TTSService {
|
|
|
1286
1461
|
return;
|
|
1287
1462
|
}
|
|
1288
1463
|
if (target.type === "text-range") {
|
|
1289
|
-
this.
|
|
1464
|
+
const nativeRange = this.createTextRange(target.node, target.startOffset, target.endOffset);
|
|
1465
|
+
if (nativeRange) {
|
|
1466
|
+
this.paintTTSWordRange(this.resolveWordHighlightRange(nativeRange).range);
|
|
1467
|
+
}
|
|
1468
|
+
else {
|
|
1469
|
+
this.highlightCoordinator.highlightTTSWord(target.node, target.startOffset, target.endOffset);
|
|
1470
|
+
}
|
|
1290
1471
|
return;
|
|
1291
1472
|
}
|
|
1292
1473
|
if (target.type === "range") {
|
|
1293
1474
|
this.highlightCatalogActiveRange(target.range);
|
|
1294
1475
|
return;
|
|
1295
1476
|
}
|
|
1477
|
+
const elementNativeRange = document.createRange();
|
|
1478
|
+
elementNativeRange.selectNodeContents(target.element);
|
|
1479
|
+
const resolvedElementRange = this.resolveWordHighlightRange(elementNativeRange);
|
|
1480
|
+
if (resolvedElementRange.remapped) {
|
|
1481
|
+
this.paintTTSWordRange(resolvedElementRange.range);
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1296
1484
|
// Element targets are atomic: a resolved math token, a whole-expression
|
|
1297
1485
|
// fallback, or a replaced element (image/svg). Paint exactly the resolved
|
|
1298
1486
|
// element. Native MathML / HTML tokens that expose a single text node keep
|
|
@@ -1413,9 +1601,15 @@ export class TTSService {
|
|
|
1413
1601
|
}
|
|
1414
1602
|
async executeSpeakPlayback(args) {
|
|
1415
1603
|
if (args.speechChunks?.length && this.provider) {
|
|
1416
|
-
for (
|
|
1604
|
+
for (let index = 0; index < args.speechChunks.length; index++) {
|
|
1417
1605
|
if (args.runId !== this.speakRunId)
|
|
1418
1606
|
return;
|
|
1607
|
+
this.currentSeekSegmentIndex = index;
|
|
1608
|
+
const segment = this.seekSegments[index];
|
|
1609
|
+
if (segment) {
|
|
1610
|
+
this.currentBoundaryOffset = segment.startOffset;
|
|
1611
|
+
}
|
|
1612
|
+
const chunk = args.speechChunks[index];
|
|
1419
1613
|
await this.speakCatalogChunk(chunk, args.runId);
|
|
1420
1614
|
}
|
|
1421
1615
|
return;
|
|
@@ -1446,6 +1640,7 @@ export class TTSService {
|
|
|
1446
1640
|
this.currentBoundaryOffset = 0;
|
|
1447
1641
|
this.activeWordBoundaryOffset = 0;
|
|
1448
1642
|
this.seekSegments = [];
|
|
1643
|
+
this.playbackChunks = [];
|
|
1449
1644
|
this.sentenceHighlightSegments = [];
|
|
1450
1645
|
this.currentSeekSegmentIndex = 0;
|
|
1451
1646
|
this.activeSentenceStartOffset = null;
|
|
@@ -1530,6 +1725,9 @@ export class TTSService {
|
|
|
1530
1725
|
if (!this.provider || this.seekSegments.length === 0)
|
|
1531
1726
|
return;
|
|
1532
1727
|
const safeTargetIndex = Math.max(0, Math.min(this.seekSegments.length - 1, targetIndex));
|
|
1728
|
+
const restartChunks = this.playbackChunks.length
|
|
1729
|
+
? this.playbackChunks.slice(safeTargetIndex)
|
|
1730
|
+
: null;
|
|
1533
1731
|
this.speakRunId += 1;
|
|
1534
1732
|
this.provider.onWordBoundary = undefined;
|
|
1535
1733
|
this.provider.stop();
|
|
@@ -1542,13 +1740,29 @@ export class TTSService {
|
|
|
1542
1740
|
highlightMode: this.activeHighlightMode,
|
|
1543
1741
|
wordBoundaryOffset: this.activeWordBoundaryOffset,
|
|
1544
1742
|
});
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1743
|
+
if (restartChunks) {
|
|
1744
|
+
for (let index = 0; index < restartChunks.length; index++) {
|
|
1745
|
+
if (runId !== this.speakRunId)
|
|
1746
|
+
return;
|
|
1747
|
+
const absoluteIndex = safeTargetIndex + index;
|
|
1748
|
+
this.currentSeekSegmentIndex = absoluteIndex;
|
|
1749
|
+
const segment = this.seekSegments[absoluteIndex];
|
|
1750
|
+
if (segment) {
|
|
1751
|
+
this.currentBoundaryOffset = segment.startOffset;
|
|
1752
|
+
}
|
|
1753
|
+
await this.speakCatalogChunk(restartChunks[index], runId);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
else {
|
|
1757
|
+
await this.speakWithPlan(restartSegments, runId, {
|
|
1758
|
+
highlightMode: this.activeHighlightMode,
|
|
1759
|
+
});
|
|
1760
|
+
}
|
|
1548
1761
|
if (runId !== this.speakRunId)
|
|
1549
1762
|
return;
|
|
1550
1763
|
this.setState(PlaybackState.IDLE);
|
|
1551
1764
|
this.clearHighlightsAndTracking();
|
|
1765
|
+
this.highlightTargetResolverProvider = null;
|
|
1552
1766
|
}
|
|
1553
1767
|
catch (error) {
|
|
1554
1768
|
if (runId !== this.speakRunId)
|
|
@@ -1556,6 +1770,7 @@ export class TTSService {
|
|
|
1556
1770
|
this.lastError = error instanceof Error ? error.message : String(error);
|
|
1557
1771
|
this.setState(PlaybackState.ERROR);
|
|
1558
1772
|
this.clearHighlightsAndTracking();
|
|
1773
|
+
this.highlightTargetResolverProvider = null;
|
|
1559
1774
|
throw error;
|
|
1560
1775
|
}
|
|
1561
1776
|
}
|
|
@@ -1607,6 +1822,7 @@ export class TTSService {
|
|
|
1607
1822
|
this.speakRunId += 1;
|
|
1608
1823
|
this.provider.onWordBoundary = undefined;
|
|
1609
1824
|
this.provider.stop();
|
|
1825
|
+
this.highlightTargetResolverProvider = null;
|
|
1610
1826
|
this.setState(PlaybackState.IDLE);
|
|
1611
1827
|
this.currentText = null;
|
|
1612
1828
|
// Clear highlights
|
|
@@ -1620,6 +1836,7 @@ export class TTSService {
|
|
|
1620
1836
|
this.currentBoundaryOffset = 0;
|
|
1621
1837
|
this.activeWordBoundaryOffset = 0;
|
|
1622
1838
|
this.seekSegments = [];
|
|
1839
|
+
this.playbackChunks = [];
|
|
1623
1840
|
this.sentenceHighlightSegments = [];
|
|
1624
1841
|
this.currentSeekSegmentIndex = 0;
|
|
1625
1842
|
this.activeSentenceStartOffset = null;
|