@pie-players/pie-assessment-toolkit 0.3.74 → 0.3.75
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/README.md +349 -313
- package/dist/attempt/TestSession.d.ts +1 -20
- package/dist/attempt/TestSession.js +8 -99
- package/dist/components/ItemScope.custom-element.d.ts +1 -0
- package/dist/components/ItemScope.custom-element.js +1 -0
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
- package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-97hx60z4.js} +1 -1
- package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
- package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
- package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
- package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
- package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
- package/dist/components/item-scope-element.d.ts +2 -0
- package/dist/components/item-scope-element.js +2 -0
- package/dist/context/assessment-toolkit-context.d.ts +27 -6
- package/dist/index.d.ts +22 -47
- package/dist/index.js +12 -36
- package/dist/policy/core/PolicySource.d.ts +1 -1
- package/dist/policy/core/ToolPolicyEngine.d.ts +77 -32
- package/dist/policy/core/ToolPolicyEngine.js +136 -46
- package/dist/policy/core/compose-decision.d.ts +28 -7
- package/dist/policy/core/compose-decision.js +75 -54
- package/dist/policy/core/decision-types.d.ts +65 -34
- package/dist/policy/core/feature-decision.d.ts +16 -9
- package/dist/policy/core/feature-decision.js +14 -8
- package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
- package/dist/policy/core/pnp-policy-inputs.js +28 -56
- package/dist/policy/core/provenance.d.ts +9 -4
- package/dist/policy/core/provenance.js +1 -1
- package/dist/policy/engine.d.ts +7 -23
- package/dist/policy/engine.js +5 -20
- package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
- package/dist/policy/sources/PnpPolicySource.js +117 -118
- package/dist/runtime/SectionControllerBinding.d.ts +279 -0
- package/dist/runtime/SectionControllerBinding.js +450 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
- package/dist/runtime/SectionRuntimeEngine.js +30 -372
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
- package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
- package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
- package/dist/runtime/adapter/dom-event-bridge.js +17 -44
- package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
- package/dist/runtime/adapter/subscriber-fanout.js +1 -2
- package/dist/runtime/catalog-context.d.ts +19 -0
- package/dist/runtime/catalog-context.js +28 -0
- package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
- package/dist/runtime/composition-emit-scheduler.js +9 -15
- package/dist/runtime/content-language.d.ts +32 -0
- package/dist/runtime/content-language.js +46 -0
- package/dist/runtime/content-region.d.ts +17 -0
- package/dist/runtime/content-region.js +29 -0
- package/dist/runtime/content-registration.d.ts +13 -0
- package/dist/runtime/content-registration.js +25 -0
- package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
- package/dist/runtime/core/SectionEngineCore.js +2 -2
- package/dist/runtime/core/cohort.d.ts +1 -1
- package/dist/runtime/core/cohort.js +1 -1
- package/dist/runtime/core/engine-input.d.ts +18 -30
- package/dist/runtime/core/engine-input.js +11 -12
- package/dist/runtime/core/engine-output.d.ts +16 -40
- package/dist/runtime/core/engine-output.js +9 -21
- package/dist/runtime/core/engine-readiness.d.ts +5 -37
- package/dist/runtime/core/engine-readiness.js +8 -41
- package/dist/runtime/core/engine-resolver.d.ts +46 -74
- package/dist/runtime/core/engine-resolver.js +18 -43
- package/dist/runtime/core/engine-state.d.ts +28 -36
- package/dist/runtime/core/engine-state.js +18 -21
- package/dist/runtime/core/engine-transition.d.ts +14 -19
- package/dist/runtime/core/engine-transition.js +71 -51
- package/dist/runtime/engine.d.ts +16 -19
- package/dist/runtime/engine.js +12 -19
- package/dist/runtime/page-warnings.d.ts +13 -0
- package/dist/runtime/page-warnings.js +22 -0
- package/dist/runtime/policy-input-forwarding.d.ts +25 -0
- package/dist/runtime/policy-input-forwarding.js +25 -0
- package/dist/runtime/registration-events.d.ts +11 -0
- package/dist/runtime/runtime-event-claim.d.ts +8 -0
- package/dist/runtime/runtime-event-claim.js +15 -0
- package/dist/runtime/shell-event-bridge.d.ts +45 -0
- package/dist/runtime/shell-event-bridge.js +127 -0
- package/dist/runtime/shell-registration.d.ts +73 -0
- package/dist/runtime/shell-registration.js +95 -0
- package/dist/runtime/shell-scope.d.ts +68 -0
- package/dist/runtime/shell-scope.js +188 -0
- package/dist/runtime/tool-host-contract.d.ts +8 -10
- package/dist/runtime/tool-host-contract.js +11 -11
- package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
- package/dist/runtime/unclaimed-registration-watch.js +23 -0
- package/dist/services/ElementToolStateStore.d.ts +14 -10
- package/dist/services/ElementToolStateStore.js +37 -13
- package/dist/services/HighlightCoordinator.d.ts +21 -21
- package/dist/services/HighlightCoordinator.js +431 -325
- package/dist/services/RangeSerializer.d.ts +10 -1
- package/dist/services/RangeSerializer.js +87 -14
- package/dist/services/TTSService.d.ts +117 -70
- package/dist/services/TTSService.js +668 -578
- package/dist/services/ToolCoordinator.d.ts +43 -60
- package/dist/services/ToolCoordinator.js +164 -206
- package/dist/services/ToolRegistry.d.ts +74 -57
- package/dist/services/ToolRegistry.js +104 -114
- package/dist/services/ToolkitCoordinator.d.ts +183 -189
- package/dist/services/ToolkitCoordinator.js +477 -431
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +0 -1
- package/dist/services/framework-error-bus.d.ts +3 -6
- package/dist/services/framework-error-bus.js +3 -6
- package/dist/services/framework-error.d.ts +22 -7
- package/dist/services/framework-error.js +9 -7
- package/dist/services/interfaces.d.ts +158 -129
- package/dist/services/section-session-assignment.d.ts +21 -0
- package/dist/services/section-session-assignment.js +82 -0
- package/dist/services/spoken-audio-cards.d.ts +0 -2
- package/dist/services/spoken-audio-cards.js +0 -2
- package/dist/services/tool-config-defaults.d.ts +3 -3
- package/dist/services/tool-config-defaults.js +3 -3
- package/dist/services/tool-config-validation.d.ts +4 -5
- package/dist/services/tool-config-validation.js +36 -61
- package/dist/services/tool-context.d.ts +0 -8
- package/dist/services/tool-context.js +0 -12
- package/dist/services/tool-icons.d.ts +4 -3
- package/dist/services/tool-icons.js +4 -3
- package/dist/services/tool-instance-id.d.ts +0 -1
- package/dist/services/tool-instance-id.js +0 -6
- package/dist/services/tool-providers/TTSToolProvider.d.ts +18 -19
- package/dist/services/tool-providers/TTSToolProvider.js +37 -34
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +30 -26
- package/dist/services/tool-providers/ToolProviderRegistry.js +119 -93
- package/dist/services/tool-request.d.ts +27 -12
- package/dist/services/tool-request.js +24 -14
- package/dist/services/toolbar-decided-tools.d.ts +15 -0
- package/dist/services/toolbar-decided-tools.js +28 -0
- package/dist/services/tools-config-normalizer.d.ts +17 -22
- package/dist/services/tools-config-normalizer.js +2 -10
- package/dist/services/tts/browser-provider.d.ts +38 -2
- package/dist/services/tts/browser-provider.js +276 -140
- package/dist/services/tts/flat-tree.d.ts +71 -0
- package/dist/services/tts/flat-tree.js +269 -0
- package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
- package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
- package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
- package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
- package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +12 -4
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
- package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
- package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
- package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +32 -18
- package/dist/services/tts/math-control-names.d.ts +21 -0
- package/dist/services/tts/math-control-names.js +142 -0
- package/dist/services/tts/math-speech.d.ts +11 -0
- package/dist/services/tts/math-speech.js +51 -18
- package/dist/services/tts/provider-options.d.ts +41 -0
- package/dist/services/tts/provider-options.js +1 -0
- package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
- package/dist/services/tts/ssml/spoken-text.js +29 -0
- package/dist/services/tts/start-failure.d.ts +11 -0
- package/dist/services/tts/start-failure.js +19 -0
- package/dist/services/tts/text-processing.d.ts +9 -0
- package/dist/services/tts/text-processing.js +46 -33
- package/dist/services/tts/text-segmentation.d.ts +7 -0
- package/dist/services/tts/text-segmentation.js +47 -0
- package/dist/services/tts-runtime-config.d.ts +25 -17
- package/dist/services/tts-runtime-config.js +16 -95
- package/dist/tools/content-capability-resolution.d.ts +7 -9
- package/dist/tools/content-capability-resolution.js +8 -45
- package/dist/tools/registration.d.ts +58 -0
- package/dist/tools/registration.js +54 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
- package/dist/tools/tool-surface-host.d.ts +6 -10
- package/dist/tools/tool-surface-host.js +44 -42
- package/dist/tools/tool-tag-map.d.ts +5 -15
- package/dist/tools/tool-tag-map.js +6 -20
- package/dist/utils/structural-equality.d.ts +11 -0
- package/dist/utils/structural-equality.js +47 -0
- package/package.json +13 -29
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
- package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
- package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
- package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
- package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -18
- package/dist/item-loader.d.ts +0 -44
- package/dist/item-loader.js +0 -61
- package/dist/policy/core/engine-context.d.ts +0 -29
- package/dist/policy/core/engine-context.js +0 -27
- package/dist/policy/core/policy-source-tag.d.ts +0 -31
- package/dist/policy/core/policy-source-tag.js +0 -28
- package/dist/policy/internal.d.ts +0 -25
- package/dist/policy/internal.js +0 -25
- package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
- package/dist/runtime/adapter/coordinator-bridge.js +0 -126
- package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
- package/dist/runtime/adapter/framework-error-bridge.js +0 -46
- package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
- package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
- package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
- package/dist/runtime/core/engine-stage-derivation.js +0 -29
- package/dist/runtime/engine-context.d.ts +0 -39
- package/dist/runtime/engine-context.js +0 -31
- package/dist/runtime/internal.d.ts +0 -43
- package/dist/runtime/internal.js +0 -45
- package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
- package/dist/runtime/section-runtime-engine-host-context.js +0 -57
- package/dist/runtime/stage-emit-gate.d.ts +0 -92
- package/dist/runtime/stage-emit-gate.js +0 -82
- package/dist/services/I18nService.d.ts +0 -47
- package/dist/services/I18nService.js +0 -84
- package/dist/services/ThemeProvider.d.ts +0 -95
- package/dist/services/ThemeProvider.js +0 -334
- package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
- package/dist/services/defaultPersonalNeedsProfile.js +0 -26
- package/dist/services/pnp-standard-features.d.ts +0 -238
- package/dist/services/pnp-standard-features.js +0 -338
- package/dist/tools/client.d.ts +0 -10
- package/dist/tools/client.js +0 -10
- package/dist/tools/internal.d.ts +0 -44
- package/dist/tools/internal.js +0 -39
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { flatQuerySelector, flatTreeChildNodes, flatTreeParentElement, rangeIntersectsComposedNode, } from "./flat-tree.js";
|
|
1
2
|
import { canonicalizeMathML } from "./mathml-sanitization.js";
|
|
2
|
-
import { collectVisibleTextAndMap, isNodeExcludedFromSpeech, normalizeTextForSpeech, shouldInsertWordBoundarySpace, } from "./text-processing.js";
|
|
3
|
+
import { collectVisibleTextAndMap, isNodeExcludedFromSpeech, normalizeTextForSpeech, rangeHoldsSpeakableElement, shouldInsertWordBoundarySpace, } from "./text-processing.js";
|
|
3
4
|
const createAccumulator = (options) => ({
|
|
4
5
|
chars: [],
|
|
5
6
|
map: new Map(),
|
|
@@ -21,7 +22,7 @@ const getDataMathML = (element) => {
|
|
|
21
22
|
return value && value.trim() ? value : null;
|
|
22
23
|
};
|
|
23
24
|
const findAssistiveMathML = (element) => {
|
|
24
|
-
const assistive = element
|
|
25
|
+
const assistive = flatQuerySelector(element, "mjx-assistive-mml math");
|
|
25
26
|
return assistive?.outerHTML || null;
|
|
26
27
|
};
|
|
27
28
|
const findCanonicalMathML = (element) => {
|
|
@@ -72,7 +73,7 @@ const TEXT_CHUNK_SOURCE_ROLES = new Set([
|
|
|
72
73
|
"rowheader",
|
|
73
74
|
]);
|
|
74
75
|
const resolveTextChunkSourceElement = (textNode, root) => {
|
|
75
|
-
let current = textNode
|
|
76
|
+
let current = flatTreeParentElement(textNode);
|
|
76
77
|
let best = null;
|
|
77
78
|
while (current && current !== root) {
|
|
78
79
|
const role = (current.getAttribute("role") || "").toLowerCase();
|
|
@@ -82,12 +83,12 @@ const resolveTextChunkSourceElement = (textNode, root) => {
|
|
|
82
83
|
best = current;
|
|
83
84
|
break;
|
|
84
85
|
}
|
|
85
|
-
current = current
|
|
86
|
+
current = flatTreeParentElement(current);
|
|
86
87
|
}
|
|
87
88
|
return best || root;
|
|
88
89
|
};
|
|
89
90
|
const hasMathCandidate = (element) => Boolean(typeof element.querySelector === "function" &&
|
|
90
|
-
element
|
|
91
|
+
flatQuerySelector(element, "math, [data-mathml], .MathJax, mjx-container, mjx-assistive-mml")) ||
|
|
91
92
|
element.tagName?.toLowerCase() === "math" ||
|
|
92
93
|
element.hasAttribute?.("data-mathml") ||
|
|
93
94
|
isMathJaxElement(element);
|
|
@@ -121,10 +122,11 @@ const shouldInsertBoundarySpace = (acc, nextCharacter) => {
|
|
|
121
122
|
// visible-text collector, instead of an ASCII-only heuristic.
|
|
122
123
|
return shouldInsertWordBoundarySpace(previous, nextCharacter, acc.options);
|
|
123
124
|
};
|
|
124
|
-
|
|
125
|
+
/** `start` and `end` bound the part of the node appended, for a selection. */
|
|
126
|
+
const appendTextNode = (acc, textNode, start = 0, end = (textNode.textContent || "").length) => {
|
|
125
127
|
const raw = textNode.textContent || "";
|
|
126
128
|
let appendedNonWhitespaceInNode = false;
|
|
127
|
-
for (let i =
|
|
129
|
+
for (let i = start; i < end; i++) {
|
|
128
130
|
const character = raw[i];
|
|
129
131
|
const isWhitespace = /\s/.test(character);
|
|
130
132
|
if (acc.inLeadingWhitespace) {
|
|
@@ -230,7 +232,7 @@ const textFallbackFromMathML = (mathml) => {
|
|
|
230
232
|
const mapFallbackToAssistiveMath = (element, fallbackText) => {
|
|
231
233
|
const map = new Map();
|
|
232
234
|
const sourceMath = isMathJaxElement(element)
|
|
233
|
-
? element
|
|
235
|
+
? flatQuerySelector(element, "mjx-assistive-mml math")
|
|
234
236
|
: null;
|
|
235
237
|
if (!sourceMath)
|
|
236
238
|
return map;
|
|
@@ -247,7 +249,7 @@ const mapFallbackToAssistiveMath = (element, fallbackText) => {
|
|
|
247
249
|
}
|
|
248
250
|
if (node.nodeType !== 1 || isAnnotationElement(node))
|
|
249
251
|
return;
|
|
250
|
-
for (const child of
|
|
252
|
+
for (const child of flatTreeChildNodes(node)) {
|
|
251
253
|
visit(child);
|
|
252
254
|
}
|
|
253
255
|
};
|
|
@@ -281,7 +283,7 @@ const collectVisibleMathFallback = (element, canonicalMathML, options) => {
|
|
|
281
283
|
isAnnotationElement(childElement)) {
|
|
282
284
|
return;
|
|
283
285
|
}
|
|
284
|
-
for (const child of
|
|
286
|
+
for (const child of flatTreeChildNodes(childElement)) {
|
|
285
287
|
visit(child);
|
|
286
288
|
}
|
|
287
289
|
};
|
|
@@ -307,7 +309,7 @@ const trimTrailingWhitespace = (acc) => {
|
|
|
307
309
|
acc.map.delete(acc.position);
|
|
308
310
|
}
|
|
309
311
|
};
|
|
310
|
-
const collectMathAware = (root, options) => {
|
|
312
|
+
const collectMathAware = (root, options, range) => {
|
|
311
313
|
const acc = createAccumulator(options);
|
|
312
314
|
const chunks = [];
|
|
313
315
|
let textChunkStart = 0;
|
|
@@ -328,22 +330,28 @@ const collectMathAware = (root, options) => {
|
|
|
328
330
|
const processNode = (node) => {
|
|
329
331
|
if (isNodeExcludedFromSpeech(node, root))
|
|
330
332
|
return;
|
|
331
|
-
if (
|
|
333
|
+
if (range && !rangeIntersectsComposedNode(range, node))
|
|
334
|
+
return;
|
|
335
|
+
if (node.nodeType === 3) {
|
|
332
336
|
const sourceElement = resolveTextChunkSourceElement(node, root);
|
|
333
337
|
if (textChunkSourceElement && sourceElement !== textChunkSourceElement) {
|
|
334
338
|
flushTextChunk();
|
|
335
339
|
}
|
|
336
340
|
textChunkSourceElement = sourceElement;
|
|
337
|
-
|
|
341
|
+
const textNode = node;
|
|
342
|
+
appendTextNode(acc, textNode, range?.startContainer === textNode ? range.startOffset : undefined, range?.endContainer === textNode ? range.endOffset : undefined);
|
|
338
343
|
return;
|
|
339
344
|
}
|
|
340
|
-
if (node.nodeType !==
|
|
345
|
+
if (node.nodeType !== 1)
|
|
341
346
|
return;
|
|
342
347
|
const element = node;
|
|
343
348
|
if (isAssistiveMathElement(element))
|
|
344
349
|
return;
|
|
345
350
|
const canonicalMathML = findCanonicalMathML(element);
|
|
346
|
-
|
|
351
|
+
// An equation is spoken as math only when the selection holds all of it;
|
|
352
|
+
// a part of one reads as the text selected.
|
|
353
|
+
if (canonicalMathML &&
|
|
354
|
+
(!range || rangeHoldsSpeakableElement(range, element, root))) {
|
|
347
355
|
flushTextChunk();
|
|
348
356
|
const collected = collectVisibleMathFallback(element, canonicalMathML, options);
|
|
349
357
|
appendCollectedText(acc, collected);
|
|
@@ -358,11 +366,11 @@ const collectMathAware = (root, options) => {
|
|
|
358
366
|
containsMathMarkup = true;
|
|
359
367
|
return;
|
|
360
368
|
}
|
|
361
|
-
for (const child of
|
|
369
|
+
for (const child of flatTreeChildNodes(element)) {
|
|
362
370
|
processNode(child);
|
|
363
371
|
}
|
|
364
372
|
};
|
|
365
|
-
for (const child of
|
|
373
|
+
for (const child of flatTreeChildNodes(root)) {
|
|
366
374
|
processNode(child);
|
|
367
375
|
}
|
|
368
376
|
trimTrailingWhitespace(acc);
|
|
@@ -378,7 +386,13 @@ const collectMathAware = (root, options) => {
|
|
|
378
386
|
containsMathMarkup,
|
|
379
387
|
};
|
|
380
388
|
};
|
|
381
|
-
|
|
389
|
+
/**
|
|
390
|
+
* With `range`, only the part of `element` the range selects: text it selects
|
|
391
|
+
* and equations it holds whole.
|
|
392
|
+
*/
|
|
393
|
+
export const collectMathAwareTextAndMap = (element, options, range) => {
|
|
394
|
+
if (range)
|
|
395
|
+
return collectMathAware(element, options, range);
|
|
382
396
|
if (!hasMathCandidate(element)) {
|
|
383
397
|
const { text, map } = collectVisibleTextAndMap(element, options);
|
|
384
398
|
return {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ResolveMathSpeechOptions, type SREMathSpeechOptions } from "./math-speech.js";
|
|
2
|
+
export interface MathControlNamesOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The host's math speech options. Names take only `engineOptions`, where
|
|
5
|
+
* SRE's locale tables load from: their domain and style are fixed.
|
|
6
|
+
*/
|
|
7
|
+
getMathSpeech?: () => SREMathSpeechOptions | undefined;
|
|
8
|
+
/** The host's `content-language`, read when an expression is named. */
|
|
9
|
+
getContentLanguage?: () => string | null | undefined;
|
|
10
|
+
loadSre?: ResolveMathSpeechOptions["loadSre"];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Labels each typeset expression inside a control under `root`, open shadow
|
|
14
|
+
* roots included, with SRE's speech, as MathJax adds it and again whenever
|
|
15
|
+
* MathJax replaces it. An `aria-label` on the role-less `mjx-container` puts
|
|
16
|
+
* that speech in the name of the control around it, among the control's other
|
|
17
|
+
* text. English reads ClearSpeak, other languages MathSpeak, in the language of
|
|
18
|
+
* the content. The label replaces the elements' own; it stays when SRE cannot
|
|
19
|
+
* speak the math. Returns the function that stops observing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function observeMathControlNames(root: Element, options?: MathControlNamesOptions): () => void;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { resolveContentLanguage } from "../../runtime/content-language.js";
|
|
2
|
+
import { flatQuerySelector, flatTreeClosest, walkFlatTree, } from "./flat-tree.js";
|
|
3
|
+
import { resolveMathSpeechFromChunks, } from "./math-speech.js";
|
|
4
|
+
import { canonicalizeMathML } from "./mathml-sanitization.js";
|
|
5
|
+
/**
|
|
6
|
+
* Elements that take their accessible name from their content. Browsers leave
|
|
7
|
+
* MathML out of that name, so a choice labelled with math is named from its
|
|
8
|
+
* bare symbols ("4 12" for 4 over 12). The elements' renderer labels the math
|
|
9
|
+
* in the same set of controls with its own English speech.
|
|
10
|
+
*/
|
|
11
|
+
const CONTROLS = [
|
|
12
|
+
"a[href]",
|
|
13
|
+
"button",
|
|
14
|
+
"label",
|
|
15
|
+
"summary",
|
|
16
|
+
...[
|
|
17
|
+
"button",
|
|
18
|
+
"checkbox",
|
|
19
|
+
"combobox",
|
|
20
|
+
"link",
|
|
21
|
+
"menuitem",
|
|
22
|
+
"menuitemcheckbox",
|
|
23
|
+
"menuitemradio",
|
|
24
|
+
"option",
|
|
25
|
+
"radio",
|
|
26
|
+
"switch",
|
|
27
|
+
"tab",
|
|
28
|
+
"treeitem",
|
|
29
|
+
].map((role) => `[role="${role}"]`),
|
|
30
|
+
].join(", ");
|
|
31
|
+
const TOKENS = new Set(["mi", "mn", "mtext"]);
|
|
32
|
+
const isToken = (element) => TOKENS.has(element.localName) ||
|
|
33
|
+
(element.localName === "mrow" &&
|
|
34
|
+
element.children.length === 1 &&
|
|
35
|
+
TOKENS.has(element.children[0].localName));
|
|
36
|
+
/**
|
|
37
|
+
* ClearSpeak's fraction preference for a name. "4 over 12" keeps the numerals
|
|
38
|
+
* as written, where the default says "four fourths" for 4 over 4 (WCAG 2.5.3:
|
|
39
|
+
* a name does not put words in place of the formula). "Over" between longer
|
|
40
|
+
* parts is ambiguous, so an expression holding such a fraction reads every
|
|
41
|
+
* fraction by numerator and denominator.
|
|
42
|
+
*/
|
|
43
|
+
const fractionStyle = (math) => Array.from(math.getElementsByTagName("mfrac")).every((fraction) => Array.from(fraction.children).every(isToken))
|
|
44
|
+
? "Fraction_Over"
|
|
45
|
+
: "Fraction_General";
|
|
46
|
+
const isEnglish = (language) => !language || language.toLowerCase().split("-")[0] === "en";
|
|
47
|
+
/**
|
|
48
|
+
* Labels each typeset expression inside a control under `root`, open shadow
|
|
49
|
+
* roots included, with SRE's speech, as MathJax adds it and again whenever
|
|
50
|
+
* MathJax replaces it. An `aria-label` on the role-less `mjx-container` puts
|
|
51
|
+
* that speech in the name of the control around it, among the control's other
|
|
52
|
+
* text. English reads ClearSpeak, other languages MathSpeak, in the language of
|
|
53
|
+
* the content. The label replaces the elements' own; it stays when SRE cannot
|
|
54
|
+
* speak the math. Returns the function that stops observing.
|
|
55
|
+
*/
|
|
56
|
+
export function observeMathControlNames(root, options = {}) {
|
|
57
|
+
if (typeof MutationObserver === "undefined")
|
|
58
|
+
return () => { };
|
|
59
|
+
const speech = new Map();
|
|
60
|
+
let stopped = false;
|
|
61
|
+
const speak = async (mathml, math, language) => {
|
|
62
|
+
const result = await resolveMathSpeechFromChunks([{ type: "math", mathml, fallbackText: "" }], {
|
|
63
|
+
language,
|
|
64
|
+
loadSre: options.loadSre,
|
|
65
|
+
mathSpeech: {
|
|
66
|
+
...(isEnglish(language) ? { style: fractionStyle(math) } : {}),
|
|
67
|
+
engineOptions: options.getMathSpeech?.()?.engineOptions,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
return result.usedMathSpeech ? result.speechText : null;
|
|
71
|
+
};
|
|
72
|
+
const name = (container) => {
|
|
73
|
+
if (!flatTreeClosest(container, CONTROLS))
|
|
74
|
+
return;
|
|
75
|
+
const math = flatQuerySelector(container, "math");
|
|
76
|
+
const mathml = math && canonicalizeMathML(math.outerHTML);
|
|
77
|
+
if (!math || !mathml)
|
|
78
|
+
return;
|
|
79
|
+
const language = resolveContentLanguage(container, {
|
|
80
|
+
contentLanguage: options.getContentLanguage?.(),
|
|
81
|
+
});
|
|
82
|
+
const key = `${language ?? ""}\u0000${mathml}`;
|
|
83
|
+
let pending = speech.get(key);
|
|
84
|
+
if (!pending) {
|
|
85
|
+
pending = speak(mathml, math, language);
|
|
86
|
+
speech.set(key, pending);
|
|
87
|
+
}
|
|
88
|
+
void pending.then((label) => {
|
|
89
|
+
if (stopped || !label || !container.isConnected)
|
|
90
|
+
return;
|
|
91
|
+
if (container.getAttribute("aria-label") !== label) {
|
|
92
|
+
container.setAttribute("aria-label", label);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
// A shadow root is a tree of its own, which an observer on `root` does not
|
|
97
|
+
// see into, so each one found is observed as well. One attached after its
|
|
98
|
+
// host was visited is not found until something is added around the host.
|
|
99
|
+
const observed = new WeakSet();
|
|
100
|
+
const observeTree = (tree) => {
|
|
101
|
+
if (observed.has(tree))
|
|
102
|
+
return;
|
|
103
|
+
observed.add(tree);
|
|
104
|
+
observer.observe(tree, { childList: true, subtree: true });
|
|
105
|
+
};
|
|
106
|
+
const visit = (node) => {
|
|
107
|
+
if (node.nodeType !== 1)
|
|
108
|
+
return;
|
|
109
|
+
const element = node;
|
|
110
|
+
const container = flatTreeClosest(element, "mjx-container");
|
|
111
|
+
if (container) {
|
|
112
|
+
name(container);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (element.shadowRoot)
|
|
116
|
+
observeTree(element.shadowRoot);
|
|
117
|
+
walkFlatTree(element, (descendant) => {
|
|
118
|
+
if (descendant.nodeType !== 1)
|
|
119
|
+
return "skip";
|
|
120
|
+
const found = descendant;
|
|
121
|
+
if (found.localName === "mjx-container") {
|
|
122
|
+
name(found);
|
|
123
|
+
return "skip";
|
|
124
|
+
}
|
|
125
|
+
if (found.shadowRoot)
|
|
126
|
+
observeTree(found.shadowRoot);
|
|
127
|
+
return undefined;
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
const observer = new MutationObserver((records) => {
|
|
131
|
+
for (const record of records) {
|
|
132
|
+
for (const node of record.addedNodes)
|
|
133
|
+
visit(node);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
observeTree(root);
|
|
137
|
+
visit(root);
|
|
138
|
+
return () => {
|
|
139
|
+
stopped = true;
|
|
140
|
+
observer.disconnect();
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -43,6 +43,17 @@ export interface ResolvedMathSpeech {
|
|
|
43
43
|
*/
|
|
44
44
|
ssml?: string;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Loads the speech-rule-engine once per page. A failed load is dropped, so the
|
|
48
|
+
* next call tries again; meanwhile math reads as its visible text. The first
|
|
49
|
+
* failure warns, later ones log at debug.
|
|
50
|
+
*/
|
|
51
|
+
export declare const createSreLoader: (importEngine: () => Promise<SpeechRuleEngineApi>) => ((engineOptions?: Record<string, unknown>) => Promise<SpeechRuleEngineApi>);
|
|
52
|
+
/**
|
|
53
|
+
* One speech-rule-engine load for a whole read: its equations share the
|
|
54
|
+
* attempt, so a failed load costs a read one attempt.
|
|
55
|
+
*/
|
|
56
|
+
export declare const createReadSreLoad: (mathSpeech?: SREMathSpeechOptions) => (() => Promise<SpeechRuleEngineApi>);
|
|
46
57
|
export declare const normalizeSREMathSpeechOptions: (options: unknown) => SREMathSpeechOptions | undefined;
|
|
47
58
|
export declare const resolveMathSpeechFromChunks: (chunks: MathAwareSpeechChunk[], options?: ResolveMathSpeechOptions) => Promise<ResolvedMathSpeech>;
|
|
48
59
|
export {};
|
|
@@ -1,19 +1,53 @@
|
|
|
1
|
+
import { createPieLogger, isTtsDebugEnabled, } from "@pie-players/pie-players-shared/pie";
|
|
1
2
|
import { setSreStartupLocaleSource, sreLocaleSource } from "./sre-locales.js";
|
|
2
3
|
import { normalizeTextForSpeech } from "./text-processing.js";
|
|
3
|
-
|
|
4
|
+
// Debug lines need PIE_TTS_DEBUG=1 or globalThis.__PIE_TTS_DEBUG__ = true.
|
|
5
|
+
const logger = createPieLogger("tts-math-speech", isTtsDebugEnabled);
|
|
4
6
|
let sreOperationQueue = Promise.resolve();
|
|
5
7
|
const normalizeLocale = (language) => (language || "en").split("-")[0].toLowerCase() || "en";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Loads the speech-rule-engine once per page. A failed load is dropped, so the
|
|
10
|
+
* next call tries again; meanwhile math reads as its visible text. The first
|
|
11
|
+
* failure warns, later ones log at debug.
|
|
12
|
+
*/
|
|
13
|
+
export const createSreLoader = (importEngine) => {
|
|
14
|
+
let load = null;
|
|
15
|
+
let warned = false;
|
|
16
|
+
return (engineOptions) => {
|
|
17
|
+
if (!load) {
|
|
18
|
+
setSreStartupLocaleSource(engineOptions);
|
|
19
|
+
const attempt = importEngine();
|
|
20
|
+
load = attempt;
|
|
21
|
+
attempt.catch((error) => {
|
|
22
|
+
if (load === attempt)
|
|
23
|
+
load = null;
|
|
24
|
+
if (warned) {
|
|
25
|
+
logger.debug("speech-rule-engine failed to load again", error);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
warned = true;
|
|
29
|
+
logger.warn("speech-rule-engine failed to load; math reads as its visible text until a later read loads it.", error);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
return load;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
const defaultLoadSre = createSreLoader(() => import("./sre-engine.js").then(({ default: candidate }) => {
|
|
36
|
+
if (!candidate || typeof candidate.toSpeech !== "function") {
|
|
37
|
+
throw new Error("speech-rule-engine did not expose toSpeech");
|
|
15
38
|
}
|
|
16
|
-
return
|
|
39
|
+
return candidate;
|
|
40
|
+
}));
|
|
41
|
+
/**
|
|
42
|
+
* One speech-rule-engine load for a whole read: its equations share the
|
|
43
|
+
* attempt, so a failed load costs a read one attempt.
|
|
44
|
+
*/
|
|
45
|
+
export const createReadSreLoad = (mathSpeech) => {
|
|
46
|
+
let attempt = null;
|
|
47
|
+
return () => {
|
|
48
|
+
attempt ??= defaultLoadSre(normalizeSREMathSpeechOptions(mathSpeech)?.engineOptions);
|
|
49
|
+
return attempt;
|
|
50
|
+
};
|
|
17
51
|
};
|
|
18
52
|
// SRE's ClearSpeak rule set is English-only; MathSpeak is localized to many more
|
|
19
53
|
// locales. Use ClearSpeak for English (highest-quality math prose) and MathSpeak
|
|
@@ -47,9 +81,9 @@ const setupSre = async (sre, language, markup, mathSpeech) => {
|
|
|
47
81
|
...engineOptions,
|
|
48
82
|
locale,
|
|
49
83
|
domain: normalizedMathSpeech?.domain || domainForLocale(locale),
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
84
|
+
// SRE keeps a style it was given until it is given another, so a call
|
|
85
|
+
// without one resets it: math control names set ClearSpeak preferences.
|
|
86
|
+
style: normalizedMathSpeech?.style || "default",
|
|
53
87
|
modality: "speech",
|
|
54
88
|
markup,
|
|
55
89
|
});
|
|
@@ -80,7 +114,7 @@ const generateMathSsml = async (sre, mathml, language, mathSpeech) => {
|
|
|
80
114
|
return ssml.includes("<speak") ? ssml : null;
|
|
81
115
|
}
|
|
82
116
|
catch (error) {
|
|
83
|
-
|
|
117
|
+
logger.debug("math SSML generation failed; using plain", {
|
|
84
118
|
message: error instanceof Error ? error.message : String(error),
|
|
85
119
|
});
|
|
86
120
|
return null;
|
|
@@ -99,8 +133,7 @@ export const resolveMathSpeechFromChunks = async (chunks, options = {}) => {
|
|
|
99
133
|
let sre = null;
|
|
100
134
|
let usedMathSpeech = false;
|
|
101
135
|
let usedFallback = false;
|
|
102
|
-
const loadSre = options.loadSre ||
|
|
103
|
-
(() => defaultLoadSre(normalizeSREMathSpeechOptions(options.mathSpeech)?.engineOptions));
|
|
136
|
+
const loadSre = options.loadSre || createReadSreLoad(options.mathSpeech);
|
|
104
137
|
const speechParts = [];
|
|
105
138
|
for (const chunk of chunks) {
|
|
106
139
|
if (chunk.type === "text") {
|
|
@@ -122,7 +155,7 @@ export const resolveMathSpeechFromChunks = async (chunks, options = {}) => {
|
|
|
122
155
|
}
|
|
123
156
|
catch (error) {
|
|
124
157
|
usedFallback = true;
|
|
125
|
-
|
|
158
|
+
logger.debug("math speech generation failed; using visible fallback", {
|
|
126
159
|
message: error instanceof Error ? error.message : String(error),
|
|
127
160
|
});
|
|
128
161
|
speechParts.push(chunk.fallbackText);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { TTSConfig, TTSProviderOptions } from "@pie-players/pie-tts";
|
|
2
|
+
import type { SREMathSpeechOptions } from "./math-speech.js";
|
|
3
|
+
import type { BoundarySpacingMode } from "./text-processing.js";
|
|
4
|
+
/**
|
|
5
|
+
* The provider options the toolkit reads, on top of the ones every provider
|
|
6
|
+
* receives. Hosts configure them as free-form options, so readers still check
|
|
7
|
+
* each value at runtime.
|
|
8
|
+
*/
|
|
9
|
+
export interface ToolkitTTSProviderOptions extends TTSProviderOptions {
|
|
10
|
+
/** Highlight words or sentences while reading. Defaults by provider capability. */
|
|
11
|
+
highlightMode?: "word" | "sentence";
|
|
12
|
+
/** Locale for text processing; the toolkit sets it per speak. */
|
|
13
|
+
locale?: string;
|
|
14
|
+
/** Speech-rule-engine options for math speech. */
|
|
15
|
+
mathSpeech?: SREMathSpeechOptions;
|
|
16
|
+
/** Sentence and word segmentation of the browser provider. */
|
|
17
|
+
segmenter?: {
|
|
18
|
+
/** `regexOnly` segments without `Intl.Segmenter`. */
|
|
19
|
+
mode?: "regexOnly";
|
|
20
|
+
locale?: string;
|
|
21
|
+
};
|
|
22
|
+
/** Pauses at structural boundaries: `baseMs` scaled by a strength unit, clamped. */
|
|
23
|
+
structuralPauses?: {
|
|
24
|
+
baseMs?: number | string;
|
|
25
|
+
units?: {
|
|
26
|
+
minor?: number | string;
|
|
27
|
+
section?: number | string;
|
|
28
|
+
major?: number | string;
|
|
29
|
+
};
|
|
30
|
+
minMs?: number | string;
|
|
31
|
+
maxMs?: number | string;
|
|
32
|
+
};
|
|
33
|
+
textNormalization?: {
|
|
34
|
+
boundarySpacingMode?: BoundarySpacingMode;
|
|
35
|
+
locale?: string;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** A provider configuration as the toolkit reads it. */
|
|
39
|
+
export interface ToolkitTTSConfig extends TTSConfig {
|
|
40
|
+
providerOptions?: ToolkitTTSProviderOptions;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,29 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Strips SSML markup to the words a TTS engine would actually voice, while
|
|
5
|
-
* recording a `rawToSpokenOffsetMap` from raw-SSML character offsets to
|
|
6
|
-
* normalized spoken-text offsets. This is the single source of truth for SSML
|
|
7
|
-
* stripping used by:
|
|
8
|
-
* - the catalog span aligner (`catalog-span-alignment.ts`),
|
|
9
|
-
* - the math speech tokenizer (`math-alignment/speech-tokenizer.ts`),
|
|
10
|
-
* - the runtime generated-speech path (PIE-623).
|
|
11
|
-
*
|
|
12
|
-
* Keeping one implementation guarantees provider word-boundary offsets (which
|
|
13
|
-
* are into the raw SSML string) map to the same spoken text on every path.
|
|
14
|
-
*
|
|
15
|
-
* Tag handling:
|
|
16
|
-
* - Structural tags (`speak`, `p`, `s`, `emphasis`, `prosody`, `say-as`,
|
|
17
|
-
* `voice`, `lang`) are transparent; their text content flows through.
|
|
18
|
-
* `p`/`s` open tags inject a word boundary space.
|
|
19
|
-
* - `break` injects a space (a pause is a word boundary).
|
|
20
|
-
* - `sub alias="…"` substitutes the alias text (and skips the original).
|
|
21
|
-
* `sub` with no alias marks the content unsupported for word tracking.
|
|
22
|
-
* - Semantic tags that change *what* is spoken without leaving trackable
|
|
23
|
-
* text (`audio`, `phoneme`) mark the content unsupported.
|
|
24
|
-
* - Any other unknown element marks the content unsupported (degrade to
|
|
25
|
-
* coarse region highlighting rather than mis-track).
|
|
26
|
-
*/
|
|
1
|
+
/** Whether `text` is an SSML document: it opens with a `<speak>` element. */
|
|
2
|
+
export declare const isSsmlDocument: (text: string) => boolean;
|
|
27
3
|
export interface ExtractedSpokenText {
|
|
28
4
|
spokenText: string;
|
|
29
5
|
rawToSpokenOffsetMap: Map<number, number>;
|
|
@@ -1,4 +1,33 @@
|
|
|
1
1
|
import { normalizeTextForSpeech } from "../text-processing.js";
|
|
2
|
+
/**
|
|
3
|
+
* Shared SSML → spoken-text extractor.
|
|
4
|
+
*
|
|
5
|
+
* Strips SSML markup to the words a TTS engine would actually voice, while
|
|
6
|
+
* recording a `rawToSpokenOffsetMap` from raw-SSML character offsets to
|
|
7
|
+
* normalized spoken-text offsets. This is the single source of truth for SSML
|
|
8
|
+
* stripping used by:
|
|
9
|
+
* - the catalog span aligner (`catalog-span-alignment.ts`),
|
|
10
|
+
* - the math speech tokenizer (`math-alignment/speech-tokenizer.ts`),
|
|
11
|
+
* - the runtime generated-speech path (PIE-623).
|
|
12
|
+
*
|
|
13
|
+
* Keeping one implementation guarantees provider word-boundary offsets (which
|
|
14
|
+
* are into the raw SSML string) map to the same spoken text on every path.
|
|
15
|
+
*
|
|
16
|
+
* Tag handling:
|
|
17
|
+
* - Structural tags (`speak`, `p`, `s`, `emphasis`, `prosody`, `say-as`,
|
|
18
|
+
* `voice`, `lang`) are transparent; their text content flows through.
|
|
19
|
+
* `p`/`s` open tags inject a word boundary space.
|
|
20
|
+
* - `break` injects a space (a pause is a word boundary).
|
|
21
|
+
* - `sub alias="…"` substitutes the alias text (and skips the original).
|
|
22
|
+
* `sub` with no alias marks the content unsupported for word tracking.
|
|
23
|
+
* - Semantic tags that change *what* is spoken without leaving trackable
|
|
24
|
+
* text (`audio`, `phoneme`) mark the content unsupported.
|
|
25
|
+
* - Any other unknown element marks the content unsupported (degrade to
|
|
26
|
+
* coarse region highlighting rather than mis-track).
|
|
27
|
+
*/
|
|
28
|
+
const SSML_DOCUMENT = /^\s*<speak[\s/>]/i;
|
|
29
|
+
/** Whether `text` is an SSML document: it opens with a `<speak>` element. */
|
|
30
|
+
export const isSsmlDocument = (text) => SSML_DOCUMENT.test(text);
|
|
2
31
|
const STRUCTURAL_SSML_TAGS = new Set([
|
|
3
32
|
"speak",
|
|
4
33
|
"p",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `code` a speak's rejection carries when speech could not start, as
|
|
3
|
+
* distinct from a failure while speaking. Read by shape: several copies of the
|
|
4
|
+
* toolkit can share a page, so an error class from one fails `instanceof` in
|
|
5
|
+
* another.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TTS_START_FAILED_CODE = "tts-start-failed";
|
|
8
|
+
/** Whether a speak rejected because speech could not start. */
|
|
9
|
+
export declare function isTTSStartFailure(error: unknown): boolean;
|
|
10
|
+
/** Marks `cause` as a start failure, keeping its message and stack. */
|
|
11
|
+
export declare function toTTSStartFailure(cause: unknown): Error;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `code` a speak's rejection carries when speech could not start, as
|
|
3
|
+
* distinct from a failure while speaking. Read by shape: several copies of the
|
|
4
|
+
* toolkit can share a page, so an error class from one fails `instanceof` in
|
|
5
|
+
* another.
|
|
6
|
+
*/
|
|
7
|
+
export const TTS_START_FAILED_CODE = "tts-start-failed";
|
|
8
|
+
/** Whether a speak rejected because speech could not start. */
|
|
9
|
+
export function isTTSStartFailure(error) {
|
|
10
|
+
return (typeof error === "object" &&
|
|
11
|
+
error !== null &&
|
|
12
|
+
error.code === TTS_START_FAILED_CODE);
|
|
13
|
+
}
|
|
14
|
+
/** Marks `cause` as a start failure, keeping its message and stack. */
|
|
15
|
+
export function toTTSStartFailure(cause) {
|
|
16
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
17
|
+
error.code = TTS_START_FAILED_CODE;
|
|
18
|
+
return error;
|
|
19
|
+
}
|
|
@@ -62,6 +62,15 @@ export declare const collectRangeTextForSpeech: (range: Range, root: Element) =>
|
|
|
62
62
|
text: string;
|
|
63
63
|
filtered: boolean;
|
|
64
64
|
};
|
|
65
|
+
/** The part of `textNode`'s text that `range` holds, for a node it intersects. */
|
|
66
|
+
export declare const textInRange: (textNode: Text, range: Range) => string;
|
|
67
|
+
/**
|
|
68
|
+
* Whether `range` holds all of `element`'s speakable text: its first and last
|
|
69
|
+
* speakable characters, which a contiguous range holding both holds everything
|
|
70
|
+
* between. An element with no speakable text, an image for one, is held when the
|
|
71
|
+
* range touches it, since no part of it can be selected alone.
|
|
72
|
+
*/
|
|
73
|
+
export declare const rangeHoldsSpeakableElement: (range: Range, element: Element, root?: Element | null) => boolean;
|
|
65
74
|
export declare const shouldInsertWordBoundarySpace: (previousChar: string | null, nextChar: string | null, options?: TextProcessingOptions) => boolean;
|
|
66
75
|
export declare const collectVisibleTextAndMap: (element: Element, options?: TextProcessingOptions) => {
|
|
67
76
|
text: string;
|