@pie-players/pie-assessment-toolkit 0.3.73 → 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 +469 -318
- package/dist/attempt/AssessmentSession.d.ts +1 -5
- package/dist/attempt/AssessmentSession.js +0 -29
- 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.d.ts +1 -0
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.d.ts +1 -0
- package/dist/components/PieAssessmentToolkit.custom-element.js +13 -13
- package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
- package/dist/components/chunks/ItemToolBar-97hx60z4.js +2 -0
- 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 +32 -6
- package/dist/index.d.ts +23 -48
- package/dist/index.js +12 -37
- package/dist/policy/core/PolicySource.d.ts +1 -1
- package/dist/policy/core/ToolPolicyEngine.d.ts +85 -34
- package/dist/policy/core/ToolPolicyEngine.js +148 -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 +14 -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 +28 -10
- package/dist/runtime/tool-host-contract.js +31 -10
- 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 +22 -28
- package/dist/services/HighlightCoordinator.js +431 -327
- package/dist/services/RangeSerializer.d.ts +10 -1
- package/dist/services/RangeSerializer.js +87 -14
- package/dist/services/TTSService.d.ts +130 -69
- package/dist/services/TTSService.js +713 -570
- package/dist/services/ToolCoordinator.d.ts +43 -66
- package/dist/services/ToolCoordinator.js +164 -208
- package/dist/services/ToolRegistry.d.ts +78 -54
- package/dist/services/ToolRegistry.js +107 -105
- package/dist/services/ToolkitCoordinator.d.ts +261 -175
- package/dist/services/ToolkitCoordinator.js +667 -433
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +11 -1
- package/dist/services/catalog-owner.js +25 -0
- package/dist/services/create-tools-config.d.ts +2 -2
- 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 +30 -8
- package/dist/services/framework-error.js +9 -7
- package/dist/services/interfaces.d.ts +158 -129
- package/dist/services/section-controller-types.d.ts +15 -0
- 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 +1 -3
- 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 +14 -4
- package/dist/services/tool-config-validation.js +63 -71
- package/dist/services/tool-context.d.ts +0 -8
- package/dist/services/tool-context.js +29 -25
- 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 +41 -80
- package/dist/services/tool-providers/TTSToolProvider.js +81 -40
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +32 -28
- package/dist/services/tool-providers/ToolProviderRegistry.js +121 -107
- package/dist/services/tool-providers/index.d.ts +0 -9
- package/dist/services/tool-providers/index.js +2 -4
- 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 +37 -23
- 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/catalog-span-alignment.d.ts +2 -17
- package/dist/services/tts/catalog-span-alignment.js +17 -142
- 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/generated-speech/types.d.ts +0 -12
- package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +12 -62
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +38 -5
- package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
- 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-alignment/index.d.ts +0 -5
- package/dist/services/tts/math-alignment/index.js +5 -25
- package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +9 -1
- package/dist/services/tts/math-alignment/speech-tokenizer.js +38 -36
- package/dist/services/tts/math-alignment/types.d.ts +4 -2
- package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +74 -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 +16 -1
- package/dist/services/tts/math-speech.js +55 -19
- package/dist/services/tts/provider-options.d.ts +41 -0
- package/dist/services/tts/provider-options.js +1 -0
- package/dist/services/tts/sre-engine.d.ts +7 -0
- package/dist/services/tts/sre-engine.js +11 -0
- package/dist/services/tts/sre-locales.d.ts +14 -0
- package/dist/services/tts/sre-locales.js +57 -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 -1
- package/dist/services/tts/text-processing.js +46 -34
- 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 +44 -18
- package/dist/services/tts-runtime-config.js +22 -93
- 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 +24 -59
- 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-byq80z42.js +0 -40
- package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -51
- 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 -80
- package/dist/runtime/section-runtime-engine-host-context.js +0 -95
- 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/ToolConfigResolver.d.ts +0 -161
- package/dist/services/ToolConfigResolver.js +0 -229
- package/dist/services/catalog-media.d.ts +0 -56
- package/dist/services/catalog-media.js +0 -190
- package/dist/services/createDefaultToolRegistry.d.ts +0 -53
- package/dist/services/createDefaultToolRegistry.js +0 -45
- 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/services/tool-providers/CortexToolProvider.d.ts +0 -13
- package/dist/services/tool-providers/CortexToolProvider.js +0 -32
- package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -14
- package/dist/services/tool-providers/DesmosToolProvider.js +0 -32
- package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +0 -16
- package/dist/services/tool-providers/GeoGebraToolProvider.js +0 -32
- package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +0 -35
- package/dist/services/tool-providers/LazyCalculatorToolProvider.js +0 -95
- package/dist/services/tts/visual-line-ranges.d.ts +0 -18
- package/dist/services/tts/visual-line-ranges.js +0 -226
- package/dist/tools/client.d.ts +0 -12
- package/dist/tools/client.js +0 -14
- package/dist/tools/index.d.ts +0 -8
- package/dist/tools/index.js +0 -10
- package/dist/tools/internal.d.ts +0 -43
- package/dist/tools/internal.js +0 -41
- package/dist/tools/response-discovery.d.ts +0 -61
- package/dist/tools/response-discovery.js +0 -113
- package/dist/tools/types.d.ts +0 -337
- package/dist/tools/types.js +0 -12
- package/dist/tools/variant-resolver.d.ts +0 -41
- package/dist/tools/variant-resolver.js +0 -207
|
@@ -1,43 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { resolveBoundaryToSpeechToken, tokenizeSpeechSource, } from "../math-alignment/speech-tokenizer.js";
|
|
3
|
-
const normalizeBoundaryWord = (word) => {
|
|
4
|
-
if (!word || /^<[^>]+>$/.test(word.trim()))
|
|
5
|
-
return null;
|
|
6
|
-
return (tokenizeSpeechSource({ speechText: word }).tokens[0]?.normalized || null);
|
|
7
|
-
};
|
|
8
|
-
// Resolve a provider boundary for a chunk that carries no catalog span
|
|
9
|
-
// alignment — notably the generated-SSML math path, whose `speechText` is a
|
|
10
|
-
// raw `<speak>` document. The provider's offset is an index into `speechText`
|
|
11
|
-
// exactly as it was sent (which may include SSML tags), so we tokenize that
|
|
12
|
-
// same string and let the shared speech tokenizer translate the offset into the
|
|
13
|
-
// extracted spoken text. Returns the boundary in spoken-text space alongside
|
|
14
|
-
// that spoken text, so the downstream word/offset checks share one coordinate
|
|
15
|
-
// system. The previous "direct" path treated the raw-SSML offset as a
|
|
16
|
-
// spoken-text offset and rejected every math boundary (PIE-623 regression).
|
|
17
|
-
const speechSourceBoundary = (chunk, event) => {
|
|
18
|
-
const tokenization = tokenizeSpeechSource({ speechText: chunk.speechText });
|
|
19
|
-
if (!Number.isFinite(event.position)) {
|
|
20
|
-
return { boundary: null, spokenText: tokenization.spokenText };
|
|
21
|
-
}
|
|
22
|
-
const resolved = resolveBoundaryToSpeechToken({
|
|
23
|
-
tokenization,
|
|
24
|
-
position: event.position,
|
|
25
|
-
length: event.length,
|
|
26
|
-
boundaryWord: event.word,
|
|
27
|
-
});
|
|
28
|
-
return {
|
|
29
|
-
boundary: resolved
|
|
30
|
-
? { start: resolved.start, length: resolved.length }
|
|
31
|
-
: null,
|
|
32
|
-
spokenText: tokenization.spokenText,
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
const tokenAtOffsetMatches = (speechText, start, normalizedWord) => {
|
|
36
|
-
if (!normalizedWord)
|
|
37
|
-
return true;
|
|
38
|
-
const token = tokenizeSpeechSource({ speechText }).tokens.find((candidate) => candidate.start <= start && start < candidate.end);
|
|
39
|
-
return token?.normalized === normalizedWord;
|
|
40
|
-
};
|
|
1
|
+
import { normalizeBoundaryWord, resolveBoundaryToSpeechToken, tokenizeSpeechSource, } from "../math-alignment/speech-tokenizer.js";
|
|
41
2
|
export const normalizeBoundaryEvent = (chunk, event) => {
|
|
42
3
|
const normalizedWord = normalizeBoundaryWord(event.word);
|
|
43
4
|
if (chunk.id !== event.chunkId) {
|
|
@@ -61,17 +22,16 @@ export const normalizeBoundaryEvent = (chunk, event) => {
|
|
|
61
22
|
reason: "boundary offset space is unsupported",
|
|
62
23
|
};
|
|
63
24
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
25
|
+
// The provider's offset indexes `speechText` exactly as sent, which may be a
|
|
26
|
+
// raw `<speak>` document; the shared tokenizer maps it into spoken text and
|
|
27
|
+
// checks it against the reported word, on the catalog path and without one.
|
|
28
|
+
const boundary = resolveBoundaryToSpeechToken({
|
|
29
|
+
tokenization: chunk.catalogAlignment?.speech ??
|
|
30
|
+
tokenizeSpeechSource({ speechText: chunk.speechText }),
|
|
31
|
+
position: event.position,
|
|
32
|
+
length: event.length,
|
|
33
|
+
boundaryWord: event.word,
|
|
34
|
+
});
|
|
75
35
|
if (!boundary) {
|
|
76
36
|
return {
|
|
77
37
|
chunkId: chunk.id,
|
|
@@ -82,22 +42,12 @@ export const normalizeBoundaryEvent = (chunk, event) => {
|
|
|
82
42
|
reason: "could not resolve provider boundary to chunk speech",
|
|
83
43
|
};
|
|
84
44
|
}
|
|
85
|
-
if (!tokenAtOffsetMatches(spokenText, boundary.start, normalizedWord)) {
|
|
86
|
-
return {
|
|
87
|
-
chunkId: chunk.id,
|
|
88
|
-
normalizedWord,
|
|
89
|
-
chunkSpokenStart: null,
|
|
90
|
-
chunkSpokenEnd: null,
|
|
91
|
-
confidence: 0,
|
|
92
|
-
reason: "could not resolve provider boundary to matching word",
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
45
|
return {
|
|
96
46
|
chunkId: chunk.id,
|
|
97
47
|
normalizedWord,
|
|
98
48
|
chunkSpokenStart: boundary.start,
|
|
99
49
|
chunkSpokenEnd: boundary.start + boundary.length,
|
|
100
|
-
confidence:
|
|
50
|
+
confidence: boundary.confidence,
|
|
101
51
|
reason: chunk.catalogAlignment
|
|
102
52
|
? "resolved through catalog alignment"
|
|
103
53
|
: "resolved through speech tokenization",
|
|
@@ -3,11 +3,19 @@ import { createHighlightDecision } from "./highlight-policy.js";
|
|
|
3
3
|
import { resolveMathBoundaryTarget } from "./math-semantic-resolver.js";
|
|
4
4
|
import { resolveProseBoundaryTarget } from "./prose-boundary-resolver.js";
|
|
5
5
|
import { classifyMathHighlightCapability, createRenderedMathTargetResolver, } from "./rendered-math-target-resolver.js";
|
|
6
|
+
import { composedContains, composedParentElement, rangeIntersectsComposedNode, } from "../flat-tree.js";
|
|
7
|
+
import { isNodeHiddenForTTS } from "../text-processing.js";
|
|
8
|
+
import { createRangesFromVisibleMap } from "./visible-map-range.js";
|
|
9
|
+
const regionRangesFor = (chunk) => {
|
|
10
|
+
const ranges = createRangesFromVisibleMap(chunk.visibleMap, 0, chunk.visibleText.length);
|
|
11
|
+
return ranges.length > 1 ? ranges : undefined;
|
|
12
|
+
};
|
|
6
13
|
const regionTargetFor = (chunk) => chunk.regionRange
|
|
7
14
|
? {
|
|
8
15
|
type: "range",
|
|
9
16
|
quality: "region",
|
|
10
17
|
range: chunk.regionRange,
|
|
18
|
+
ranges: regionRangesFor(chunk),
|
|
11
19
|
}
|
|
12
20
|
: chunk.regionElement
|
|
13
21
|
? {
|
|
@@ -28,12 +36,13 @@ const mathCandidatesForTarget = (target, chunk) => {
|
|
|
28
36
|
return chunk.mathAlignments;
|
|
29
37
|
return chunk.mathAlignments.filter(({ element }) => {
|
|
30
38
|
try {
|
|
31
|
-
if (target.type === "range")
|
|
32
|
-
return target.range.
|
|
39
|
+
if (target.type === "range") {
|
|
40
|
+
return (target.ranges ?? [target.range]).some((range) => rangeIntersectsComposedNode(range, element));
|
|
41
|
+
}
|
|
33
42
|
if (target.type === "element") {
|
|
34
|
-
return target.element
|
|
43
|
+
return composedContains(target.element, element);
|
|
35
44
|
}
|
|
36
|
-
return target.node
|
|
45
|
+
return composedContains(composedParentElement(target.node), element);
|
|
37
46
|
}
|
|
38
47
|
catch {
|
|
39
48
|
return false;
|
|
@@ -73,6 +82,25 @@ const wouldEscalateInsideTokenMath = (planned, target, candidates) => {
|
|
|
73
82
|
element.contains(startContainer) &&
|
|
74
83
|
element.contains(endContainer));
|
|
75
84
|
};
|
|
85
|
+
// A prose range in hidden content paints nothing. Catalog words over MathJax
|
|
86
|
+
// math land in its clipped source MathML, so they select the equation, and a
|
|
87
|
+
// range over one source token resolves to that token's rendered glyph — the
|
|
88
|
+
// fill-in a single-text-node range gives native MathML.
|
|
89
|
+
const isHiddenRange = (target) => target?.type === "range" && isNodeHiddenForTTS(target.range.startContainer);
|
|
90
|
+
const renderHiddenProseTarget = (planned, target) => {
|
|
91
|
+
if (!isHiddenRange(target))
|
|
92
|
+
return null;
|
|
93
|
+
const { startContainer, endContainer, startOffset, endOffset } = target.range;
|
|
94
|
+
if (startContainer !== endContainer || startContainer.nodeType !== TEXT_NODE)
|
|
95
|
+
return null;
|
|
96
|
+
return planned.renderedMathTargetResolver({
|
|
97
|
+
type: "text-range",
|
|
98
|
+
quality: "exact-word",
|
|
99
|
+
node: startContainer,
|
|
100
|
+
startOffset,
|
|
101
|
+
endOffset,
|
|
102
|
+
});
|
|
103
|
+
};
|
|
76
104
|
const createPlannedChunk = (chunk, mathTokenHighlighting) => ({
|
|
77
105
|
chunk,
|
|
78
106
|
regionTarget: regionTargetFor(chunk),
|
|
@@ -136,9 +164,13 @@ export const createTTSHighlightPlan = (args) => {
|
|
|
136
164
|
const normalized = normalizeBoundaryEvent(chunk, event);
|
|
137
165
|
const proseTarget = resolveProseBoundaryTarget(chunk, normalized);
|
|
138
166
|
const mathCandidates = mathCandidatesForTarget(proseTarget, chunk);
|
|
139
|
-
const
|
|
167
|
+
const resolvedMathTarget = (!proseTarget || mathCandidates.length > 0
|
|
140
168
|
? resolveMathBoundaryTarget(chunk, normalized, event, mathCandidates, planned.renderedMathTargetResolver)
|
|
141
169
|
: null) || null;
|
|
170
|
+
const mathTarget = resolvedMathTarget?.quality === "semantic-token"
|
|
171
|
+
? resolvedMathTarget
|
|
172
|
+
: (renderHiddenProseTarget(planned, proseTarget) ??
|
|
173
|
+
resolvedMathTarget);
|
|
142
174
|
const mathTokenTarget = mathTokenHighlighting &&
|
|
143
175
|
mathTarget &&
|
|
144
176
|
mathTarget.quality !== "expression"
|
|
@@ -169,6 +201,7 @@ export const createTTSHighlightPlan = (args) => {
|
|
|
169
201
|
// highlighting is disabled, any prose range that lands inside a
|
|
170
202
|
// formula is likewise rejected so the formula stays a single block.
|
|
171
203
|
if (proseTarget &&
|
|
204
|
+
!isHiddenRange(proseTarget) &&
|
|
172
205
|
(mathTokenHighlighting
|
|
173
206
|
? !wouldEscalateInsideTokenMath(planned, proseTarget, mathCandidates)
|
|
174
207
|
: mathCandidates.length === 0)) {
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
const chunkOffsetSpaceFor = (chunk) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (mathOffsetSpace)
|
|
6
|
-
return mathOffsetSpace;
|
|
7
|
-
return "plain-spoken-text";
|
|
2
|
+
return (chunk.alignment?.speech.boundaryOffsetSpace ??
|
|
3
|
+
chunk.mathAlignment?.speech.boundaryOffsetSpace ??
|
|
4
|
+
"plain-spoken-text");
|
|
8
5
|
};
|
|
9
6
|
const mathAlignmentsFor = (chunk) => {
|
|
10
7
|
if (chunk.mathAlignments?.length)
|
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import { resolveVisibleSpanForBoundary } from "../catalog-span-alignment.js";
|
|
2
|
-
import {
|
|
2
|
+
import { createRangesFromVisibleMap } from "./visible-map-range.js";
|
|
3
3
|
export const resolveProseBoundaryTarget = (chunk, boundary) => {
|
|
4
4
|
if (!chunk.catalogAlignment || boundary.chunkSpokenStart === null)
|
|
5
5
|
return null;
|
|
6
6
|
const visibleSpan = resolveVisibleSpanForBoundary(chunk.catalogAlignment, boundary.chunkSpokenStart);
|
|
7
7
|
if (!visibleSpan)
|
|
8
8
|
return null;
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
// One range per tree, as the uncataloged word path builds them.
|
|
10
|
+
const ranges = createRangesFromVisibleMap(chunk.visibleMap, visibleSpan.start, visibleSpan.end);
|
|
11
|
+
if (ranges.length === 0)
|
|
11
12
|
return null;
|
|
12
13
|
return {
|
|
13
14
|
type: "range",
|
|
14
15
|
quality: chunk.catalogAlignment.playbackMode === "exact-word"
|
|
15
16
|
? "exact-word"
|
|
16
17
|
: "semantic-token",
|
|
17
|
-
range,
|
|
18
|
+
range: ranges[0],
|
|
19
|
+
ranges: ranges.length > 1 ? ranges : undefined,
|
|
18
20
|
};
|
|
19
21
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { composedClosest, composedContains } from "../flat-tree.js";
|
|
1
2
|
const READABLE_REGION_SELECTOR = [
|
|
2
3
|
"[data-pie-tts-region]",
|
|
3
4
|
"[data-catalog-region]",
|
|
@@ -10,8 +11,8 @@ const READABLE_REGION_SELECTOR = [
|
|
|
10
11
|
"div",
|
|
11
12
|
].join(",");
|
|
12
13
|
export const resolveReadableRegion = (sourceElement, contentRoot) => {
|
|
13
|
-
const region = sourceElement
|
|
14
|
-
if (region && contentRoot
|
|
14
|
+
const region = composedClosest(sourceElement, READABLE_REGION_SELECTOR);
|
|
15
|
+
if (region && composedContains(contentRoot, region)) {
|
|
15
16
|
return region;
|
|
16
17
|
}
|
|
17
18
|
return sourceElement;
|
|
@@ -44,6 +44,11 @@ export type RenderableHighlightTarget = {
|
|
|
44
44
|
type: "range";
|
|
45
45
|
quality: "exact-word" | "semantic-token" | "expression" | "region";
|
|
46
46
|
range: Range;
|
|
47
|
+
/**
|
|
48
|
+
* Every range to paint when the target crosses shadow boundaries, one per
|
|
49
|
+
* tree, `range` among them; absent when `range` covers the target.
|
|
50
|
+
*/
|
|
51
|
+
ranges?: Range[];
|
|
47
52
|
} | {
|
|
48
53
|
type: "element";
|
|
49
54
|
quality: "semantic-token" | "expression" | "region";
|
|
@@ -1,2 +1,14 @@
|
|
|
1
1
|
import type { NormalizedTextMap } from "../text-processing.js";
|
|
2
|
+
/**
|
|
3
|
+
* The range over visible characters `start` to `end` (exclusive). A live range
|
|
4
|
+
* holds one tree, so when the span runs into a shadow tree, or out of one, the
|
|
5
|
+
* range ends at the last character in the tree it starts in.
|
|
6
|
+
*/
|
|
2
7
|
export declare const createRangeFromVisibleMap: (visibleMap: NormalizedTextMap | undefined, start: number, end: number) => Range | null;
|
|
8
|
+
/**
|
|
9
|
+
* The ranges over visible characters `start` to `end` (exclusive), one per run of
|
|
10
|
+
* characters in the same tree, so a span crossing shadow boundaries paints in
|
|
11
|
+
* each tree it covers. A span within one tree gives exactly
|
|
12
|
+
* {@link createRangeFromVisibleMap}'s range.
|
|
13
|
+
*/
|
|
14
|
+
export declare const createRangesFromVisibleMap: (visibleMap: NormalizedTextMap | undefined, start: number, end: number) => Range[];
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
const treeOf = (mapping) => mapping.node.getRootNode?.() ?? mapping.node;
|
|
2
|
+
const rangeBetween = (start, end) => {
|
|
3
|
+
try {
|
|
4
|
+
const range = document.createRange();
|
|
5
|
+
range.setStart(start.node, start.offset);
|
|
6
|
+
range.setEnd(end.node, end.offset + 1);
|
|
7
|
+
return range;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* The range over visible characters `start` to `end` (exclusive). A live range
|
|
15
|
+
* holds one tree, so when the span runs into a shadow tree, or out of one, the
|
|
16
|
+
* range ends at the last character in the tree it starts in.
|
|
17
|
+
*/
|
|
1
18
|
export const createRangeFromVisibleMap = (visibleMap, start, end) => {
|
|
2
19
|
if (!visibleMap || typeof document === "undefined")
|
|
3
20
|
return null;
|
|
@@ -9,13 +26,62 @@ export const createRangeFromVisibleMap = (visibleMap, start, end) => {
|
|
|
9
26
|
return null;
|
|
10
27
|
if (!endMapping)
|
|
11
28
|
endMapping = startMapping;
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
29
|
+
if (treeOf(endMapping) !== treeOf(startMapping)) {
|
|
30
|
+
const tree = treeOf(startMapping);
|
|
31
|
+
endMapping = startMapping;
|
|
32
|
+
for (let index = start + 1; index < end; index++) {
|
|
33
|
+
const mapping = visibleMap.get(index);
|
|
34
|
+
if (mapping && treeOf(mapping) === tree)
|
|
35
|
+
endMapping = mapping;
|
|
36
|
+
}
|
|
17
37
|
}
|
|
18
|
-
|
|
19
|
-
|
|
38
|
+
return rangeBetween(startMapping, endMapping);
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The ranges over visible characters `start` to `end` (exclusive), one per run of
|
|
42
|
+
* characters in the same tree, so a span crossing shadow boundaries paints in
|
|
43
|
+
* each tree it covers. A span within one tree gives exactly
|
|
44
|
+
* {@link createRangeFromVisibleMap}'s range.
|
|
45
|
+
*/
|
|
46
|
+
export const createRangesFromVisibleMap = (visibleMap, start, end) => {
|
|
47
|
+
if (!visibleMap || typeof document === "undefined" || end <= start)
|
|
48
|
+
return [];
|
|
49
|
+
const startMapping = visibleMap.get(start);
|
|
50
|
+
if (!startMapping)
|
|
51
|
+
return [];
|
|
52
|
+
const tree = treeOf(startMapping);
|
|
53
|
+
let crossesTrees = false;
|
|
54
|
+
for (let index = start + 1; index < end && !crossesTrees; index++) {
|
|
55
|
+
const mapping = visibleMap.get(index);
|
|
56
|
+
if (mapping && treeOf(mapping) !== tree)
|
|
57
|
+
crossesTrees = true;
|
|
58
|
+
}
|
|
59
|
+
if (!crossesTrees) {
|
|
60
|
+
const range = createRangeFromVisibleMap(visibleMap, start, end);
|
|
61
|
+
return range ? [range] : [];
|
|
62
|
+
}
|
|
63
|
+
const ranges = [];
|
|
64
|
+
let runStart = null;
|
|
65
|
+
let runEnd = null;
|
|
66
|
+
const closeRun = () => {
|
|
67
|
+
if (runStart && runEnd) {
|
|
68
|
+
const range = rangeBetween(runStart, runEnd);
|
|
69
|
+
if (range)
|
|
70
|
+
ranges.push(range);
|
|
71
|
+
}
|
|
72
|
+
runStart = null;
|
|
73
|
+
runEnd = null;
|
|
74
|
+
};
|
|
75
|
+
for (let index = start; index < end; index++) {
|
|
76
|
+
const mapping = visibleMap.get(index);
|
|
77
|
+
if (!mapping)
|
|
78
|
+
continue;
|
|
79
|
+
if (runStart && treeOf(runStart) !== treeOf(mapping))
|
|
80
|
+
closeRun();
|
|
81
|
+
if (!runStart)
|
|
82
|
+
runStart = mapping;
|
|
83
|
+
runEnd = mapping;
|
|
20
84
|
}
|
|
85
|
+
closeRun();
|
|
86
|
+
return ranges;
|
|
21
87
|
};
|
|
@@ -14,11 +14,6 @@ export declare const resolveHighlightTargetForBoundary: (alignment: MathAwareAli
|
|
|
14
14
|
length?: number;
|
|
15
15
|
boundaryWord?: string;
|
|
16
16
|
}) => HighlightTarget;
|
|
17
|
-
export declare const resolveMatchedHighlightTargetForBoundary: (alignment: MathAwareAlignment, args: {
|
|
18
|
-
position: number;
|
|
19
|
-
length?: number;
|
|
20
|
-
boundaryWord?: string;
|
|
21
|
-
}) => HighlightTarget | null;
|
|
22
17
|
export declare const resolveMatchedHighlightTargetForSpokenBoundary: (alignment: MathAwareAlignment, args: {
|
|
23
18
|
position: number;
|
|
24
19
|
length?: number;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { tokenizeMathML } from "./mathml-tokenizer.js";
|
|
2
2
|
import { resolveMatchedHighlightTargetForBoundary as resolveMatchedTargetForBoundary, resolveHighlightTargetForBoundary as resolveTargetForBoundary, } from "./range-resolver.js";
|
|
3
3
|
import { alignSpeechToMath } from "./sequence-aligner.js";
|
|
4
|
-
import { resolveBoundaryToSpeechToken, resolveSpokenOffsetToSpeechToken, tokenizeSpeechSource, } from "./speech-tokenizer.js";
|
|
4
|
+
import { normalizeBoundaryWord, resolveBoundaryToSpeechToken, resolveSpokenOffsetToSpeechToken, tokenizeSpeechSource, } from "./speech-tokenizer.js";
|
|
5
5
|
export const createMathAwareAlignment = (args) => {
|
|
6
6
|
const speech = tokenizeSpeechSource({ speechText: args.speechText });
|
|
7
7
|
const math = tokenizeMathML(args.mathElement);
|
|
@@ -23,18 +23,6 @@ export const resolveHighlightTargetForBoundary = (alignment, args) => {
|
|
|
23
23
|
boundary,
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
|
-
export const resolveMatchedHighlightTargetForBoundary = (alignment, args) => {
|
|
27
|
-
const boundary = resolveBoundaryToSpeechToken({
|
|
28
|
-
tokenization: alignment.speech,
|
|
29
|
-
position: args.position,
|
|
30
|
-
length: args.length,
|
|
31
|
-
boundaryWord: args.boundaryWord,
|
|
32
|
-
});
|
|
33
|
-
return resolveMatchedTargetForBoundary({
|
|
34
|
-
alignment: alignment.result,
|
|
35
|
-
boundary,
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
26
|
export const resolveMatchedHighlightTargetForSpokenBoundary = (alignment, args) => {
|
|
39
27
|
const boundary = resolveSpokenOffsetToSpeechToken({
|
|
40
28
|
tokenization: alignment.speech,
|
|
@@ -48,18 +36,10 @@ export const resolveMatchedHighlightTargetForSpokenBoundary = (alignment, args)
|
|
|
48
36
|
});
|
|
49
37
|
};
|
|
50
38
|
export const resolveUniqueMathTargetForBoundaryWord = (alignment, args) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
const boundaryToken = tokenizeSpeechSource({
|
|
55
|
-
speechText: args.boundaryWord,
|
|
56
|
-
}).tokens[0];
|
|
57
|
-
if (!boundaryToken)
|
|
39
|
+
const normalized = normalizeBoundaryWord(args.boundaryWord);
|
|
40
|
+
if (!normalized)
|
|
58
41
|
return null;
|
|
59
|
-
const matches = alignment.math.tokens.filter((token) =>
|
|
60
|
-
|
|
61
|
-
return (token.normalized.toLowerCase() === normalized ||
|
|
62
|
-
token.spokenAliases.some((alias) => alias.toLowerCase() === normalized));
|
|
63
|
-
});
|
|
42
|
+
const matches = alignment.math.tokens.filter((token) => token.normalized.toLowerCase() === normalized ||
|
|
43
|
+
token.spokenAliases.some((alias) => alias.toLowerCase() === normalized));
|
|
64
44
|
return matches.length === 1 ? matches[0].target : null;
|
|
65
45
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BoundaryOffsetSpace, SpeechAlignmentToken } from "./types.js";
|
|
1
|
+
import type { AlignmentTextToken, BoundaryOffsetSpace, SpeechAlignmentToken } from "./types.js";
|
|
2
2
|
export interface SpeechSourceTokenization {
|
|
3
3
|
speechText: string;
|
|
4
4
|
spokenText: string;
|
|
@@ -14,9 +14,17 @@ export interface ResolvedSpeechBoundary {
|
|
|
14
14
|
length: number;
|
|
15
15
|
confidence: number;
|
|
16
16
|
}
|
|
17
|
+
export declare const tokenizeAlignmentText: (text: string) => AlignmentTextToken[];
|
|
17
18
|
export declare const tokenizeSpeechSource: (args: {
|
|
18
19
|
speechText: string;
|
|
19
20
|
}) => SpeechSourceTokenization;
|
|
21
|
+
/**
|
|
22
|
+
* A provider boundary word in the form spoken tokens compare in, or null when
|
|
23
|
+
* the word carries no token (empty, a control tag, punctuation). The word goes
|
|
24
|
+
* through the same SSML extraction as the chunk, so an entity-encoded word
|
|
25
|
+
* normalizes to the text it encodes.
|
|
26
|
+
*/
|
|
27
|
+
export declare const normalizeBoundaryWord: (word?: string) => string | null;
|
|
20
28
|
export declare const resolveBoundaryToSpeechToken: (args: {
|
|
21
29
|
tokenization: SpeechSourceTokenization;
|
|
22
30
|
position: number;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { extractSpokenText } from "../ssml/spoken-text.js";
|
|
2
2
|
import { createSpeechAlignmentTokenPattern } from "../text-processing.js";
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// The one tokenizer and normalization table for TTS alignment. Spoken text,
|
|
4
|
+
// visible catalog text and provider boundary words all tokenize here, so a word
|
|
5
|
+
// normalizes the same way on the catalog, math and generated-speech paths.
|
|
6
6
|
const TOKEN_PATTERN = createSpeechAlignmentTokenPattern();
|
|
7
7
|
const NUMERIC_WORDS = new Map([
|
|
8
8
|
["zero", "0"],
|
|
@@ -21,24 +21,27 @@ const normalizedTokenValue = (value) => {
|
|
|
21
21
|
const normalized = value.toLowerCase();
|
|
22
22
|
return NUMERIC_WORDS.get(normalized) || normalized;
|
|
23
23
|
};
|
|
24
|
-
const
|
|
24
|
+
export const tokenizeAlignmentText = (text) => {
|
|
25
25
|
const tokens = [];
|
|
26
|
-
for (const match of
|
|
27
|
-
const
|
|
26
|
+
for (const match of text.matchAll(TOKEN_PATTERN)) {
|
|
27
|
+
const value = match[0];
|
|
28
28
|
const start = match.index ?? 0;
|
|
29
29
|
tokens.push({
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
normalized: normalizedTokenValue(text),
|
|
30
|
+
text: value,
|
|
31
|
+
normalized: normalizedTokenValue(value),
|
|
33
32
|
start,
|
|
34
|
-
end: start +
|
|
35
|
-
sourceStart: start,
|
|
36
|
-
sourceEnd: start + text.length,
|
|
37
|
-
coordinateSystem: "normalized-speech",
|
|
33
|
+
end: start + value.length,
|
|
38
34
|
});
|
|
39
35
|
}
|
|
40
36
|
return tokens;
|
|
41
37
|
};
|
|
38
|
+
const tokenizeSpokenText = (spokenText) => tokenizeAlignmentText(spokenText).map((token, index) => ({
|
|
39
|
+
...token,
|
|
40
|
+
id: `speech-token-${index}`,
|
|
41
|
+
sourceStart: token.start,
|
|
42
|
+
sourceEnd: token.end,
|
|
43
|
+
coordinateSystem: "normalized-speech",
|
|
44
|
+
}));
|
|
42
45
|
export const tokenizeSpeechSource = (args) => {
|
|
43
46
|
const extracted = extractSpokenText(args.speechText);
|
|
44
47
|
return {
|
|
@@ -55,15 +58,17 @@ export const tokenizeSpeechSource = (args) => {
|
|
|
55
58
|
hasMarkup: extracted.hasMarkup,
|
|
56
59
|
};
|
|
57
60
|
};
|
|
58
|
-
const
|
|
59
|
-
|
|
61
|
+
const isControlTagWord = (word) => Boolean(word && /^<[^>]+>$/.test(word.trim()));
|
|
62
|
+
/**
|
|
63
|
+
* A provider boundary word in the form spoken tokens compare in, or null when
|
|
64
|
+
* the word carries no token (empty, a control tag, punctuation). The word goes
|
|
65
|
+
* through the same SSML extraction as the chunk, so an entity-encoded word
|
|
66
|
+
* normalizes to the text it encodes.
|
|
67
|
+
*/
|
|
68
|
+
export const normalizeBoundaryWord = (word) => {
|
|
69
|
+
if (!word || isControlTagWord(word))
|
|
60
70
|
return null;
|
|
61
|
-
|
|
62
|
-
return null;
|
|
63
|
-
const match = word.match(TOKEN_PATTERN);
|
|
64
|
-
if (!match?.[0])
|
|
65
|
-
return null;
|
|
66
|
-
return normalizedTokenValue(match[0]);
|
|
71
|
+
return (tokenizeSpeechSource({ speechText: word }).tokens[0]?.normalized ?? null);
|
|
67
72
|
};
|
|
68
73
|
const findTokenAtOffset = (tokens, offset) => tokens.find((token) => token.start <= offset && offset < token.end) || null;
|
|
69
74
|
const mapRawOffsetToSpokenOffset = (tokenization, position) => {
|
|
@@ -92,32 +97,29 @@ const candidateForOffset = (tokenization, offset, length) => {
|
|
|
92
97
|
confidence: 0.75,
|
|
93
98
|
};
|
|
94
99
|
};
|
|
95
|
-
const candidateMatchesWord = (candidate,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
normalizedBoundary &&
|
|
99
|
-
candidate.token.normalized === normalizedBoundary);
|
|
100
|
-
};
|
|
100
|
+
const candidateMatchesWord = (candidate, normalizedWord) => Boolean(candidate &&
|
|
101
|
+
normalizedWord &&
|
|
102
|
+
candidate.token.normalized === normalizedWord);
|
|
101
103
|
export const resolveBoundaryToSpeechToken = (args) => {
|
|
102
104
|
if (args.tokenization.boundaryOffsetSpace === "unsupported" ||
|
|
103
105
|
!Number.isFinite(args.position) ||
|
|
104
|
-
(args.boundaryWord
|
|
106
|
+
isControlTagWord(args.boundaryWord)) {
|
|
105
107
|
return null;
|
|
106
108
|
}
|
|
107
109
|
const safeLength = Math.max(1, Number.isFinite(args.length) ? args.length : 1);
|
|
110
|
+
const normalizedWord = normalizeBoundaryWord(args.boundaryWord);
|
|
108
111
|
const rawCandidate = candidateForOffset(args.tokenization, mapRawOffsetToSpokenOffset(args.tokenization, args.position), safeLength);
|
|
109
112
|
const plainCandidate = candidateForOffset(args.tokenization, args.position >= 0 && args.position < args.tokenization.spokenText.length
|
|
110
113
|
? args.position
|
|
111
114
|
: null, safeLength);
|
|
112
|
-
if (candidateMatchesWord(rawCandidate,
|
|
115
|
+
if (candidateMatchesWord(rawCandidate, normalizedWord)) {
|
|
113
116
|
return { ...rawCandidate, confidence: 1 };
|
|
114
117
|
}
|
|
115
|
-
if (candidateMatchesWord(plainCandidate,
|
|
118
|
+
if (candidateMatchesWord(plainCandidate, normalizedWord)) {
|
|
116
119
|
return { ...plainCandidate, confidence: 1 };
|
|
117
120
|
}
|
|
118
|
-
if (
|
|
121
|
+
if (normalizedWord)
|
|
119
122
|
return null;
|
|
120
|
-
}
|
|
121
123
|
return args.tokenization.boundaryOffsetSpace === "raw-ssml"
|
|
122
124
|
? rawCandidate || plainCandidate
|
|
123
125
|
: plainCandidate || rawCandidate;
|
|
@@ -125,18 +127,18 @@ export const resolveBoundaryToSpeechToken = (args) => {
|
|
|
125
127
|
export const resolveSpokenOffsetToSpeechToken = (args) => {
|
|
126
128
|
if (args.tokenization.boundaryOffsetSpace === "unsupported" ||
|
|
127
129
|
!Number.isFinite(args.position) ||
|
|
128
|
-
(args.boundaryWord
|
|
130
|
+
isControlTagWord(args.boundaryWord)) {
|
|
129
131
|
return null;
|
|
130
132
|
}
|
|
131
133
|
const safeLength = Math.max(1, Number.isFinite(args.length) ? args.length : 1);
|
|
134
|
+
const normalizedWord = normalizeBoundaryWord(args.boundaryWord);
|
|
132
135
|
const spokenCandidate = candidateForOffset(args.tokenization, args.position >= 0 && args.position < args.tokenization.spokenText.length
|
|
133
136
|
? args.position
|
|
134
137
|
: null, safeLength);
|
|
135
|
-
if (candidateMatchesWord(spokenCandidate,
|
|
138
|
+
if (candidateMatchesWord(spokenCandidate, normalizedWord)) {
|
|
136
139
|
return { ...spokenCandidate, confidence: 1 };
|
|
137
140
|
}
|
|
138
|
-
if (
|
|
141
|
+
if (normalizedWord)
|
|
139
142
|
return null;
|
|
140
|
-
}
|
|
141
143
|
return spokenCandidate;
|
|
142
144
|
};
|
|
@@ -34,12 +34,14 @@ export interface MathMLTokenizationResult {
|
|
|
34
34
|
}
|
|
35
35
|
export type BoundaryCoordinateSystem = "normalized-speech" | "raw-ssml" | "provider-speech-marks";
|
|
36
36
|
export type BoundaryOffsetSpace = "plain-spoken-text" | "raw-ssml" | "unsupported";
|
|
37
|
-
export interface
|
|
38
|
-
id: string;
|
|
37
|
+
export interface AlignmentTextToken {
|
|
39
38
|
text: string;
|
|
40
39
|
normalized: string;
|
|
41
40
|
start: number;
|
|
42
41
|
end: number;
|
|
42
|
+
}
|
|
43
|
+
export interface SpeechAlignmentToken extends AlignmentTextToken {
|
|
44
|
+
id: string;
|
|
43
45
|
sourceStart: number;
|
|
44
46
|
sourceEnd: number;
|
|
45
47
|
coordinateSystem: BoundaryCoordinateSystem;
|
|
@@ -15,4 +15,8 @@ export interface MathAwareTextResult {
|
|
|
15
15
|
chunks: MathAwareSpeechChunk[];
|
|
16
16
|
containsMathMarkup: boolean;
|
|
17
17
|
}
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* With `range`, only the part of `element` the range selects: text it selects
|
|
20
|
+
* and equations it holds whole.
|
|
21
|
+
*/
|
|
22
|
+
export declare const collectMathAwareTextAndMap: (element: Element, options?: TextProcessingOptions, range?: Range) => MathAwareTextResult;
|