@pie-players/pie-assessment-toolkit 0.3.61 → 0.3.63

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.
Files changed (270) hide show
  1. package/dist/attempt/AssessmentSession.js +0 -1
  2. package/dist/attempt/TestSession.js +0 -1
  3. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -1
  4. package/dist/components/ItemToolBar.custom-element.js +1 -10106
  5. package/dist/components/PieAssessmentToolkit.custom-element.js +48 -29930
  6. package/dist/components/SectionToolBar.custom-element.js +1 -14372
  7. package/dist/components/chunks/ItemToolBar-843902tp.js +51 -0
  8. package/dist/components/chunks/ItemToolBar-84nv78dy.js +22 -0
  9. package/dist/components/item-toolbar-element.js +0 -1
  10. package/dist/components/pie-assessment-toolkit-element.js +0 -1
  11. package/dist/components/section-toolbar-element.js +0 -1
  12. package/dist/context/assessment-toolkit-context.js +0 -1
  13. package/dist/context/runtime-context-consumer.js +0 -1
  14. package/dist/index.js +0 -1
  15. package/dist/item-loader.js +0 -1
  16. package/dist/policy/core/PolicySource.js +0 -1
  17. package/dist/policy/core/ToolPolicyEngine.js +0 -1
  18. package/dist/policy/core/compose-decision.js +0 -1
  19. package/dist/policy/core/decision-types.js +0 -1
  20. package/dist/policy/core/engine-context.js +0 -1
  21. package/dist/policy/core/pnp-policy-inputs.js +0 -1
  22. package/dist/policy/core/policy-source-tag.js +0 -1
  23. package/dist/policy/core/provenance.js +0 -1
  24. package/dist/policy/engine.js +0 -1
  25. package/dist/policy/internal.js +0 -1
  26. package/dist/policy/sources/PnpPolicySource.js +0 -1
  27. package/dist/runtime/RuntimeRegistry.js +0 -1
  28. package/dist/runtime/SectionRuntimeEngine.js +0 -1
  29. package/dist/runtime/adapter/SectionEngineAdapter.js +0 -1
  30. package/dist/runtime/adapter/coordinator-bridge.js +0 -1
  31. package/dist/runtime/adapter/dom-event-bridge.js +0 -1
  32. package/dist/runtime/adapter/framework-error-bridge.js +0 -1
  33. package/dist/runtime/adapter/instrumentation-bridge.js +0 -1
  34. package/dist/runtime/adapter/subscriber-fanout.js +0 -1
  35. package/dist/runtime/catalog-registration.js +0 -1
  36. package/dist/runtime/core/SectionEngineCore.js +0 -1
  37. package/dist/runtime/core/cohort.js +0 -1
  38. package/dist/runtime/core/engine-input.js +0 -1
  39. package/dist/runtime/core/engine-output.js +0 -1
  40. package/dist/runtime/core/engine-readiness.js +0 -1
  41. package/dist/runtime/core/engine-resolver.js +0 -1
  42. package/dist/runtime/core/engine-stage-derivation.js +0 -1
  43. package/dist/runtime/core/engine-state.js +0 -1
  44. package/dist/runtime/core/engine-transition.js +0 -1
  45. package/dist/runtime/engine-context.js +0 -1
  46. package/dist/runtime/engine.js +0 -1
  47. package/dist/runtime/internal.js +0 -1
  48. package/dist/runtime/registration-events.js +0 -1
  49. package/dist/runtime/runtime-id.js +0 -1
  50. package/dist/runtime/section-runtime-engine-host-context.js +0 -1
  51. package/dist/runtime/session-event-emitter-policy.js +0 -1
  52. package/dist/runtime/stage-emit-gate.js +0 -1
  53. package/dist/runtime/tool-host-contract.js +0 -1
  54. package/dist/services/AccessibilityCatalogResolver.js +0 -1
  55. package/dist/services/ContextVariableStore.js +0 -1
  56. package/dist/services/ElementToolStateStore.js +0 -1
  57. package/dist/services/HighlightCoordinator.js +35 -3
  58. package/dist/services/I18nService.js +0 -1
  59. package/dist/services/RangeSerializer.js +0 -1
  60. package/dist/services/SSMLExtractor.js +0 -1
  61. package/dist/services/TTSService.js +0 -1
  62. package/dist/services/ThemeProvider.js +0 -1
  63. package/dist/services/ToolConfigResolver.js +0 -1
  64. package/dist/services/ToolCoordinator.js +0 -1
  65. package/dist/services/ToolRegistry.js +0 -1
  66. package/dist/services/ToolkitCoordinator.js +0 -1
  67. package/dist/services/create-tools-config.js +0 -1
  68. package/dist/services/createDefaultToolRegistry.js +0 -1
  69. package/dist/services/defaultPersonalNeedsProfile.js +0 -1
  70. package/dist/services/framework-error-bus.js +0 -1
  71. package/dist/services/framework-error.js +0 -1
  72. package/dist/services/interfaces.js +0 -1
  73. package/dist/services/pnp-standard-features.js +0 -1
  74. package/dist/services/section-controller-types.js +0 -1
  75. package/dist/services/tool-config-defaults.js +0 -1
  76. package/dist/services/tool-config-validation.js +0 -1
  77. package/dist/services/tool-context.js +0 -1
  78. package/dist/services/tool-instance-id.js +0 -1
  79. package/dist/services/tool-providers/DesmosToolProvider.js +0 -1
  80. package/dist/services/tool-providers/TTSToolProvider.js +0 -1
  81. package/dist/services/tool-providers/ToolProviderApi.js +0 -1
  82. package/dist/services/tool-providers/ToolProviderRegistry.js +0 -1
  83. package/dist/services/tool-providers/index.js +0 -1
  84. package/dist/services/toolbar-items.js +0 -1
  85. package/dist/services/tools-config-normalizer.js +0 -1
  86. package/dist/services/tts/browser-provider.js +0 -1
  87. package/dist/services/tts/catalog-span-alignment.js +0 -1
  88. package/dist/services/tts/generated-speech/assemble-plan.js +0 -1
  89. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +0 -1
  90. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +0 -1
  91. package/dist/services/tts/generated-speech/dom/types.js +0 -1
  92. package/dist/services/tts/generated-speech/index.js +0 -1
  93. package/dist/services/tts/generated-speech/math-speech-cache.js +0 -1
  94. package/dist/services/tts/generated-speech/types.js +0 -1
  95. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +0 -1
  96. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +0 -1
  97. package/dist/services/tts/highlight-pipeline/highlight-policy.js +0 -1
  98. package/dist/services/tts/highlight-pipeline/index.js +0 -1
  99. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +0 -1
  100. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +0 -1
  101. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +0 -1
  102. package/dist/services/tts/highlight-pipeline/readable-region.js +0 -1
  103. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +0 -1
  104. package/dist/services/tts/highlight-pipeline/types.js +0 -1
  105. package/dist/services/tts/highlight-pipeline/visible-map-range.js +0 -1
  106. package/dist/services/tts/highlight-target-resolver.js +0 -1
  107. package/dist/services/tts/math-alignment/index.js +0 -1
  108. package/dist/services/tts/math-alignment/mathml-tokenizer.js +0 -1
  109. package/dist/services/tts/math-alignment/range-resolver.js +0 -1
  110. package/dist/services/tts/math-alignment/sequence-aligner.js +0 -1
  111. package/dist/services/tts/math-alignment/speech-tokenizer.js +0 -1
  112. package/dist/services/tts/math-alignment/types.js +0 -1
  113. package/dist/services/tts/math-aware-text-processing.js +0 -1
  114. package/dist/services/tts/math-speech.js +0 -1
  115. package/dist/services/tts/mathml-sanitization.js +0 -1
  116. package/dist/services/tts/ssml/spoken-text.js +0 -1
  117. package/dist/services/tts/text-processing.js +0 -1
  118. package/dist/services/tts/text-segmentation.js +0 -1
  119. package/dist/services/tts/visual-line-ranges.js +0 -1
  120. package/dist/services/tts-control-events.js +0 -1
  121. package/dist/services/tts-runtime-config.js +0 -1
  122. package/dist/tools/calculators/desmos-provider.js +0 -1
  123. package/dist/tools/client.js +0 -1
  124. package/dist/tools/index.js +0 -1
  125. package/dist/tools/library-loader.js +0 -1
  126. package/dist/tools/registrations/accessibility-tools.js +0 -1
  127. package/dist/tools/registrations/calculator.js +0 -1
  128. package/dist/tools/registrations/interaction-tools.js +0 -1
  129. package/dist/tools/registrations/measurement-tools.js +0 -1
  130. package/dist/tools/registrations/subject-specific-tools.js +0 -1
  131. package/dist/tools/registrations/toolbar-registration-helpers.js +0 -1
  132. package/dist/tools/registrations/tts.js +0 -1
  133. package/dist/tools/response-discovery.js +0 -1
  134. package/dist/tools/tool-tag-map.js +0 -1
  135. package/dist/tools/types.js +0 -1
  136. package/dist/tools/variant-resolver.js +0 -1
  137. package/dist/utils/logger.js +0 -1
  138. package/package.json +13 -11
  139. package/dist/attempt/AssessmentSession.js.map +0 -1
  140. package/dist/attempt/TestSession.js.map +0 -1
  141. package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +0 -1
  142. package/dist/components/item-toolbar-element.js.map +0 -1
  143. package/dist/components/pie-assessment-toolkit-element.js.map +0 -1
  144. package/dist/components/section-toolbar-element.js.map +0 -1
  145. package/dist/context/assessment-toolkit-context.js.map +0 -1
  146. package/dist/context/runtime-context-consumer.js.map +0 -1
  147. package/dist/index.js.map +0 -1
  148. package/dist/item-loader.js.map +0 -1
  149. package/dist/policy/core/PolicySource.js.map +0 -1
  150. package/dist/policy/core/ToolPolicyEngine.js.map +0 -1
  151. package/dist/policy/core/compose-decision.js.map +0 -1
  152. package/dist/policy/core/decision-types.js.map +0 -1
  153. package/dist/policy/core/engine-context.js.map +0 -1
  154. package/dist/policy/core/pnp-policy-inputs.js.map +0 -1
  155. package/dist/policy/core/policy-source-tag.js.map +0 -1
  156. package/dist/policy/core/provenance.js.map +0 -1
  157. package/dist/policy/engine.js.map +0 -1
  158. package/dist/policy/internal.js.map +0 -1
  159. package/dist/policy/sources/PnpPolicySource.js.map +0 -1
  160. package/dist/runtime/RuntimeRegistry.js.map +0 -1
  161. package/dist/runtime/SectionRuntimeEngine.js.map +0 -1
  162. package/dist/runtime/adapter/SectionEngineAdapter.js.map +0 -1
  163. package/dist/runtime/adapter/coordinator-bridge.js.map +0 -1
  164. package/dist/runtime/adapter/dom-event-bridge.js.map +0 -1
  165. package/dist/runtime/adapter/framework-error-bridge.js.map +0 -1
  166. package/dist/runtime/adapter/instrumentation-bridge.js.map +0 -1
  167. package/dist/runtime/adapter/subscriber-fanout.js.map +0 -1
  168. package/dist/runtime/catalog-registration.js.map +0 -1
  169. package/dist/runtime/core/SectionEngineCore.js.map +0 -1
  170. package/dist/runtime/core/cohort.js.map +0 -1
  171. package/dist/runtime/core/engine-input.js.map +0 -1
  172. package/dist/runtime/core/engine-output.js.map +0 -1
  173. package/dist/runtime/core/engine-readiness.js.map +0 -1
  174. package/dist/runtime/core/engine-resolver.js.map +0 -1
  175. package/dist/runtime/core/engine-stage-derivation.js.map +0 -1
  176. package/dist/runtime/core/engine-state.js.map +0 -1
  177. package/dist/runtime/core/engine-transition.js.map +0 -1
  178. package/dist/runtime/engine-context.js.map +0 -1
  179. package/dist/runtime/engine.js.map +0 -1
  180. package/dist/runtime/internal.js.map +0 -1
  181. package/dist/runtime/registration-events.js.map +0 -1
  182. package/dist/runtime/runtime-id.js.map +0 -1
  183. package/dist/runtime/section-runtime-engine-host-context.js.map +0 -1
  184. package/dist/runtime/session-event-emitter-policy.js.map +0 -1
  185. package/dist/runtime/stage-emit-gate.js.map +0 -1
  186. package/dist/runtime/tool-host-contract.js.map +0 -1
  187. package/dist/services/AccessibilityCatalogResolver.js.map +0 -1
  188. package/dist/services/ContextVariableStore.js.map +0 -1
  189. package/dist/services/ElementToolStateStore.js.map +0 -1
  190. package/dist/services/HighlightCoordinator.js.map +0 -1
  191. package/dist/services/I18nService.js.map +0 -1
  192. package/dist/services/RangeSerializer.js.map +0 -1
  193. package/dist/services/SSMLExtractor.js.map +0 -1
  194. package/dist/services/TTSService.js.map +0 -1
  195. package/dist/services/ThemeProvider.js.map +0 -1
  196. package/dist/services/ToolConfigResolver.js.map +0 -1
  197. package/dist/services/ToolCoordinator.js.map +0 -1
  198. package/dist/services/ToolRegistry.js.map +0 -1
  199. package/dist/services/ToolkitCoordinator.js.map +0 -1
  200. package/dist/services/create-tools-config.js.map +0 -1
  201. package/dist/services/createDefaultToolRegistry.js.map +0 -1
  202. package/dist/services/defaultPersonalNeedsProfile.js.map +0 -1
  203. package/dist/services/framework-error-bus.js.map +0 -1
  204. package/dist/services/framework-error.js.map +0 -1
  205. package/dist/services/interfaces.js.map +0 -1
  206. package/dist/services/pnp-standard-features.js.map +0 -1
  207. package/dist/services/section-controller-types.js.map +0 -1
  208. package/dist/services/tool-config-defaults.js.map +0 -1
  209. package/dist/services/tool-config-validation.js.map +0 -1
  210. package/dist/services/tool-context.js.map +0 -1
  211. package/dist/services/tool-instance-id.js.map +0 -1
  212. package/dist/services/tool-providers/DesmosToolProvider.js.map +0 -1
  213. package/dist/services/tool-providers/TTSToolProvider.js.map +0 -1
  214. package/dist/services/tool-providers/ToolProviderApi.js.map +0 -1
  215. package/dist/services/tool-providers/ToolProviderRegistry.js.map +0 -1
  216. package/dist/services/tool-providers/index.js.map +0 -1
  217. package/dist/services/toolbar-items.js.map +0 -1
  218. package/dist/services/tools-config-normalizer.js.map +0 -1
  219. package/dist/services/tts/browser-provider.js.map +0 -1
  220. package/dist/services/tts/catalog-span-alignment.js.map +0 -1
  221. package/dist/services/tts/generated-speech/assemble-plan.js.map +0 -1
  222. package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +0 -1
  223. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +0 -1
  224. package/dist/services/tts/generated-speech/dom/types.js.map +0 -1
  225. package/dist/services/tts/generated-speech/index.js.map +0 -1
  226. package/dist/services/tts/generated-speech/math-speech-cache.js.map +0 -1
  227. package/dist/services/tts/generated-speech/types.js.map +0 -1
  228. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +0 -1
  229. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +0 -1
  230. package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +0 -1
  231. package/dist/services/tts/highlight-pipeline/index.js.map +0 -1
  232. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +0 -1
  233. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +0 -1
  234. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +0 -1
  235. package/dist/services/tts/highlight-pipeline/readable-region.js.map +0 -1
  236. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +0 -1
  237. package/dist/services/tts/highlight-pipeline/types.js.map +0 -1
  238. package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +0 -1
  239. package/dist/services/tts/highlight-target-resolver.js.map +0 -1
  240. package/dist/services/tts/math-alignment/index.js.map +0 -1
  241. package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +0 -1
  242. package/dist/services/tts/math-alignment/range-resolver.js.map +0 -1
  243. package/dist/services/tts/math-alignment/sequence-aligner.js.map +0 -1
  244. package/dist/services/tts/math-alignment/speech-tokenizer.js.map +0 -1
  245. package/dist/services/tts/math-alignment/types.js.map +0 -1
  246. package/dist/services/tts/math-aware-text-processing.js.map +0 -1
  247. package/dist/services/tts/math-speech.js.map +0 -1
  248. package/dist/services/tts/mathml-sanitization.js.map +0 -1
  249. package/dist/services/tts/ssml/spoken-text.js.map +0 -1
  250. package/dist/services/tts/text-processing.js.map +0 -1
  251. package/dist/services/tts/text-segmentation.js.map +0 -1
  252. package/dist/services/tts/visual-line-ranges.js.map +0 -1
  253. package/dist/services/tts-control-events.js.map +0 -1
  254. package/dist/services/tts-runtime-config.js.map +0 -1
  255. package/dist/tools/calculators/desmos-provider.js.map +0 -1
  256. package/dist/tools/client.js.map +0 -1
  257. package/dist/tools/index.js.map +0 -1
  258. package/dist/tools/library-loader.js.map +0 -1
  259. package/dist/tools/registrations/accessibility-tools.js.map +0 -1
  260. package/dist/tools/registrations/calculator.js.map +0 -1
  261. package/dist/tools/registrations/interaction-tools.js.map +0 -1
  262. package/dist/tools/registrations/measurement-tools.js.map +0 -1
  263. package/dist/tools/registrations/subject-specific-tools.js.map +0 -1
  264. package/dist/tools/registrations/toolbar-registration-helpers.js.map +0 -1
  265. package/dist/tools/registrations/tts.js.map +0 -1
  266. package/dist/tools/response-discovery.js.map +0 -1
  267. package/dist/tools/tool-tag-map.js.map +0 -1
  268. package/dist/tools/types.js.map +0 -1
  269. package/dist/tools/variant-resolver.js.map +0 -1
  270. package/dist/utils/logger.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"create-highlight-plan.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/create-highlight-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeBoundaryEvent } from \"./boundary-normalizer.js\";\nimport { createHighlightDecision } from \"./highlight-policy.js\";\nimport { resolveMathBoundaryTarget } from \"./math-semantic-resolver.js\";\nimport { resolveProseBoundaryTarget } from \"./prose-boundary-resolver.js\";\nimport {\n\tclassifyMathHighlightCapability,\n\tcreateRenderedMathTargetResolver,\n\ttype MathHighlightCapability,\n\ttype RenderedMathTargetResolver,\n} from \"./rendered-math-target-resolver.js\";\nimport type {\n\tHighlightDecision,\n\tRenderableHighlightTarget,\n\tTTSBoundaryEvent,\n\tTTSHighlightChunk,\n\tTTSHighlightPlan,\n} from \"./types.js\";\n\nexport interface CreateTTSHighlightPlanArgs {\n\tchunks: TTSHighlightChunk[];\n\t// When false, math is never broken into per-token highlights: every equation\n\t// is painted as a single block — the same expression/region fallback used\n\t// whenever token alignment is not confident. Prose word tracking is\n\t// unaffected. Defaults to true.\n\tmathTokenHighlighting?: boolean;\n}\n\ninterface PlannedChunk {\n\tchunk: TTSHighlightChunk;\n\tregionTarget: RenderableHighlightTarget | null;\n\trenderedMathTargetResolver: RenderedMathTargetResolver;\n\t// Capability decided once per equation (keyed by its source math element).\n\t// Sticky for the whole chunk so the same equation never switches between\n\t// token tracking and whole-expression highlighting mid-read.\n\tmathCapabilityByElement: Map<Element, MathHighlightCapability>;\n\t// The last precise token painted for each token-mode equation. Lets a gap\n\t// (SSML break / pause / unmappable boundary) hold the last token instead of\n\t// flashing the whole expression.\n\theldTokenByElement: Map<Element, RenderableHighlightTarget>;\n}\n\nconst regionTargetFor = (\n\tchunk: TTSHighlightChunk,\n): RenderableHighlightTarget | null =>\n\tchunk.regionRange\n\t\t? {\n\t\t\t\ttype: \"range\",\n\t\t\t\tquality: \"region\",\n\t\t\t\trange: chunk.regionRange,\n\t\t\t}\n\t\t: chunk.regionElement\n\t\t\t? {\n\t\t\t\t\ttype: \"element\",\n\t\t\t\t\tquality: \"region\",\n\t\t\t\t\telement: chunk.regionElement,\n\t\t\t\t}\n\t\t\t: null;\n\nconst expressionTargetFor = (\n\tplanned: PlannedChunk,\n\tcandidates: TTSHighlightChunk[\"mathAlignments\"] = planned.chunk\n\t\t.mathAlignments,\n): RenderableHighlightTarget | null => {\n\tif (candidates.length !== 1) return null;\n\tconst fallback = candidates[0]?.alignment.result.fallbackTarget;\n\treturn fallback ? planned.renderedMathTargetResolver(fallback) : null;\n};\n\nconst mathCandidatesForTarget = (\n\ttarget: RenderableHighlightTarget | null,\n\tchunk: TTSHighlightChunk,\n): TTSHighlightChunk[\"mathAlignments\"] => {\n\tif (!target) return chunk.mathAlignments;\n\treturn chunk.mathAlignments.filter(({ element }) => {\n\t\ttry {\n\t\t\tif (target.type === \"range\") return target.range.intersectsNode(element);\n\t\t\tif (target.type === \"element\") {\n\t\t\t\treturn target.element === element || target.element.contains(element);\n\t\t\t}\n\t\t\treturn target.node.parentElement?.contains(element) || false;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n};\n\nconst missingChunkDecision = (): HighlightDecision => ({\n\tactiveTarget: null,\n\tregionTarget: null,\n\tquality: \"region\",\n\tconfidence: 0,\n\treason: \"boundary chunk id was not found\",\n});\n\nconst soleEquationElement = (\n\tcandidates: TTSHighlightChunk[\"mathAlignments\"],\n): Element | null => (candidates.length === 1 ? candidates[0].element : null);\n\nconst everyCandidateIsTokenMode = (\n\tplanned: PlannedChunk,\n\tcandidates: TTSHighlightChunk[\"mathAlignments\"],\n): boolean =>\n\tcandidates.length > 0 &&\n\tcandidates.every(\n\t\t({ element }) => planned.mathCapabilityByElement.get(element) === \"token\",\n\t);\n\nconst TEXT_NODE = 3;\n\n// Safety policy for token-mode equations. A prose \"range\" target comes from the\n// catalog span aligner, whose spoken→visible mapping inside an equation is\n// heuristic and not trustworthy at glyph granularity. A multi-node range fully\n// inside a token-mode equation is therefore rejected here in favor of holding\n// the last token — painting it would be a likely false positive, and \"false\n// positives are worse than coarse highlighting.\" A single-text-node prose range\n// is kept: it underlines one token cleanly and usefully fills in any token the\n// math resolver missed. (The highlight coordinator no longer escalates word\n// ranges to the whole <math> / <mjx-container>, so this is now a precision\n// policy rather than flash-suppression — see HighlightCoordinator\n// WORD_FALLBACK_SELECTOR.)\nconst wouldEscalateInsideTokenMath = (\n\tplanned: PlannedChunk,\n\ttarget: RenderableHighlightTarget | null,\n\tcandidates: TTSHighlightChunk[\"mathAlignments\"],\n): boolean => {\n\tif (!target || target.type !== \"range\") return false;\n\tconst { startContainer, endContainer } = target.range;\n\tconst singleTextNode =\n\t\tstartContainer === endContainer && startContainer.nodeType === TEXT_NODE;\n\tif (singleTextNode) return false;\n\treturn candidates.some(\n\t\t({ element }) =>\n\t\t\tplanned.mathCapabilityByElement.get(element) === \"token\" &&\n\t\t\telement.contains(startContainer) &&\n\t\t\telement.contains(endContainer),\n\t);\n};\n\nconst createPlannedChunk = (\n\tchunk: TTSHighlightChunk,\n\tmathTokenHighlighting: boolean,\n): PlannedChunk => ({\n\tchunk,\n\tregionTarget: regionTargetFor(chunk),\n\trenderedMathTargetResolver: createRenderedMathTargetResolver(),\n\tmathCapabilityByElement: new Map(\n\t\tchunk.mathAlignments.map(({ element }) => [\n\t\t\telement,\n\t\t\t// Forcing \"expression\" when per-token math highlighting is disabled\n\t\t\t// routes every equation through the sticky whole-block path, so the\n\t\t\t// formula is highlighted as one unit instead of glyph by glyph.\n\t\t\tmathTokenHighlighting\n\t\t\t\t? classifyMathHighlightCapability(element)\n\t\t\t\t: \"expression\",\n\t\t]),\n\t),\n\theldTokenByElement: new Map(),\n});\n\nexport const createTTSHighlightPlan = (\n\targs: CreateTTSHighlightPlanArgs,\n): TTSHighlightPlan => {\n\tconst mathTokenHighlighting = args.mathTokenHighlighting !== false;\n\tconst plannedChunksById = new Map(\n\t\targs.chunks.map((chunk) => [\n\t\t\tchunk.id,\n\t\t\tcreatePlannedChunk(chunk, mathTokenHighlighting),\n\t\t]),\n\t);\n\treturn {\n\t\tchunks: args.chunks,\n\t\tresolveInitial(chunkId: string): HighlightDecision {\n\t\t\tconst planned = plannedChunksById.get(chunkId);\n\t\t\tif (!planned) return missingChunkDecision();\n\t\t\tconst alignments = planned.chunk.mathAlignments;\n\t\t\tconst soleElement =\n\t\t\t\talignments.length === 1 ? alignments[0].element : null;\n\t\t\tconst capability = soleElement\n\t\t\t\t? planned.mathCapabilityByElement.get(soleElement)\n\t\t\t\t: null;\n\t\t\t// Sticky mode. Only an equation we already know cannot be tracked per\n\t\t\t// token gets the whole-expression paint up front. A token-mode equation\n\t\t\t// stays on the region layer until its first token resolves, so the full\n\t\t\t// equation never flashes before (or between) tokens.\n\t\t\tif (capability === \"expression\") {\n\t\t\t\treturn createHighlightDecision({\n\t\t\t\t\tsemanticTarget: null,\n\t\t\t\t\texpressionTarget: expressionTargetFor(planned),\n\t\t\t\t\tregionTarget: planned.regionTarget,\n\t\t\t\t\tconfidence: 0.5,\n\t\t\t\t\treason: \"initial expression (sticky expression mode)\",\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn createHighlightDecision({\n\t\t\t\tsemanticTarget: null,\n\t\t\t\texpressionTarget: null,\n\t\t\t\tregionTarget: planned.regionTarget,\n\t\t\t\tconfidence: 0,\n\t\t\t\treason:\n\t\t\t\t\tcapability === \"token\"\n\t\t\t\t\t\t? \"initial region (sticky token mode, awaiting first token)\"\n\t\t\t\t\t\t: \"initial region fallback\",\n\t\t\t});\n\t\t},\n\t\tresolveBoundary(event: TTSBoundaryEvent): HighlightDecision {\n\t\t\tconst planned = plannedChunksById.get(event.chunkId);\n\t\t\tif (!planned) return missingChunkDecision();\n\t\t\tconst { chunk } = planned;\n\t\t\tconst normalized = normalizeBoundaryEvent(chunk, event);\n\t\t\tconst proseTarget = resolveProseBoundaryTarget(chunk, normalized);\n\t\t\tconst mathCandidates = mathCandidatesForTarget(proseTarget, chunk);\n\t\t\tconst mathTarget =\n\t\t\t\t(!proseTarget || mathCandidates.length > 0\n\t\t\t\t\t? resolveMathBoundaryTarget(\n\t\t\t\t\t\t\tchunk,\n\t\t\t\t\t\t\tnormalized,\n\t\t\t\t\t\t\tevent,\n\t\t\t\t\t\t\tmathCandidates,\n\t\t\t\t\t\t\tplanned.renderedMathTargetResolver,\n\t\t\t\t\t\t)\n\t\t\t\t\t: null) || null;\n\t\t\tconst mathTokenTarget =\n\t\t\t\tmathTokenHighlighting &&\n\t\t\t\tmathTarget &&\n\t\t\t\tmathTarget.quality !== \"expression\"\n\t\t\t\t\t? mathTarget\n\t\t\t\t\t: null;\n\t\t\tconst mathExpressionFallback =\n\t\t\t\tmathTarget && mathTarget.quality === \"expression\" ? mathTarget : null;\n\n\t\t\t// (1) A precise token always wins, and becomes the held target for its\n\t\t\t// equation so a later gap can keep it lit instead of flashing the whole\n\t\t\t// expression.\n\t\t\tif (mathTokenTarget) {\n\t\t\t\tconst equationElement = soleEquationElement(mathCandidates);\n\t\t\t\tif (equationElement) {\n\t\t\t\t\tplanned.heldTokenByElement.set(equationElement, mathTokenTarget);\n\t\t\t\t}\n\t\t\t\treturn createHighlightDecision({\n\t\t\t\t\tsemanticTarget: mathTokenTarget,\n\t\t\t\t\texpressionTarget: null,\n\t\t\t\t\tregionTarget: planned.regionTarget,\n\t\t\t\t\tconfidence: normalized.confidence,\n\t\t\t\t\treason: normalized.reason,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// (2) A spoken word that maps to visible prose. The prose anchor is\n\t\t\t// precise; a coarse math expression fallback must never override it.\n\t\t\t// Exception: a multi-node prose range inside a token-mode equation\n\t\t\t// would escalate to the whole expression, so it is rejected here and\n\t\t\t// falls through to the token hold below. When per-token math\n\t\t\t// highlighting is disabled, any prose range that lands inside a\n\t\t\t// formula is likewise rejected so the formula stays a single block.\n\t\t\tif (\n\t\t\t\tproseTarget &&\n\t\t\t\t(mathTokenHighlighting\n\t\t\t\t\t? !wouldEscalateInsideTokenMath(planned, proseTarget, mathCandidates)\n\t\t\t\t\t: mathCandidates.length === 0)\n\t\t\t) {\n\t\t\t\treturn createHighlightDecision({\n\t\t\t\t\tsemanticTarget: proseTarget,\n\t\t\t\t\texpressionTarget: null,\n\t\t\t\t\tregionTarget: planned.regionTarget,\n\t\t\t\t\tconfidence: normalized.confidence,\n\t\t\t\t\treason: normalized.reason,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// (3) A boundary inside a token-mode equation that did not map to a\n\t\t\t// token — an SSML break, a pause, or a gap between spoken tokens. Hold\n\t\t\t// the last token instead of flashing the whole expression; before the\n\t\t\t// first token there is nothing to hold, so fall to the region layer.\n\t\t\t// The whole expression is deliberately never painted in token mode.\n\t\t\tif (everyCandidateIsTokenMode(planned, mathCandidates)) {\n\t\t\t\tconst equationElement = soleEquationElement(mathCandidates);\n\t\t\t\tconst heldToken = equationElement\n\t\t\t\t\t? (planned.heldTokenByElement.get(equationElement) ?? null)\n\t\t\t\t\t: null;\n\t\t\t\treturn createHighlightDecision({\n\t\t\t\t\tsemanticTarget: heldToken,\n\t\t\t\t\texpressionTarget: null,\n\t\t\t\t\tregionTarget: planned.regionTarget,\n\t\t\t\t\tconfidence: normalized.confidence,\n\t\t\t\t\treason: heldToken\n\t\t\t\t\t\t? \"hold last token (sticky token mode)\"\n\t\t\t\t\t\t: \"region (sticky token mode, awaiting first token)\",\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// (4) An expression-mode (or mixed/ambiguous) math region: paint the\n\t\t\t// whole equation. Stable for the equation's full duration because\n\t\t\t// tokens never resolve here, so there is no flicker.\n\t\t\treturn createHighlightDecision({\n\t\t\t\tsemanticTarget: null,\n\t\t\t\texpressionTarget:\n\t\t\t\t\tmathExpressionFallback ??\n\t\t\t\t\texpressionTargetFor(planned, mathCandidates),\n\t\t\t\tregionTarget: planned.regionTarget,\n\t\t\t\tconfidence: normalized.confidence,\n\t\t\t\treason: normalized.reason,\n\t\t\t});\n\t\t},\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"highlight-policy.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/highlight-policy.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACtC,KAA6B,EACT,EAAE;IACtB,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;QAC1B,OAAO;YACN,YAAY,EAAE,KAAK,CAAC,cAAc;YAClC,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,OAAO,EACN,KAAK,CAAC,cAAc,CAAC,OAAO,KAAK,YAAY;gBAC5C,CAAC,CAAC,YAAY;gBACd,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,KAAK,YAAY;oBAC9C,CAAC,CAAC,YAAY;oBACd,CAAC,CAAC,gBAAgB;YACrB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;SACpB,CAAC;IACH,CAAC;IACD,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC5B,OAAO;YACN,YAAY,EAAE,KAAK,CAAC,gBAAgB;YACpC,YAAY,EAAE,KAAK,CAAC,YAAY;YAChC,OAAO,EAAE,YAAY;YACrB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;SACpB,CAAC;IACH,CAAC;IACD,OAAO;QACN,YAAY,EAAE,IAAI;QAClB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,OAAO,EAAE,QAAQ;QACjB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,MAAM,EAAE,KAAK,CAAC,MAAM;KACpB,CAAC;AACH,CAAC,CAAC","sourcesContent":["import type { HighlightDecision, HighlightDecisionInput } from \"./types.js\";\n\nexport const createHighlightDecision = (\n\tinput: HighlightDecisionInput,\n): HighlightDecision => {\n\tif (input.semanticTarget) {\n\t\treturn {\n\t\t\tactiveTarget: input.semanticTarget,\n\t\t\tregionTarget: input.regionTarget,\n\t\t\tquality:\n\t\t\t\tinput.semanticTarget.quality === \"exact-word\"\n\t\t\t\t\t? \"exact-word\"\n\t\t\t\t\t: input.semanticTarget.quality === \"expression\"\n\t\t\t\t\t\t? \"expression\"\n\t\t\t\t\t\t: \"semantic-token\",\n\t\t\tconfidence: input.confidence,\n\t\t\treason: input.reason,\n\t\t};\n\t}\n\tif (input.expressionTarget) {\n\t\treturn {\n\t\t\tactiveTarget: input.expressionTarget,\n\t\t\tregionTarget: input.regionTarget,\n\t\t\tquality: \"expression\",\n\t\t\tconfidence: input.confidence,\n\t\t\treason: input.reason,\n\t\t};\n\t}\n\treturn {\n\t\tactiveTarget: null,\n\t\tregionTarget: input.regionTarget,\n\t\tquality: \"region\",\n\t\tconfidence: input.confidence,\n\t\treason: input.reason,\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uBAAuB,CAAC;AACtC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,sBAAsB,CAAC;AACrC,cAAc,oCAAoC,CAAC","sourcesContent":["export * from \"./boundary-normalizer.js\";\nexport * from \"./create-highlight-plan.js\";\nexport * from \"./highlight-policy.js\";\nexport * from \"./math-semantic-resolver.js\";\nexport * from \"./normalize-speech-chunks.js\";\nexport * from \"./prose-boundary-resolver.js\";\nexport * from \"./readable-region.js\";\nexport * from \"./rendered-math-target-resolver.js\";\nexport type * from \"./types.js\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"math-semantic-resolver.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/math-semantic-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,8CAA8C,EAC9C,sCAAsC,GACtC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACN,yBAAyB,GAEzB,MAAM,oCAAoC,CAAC;AAQ5C,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACxC,KAAwB,EACxB,QAAiC,EACjC,KAAuB,EACvB,aAAkD,KAAK,CAAC,cAAc,EACtE,mBAA+C,yBAAyB,EACrC,EAAE;IACrC,IAAI,QAAQ,CAAC,gBAAgB,KAAK,IAAI,IAAI,QAAQ,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QAC5E,OAAO,IAAI,CAAC;IACb,CAAC;IACD,0EAA0E;IAC1E,wEAAwE;IACxE,6EAA6E;IAC7E,iBAAiB;IACjB,IAAI,kBAAkB,GAAqC,IAAI,CAAC;IAChE,KAAK,MAAM,aAAa,IAAI,UAAU,EAAE,CAAC;QACxC,MAAM,MAAM,GACX,8CAA8C,CAAC,aAAa,CAAC,SAAS,EAAE;YACvE,QAAQ,EAAE,QAAQ,CAAC,gBAAgB;YACnC,MAAM,EAAE,IAAI,CAAC,GAAG,CACf,CAAC,EACD,QAAQ,CAAC,cAAc,GAAG,QAAQ,CAAC,gBAAgB,CACnD;YACD,YAAY,EAAE,KAAK,CAAC,IAAI;SACxB,CAAC;YACF,sCAAsC,CAAC,aAAa,CAAC,SAAS,EAAE;gBAC/D,YAAY,EAAE,KAAK,CAAC,IAAI;aACxB,CAAC,CAAC;QACJ,IAAI,CAAC,MAAM;YAAE,SAAS;QACtB,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ;YAAE,SAAS;QACxB,IAAI,QAAQ,CAAC,OAAO,KAAK,gBAAgB;YAAE,OAAO,QAAQ,CAAC;QAC3D,kBAAkB,KAAK,QAAQ,CAAC;IACjC,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC3B,CAAC,CAAC","sourcesContent":["import {\n\tresolveMatchedHighlightTargetForSpokenBoundary,\n\tresolveUniqueMathTargetForBoundaryWord,\n} from \"../math-alignment/index.js\";\nimport {\n\tresolveRenderedMathTarget,\n\ttype RenderedMathTargetResolver,\n} from \"./rendered-math-target-resolver.js\";\nimport type {\n\tNormalizedBoundaryEvent,\n\tRenderableHighlightTarget,\n\tTTSBoundaryEvent,\n\tTTSHighlightChunk,\n} from \"./types.js\";\n\nexport const resolveMathBoundaryTarget = (\n\tchunk: TTSHighlightChunk,\n\tboundary: NormalizedBoundaryEvent,\n\tevent: TTSBoundaryEvent,\n\tcandidates: TTSHighlightChunk[\"mathAlignments\"] = chunk.mathAlignments,\n\trenderedResolver: RenderedMathTargetResolver = resolveRenderedMathTarget,\n): RenderableHighlightTarget | null => {\n\tif (boundary.chunkSpokenStart === null || boundary.chunkSpokenEnd === null) {\n\t\treturn null;\n\t}\n\t// Prefer a precise per-token target across all candidate equations before\n\t// settling for a coarse expression fallback: scan every candidate for a\n\t// semantic-token first, remembering the first expression-quality hit only as\n\t// a last resort.\n\tlet expressionFallback: RenderableHighlightTarget | null = null;\n\tfor (const mathAlignment of candidates) {\n\t\tconst target =\n\t\t\tresolveMatchedHighlightTargetForSpokenBoundary(mathAlignment.alignment, {\n\t\t\t\tposition: boundary.chunkSpokenStart,\n\t\t\t\tlength: Math.max(\n\t\t\t\t\t1,\n\t\t\t\t\tboundary.chunkSpokenEnd - boundary.chunkSpokenStart,\n\t\t\t\t),\n\t\t\t\tboundaryWord: event.word,\n\t\t\t}) ||\n\t\t\tresolveUniqueMathTargetForBoundaryWord(mathAlignment.alignment, {\n\t\t\t\tboundaryWord: event.word,\n\t\t\t});\n\t\tif (!target) continue;\n\t\tconst rendered = renderedResolver(target);\n\t\tif (!rendered) continue;\n\t\tif (rendered.quality === \"semantic-token\") return rendered;\n\t\texpressionFallback ??= rendered;\n\t}\n\treturn expressionFallback;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"normalize-speech-chunks.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/normalize-speech-chunks.ts"],"names":[],"mappings":"AA4BA,MAAM,mBAAmB,GAAG,CAC3B,KAAkC,EACf,EAAE;IACrB,IAAI,KAAK,CAAC,SAAS,EAAE,kBAAkB;QACtC,OAAO,KAAK,CAAC,SAAS,CAAC,kBAAkB,CAAC;IAC3C,MAAM,eAAe,GAAG,KAAK,CAAC,aAAa,EAAE,MAAM,CAAC,mBAAmB,CAAC;IACxE,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAC5C,OAAO,mBAAmB,CAAC;AAC5B,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACzB,KAAkC,EAC2B,EAAE;IAC/D,IAAI,KAAK,CAAC,cAAc,EAAE,MAAM;QAAE,OAAO,KAAK,CAAC,cAAc,CAAC;IAC9D,IAAI,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;QAChD,OAAO,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,aAAa,EAAE,SAAS,EAAE,KAAK,CAAC,aAAa,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,EAAE,CAAC;AACX,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACpC,IAA+B,EACT,EAAE;IACxB,MAAM,YAAY,GACjB,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM;QAChC,CAAC,CAAC,IAAI,CAAC,MAAM;QACb,CAAC,CAAC;YACA;gBACC,UAAU,EAAE,IAAI,CAAC,UAAU,IAAI,EAAE;gBACjC,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE;gBACtD,aAAa,EAAE,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,WAAW;gBACrD,aAAa,EACZ,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,aAAa,IAAI,IAAI,CAAC,WAAW;gBAC7D,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,UAAU,EAAE,IAAI,CAAC,UAAU;aAC3B;SACD,CAAC;IACL,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC1C,EAAE,EAAE,aAAa,KAAK,EAAE;QACxB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK,CAAC,aAAa;QACzD,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,gBAAgB,EAAE,KAAK,CAAC,SAAS;QACjC,cAAc,EAAE,iBAAiB,CAAC,KAAK,CAAC;QACxC,WAAW,EAAE,mBAAmB,CAAC,KAAK,CAAC;KACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC","sourcesContent":["import type { CatalogSpanAlignment } from \"../catalog-span-alignment.js\";\nimport type { MathAwareAlignment } from \"../math-alignment/index.js\";\nimport type { NormalizedTextMap } from \"../text-processing.js\";\nimport type { ChunkOffsetSpace, TTSHighlightChunk } from \"./types.js\";\n\nexport interface SpeechCompositionChunkInput {\n\tspeechText: string;\n\tvisibleText: string;\n\tsourceElement: Element | null;\n\tregionElement?: Element | null;\n\tregionRange?: Range;\n\talignment?: CatalogSpanAlignment;\n\tmathAlignment?: MathAwareAlignment;\n\tmathAlignments?: Array<{ element: Element; alignment: MathAwareAlignment }>;\n\tvisibleMap?: NormalizedTextMap;\n}\n\nexport interface NormalizeSpeechChunksArgs {\n\tcontentRoot: Element;\n\tchunks?: SpeechCompositionChunkInput[];\n\tspeechText?: string;\n\tvisibleText?: string;\n\tsourceElement?: Element | null;\n\tregionElement?: Element | null;\n\tregionRange?: Range;\n\tvisibleMap?: NormalizedTextMap;\n}\n\nconst chunkOffsetSpaceFor = (\n\tchunk: SpeechCompositionChunkInput,\n): ChunkOffsetSpace => {\n\tif (chunk.alignment?.boundaryOffsetMode)\n\t\treturn chunk.alignment.boundaryOffsetMode;\n\tconst mathOffsetSpace = chunk.mathAlignment?.speech.boundaryOffsetSpace;\n\tif (mathOffsetSpace) return mathOffsetSpace;\n\treturn \"plain-spoken-text\";\n};\n\nconst mathAlignmentsFor = (\n\tchunk: SpeechCompositionChunkInput,\n): Array<{ element: Element; alignment: MathAwareAlignment }> => {\n\tif (chunk.mathAlignments?.length) return chunk.mathAlignments;\n\tif (chunk.mathAlignment && chunk.sourceElement) {\n\t\treturn [{ element: chunk.sourceElement, alignment: chunk.mathAlignment }];\n\t}\n\treturn [];\n};\n\nexport const normalizeSpeechChunks = (\n\targs: NormalizeSpeechChunksArgs,\n): TTSHighlightChunk[] => {\n\tconst sourceChunks =\n\t\targs.chunks && args.chunks.length\n\t\t\t? args.chunks\n\t\t\t: [\n\t\t\t\t\t{\n\t\t\t\t\t\tspeechText: args.speechText || \"\",\n\t\t\t\t\t\tvisibleText: args.visibleText || args.speechText || \"\",\n\t\t\t\t\t\tsourceElement: args.sourceElement || args.contentRoot,\n\t\t\t\t\t\tregionElement:\n\t\t\t\t\t\t\targs.regionElement || args.sourceElement || args.contentRoot,\n\t\t\t\t\t\tregionRange: args.regionRange,\n\t\t\t\t\t\tvisibleMap: args.visibleMap,\n\t\t\t\t\t},\n\t\t\t\t];\n\treturn sourceChunks.map((chunk, index) => ({\n\t\tid: `tts-chunk-${index}`,\n\t\tspeechText: chunk.speechText,\n\t\tvisibleText: chunk.visibleText,\n\t\tsourceElement: chunk.sourceElement,\n\t\tcontentRoot: args.contentRoot,\n\t\tregionElement: chunk.regionElement ?? chunk.sourceElement,\n\t\tregionRange: chunk.regionRange,\n\t\tvisibleMap: chunk.visibleMap,\n\t\tcatalogAlignment: chunk.alignment,\n\t\tmathAlignments: mathAlignmentsFor(chunk),\n\t\toffsetSpace: chunkOffsetSpaceFor(chunk),\n\t}));\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"prose-boundary-resolver.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/prose-boundary-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,6BAA6B,EAAE,MAAM,8BAA8B,CAAC;AAC7E,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAOnE,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACzC,KAAwB,EACxB,QAAiC,EACE,EAAE;IACrC,IAAI,CAAC,KAAK,CAAC,gBAAgB,IAAI,QAAQ,CAAC,gBAAgB,KAAK,IAAI;QAChE,OAAO,IAAI,CAAC;IACb,MAAM,WAAW,GAAG,6BAA6B,CAChD,KAAK,CAAC,gBAAgB,EACtB,QAAQ,CAAC,gBAAgB,CACzB,CAAC;IACF,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,KAAK,GAAG,yBAAyB,CACtC,KAAK,CAAC,UAAU,EAChB,WAAW,CAAC,KAAK,EACjB,WAAW,CAAC,GAAG,CACf,CAAC;IACF,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO;QACN,IAAI,EAAE,OAAO;QACb,OAAO,EACN,KAAK,CAAC,gBAAgB,CAAC,YAAY,KAAK,YAAY;YACnD,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,gBAAgB;QACpB,KAAK;KACL,CAAC;AACH,CAAC,CAAC","sourcesContent":["import { resolveVisibleSpanForBoundary } from \"../catalog-span-alignment.js\";\nimport { createRangeFromVisibleMap } from \"./visible-map-range.js\";\nimport type {\n\tNormalizedBoundaryEvent,\n\tRenderableHighlightTarget,\n\tTTSHighlightChunk,\n} from \"./types.js\";\n\nexport const resolveProseBoundaryTarget = (\n\tchunk: TTSHighlightChunk,\n\tboundary: NormalizedBoundaryEvent,\n): RenderableHighlightTarget | null => {\n\tif (!chunk.catalogAlignment || boundary.chunkSpokenStart === null)\n\t\treturn null;\n\tconst visibleSpan = resolveVisibleSpanForBoundary(\n\t\tchunk.catalogAlignment,\n\t\tboundary.chunkSpokenStart,\n\t);\n\tif (!visibleSpan) return null;\n\tconst range = createRangeFromVisibleMap(\n\t\tchunk.visibleMap,\n\t\tvisibleSpan.start,\n\t\tvisibleSpan.end,\n\t);\n\tif (!range) return null;\n\treturn {\n\t\ttype: \"range\",\n\t\tquality:\n\t\t\tchunk.catalogAlignment.playbackMode === \"exact-word\"\n\t\t\t\t? \"exact-word\"\n\t\t\t\t: \"semantic-token\",\n\t\trange,\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"readable-region.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/readable-region.ts"],"names":[],"mappings":"AAAA,MAAM,wBAAwB,GAAG;IAChC,uBAAuB;IACvB,uBAAuB;IACvB,IAAI;IACJ,GAAG;IACH,OAAO;IACP,SAAS;IACT,gBAAgB;IAChB,iBAAiB;IACjB,KAAK;CACL,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,MAAM,CAAC,MAAM,qBAAqB,GAAG,CACpC,aAAsB,EACtB,WAAoB,EACV,EAAE;IACZ,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC/D,IAAI,MAAM,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,aAAa,CAAC;AACtB,CAAC,CAAC","sourcesContent":["const READABLE_REGION_SELECTOR = [\n\t\"[data-pie-tts-region]\",\n\t\"[data-catalog-region]\",\n\t\"li\",\n\t\"p\",\n\t\"label\",\n\t\"section\",\n\t\"[role='group']\",\n\t\"[role='region']\",\n\t\"div\",\n].join(\",\");\n\nexport const resolveReadableRegion = (\n\tsourceElement: Element,\n\tcontentRoot: Element,\n): Element => {\n\tconst region = sourceElement.closest(READABLE_REGION_SELECTOR);\n\tif (region && contentRoot.contains(region)) {\n\t\treturn region;\n\t}\n\treturn sourceElement;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"rendered-math-target-resolver.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/rendered-math-target-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { isNodeHiddenForTTS } from \"../text-processing.js\";\nimport type {\n\tSemanticHighlightTarget,\n\tRenderableHighlightTarget,\n} from \"./types.js\";\n\nconst MATHML_LEAF_TO_MATHJAX = new Map([\n\t[\"mi\", \"mjx-mi\"],\n\t[\"mn\", \"mjx-mn\"],\n\t[\"mo\", \"mjx-mo\"],\n\t[\"mtext\", \"mjx-mtext\"],\n]);\n\nconst MATHML_LEAF_SELECTOR = \"mi,mn,mo,mtext\";\nconst MATHJAX_LEAF_SELECTOR = \"mjx-mi,mjx-mn,mjx-mo,mjx-mtext\";\n\n// 2D layouts MathJax renders with extra, glyph-only nodes (radical surds,\n// fraction bars drawn as borders, table scaffolding) that have no source-MathML\n// leaf counterpart. They break the 1:1 token correspondence we rely on below,\n// so we refuse positional mapping inside them and fall back to the whole\n// expression instead of guessing.\nconst UNSUPPORTED_MATHJAX_LAYOUT_SELECTOR = [\n\t\"mjx-mfrac\",\n\t\"mjx-msqrt\",\n\t\"mjx-mroot\",\n\t\"mjx-mtable\",\n\t\"mjx-munder\",\n\t\"mjx-mover\",\n\t\"mjx-munderover\",\n].join(\",\");\n\nconst nearestElement = (target: SemanticHighlightTarget): Element | null => {\n\tif (target.type === \"text-range\") return target.node.parentElement;\n\treturn target.element;\n};\n\nconst owningMathJaxContainer = (element: Element | null): Element | null => {\n\tlet current: Element | null = element;\n\twhile (current) {\n\t\tif (current.localName?.toLowerCase() === \"mjx-container\") return current;\n\t\tcurrent = current.parentElement;\n\t}\n\treturn null;\n};\n\nconst isVisibleNativeTarget = (element: Element | null): boolean => {\n\tif (!element) return false;\n\tif (owningMathJaxContainer(element)) return false;\n\treturn !isNodeHiddenForTTS(element);\n};\n\nconst hasVisibleGeometry = (element: Element): boolean => {\n\tconst rect = (element as HTMLElement).getBoundingClientRect?.();\n\treturn Boolean(rect && rect.width > 0 && rect.height > 0);\n};\n\nconst closestMathElement = (element: Element | null): Element | null => {\n\tlet current: Element | null = element;\n\twhile (current) {\n\t\tif (current.localName?.toLowerCase() === \"math\") return current;\n\t\tcurrent = current.parentElement;\n\t}\n\treturn null;\n};\n\n// MathJax CHTML renders each source-MathML token (mi/mn/mo/mtext) as exactly\n// one mjx-* token in the same document order — including invisible operators,\n// which it keeps as zero-size nodes. The original <math> survives, clipped, in\n// mjx-assistive-mml, so its glyph text nodes are not visible and the visible\n// CHTML tokens carry no readable textContent (glyphs come from web fonts).\n// We therefore map a source token to its rendered glyph by *structural\n// position* rather than text, but only after proving the two leaf lists are\n// isomorphic (equal length, matching token type at every index). When the\n// structure diverges we refuse to guess and let the caller fall back to the\n// whole expression. \"False positives are worse than coarse highlighting.\"\nconst tokensAreIsomorphic = (\n\tmathLeaves: Element[],\n\tchtmlLeaves: Element[],\n): boolean => {\n\tif (mathLeaves.length === 0) return false;\n\tif (mathLeaves.length !== chtmlLeaves.length) return false;\n\tfor (let index = 0; index < mathLeaves.length; index += 1) {\n\t\tconst expected = MATHML_LEAF_TO_MATHJAX.get(\n\t\t\tmathLeaves[index].localName.toLowerCase(),\n\t\t);\n\t\tif (!expected) return false;\n\t\tif (chtmlLeaves[index].localName.toLowerCase() !== expected) return false;\n\t}\n\treturn true;\n};\n\nconst resolveMathJaxTokenByPosition = (\n\tsemanticElement: Element | null,\n\tcontainer: Element,\n): Element | null => {\n\tif (!semanticElement) return null;\n\tif (!MATHML_LEAF_TO_MATHJAX.has(semanticElement.localName.toLowerCase()))\n\t\treturn null;\n\tif (container.querySelector(UNSUPPORTED_MATHJAX_LAYOUT_SELECTOR)) return null;\n\tconst sourceMath = closestMathElement(semanticElement);\n\tconst renderedMath = container.querySelector(\"mjx-math\");\n\tif (!sourceMath || !renderedMath) return null;\n\tconst mathLeaves = Array.from(\n\t\tsourceMath.querySelectorAll(MATHML_LEAF_SELECTOR),\n\t);\n\tconst index = mathLeaves.indexOf(semanticElement);\n\tif (index < 0) return null;\n\tconst chtmlLeaves = Array.from(\n\t\trenderedMath.querySelectorAll(MATHJAX_LEAF_SELECTOR),\n\t);\n\tif (!tokensAreIsomorphic(mathLeaves, chtmlLeaves)) return null;\n\tconst token = chtmlLeaves[index];\n\t// The matched token must be a real, visible glyph. We deliberately do NOT\n\t// run isNodeHiddenForTTS here: MathJax marks its visual CHTML\n\t// (mjx-math) with aria-hidden=\"true\" because the accessible tree lives in\n\t// the clipped mjx-assistive-mml. aria-hidden is correct for screen readers\n\t// but irrelevant for a visual highlight — the glyph is painted on screen.\n\t// Visible geometry is the right test: it rejects display:none nodes and\n\t// zero-size invisible operators (e.g. U+2062 invisible-times, which is\n\t// never spoken anyway) without discarding the real, on-screen tokens.\n\tif (!token || !hasVisibleGeometry(token)) return null;\n\treturn token;\n};\n\n// Per-equation highlight capability, decided once before playback. \"token\"\n// means we are confident every spoken token can be mapped to a visible glyph,\n// so the equation tracks word-by-word and the whole expression is never\n// painted. \"expression\" means we cannot reliably map tokens (a 2D MathJax\n// layout, a non-isomorphic render, or nothing visible), so the equation is\n// highlighted as one stable block for its entire spoken duration. Deciding\n// once and committing is what stops the highlight from flickering between a\n// token and the whole expression during breaks/pauses.\nexport type MathHighlightCapability = \"token\" | \"expression\";\n\nexport const classifyMathHighlightCapability = (\n\tmathElement: Element,\n): MathHighlightCapability => {\n\tconst container = owningMathJaxContainer(mathElement);\n\tif (container) {\n\t\t// 2D MathJax layouts add glyph-only nodes with no source-MathML\n\t\t// counterpart, so positional mapping is unsafe — commit to expression.\n\t\tif (container.querySelector(UNSUPPORTED_MATHJAX_LAYOUT_SELECTOR)) {\n\t\t\treturn \"expression\";\n\t\t}\n\t\tconst sourceMath = closestMathElement(mathElement) ?? mathElement;\n\t\tconst renderedMath = container.querySelector(\"mjx-math\");\n\t\tif (!renderedMath) return \"expression\";\n\t\tconst mathLeaves = Array.from(\n\t\t\tsourceMath.querySelectorAll(MATHML_LEAF_SELECTOR),\n\t\t);\n\t\tconst chtmlLeaves = Array.from(\n\t\t\trenderedMath.querySelectorAll(MATHJAX_LEAF_SELECTOR),\n\t\t);\n\t\tif (!tokensAreIsomorphic(mathLeaves, chtmlLeaves)) return \"expression\";\n\t\t// At least one rendered token must paint a real glyph; otherwise a\n\t\t// per-token highlight would have nothing visible to land on.\n\t\treturn chtmlLeaves.some((leaf) => hasVisibleGeometry(leaf))\n\t\t\t? \"token\"\n\t\t\t: \"expression\";\n\t}\n\t// Native MathML: every leaf is a real, browser-positioned element, so\n\t// per-token highlighting is reliable even inside 2D layouts (fractions,\n\t// radicals). It is token-capable as long as a visible leaf exists.\n\tconst sourceMath = closestMathElement(mathElement) ?? mathElement;\n\tconst leaves = Array.from(sourceMath.querySelectorAll(MATHML_LEAF_SELECTOR));\n\treturn leaves.some((leaf) => !isNodeHiddenForTTS(leaf))\n\t\t? \"token\"\n\t\t: \"expression\";\n};\n\nexport type RenderedMathTargetResolver = (\n\ttarget: SemanticHighlightTarget,\n) => RenderableHighlightTarget | null;\n\nconst resolveRenderedMathTargetUncached = (\n\ttarget: SemanticHighlightTarget,\n): RenderableHighlightTarget | null => {\n\tconst element = nearestElement(target);\n\tconst mathJaxContainer = owningMathJaxContainer(element);\n\tif (mathJaxContainer) {\n\t\tconst visibleToken = resolveMathJaxTokenByPosition(\n\t\t\telement,\n\t\t\tmathJaxContainer,\n\t\t);\n\t\tif (visibleToken) {\n\t\t\treturn {\n\t\t\t\ttype: \"element\",\n\t\t\t\tquality: \"semantic-token\",\n\t\t\t\telement: visibleToken,\n\t\t\t};\n\t\t}\n\t\treturn {\n\t\t\ttype: \"element\",\n\t\t\tquality: \"expression\",\n\t\t\telement: mathJaxContainer,\n\t\t};\n\t}\n\tif (target.type === \"text-range\") {\n\t\tif (!isVisibleNativeTarget(target.node.parentElement)) return null;\n\t\treturn {\n\t\t\ttype: \"text-range\",\n\t\t\tquality: \"semantic-token\",\n\t\t\tnode: target.node,\n\t\t\tstartOffset: target.startOffset,\n\t\t\tendOffset: target.endOffset,\n\t\t};\n\t}\n\tif (!isVisibleNativeTarget(target.element)) return null;\n\treturn {\n\t\ttype: \"element\",\n\t\tquality:\n\t\t\ttarget.quality === \"region-fallback\" ? \"expression\" : \"semantic-token\",\n\t\telement: target.element,\n\t};\n};\n\nexport const resolveRenderedMathTarget: RenderedMathTargetResolver =\n\tresolveRenderedMathTargetUncached;\n\nexport const createRenderedMathTargetResolver =\n\t(): RenderedMathTargetResolver => {\n\t\tconst mathJaxTargetCache = new WeakMap<\n\t\t\tElement,\n\t\t\tRenderableHighlightTarget | null\n\t\t>();\n\t\treturn (target) => {\n\t\t\tconst element = nearestElement(target);\n\t\t\tconst mathJaxContainer = owningMathJaxContainer(element);\n\t\t\tif (!element || !mathJaxContainer) {\n\t\t\t\treturn resolveRenderedMathTargetUncached(target);\n\t\t\t}\n\t\t\tif (mathJaxTargetCache.has(element)) {\n\t\t\t\treturn mathJaxTargetCache.get(element) ?? null;\n\t\t\t}\n\t\t\tconst rendered = resolveRenderedMathTargetUncached(target);\n\t\t\tmathJaxTargetCache.set(element, rendered);\n\t\t\treturn rendered;\n\t\t};\n\t};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { CatalogSpanAlignment } from \"../catalog-span-alignment.js\";\nimport type {\n\tMathAwareAlignment,\n\tHighlightTarget,\n} from \"../math-alignment/index.js\";\nimport type { NormalizedTextMap } from \"../text-processing.js\";\n\nexport type ProviderOffsetSpace =\n\t| \"plain-spoken-text\"\n\t| \"raw-ssml\"\n\t| \"provider-speech-marks\"\n\t| \"unknown\";\n\nexport type ChunkOffsetSpace =\n\t| Exclude<ProviderOffsetSpace, \"unknown\">\n\t| \"unsupported\";\n\nexport interface TTSHighlightChunk {\n\tid: string;\n\tspeechText: string;\n\tvisibleText: string;\n\tsourceElement: Element | null;\n\tcontentRoot: Element;\n\tregionElement: Element | null;\n\tregionRange?: Range;\n\tvisibleMap?: NormalizedTextMap;\n\tcatalogAlignment?: CatalogSpanAlignment;\n\tmathAlignments: Array<{ element: Element; alignment: MathAwareAlignment }>;\n\toffsetSpace: ChunkOffsetSpace;\n}\n\nexport interface TTSBoundaryEvent {\n\tchunkId: string;\n\tword: string;\n\tposition: number;\n\tlength?: number;\n\tproviderOffsetSpace: ProviderOffsetSpace;\n}\n\nexport interface NormalizedBoundaryEvent {\n\tchunkId: string;\n\tnormalizedWord: string | null;\n\tchunkSpokenStart: number | null;\n\tchunkSpokenEnd: number | null;\n\tconfidence: number;\n\treason: string;\n}\n\nexport type RenderableHighlightTarget =\n\t| {\n\t\t\ttype: \"text-range\";\n\t\t\tquality: \"exact-word\" | \"semantic-token\";\n\t\t\tnode: Text;\n\t\t\tstartOffset: number;\n\t\t\tendOffset: number;\n\t }\n\t| {\n\t\t\ttype: \"range\";\n\t\t\tquality: \"exact-word\" | \"semantic-token\" | \"expression\" | \"region\";\n\t\t\trange: Range;\n\t }\n\t| {\n\t\t\ttype: \"element\";\n\t\t\tquality: \"semantic-token\" | \"expression\" | \"region\";\n\t\t\telement: Element;\n\t };\n\nexport type HighlightDecisionQuality =\n\t| \"exact-word\"\n\t| \"semantic-token\"\n\t| \"expression\"\n\t| \"region\";\n\nexport interface HighlightDecision {\n\tactiveTarget: RenderableHighlightTarget | null;\n\tregionTarget: RenderableHighlightTarget | null;\n\tquality: HighlightDecisionQuality;\n\tconfidence: number;\n\treason: string;\n}\n\nexport interface HighlightDecisionInput {\n\tsemanticTarget: RenderableHighlightTarget | null;\n\texpressionTarget: RenderableHighlightTarget | null;\n\tregionTarget: RenderableHighlightTarget | null;\n\tconfidence: number;\n\treason: string;\n}\n\nexport interface TTSHighlightPlan {\n\tchunks: TTSHighlightChunk[];\n\tresolveInitial(chunkId: string): HighlightDecision;\n\tresolveBoundary(event: TTSBoundaryEvent): HighlightDecision;\n}\n\nexport type SemanticHighlightTarget = HighlightTarget;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"visible-map-range.js","sourceRoot":"","sources":["../../../../src/services/tts/highlight-pipeline/visible-map-range.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,yBAAyB,GAAG,CACxC,UAAyC,EACzC,KAAa,EACb,GAAW,EACI,EAAE;IACjB,IAAI,CAAC,UAAU,IAAI,OAAO,QAAQ,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAChE,IAAI,GAAG,IAAI,KAAK;QAAE,OAAO,IAAI,CAAC;IAC9B,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;IAC1D,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAC/B,IAAI,CAAC,UAAU;QAAE,UAAU,GAAG,YAAY,CAAC;IAC3C,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;QACrC,KAAK,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;QACvD,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACrD,OAAO,KAAK,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC,CAAC","sourcesContent":["import type { NormalizedTextMap } from \"../text-processing.js\";\n\nexport const createRangeFromVisibleMap = (\n\tvisibleMap: NormalizedTextMap | undefined,\n\tstart: number,\n\tend: number,\n): Range | null => {\n\tif (!visibleMap || typeof document === \"undefined\") return null;\n\tif (end <= start) return null;\n\tconst startMapping = visibleMap.get(start);\n\tlet endMapping = visibleMap.get(Math.max(start, end - 1));\n\tif (!startMapping) return null;\n\tif (!endMapping) endMapping = startMapping;\n\ttry {\n\t\tconst range = document.createRange();\n\t\trange.setStart(startMapping.node, startMapping.offset);\n\t\trange.setEnd(endMapping.node, endMapping.offset + 1);\n\t\treturn range;\n\t} catch {\n\t\treturn null;\n\t}\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"highlight-target-resolver.js","sourceRoot":"","sources":["../../../src/services/tts/highlight-target-resolver.ts"],"names":[],"mappings":"","sourcesContent":["export interface TTSHighlightContext {\n\tscopeElement?: HTMLElement | null;\n\titemId?: string;\n\tcanonicalItemId?: string;\n\tkind?: string;\n\tcontentKind?: string;\n\tregionPolicy?: string;\n}\n\nexport interface TTSHighlightTargetResolver {\n\tresolveWordRange?(\n\t\trange: Range,\n\t\tcontext: TTSHighlightContext,\n\t): Range | null | undefined;\n\tresolveSentenceRanges?(\n\t\tranges: Range[],\n\t\tcontext: TTSHighlightContext,\n\t): Array<Range | HTMLElement> | null | undefined;\n}\n\nexport interface TTSHighlightTargetResolverRuntime {\n\tcontext: TTSHighlightContext;\n\tresolver?: TTSHighlightTargetResolver | null;\n}\n\nexport type TTSHighlightTargetResolverProvider =\n\t() => TTSHighlightTargetResolverRuntime | null | undefined;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/services/tts/math-alignment/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { tokenizeMathML } from \"./mathml-tokenizer.js\";\nimport {\n\tresolveMatchedHighlightTargetForBoundary as resolveMatchedTargetForBoundary,\n\tresolveHighlightTargetForBoundary as resolveTargetForBoundary,\n} from \"./range-resolver.js\";\nimport { alignSpeechToMath } from \"./sequence-aligner.js\";\nimport {\n\tresolveBoundaryToSpeechToken,\n\tresolveSpokenOffsetToSpeechToken,\n\ttokenizeSpeechSource,\n\ttype SpeechSourceTokenization,\n} from \"./speech-tokenizer.js\";\nimport type {\n\tAlignmentResult,\n\tHighlightTarget,\n\tMathMLTokenizationResult,\n} from \"./types.js\";\n\nexport interface MathAwareAlignment {\n\tspeech: SpeechSourceTokenization;\n\tmath: MathMLTokenizationResult;\n\tresult: AlignmentResult;\n}\n\nexport const createMathAwareAlignment = (args: {\n\tmathElement: Element;\n\tspeechText: string;\n}): MathAwareAlignment => {\n\tconst speech = tokenizeSpeechSource({ speechText: args.speechText });\n\tconst math = tokenizeMathML(args.mathElement);\n\treturn {\n\t\tspeech,\n\t\tmath,\n\t\tresult: alignSpeechToMath({ speech, math }),\n\t};\n};\n\nexport const resolveHighlightTargetForBoundary = (\n\talignment: MathAwareAlignment,\n\targs: {\n\t\tposition: number;\n\t\tlength?: number;\n\t\tboundaryWord?: string;\n\t},\n): HighlightTarget => {\n\tconst boundary = resolveBoundaryToSpeechToken({\n\t\ttokenization: alignment.speech,\n\t\tposition: args.position,\n\t\tlength: args.length,\n\t\tboundaryWord: args.boundaryWord,\n\t});\n\treturn resolveTargetForBoundary({\n\t\talignment: alignment.result,\n\t\tboundary,\n\t});\n};\n\nexport const resolveMatchedHighlightTargetForBoundary = (\n\talignment: MathAwareAlignment,\n\targs: {\n\t\tposition: number;\n\t\tlength?: number;\n\t\tboundaryWord?: string;\n\t},\n): HighlightTarget | null => {\n\tconst boundary = resolveBoundaryToSpeechToken({\n\t\ttokenization: alignment.speech,\n\t\tposition: args.position,\n\t\tlength: args.length,\n\t\tboundaryWord: args.boundaryWord,\n\t});\n\treturn resolveMatchedTargetForBoundary({\n\t\talignment: alignment.result,\n\t\tboundary,\n\t});\n};\n\nexport const resolveMatchedHighlightTargetForSpokenBoundary = (\n\talignment: MathAwareAlignment,\n\targs: {\n\t\tposition: number;\n\t\tlength?: number;\n\t\tboundaryWord?: string;\n\t},\n): HighlightTarget | null => {\n\tconst boundary = resolveSpokenOffsetToSpeechToken({\n\t\ttokenization: alignment.speech,\n\t\tposition: args.position,\n\t\tlength: args.length,\n\t\tboundaryWord: args.boundaryWord,\n\t});\n\treturn resolveMatchedTargetForBoundary({\n\t\talignment: alignment.result,\n\t\tboundary,\n\t});\n};\n\nexport const resolveUniqueMathTargetForBoundaryWord = (\n\talignment: MathAwareAlignment,\n\targs: {\n\t\tboundaryWord?: string;\n\t},\n): HighlightTarget | null => {\n\tif (!args.boundaryWord || /^<[^>]+>$/.test(args.boundaryWord.trim())) {\n\t\treturn null;\n\t}\n\tconst boundaryToken = tokenizeSpeechSource({\n\t\tspeechText: args.boundaryWord,\n\t}).tokens[0];\n\tif (!boundaryToken) return null;\n\tconst matches = alignment.math.tokens.filter((token) => {\n\t\tconst normalized = boundaryToken.normalized.toLowerCase();\n\t\treturn (\n\t\t\ttoken.normalized.toLowerCase() === normalized ||\n\t\t\ttoken.spokenAliases.some((alias) => alias.toLowerCase() === normalized)\n\t\t);\n\t});\n\treturn matches.length === 1 ? matches[0].target : null;\n};\n\nexport type * from \"./types.js\";\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mathml-tokenizer.js","sourceRoot":"","sources":["../../../../src/services/tts/math-alignment/mathml-tokenizer.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tHighlightTarget,\n\tMathAlignmentToken,\n\tMathAlignmentTokenKind,\n\tMathMLTokenizationResult,\n} from \"./types.js\";\n\nconst OPERATOR_ALIASES = new Map<string, string[]>([\n\t[\"+\", [\"plus\"]],\n\t[\"-\", [\"minus\", \"negative\"]],\n\t[\"−\", [\"minus\", \"negative\"]],\n\t[\"×\", [\"times\", \"multiplied by\"]],\n\t[\"÷\", [\"divided by\"]],\n\t[\"=\", [\"equals\", \"equal to\"]],\n\t[\"≠\", [\"is not equal to\", \"not equal to\"]],\n\t[\"≈\", [\"approximately equals\"]],\n\t[\"≤\", [\"less than or equal to\"]],\n\t[\"≥\", [\"greater than or equal to\"]],\n\t[\"<\", [\"less than\"]],\n\t[\">\", [\"greater than\"]],\n\t[\"∠\", [\"angle\"]],\n\t[\"△\", [\"triangle\"]],\n\t[\"□\", [\"box\"]],\n\t[\"○\", [\"circle\"]],\n\t[\"°\", [\"degrees\", \"degree\"]],\n\t[\"%\", [\"percent\"]],\n\t[\"'\", [\"prime\"]],\n\t[\"′\", [\"prime\"]],\n\t[\"•\", [\"times\"]],\n\t[\"·\", [\"times\"]],\n\t[\"\\u2062\", [\"times\", \"multiplied by\"]],\n]);\n\n// Invisible operators (no glyph). A TTS engine usually does not voice them, so\n// they are tokenized as OPTIONAL — they only consume a spoken token when one\n// happens to match (e.g. an invisible-times read aloud as \"times\"). Marking\n// them required would force a coarse fallback for any function call (sin, f(x))\n// or implicit product.\nconst INVISIBLE_OPERATOR_ROLES = new Map<string, string>([\n\t[\"\\u2061\", \"function-application\"],\n\t[\"\\u2062\", \"implicit-multiply\"],\n\t[\"\\u2063\", \"invisible-separator\"],\n\t[\"\\u2064\", \"invisible-plus\"],\n]);\n\n// Exponents a TTS engine reliably reads as a single power word. Other exponents\n// (variables, 4 and up, nested) are voiced in ways we cannot anchor, so they\n// recurse and let the all-or-nothing cover check fall back to the expression.\nconst SUPERSCRIPT_ALIASES = new Map<string, string[]>([\n\t[\"2\", [\"squared\"]],\n\t[\"3\", [\"cubed\"]],\n]);\n\n// 2D / scripted containers we do not attempt to track per token. Each becomes a\n// single opaque, unmatchable structure token so any expression containing one\n// degrades to a coarse whole-expression highlight rather than risking a\n// misaligned per-glyph highlight from flattened inner tokens.\nconst OPAQUE_STRUCTURE_ROLES = new Map<string, string>([\n\t[\"msqrt\", \"radical\"],\n\t[\"mroot\", \"radical\"],\n\t[\"msub\", \"subscript\"],\n\t[\"msubsup\", \"subsupscript\"],\n\t[\"munder\", \"underscript\"],\n\t[\"mover\", \"overscript\"],\n\t[\"munderover\", \"underoverscript\"],\n\t[\"mmultiscripts\", \"multiscript\"],\n\t[\"menclose\", \"enclosure\"],\n]);\n\nconst normalizeWhitespace = (value: string): string =>\n\tvalue.replace(/\\s+/g, \" \").trim();\n\nconst localNameOf = (element: Element): string =>\n\t(element.localName || element.tagName || \"\").toLowerCase();\n\nconst isElement = (node: Node): node is Element =>\n\tnode.nodeType === Node.ELEMENT_NODE;\n\nconst elementChildren = (element: Element): Element[] =>\n\tArray.from(element.childNodes).filter(isElement);\n\nconst textContentOf = (element: Element): string =>\n\tnormalizeWhitespace(element.textContent || \"\");\n\nconst elementTarget = (\n\telement: Element,\n\tquality: \"element-range\" | \"region-fallback\",\n): HighlightTarget => ({\n\ttype: \"element-range\",\n\tquality,\n\telement,\n});\n\nconst tokenKindFor = (tagName: string): MathAlignmentTokenKind => {\n\tswitch (tagName) {\n\t\tcase \"mi\":\n\t\t\treturn \"identifier\";\n\t\tcase \"mn\":\n\t\t\treturn \"number\";\n\t\tcase \"mo\":\n\t\t\treturn \"operator\";\n\t\tcase \"mtext\":\n\t\tcase \"ms\":\n\t\t\treturn \"text\";\n\t\tcase \"mspace\":\n\t\t\treturn \"space\";\n\t\tdefault:\n\t\t\treturn \"unknown\";\n\t}\n};\n\nconst normalizeToken = (text: string, kind: MathAlignmentTokenKind): string => {\n\tif (kind === \"operator\") {\n\t\tif (text === \"−\") return \"-\";\n\t\treturn text;\n\t}\n\treturn text.toLowerCase();\n};\n\nconst aliasesFor = (\n\ttext: string,\n\tkind: MathAlignmentTokenKind,\n\trole: string,\n): string[] => {\n\tif (role === \"fraction\") return [\"over\", \"divided by\"];\n\tif (role === \"superscript\") return SUPERSCRIPT_ALIASES.get(text) ?? [];\n\tif (text === \"|\") return [\"absolute value\"];\n\tif (kind === \"operator\") return OPERATOR_ALIASES.get(text) || [];\n\treturn [];\n};\n\ninterface TokenizerState {\n\ttokens: MathAlignmentToken[];\n\tlayoutTargets: MathAlignmentToken[];\n\tnextId: number;\n}\n\nconst createToken = (args: {\n\tstate: TokenizerState;\n\tkind: MathAlignmentTokenKind;\n\trole: string;\n\ttext: string;\n\tnormalized?: string;\n\tsourceElement: Element;\n\tpath: string;\n\ttarget?: HighlightTarget;\n\toptional?: boolean;\n}): MathAlignmentToken => {\n\tconst normalized =\n\t\targs.normalized ?? normalizeToken(args.text, args.kind || \"unknown\");\n\treturn {\n\t\tid: `math-token-${args.state.nextId++}`,\n\t\tkind: args.kind,\n\t\trole: args.role,\n\t\ttext: args.text,\n\t\tnormalized,\n\t\tspokenAliases: aliasesFor(args.text, args.kind, args.role),\n\t\tsourceElement: args.sourceElement,\n\t\tpath: args.path,\n\t\ttarget: args.target || elementTarget(args.sourceElement, \"element-range\"),\n\t\toptional: args.optional,\n\t};\n};\n\nconst pushToken = (\n\tstate: TokenizerState,\n\ttoken: MathAlignmentToken,\n\tcollection: \"tokens\" | \"layoutTargets\" = \"tokens\",\n): void => {\n\tstate[collection].push(token);\n};\n\nconst visitChildren = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n): void => {\n\telementChildren(element).forEach((child, index) =>\n\t\tvisitElement(child, state, `${path}/${localNameOf(child)}[${index}]`),\n\t);\n};\n\nconst visitFraction = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n): void => {\n\tconst children = elementChildren(element);\n\tif (children[0]) visitElement(children[0], state, `${path}/numerator`);\n\tpushToken(\n\t\tstate,\n\t\tcreateToken({\n\t\t\tstate,\n\t\t\tkind: \"structure\",\n\t\t\trole: \"fraction\",\n\t\t\ttext: \"/\",\n\t\t\tsourceElement: element,\n\t\t\tpath,\n\t\t\ttarget: elementTarget(element, \"element-range\"),\n\t\t}),\n\t);\n\tif (children[1]) visitElement(children[1], state, `${path}/denominator`);\n};\n\nconst visitFenced = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n): void => {\n\tconst open = element.getAttribute(\"open\") || \"(\";\n\tconst close = element.getAttribute(\"close\") || \")\";\n\tpushToken(\n\t\tstate,\n\t\tcreateToken({\n\t\t\tstate,\n\t\t\tkind: \"operator\",\n\t\t\trole: open === \"|\" ? \"absolute-value-fence\" : \"open-fence\",\n\t\t\ttext: open,\n\t\t\tsourceElement: element,\n\t\t\tpath: `${path}/open`,\n\t\t\ttarget: elementTarget(element, \"element-range\"),\n\t\t}),\n\t);\n\tvisitChildren(element, state, path);\n\tpushToken(\n\t\tstate,\n\t\tcreateToken({\n\t\t\tstate,\n\t\t\tkind: \"operator\",\n\t\t\trole: close === \"|\" ? \"absolute-value-fence\" : \"close-fence\",\n\t\t\ttext: close,\n\t\t\tsourceElement: element,\n\t\t\tpath: `${path}/close`,\n\t\t\ttarget: elementTarget(element, \"element-range\"),\n\t\t}),\n\t);\n};\n\nconst visitSuperscript = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n): void => {\n\tconst children = elementChildren(element);\n\tif (children[0]) visitElement(children[0], state, `${path}/base`);\n\tconst exponent = children[1];\n\tif (!exponent) return;\n\tconst exponentText = textContentOf(exponent);\n\tif (SUPERSCRIPT_ALIASES.has(exponentText)) {\n\t\t// One token aliased to the power word (\"squared\" / \"cubed\"), targeting the\n\t\t// whole superscript so the spoken power word highlights the base-and-\n\t\t// exponent unit.\n\t\tpushToken(\n\t\t\tstate,\n\t\t\tcreateToken({\n\t\t\t\tstate,\n\t\t\t\tkind: \"structure\",\n\t\t\t\trole: \"superscript\",\n\t\t\t\ttext: exponentText,\n\t\t\t\tnormalized: exponentText,\n\t\t\t\tsourceElement: exponent,\n\t\t\t\tpath: `${path}/exponent`,\n\t\t\t\ttarget: elementTarget(element, \"element-range\"),\n\t\t\t}),\n\t\t);\n\t\treturn;\n\t}\n\tvisitElement(exponent, state, `${path}/exponent`);\n};\n\nconst visitOpaqueStructure = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n\trole: string,\n): void => {\n\t// A single required, unmatchable token. The NUL-prefixed normalized value can\n\t// never equal a spoken token, so the cover check always falls back to the\n\t// whole-expression target when this structure is present — no per-glyph\n\t// guessing inside fractions-under-radicals, subscripts, accents, etc.\n\tpushToken(\n\t\tstate,\n\t\tcreateToken({\n\t\t\tstate,\n\t\t\tkind: \"structure\",\n\t\t\trole,\n\t\t\ttext: textContentOf(element) || role,\n\t\t\tnormalized: `\\u0000${role}`,\n\t\t\tsourceElement: element,\n\t\t\tpath,\n\t\t\ttarget: elementTarget(element, \"element-range\"),\n\t\t}),\n\t);\n};\n\nconst visitTable = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n): void => {\n\tlet rowNumber = 0;\n\tfor (const child of elementChildren(element)) {\n\t\tif (localNameOf(child) === \"mtr\" || localNameOf(child) === \"mlabeledtr\") {\n\t\t\trowNumber++;\n\t\t\tpushToken(\n\t\t\t\tstate,\n\t\t\t\tcreateToken({\n\t\t\t\t\tstate,\n\t\t\t\t\tkind: \"layout\",\n\t\t\t\t\trole: \"table-row\",\n\t\t\t\t\ttext: `row-${rowNumber}`,\n\t\t\t\t\tnormalized: `row-${rowNumber}`,\n\t\t\t\t\tsourceElement: child,\n\t\t\t\t\tpath: `${path}/row[${rowNumber}]`,\n\t\t\t\t\ttarget: elementTarget(child, \"region-fallback\"),\n\t\t\t\t}),\n\t\t\t\t\"layoutTargets\",\n\t\t\t);\n\t\t}\n\t\tvisitElement(child, state, `${path}/${localNameOf(child)}[${rowNumber}]`);\n\t}\n};\n\nconst visitTokenElement = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n): void => {\n\tconst text = textContentOf(element);\n\tif (!text) return;\n\tconst kind = tokenKindFor(localNameOf(element));\n\tconst invisibleRole =\n\t\tkind === \"operator\" ? INVISIBLE_OPERATOR_ROLES.get(text) : undefined;\n\tconst role =\n\t\tinvisibleRole ??\n\t\t(kind === \"operator\" && (text === \"(\" || text === \")\")\n\t\t\t? \"grouping-fence\"\n\t\t\t: kind);\n\tpushToken(\n\t\tstate,\n\t\tcreateToken({\n\t\t\tstate,\n\t\t\tkind,\n\t\t\trole,\n\t\t\ttext,\n\t\t\tsourceElement: element,\n\t\t\tpath,\n\t\t\ttarget:\n\t\t\t\tinvisibleRole && element.parentElement\n\t\t\t\t\t? elementTarget(element.parentElement, \"region-fallback\")\n\t\t\t\t\t: undefined,\n\t\t\toptional: Boolean(invisibleRole) || role === \"grouping-fence\",\n\t\t}),\n\t);\n};\n\nconst visitElement = (\n\telement: Element,\n\tstate: TokenizerState,\n\tpath: string,\n): void => {\n\tconst tagName = localNameOf(element);\n\tswitch (tagName) {\n\t\tcase \"annotation\":\n\t\tcase \"annotation-xml\":\n\t\t\treturn;\n\t\tcase \"semantics\": {\n\t\t\tconst presentation = elementChildren(element).find((child) => {\n\t\t\t\tconst childName = localNameOf(child);\n\t\t\t\treturn childName !== \"annotation\" && childName !== \"annotation-xml\";\n\t\t\t});\n\t\t\tif (presentation)\n\t\t\t\tvisitElement(presentation, state, `${path}/presentation`);\n\t\t\treturn;\n\t\t}\n\t\tcase \"mi\":\n\t\tcase \"mn\":\n\t\tcase \"mo\":\n\t\tcase \"mtext\":\n\t\tcase \"ms\":\n\t\tcase \"mspace\":\n\t\t\tvisitTokenElement(element, state, path);\n\t\t\treturn;\n\t\tcase \"mfrac\":\n\t\t\tvisitFraction(element, state, path);\n\t\t\treturn;\n\t\tcase \"msup\":\n\t\t\tvisitSuperscript(element, state, path);\n\t\t\treturn;\n\t\tcase \"mfenced\":\n\t\t\tvisitFenced(element, state, path);\n\t\t\treturn;\n\t\tcase \"mtable\":\n\t\t\tvisitTable(element, state, path);\n\t\t\treturn;\n\t\tcase \"mphantom\":\n\t\t\t// Invisible spacing content — must not become spoken tokens.\n\t\t\treturn;\n\t\tcase \"maction\": {\n\t\t\t// Only the shown alternative (first child) is rendered/spoken.\n\t\t\tconst shown = elementChildren(element)[0];\n\t\t\tif (shown) visitElement(shown, state, `${path}/action`);\n\t\t\treturn;\n\t\t}\n\t\tdefault: {\n\t\t\tconst opaqueRole = OPAQUE_STRUCTURE_ROLES.get(tagName);\n\t\t\tif (opaqueRole) {\n\t\t\t\tvisitOpaqueStructure(element, state, path, opaqueRole);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tvisitChildren(element, state, path);\n\t\t}\n\t}\n};\n\nexport const tokenizeMathML = (\n\tmathElement: Element,\n): MathMLTokenizationResult => {\n\tconst state: TokenizerState = {\n\t\ttokens: [],\n\t\tlayoutTargets: [],\n\t\tnextId: 0,\n\t};\n\tvisitElement(mathElement, state, \"/math\");\n\n\treturn {\n\t\ttokens: state.tokens,\n\t\tlayoutTargets: state.layoutTargets,\n\t\texpressionTarget: elementTarget(mathElement, \"region-fallback\"),\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"range-resolver.js","sourceRoot":"","sources":["../../../../src/services/tts/math-alignment/range-resolver.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,IAGjD,EAAmB,EAAE;IACrB,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;IACzD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAC1D,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAC1D,CAAC;IACF,OAAO,OAAO,EAAE,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC;AACzD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,wCAAwC,GAAG,CAAC,IAGxD,EAA0B,EAAE;IAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAChC,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAC1D,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAC1D,CAAC;IACF,OAAO,OAAO,EAAE,MAAM,IAAI,IAAI,CAAC;AAChC,CAAC,CAAC","sourcesContent":["import type { AlignmentResult, HighlightTarget } from \"./types.js\";\nimport type { ResolvedSpeechBoundary } from \"./speech-tokenizer.js\";\n\nexport const resolveHighlightTargetForBoundary = (args: {\n\talignment: AlignmentResult;\n\tboundary: ResolvedSpeechBoundary | null;\n}): HighlightTarget => {\n\tif (!args.boundary) return args.alignment.fallbackTarget;\n\tconst segment = args.alignment.segments.find((candidate) =>\n\t\tcandidate.speechTokenIds.includes(args.boundary!.token.id),\n\t);\n\treturn segment?.target || args.alignment.fallbackTarget;\n};\n\nexport const resolveMatchedHighlightTargetForBoundary = (args: {\n\talignment: AlignmentResult;\n\tboundary: ResolvedSpeechBoundary | null;\n}): HighlightTarget | null => {\n\tif (!args.boundary) return null;\n\tconst segment = args.alignment.segments.find((candidate) =>\n\t\tcandidate.speechTokenIds.includes(args.boundary!.token.id),\n\t);\n\treturn segment?.target || null;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"sequence-aligner.js","sourceRoot":"","sources":["../../../../src/services/tts/math-alignment/sequence-aligner.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n\tAlignmentResult,\n\tAlignmentSegment,\n\tMathAlignmentToken,\n\tMathMLTokenizationResult,\n\tSpeechAlignmentToken,\n} from \"./types.js\";\nimport type { SpeechSourceTokenization } from \"./speech-tokenizer.js\";\n\nconst MIN_EXACT_WORD_CONFIDENCE = 0.95;\n\ninterface PhraseMatch {\n\tspeechTokens: SpeechAlignmentToken[];\n\tmath: MathAlignmentToken;\n}\n\n// Candidate spoken forms for a math token: its own normalized value plus every\n// spoken alias, each split into the sequence of words a TTS engine emits (e.g.\n// \"÷\" → [\"divided\", \"by\"], \"≤\" → [\"less\", \"than\", \"or\", \"equal\", \"to\"]). This is\n// what lets multi-word operator names match — a single speech token can never\n// equal \"divided by\". Speech tokens are already number-word normalized by the\n// speech tokenizer (two → 2), so no numeric mapping is repeated here.\nconst candidatePhrasesFor = (math: MathAlignmentToken): string[][] => {\n\tconst phrases: string[][] = [];\n\tconst seen = new Set<string>();\n\tconst add = (value: string): void => {\n\t\tconst words = value.toLowerCase().trim().split(/\\s+/).filter(Boolean);\n\t\tif (words.length === 0) return;\n\t\tconst key = words.join(\" \");\n\t\tif (seen.has(key)) return;\n\t\tseen.add(key);\n\t\tphrases.push(words);\n\t};\n\tadd(math.normalized);\n\tfor (const alias of math.spokenAliases) add(alias);\n\treturn phrases;\n};\n\n// Length (in speech tokens) of the longest candidate phrase that matches the\n// run of speech tokens starting at `startIndex`, or 0 if none match. Longest\n// match wins so a multi-word alias is preferred over a coincidental one-word\n// prefix.\nconst matchPhraseLengthAt = (\n\tspeechTokens: SpeechAlignmentToken[],\n\tstartIndex: number,\n\tphrases: string[][],\n): number => {\n\tlet best = 0;\n\tfor (const phrase of phrases) {\n\t\tif (phrase.length <= best) continue;\n\t\tlet matched = true;\n\t\tfor (let i = 0; i < phrase.length; i++) {\n\t\t\tconst token = speechTokens[startIndex + i];\n\t\t\tif (!token || token.normalized.toLowerCase() !== phrase[i]) {\n\t\t\t\tmatched = false;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (matched) best = phrase.length;\n\t}\n\treturn best;\n};\n\n// Greedy forward (monotonic) pass: each math token consumes the next run of\n// speech tokens that spells one of its candidate phrases. Optional math tokens\n// (invisible operators, grouping fences) are consumed only when present.\n// Unmatched speech tokens are skipped, which the perfect-cover check below then\n// treats as a miss.\nconst computeMonotonicMatches = (\n\tspeechTokens: SpeechAlignmentToken[],\n\tmathTokens: MathAlignmentToken[],\n): PhraseMatch[] => {\n\tconst matches: PhraseMatch[] = [];\n\tlet speechIndex = 0;\n\n\tfor (const math of mathTokens) {\n\t\tconst phrases = candidatePhrasesFor(math);\n\t\tif (math.optional) {\n\t\t\tconst consumed = matchPhraseLengthAt(speechTokens, speechIndex, phrases);\n\t\t\tif (consumed > 0) {\n\t\t\t\tmatches.push({\n\t\t\t\t\tspeechTokens: speechTokens.slice(speechIndex, speechIndex + consumed),\n\t\t\t\t\tmath,\n\t\t\t\t});\n\t\t\t\tspeechIndex += consumed;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\twhile (speechIndex < speechTokens.length) {\n\t\t\tconst consumed = matchPhraseLengthAt(speechTokens, speechIndex, phrases);\n\t\t\tif (consumed > 0) {\n\t\t\t\tmatches.push({\n\t\t\t\t\tspeechTokens: speechTokens.slice(speechIndex, speechIndex + consumed),\n\t\t\t\t\tmath,\n\t\t\t\t});\n\t\t\t\tspeechIndex += consumed;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tspeechIndex++;\n\t\t}\n\t}\n\n\treturn matches;\n};\n\n// Word-level highlighting is all-or-nothing: it is emitted only when every\n// required math token AND every spoken token is accounted for. Anything less\n// falls back to a coarse whole-expression highlight, because \"false positives\n// are worse than coarse highlighting\" — a partial alignment risks underlining\n// the wrong glyph.\nconst isPerfectCover = (\n\tmatches: PhraseMatch[],\n\tspeechTokens: SpeechAlignmentToken[],\n\tmathTokens: MathAlignmentToken[],\n): boolean => {\n\tconst requiredTokenCount = mathTokens.filter(\n\t\t(token) => !token.optional,\n\t).length;\n\tconst requiredMatched = matches.filter(\n\t\t(match) => !match.math.optional,\n\t).length;\n\tconst consumedSpeech = matches.reduce(\n\t\t(total, match) => total + match.speechTokens.length,\n\t\t0,\n\t);\n\treturn (\n\t\trequiredTokenCount > 0 &&\n\t\trequiredMatched === requiredTokenCount &&\n\t\tconsumedSpeech === speechTokens.length\n\t);\n};\n\nconst createSegments = (matches: PhraseMatch[]): AlignmentSegment[] =>\n\tmatches.map(({ speechTokens, math }) => ({\n\t\t// Every spoken word that spells this math token maps back to it, so a\n\t\t// boundary on any of those words (\"divided\" or \"by\") resolves to the\n\t\t// same glyph.\n\t\tspeechTokenIds: speechTokens.map((token) => token.id),\n\t\tmathTokenIds: [math.id],\n\t\tscore: 1,\n\t\tconfidence: 1,\n\t\ttarget: math.target,\n\t}));\n\nexport const alignSpeechToMath = (args: {\n\tspeech: SpeechSourceTokenization;\n\tmath: MathMLTokenizationResult;\n\tminExactWordConfidence?: number;\n}): AlignmentResult => {\n\tconst minConfidence =\n\t\targs.minExactWordConfidence ?? MIN_EXACT_WORD_CONFIDENCE;\n\tconst matches = computeMonotonicMatches(args.speech.tokens, args.math.tokens);\n\tconst confidence = isPerfectCover(\n\t\tmatches,\n\t\targs.speech.tokens,\n\t\targs.math.tokens,\n\t)\n\t\t? 1\n\t\t: 0;\n\n\treturn {\n\t\tsegments: confidence >= minConfidence ? createSegments(matches) : [],\n\t\tconfidence,\n\t\tfallbackTarget: args.math.expressionTarget,\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"speech-tokenizer.js","sourceRoot":"","sources":["../../../../src/services/tts/math-alignment/speech-tokenizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { extractSpokenText } from \"../ssml/spoken-text.js\";\nimport type { BoundaryOffsetSpace, SpeechAlignmentToken } from \"./types.js\";\nimport { createSpeechAlignmentTokenPattern } from \"../text-processing.js\";\n\nexport interface SpeechSourceTokenization {\n\tspeechText: string;\n\tspokenText: string;\n\ttokens: SpeechAlignmentToken[];\n\trawToSpokenOffsetMap: Map<number, number>;\n\tboundaryOffsetSpace: BoundaryOffsetSpace;\n\tunsupportedSemantic: boolean;\n\thasMarkup: boolean;\n}\n\nexport interface ResolvedSpeechBoundary {\n\ttoken: SpeechAlignmentToken;\n\tstart: number;\n\tlength: number;\n\tconfidence: number;\n}\n\n// Shared Unicode-aware tokenizer, identical to the catalog span aligner's, so\n// spoken text tokenizes the same way on both paths (see\n// `createSpeechAlignmentTokenPattern`).\nconst TOKEN_PATTERN = createSpeechAlignmentTokenPattern();\n\nconst NUMERIC_WORDS = new Map<string, string>([\n\t[\"zero\", \"0\"],\n\t[\"one\", \"1\"],\n\t[\"two\", \"2\"],\n\t[\"three\", \"3\"],\n\t[\"four\", \"4\"],\n\t[\"five\", \"5\"],\n\t[\"six\", \"6\"],\n\t[\"seven\", \"7\"],\n\t[\"eight\", \"8\"],\n\t[\"nine\", \"9\"],\n\t[\"ten\", \"10\"],\n]);\n\nconst normalizedTokenValue = (value: string): string => {\n\tconst normalized = value.toLowerCase();\n\treturn NUMERIC_WORDS.get(normalized) || normalized;\n};\n\nconst tokenizeSpokenText = (spokenText: string): SpeechAlignmentToken[] => {\n\tconst tokens: SpeechAlignmentToken[] = [];\n\tfor (const match of spokenText.matchAll(TOKEN_PATTERN)) {\n\t\tconst text = match[0];\n\t\tconst start = match.index ?? 0;\n\t\ttokens.push({\n\t\t\tid: `speech-token-${tokens.length}`,\n\t\t\ttext,\n\t\t\tnormalized: normalizedTokenValue(text),\n\t\t\tstart,\n\t\t\tend: start + text.length,\n\t\t\tsourceStart: start,\n\t\t\tsourceEnd: start + text.length,\n\t\t\tcoordinateSystem: \"normalized-speech\",\n\t\t});\n\t}\n\treturn tokens;\n};\n\nexport const tokenizeSpeechSource = (args: {\n\tspeechText: string;\n}): SpeechSourceTokenization => {\n\tconst extracted = extractSpokenText(args.speechText);\n\treturn {\n\t\tspeechText: args.speechText,\n\t\tspokenText: extracted.spokenText,\n\t\ttokens: tokenizeSpokenText(extracted.spokenText),\n\t\trawToSpokenOffsetMap: extracted.rawToSpokenOffsetMap,\n\t\tboundaryOffsetSpace: extracted.unsupportedSemantic\n\t\t\t? \"unsupported\"\n\t\t\t: extracted.hasMarkup\n\t\t\t\t? \"raw-ssml\"\n\t\t\t\t: \"plain-spoken-text\",\n\t\tunsupportedSemantic: extracted.unsupportedSemantic,\n\t\thasMarkup: extracted.hasMarkup,\n\t};\n};\n\nconst normalizeBoundaryWord = (word?: string): string | null => {\n\tif (!word) return null;\n\tif (/^<[^>]+>$/.test(word.trim())) return null;\n\tconst match = word.match(TOKEN_PATTERN);\n\tif (!match?.[0]) return null;\n\treturn normalizedTokenValue(match[0]);\n};\n\nconst findTokenAtOffset = (\n\ttokens: SpeechAlignmentToken[],\n\toffset: number,\n): SpeechAlignmentToken | null =>\n\ttokens.find((token) => token.start <= offset && offset < token.end) || null;\n\nconst mapRawOffsetToSpokenOffset = (\n\ttokenization: SpeechSourceTokenization,\n\tposition: number,\n): number | null => {\n\tconst direct = tokenization.rawToSpokenOffsetMap.get(position);\n\tif (direct !== undefined) return direct;\n\tfor (\n\t\tlet offset = position;\n\t\toffset < tokenization.speechText.length;\n\t\toffset++\n\t) {\n\t\tconst mapped = tokenization.rawToSpokenOffsetMap.get(offset);\n\t\tif (mapped !== undefined) return mapped;\n\t\tif (tokenization.speechText[offset] === \">\") break;\n\t}\n\treturn null;\n};\n\nconst candidateForOffset = (\n\ttokenization: SpeechSourceTokenization,\n\toffset: number | null,\n\tlength: number,\n): ResolvedSpeechBoundary | null => {\n\tif (offset === null) return null;\n\tconst token = findTokenAtOffset(tokenization.tokens, offset);\n\tif (!token) return null;\n\treturn {\n\t\ttoken,\n\t\tstart: offset,\n\t\tlength: Math.max(1, Math.min(length, token.end - offset)),\n\t\tconfidence: 0.75,\n\t};\n};\n\nconst candidateMatchesWord = (\n\tcandidate: ResolvedSpeechBoundary | null,\n\tboundaryWord?: string,\n): boolean => {\n\tconst normalizedBoundary = normalizeBoundaryWord(boundaryWord);\n\treturn Boolean(\n\t\tcandidate &&\n\t\t\tnormalizedBoundary &&\n\t\t\tcandidate.token.normalized === normalizedBoundary,\n\t);\n};\n\nexport const resolveBoundaryToSpeechToken = (args: {\n\ttokenization: SpeechSourceTokenization;\n\tposition: number;\n\tlength?: number;\n\tboundaryWord?: string;\n}): ResolvedSpeechBoundary | null => {\n\tif (\n\t\targs.tokenization.boundaryOffsetSpace === \"unsupported\" ||\n\t\t!Number.isFinite(args.position) ||\n\t\t(args.boundaryWord && /^<[^>]+>$/.test(args.boundaryWord.trim()))\n\t) {\n\t\treturn null;\n\t}\n\tconst safeLength = Math.max(\n\t\t1,\n\t\tNumber.isFinite(args.length) ? args.length! : 1,\n\t);\n\tconst rawCandidate = candidateForOffset(\n\t\targs.tokenization,\n\t\tmapRawOffsetToSpokenOffset(args.tokenization, args.position),\n\t\tsafeLength,\n\t);\n\tconst plainCandidate = candidateForOffset(\n\t\targs.tokenization,\n\t\targs.position >= 0 && args.position < args.tokenization.spokenText.length\n\t\t\t? args.position\n\t\t\t: null,\n\t\tsafeLength,\n\t);\n\n\tif (candidateMatchesWord(rawCandidate, args.boundaryWord)) {\n\t\treturn { ...rawCandidate!, confidence: 1 };\n\t}\n\tif (candidateMatchesWord(plainCandidate, args.boundaryWord)) {\n\t\treturn { ...plainCandidate!, confidence: 1 };\n\t}\n\tif (args.boundaryWord && normalizeBoundaryWord(args.boundaryWord)) {\n\t\treturn null;\n\t}\n\n\treturn args.tokenization.boundaryOffsetSpace === \"raw-ssml\"\n\t\t? rawCandidate || plainCandidate\n\t\t: plainCandidate || rawCandidate;\n};\n\nexport const resolveSpokenOffsetToSpeechToken = (args: {\n\ttokenization: SpeechSourceTokenization;\n\tposition: number;\n\tlength?: number;\n\tboundaryWord?: string;\n}): ResolvedSpeechBoundary | null => {\n\tif (\n\t\targs.tokenization.boundaryOffsetSpace === \"unsupported\" ||\n\t\t!Number.isFinite(args.position) ||\n\t\t(args.boundaryWord && /^<[^>]+>$/.test(args.boundaryWord.trim()))\n\t) {\n\t\treturn null;\n\t}\n\tconst safeLength = Math.max(\n\t\t1,\n\t\tNumber.isFinite(args.length) ? args.length! : 1,\n\t);\n\tconst spokenCandidate = candidateForOffset(\n\t\targs.tokenization,\n\t\targs.position >= 0 && args.position < args.tokenization.spokenText.length\n\t\t\t? args.position\n\t\t\t: null,\n\t\tsafeLength,\n\t);\n\tif (candidateMatchesWord(spokenCandidate, args.boundaryWord)) {\n\t\treturn { ...spokenCandidate!, confidence: 1 };\n\t}\n\tif (args.boundaryWord && normalizeBoundaryWord(args.boundaryWord)) {\n\t\treturn null;\n\t}\n\treturn spokenCandidate;\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/services/tts/math-alignment/types.ts"],"names":[],"mappings":"","sourcesContent":["export type HighlightTargetQuality =\n\t| \"exact-word\"\n\t| \"element-range\"\n\t| \"region-fallback\";\n\nexport type HighlightTarget =\n\t| {\n\t\t\ttype: \"text-range\";\n\t\t\tquality: \"exact-word\";\n\t\t\tnode: Text;\n\t\t\tstartOffset: number;\n\t\t\tendOffset: number;\n\t }\n\t| {\n\t\t\ttype: \"element-range\";\n\t\t\tquality: Exclude<HighlightTargetQuality, \"exact-word\">;\n\t\t\telement: Element;\n\t }\n\t| {\n\t\t\ttype: \"region\";\n\t\t\tquality: \"region-fallback\";\n\t\t\telement: Element;\n\t };\n\nexport type MathAlignmentTokenKind =\n\t| \"identifier\"\n\t| \"number\"\n\t| \"operator\"\n\t| \"text\"\n\t| \"space\"\n\t| \"structure\"\n\t| \"layout\"\n\t| \"unknown\";\n\nexport interface MathAlignmentToken {\n\tid: string;\n\tkind: MathAlignmentTokenKind;\n\trole: string;\n\ttext: string;\n\tnormalized: string;\n\tspokenAliases: string[];\n\tsourceElement: Element;\n\tpath: string;\n\ttarget: HighlightTarget;\n\toptional?: boolean;\n}\n\nexport interface MathMLTokenizationResult {\n\ttokens: MathAlignmentToken[];\n\tlayoutTargets: MathAlignmentToken[];\n\texpressionTarget: HighlightTarget;\n}\n\nexport type BoundaryCoordinateSystem =\n\t| \"normalized-speech\"\n\t| \"raw-ssml\"\n\t| \"provider-speech-marks\";\n\nexport type BoundaryOffsetSpace =\n\t| \"plain-spoken-text\"\n\t| \"raw-ssml\"\n\t| \"unsupported\";\n\nexport interface SpeechAlignmentToken {\n\tid: string;\n\ttext: string;\n\tnormalized: string;\n\tstart: number;\n\tend: number;\n\tsourceStart: number;\n\tsourceEnd: number;\n\tcoordinateSystem: BoundaryCoordinateSystem;\n}\n\nexport interface AlignmentSegment {\n\tspeechTokenIds: string[];\n\tmathTokenIds: string[];\n\tscore: number;\n\tconfidence: number;\n\ttarget: HighlightTarget;\n}\n\nexport interface AlignmentResult {\n\tsegments: AlignmentSegment[];\n\tconfidence: number;\n\tfallbackTarget: HighlightTarget;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"math-aware-text-processing.js","sourceRoot":"","sources":["../../../src/services/tts/math-aware-text-processing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EACN,wBAAwB,EAGxB,kBAAkB,EAClB,sBAAsB,EACtB,6BAA6B,GAC7B,MAAM,sBAAsB,CAAC;AAgC9B,MAAM,iBAAiB,GAAG,CACzB,OAA+B,EACb,EAAE,CAAC,CAAC;IACtB,KAAK,EAAE,EAAE;IACT,GAAG,EAAE,IAAI,GAAG,EAAE;IACd,QAAQ,EAAE,CAAC;IACX,mBAAmB,EAAE,IAAI;IACzB,qBAAqB,EAAE,KAAK;IAC5B,UAAU,EAAE,IAAI;IAChB,OAAO;CACP,CAAC,CAAC;AAEH,MAAM,gBAAgB,GAAG,CAAC,OAAgB,EAAW,EAAE,CACtD,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,SAAS,CAAC;IACtC,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,CAAC;AAEpD,MAAM,sBAAsB,GAAG,CAAC,OAAgB,EAAW,EAAE,CAC5D,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,mBAAmB,CAAC;AAExD,MAAM,mBAAmB,GAAG,CAAC,OAAgB,EAAW,EAAE;IACzD,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;IAC/C,OAAO,OAAO,KAAK,YAAY,IAAI,OAAO,KAAK,gBAAgB,CAAC;AACjE,CAAC,CAAC;AAEF,MAAM,aAAa,GAAG,CAAC,OAAgB,EAAiB,EAAE;IACzD,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;IAClD,OAAO,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,OAAgB,EAAiB,EAAE;IAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,wBAAwB,CAAC,CAAC;IAClE,OAAO,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC;AACrC,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAAC,OAAgB,EAAiB,EAAE;IAC/D,IAAI,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QAC9C,OAAO,kBAAkB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC9C,CAAC;IACD,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,SAAS,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,SAAS;YAAE,OAAO,SAAS,CAAC;IACjC,CAAC;IACD,IAAI,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,SAAS,EAAE,CAAC;YACf,MAAM,SAAS,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;YAChD,IAAI,SAAS;gBAAE,OAAO,SAAS,CAAC;QACjC,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,eAAe,GAAG,CAAC,OAAgB,EAAW,EAAE,CACrD,OAAO,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;AAEvC,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACtC,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,YAAY;IACZ,OAAO;IACP,QAAQ;CACR,CAAC,CAAC;AAEH,MAAM,uBAAuB,GAAG,IAAI,GAAG,CAAC;IACvC,SAAS;IACT,UAAU;IACV,QAAQ;IACR,OAAO;IACP,KAAK;IACL,MAAM;IACN,cAAc;IACd,WAAW;CACX,CAAC,CAAC;AAEH,MAAM,6BAA6B,GAAG,CACrC,QAAc,EACd,IAAa,EACH,EAAE;IACZ,IAAI,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC;IACrC,IAAI,IAAI,GAAmB,IAAI,CAAC;IAChC,OAAO,OAAO,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAChE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QAC9C,IACC,sBAAsB,CAAC,GAAG,CAAC,OAAO,CAAC;YACnC,uBAAuB,CAAC,GAAG,CAAC,IAAI,CAAC,EAChC,CAAC;YACF,IAAI,GAAG,OAAO,CAAC;YACf,MAAM;QACP,CAAC;QACD,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IACjC,CAAC;IACD,OAAO,IAAI,IAAI,IAAI,CAAC;AACrB,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CAAC,OAAgB,EAAW,EAAE,CACtD,OAAO,CACN,OAAO,OAAO,CAAC,aAAa,KAAK,UAAU;IAC1C,OAAO,CAAC,aAAa,CACpB,iEAAiE,CACjE,CACF;IACD,OAAO,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,MAAM;IACzC,OAAO,CAAC,YAAY,EAAE,CAAC,aAAa,CAAC;IACrC,gBAAgB,CAAC,OAAO,CAAC,CAAC;AAE3B,MAAM,eAAe,GAAG,CACvB,GAAoB,EACpB,SAAiB,EACjB,OAA8C,EACvC,EAAE;IACT,IACC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC;QAC1B,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QACpB,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG,EACtC,CAAC;QACF,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAChB,GAAG,CAAC,QAAQ,EAAE,CAAC;QACf,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;IACD,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC1B,IAAI,OAAO,EAAE,CAAC;QACb,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACnC,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC;IAC1B,CAAC;SAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QAC3B,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;IAC3C,CAAC;IACD,GAAG,CAAC,QAAQ,EAAE,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CACjC,GAAoB,EACpB,aAAqB,EACX,EAAE;IACZ,IACC,GAAG,CAAC,mBAAmB;QACvB,GAAG,CAAC,qBAAqB;QACzB,GAAG,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,EACrB,CAAC;QACF,OAAO,KAAK,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjD,6EAA6E;IAC7E,4EAA4E;IAC5E,8DAA8D;IAC9D,OAAO,6BAA6B,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;AAC5E,CAAC,CAAC;AAEF,MAAM,cAAc,GAAG,CAAC,GAAoB,EAAE,QAAc,EAAQ,EAAE;IACrE,MAAM,GAAG,GAAG,QAAQ,CAAC,WAAW,IAAI,EAAE,CAAC;IACvC,IAAI,2BAA2B,GAAG,KAAK,CAAC;IACxC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1C,IAAI,GAAG,CAAC,mBAAmB,EAAE,CAAC;YAC7B,IAAI,CAAC,YAAY,EAAE,CAAC;gBACnB,GAAG,CAAC,mBAAmB,GAAG,KAAK,CAAC;gBAChC,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;gBAC/D,2BAA2B,GAAG,IAAI,CAAC;gBACnC,GAAG,CAAC,qBAAqB,GAAG,KAAK,CAAC;YACnC,CAAC;YACD,SAAS;QACV,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC;gBAChC,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;YAC1D,CAAC;YACD,GAAG,CAAC,qBAAqB,GAAG,IAAI,CAAC;YACjC,SAAS;QACV,CAAC;QACD,IACC,CAAC,2BAA2B;YAC5B,yBAAyB,CAAC,GAAG,EAAE,SAAS,CAAC,EACxC,CAAC;YACF,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAC/D,2BAA2B,GAAG,IAAI,CAAC;QACnC,GAAG,CAAC,qBAAqB,GAAG,KAAK,CAAC;IACnC,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,mBAAmB,GAAG,CAC3B,GAAoB,EACpB,SAAmD,EAC5C,EAAE;IACT,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACpC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;QAC7C,MAAM,aAAa,GAAG,OAAO,EAAE,IAAI,IAAI,GAAG,CAAC,UAAU,EAAE,IAAI,IAAI,IAAI,CAAC;QACpE,MAAM,eAAe,GAAG,OAAO,EAAE,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC,CAAC;QACvE,MAAM,iBAAiB,GAAG,OAAO;YAChC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE;YAChD,CAAC,CAAC,aAAa;gBACd,CAAC,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,EAAE;gBAClD,CAAC,CAAC,IAAI,CAAC;QACT,IAAI,GAAG,CAAC,mBAAmB,EAAE,CAAC;YAC7B,IAAI,YAAY;gBAAE,SAAS;YAC3B,GAAG,CAAC,mBAAmB,GAAG,KAAK,CAAC;QACjC,CAAC;QACD,IAAI,YAAY,EAAE,CAAC;YAClB,IAAI,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC;gBAChC,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC;YAC9C,CAAC;YACD,GAAG,CAAC,qBAAqB,GAAG,IAAI,CAAC;YACjC,SAAS;QACV,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,IAAI,yBAAyB,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,CAAC;YAC1D,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,iBAAiB,CAAC,CAAC;QAC9C,CAAC;QACD,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,iBAAiB,CAAC,CAAC;QACnD,GAAG,CAAC,qBAAqB,GAAG,KAAK,CAAC;QAClC,IAAI,OAAO,EAAE,CAAC;YACb,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE;gBAC7B,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,MAAM,EAAE,OAAO,CAAC,MAAM;aACtB,CAAC,CAAC;YACH,GAAG,CAAC,UAAU,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;QACjE,CAAC;aAAM,IAAI,aAAa,EAAE,CAAC;YAC1B,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,EAAE;gBAC7B,IAAI,EAAE,aAAa;gBACnB,MAAM,EAAE,eAAe;aACvB,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAAC,MAAc,EAAU,EAAE;IACzD,IAAI,OAAO,SAAS,KAAK,WAAW;QAAE,OAAO,EAAE,CAAC;IAChD,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC,eAAe,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;IAC1E,IAAI,MAAM,CAAC,oBAAoB,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACrE,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,CAAC,IAAU,EAAQ,EAAE;QAClC,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,IAAI,EAAE;gBAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClC,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;YAAE,OAAO;QAChC,MAAM,OAAO,GAAG,IAAe,CAAC;QAChC,IAAI,mBAAmB,CAAC,OAAO,CAAC;YAAE,OAAO;QACzC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACpD,KAAK,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACF,CAAC,CAAC;IACF,KAAK,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAC9B,OAAO,sBAAsB,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF,MAAM,0BAA0B,GAAG,CAClC,OAAgB,EAChB,eAAuB,EACvB,OAA+B,EACY,EAAE;IAC7C,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAG,CAAC,IAAU,EAAQ,EAAE;QAClC,IAAI,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC;YAAE,OAAO;QAC9C,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;YACzB,cAAc,CAAC,OAAO,EAAE,IAAY,CAAC,CAAC;YACtC,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,KAAK,CAAC;YAAE,OAAO;QAChC,MAAM,YAAY,GAAG,IAAe,CAAC;QACrC,IACC,sBAAsB,CAAC,YAAY,CAAC;YACpC,mBAAmB,CAAC,YAAY,CAAC,EAChC,CAAC;YACF,OAAO;QACR,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YACzD,KAAK,CAAC,KAAK,CAAC,CAAC;QACd,CAAC;IACF,CAAC,CAAC;IACF,KAAK,CAAC,OAAO,CAAC,CAAC;IACf,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,sBAAsB,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;IAC5D,IAAI,IAAI,EAAE,CAAC;QACV,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;IACnC,CAAC;IACD,MAAM,YAAY,GAAG,sBAAsB,CAAC,eAAe,CAAC,CAAC;IAC7D,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,OAAO,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IACnD,CAAC;IACD,OAAO;QACN,IAAI,EAAE,YAAY;QAClB,GAAG,EAAE,IAAI,GAAG,EAAE;KACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAAC,GAAoB,EAAQ,EAAE;IAC7D,OAAO,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;QAChB,GAAG,CAAC,QAAQ,EAAE,CAAC;QACf,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC9B,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,gBAAgB,GAAG,CACxB,IAAa,EACb,OAA+B,EACT,EAAE;IACxB,MAAM,GAAG,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,sBAA2C,CAAC;IAChD,IAAI,kBAAkB,GAAG,KAAK,CAAC;IAE/B,MAAM,cAAc,GAAG,GAAG,EAAE;QAC3B,MAAM,IAAI,GAAG,sBAAsB,CAClC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAC1D,CAAC;QACF,IAAI,IAAI,EAAE,CAAC;YACV,MAAM,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,IAAI;gBACJ,aAAa,EAAE,sBAAsB;aACrC,CAAC,CAAC;QACJ,CAAC;QACD,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;QAClC,sBAAsB,GAAG,SAAS,CAAC;IACpC,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,IAAU,EAAQ,EAAE;QACxC,IAAI,kBAAkB,CAAC,IAAI,EAAE,IAAI,CAAC;YAAE,OAAO;QAC3C,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,MAAM,aAAa,GAAG,6BAA6B,CAAC,IAAY,EAAE,IAAI,CAAC,CAAC;YACxE,IAAI,sBAAsB,IAAI,aAAa,KAAK,sBAAsB,EAAE,CAAC;gBACxE,cAAc,EAAE,CAAC;YAClB,CAAC;YACD,sBAAsB,GAAG,aAAa,CAAC;YACvC,cAAc,CAAC,GAAG,EAAE,IAAY,CAAC,CAAC;YAClC,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,YAAY;YAAE,OAAO;QAChD,MAAM,OAAO,GAAG,IAAe,CAAC;QAChC,IAAI,sBAAsB,CAAC,OAAO,CAAC;YAAE,OAAO;QAC5C,MAAM,eAAe,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QACrD,IAAI,eAAe,EAAE,CAAC;YACrB,cAAc,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,0BAA0B,CAC3C,OAAO,EACP,eAAe,EACf,OAAO,CACP,CAAC;YACF,mBAAmB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;YACpC,MAAM,YAAY,GAAG,sBAAsB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC5D,MAAM,CAAC,IAAI,CAAC;gBACX,IAAI,EAAE,MAAM;gBACZ,MAAM,EAAE,eAAe;gBACvB,YAAY;gBACZ,aAAa,EAAE,OAAO;aACtB,CAAC,CAAC;YACH,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC;YAClC,kBAAkB,GAAG,IAAI,CAAC;YAC1B,OAAO;QACR,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACpD,WAAW,CAAC,KAAK,CAAC,CAAC;QACpB,CAAC;IACF,CAAC,CAAC;IAEF,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,WAAW,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IAED,sBAAsB,CAAC,GAAG,CAAC,CAAC;IAC5B,cAAc,EAAE,CAAC;IACjB,MAAM,WAAW,GAAG,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvC,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IACD,OAAO;QACN,WAAW;QACX,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,MAAM;QACN,kBAAkB;KAClB,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACzC,OAAgB,EAChB,OAA+B,EACT,EAAE;IACxB,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACjE,OAAO;YACN,WAAW,EAAE,IAAI;YACjB,GAAG;YACH,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;YAC5C,kBAAkB,EAAE,KAAK;SACzB,CAAC;IACH,CAAC;IACD,OAAO,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;AAC3C,CAAC,CAAC","sourcesContent":["import { canonicalizeMathML } from \"./mathml-sanitization.js\";\nimport {\n\tcollectVisibleTextAndMap,\n\ttype NormalizedTextMap,\n\ttype TextProcessingOptions,\n\tisNodeHiddenForTTS,\n\tnormalizeTextForSpeech,\n\tshouldInsertWordBoundarySpace,\n} from \"./text-processing.js\";\n\nexport type MathAwareSpeechChunk =\n\t| {\n\t\t\ttype: \"text\";\n\t\t\ttext: string;\n\t\t\tsourceElement?: Element;\n\t }\n\t| {\n\t\t\ttype: \"math\";\n\t\t\tmathml: string;\n\t\t\tfallbackText: string;\n\t\t\tsourceElement?: Element;\n\t };\n\nexport interface MathAwareTextResult {\n\tvisibleText: string;\n\tmap: NormalizedTextMap;\n\tchunks: MathAwareSpeechChunk[];\n\tcontainsMathMarkup: boolean;\n}\n\ninterface TextAccumulator {\n\tchars: string[];\n\tmap: NormalizedTextMap;\n\tposition: number;\n\tinLeadingWhitespace: boolean;\n\tlastCharWasWhitespace: boolean;\n\tlastMapped: { node: Text; offset: number } | null;\n\toptions?: TextProcessingOptions;\n}\n\nconst createAccumulator = (\n\toptions?: TextProcessingOptions,\n): TextAccumulator => ({\n\tchars: [],\n\tmap: new Map(),\n\tposition: 0,\n\tinLeadingWhitespace: true,\n\tlastCharWasWhitespace: false,\n\tlastMapped: null,\n\toptions,\n});\n\nconst isMathJaxElement = (element: Element): boolean =>\n\telement.classList?.contains(\"MathJax\") ||\n\telement.tagName?.toLowerCase() === \"mjx-container\";\n\nconst isAssistiveMathElement = (element: Element): boolean =>\n\telement.tagName?.toLowerCase() === \"mjx-assistive-mml\";\n\nconst isAnnotationElement = (element: Element): boolean => {\n\tconst tagName = element.tagName?.toLowerCase();\n\treturn tagName === \"annotation\" || tagName === \"annotation-xml\";\n};\n\nconst getDataMathML = (element: Element): string | null => {\n\tconst value = element.getAttribute(\"data-mathml\");\n\treturn value && value.trim() ? value : null;\n};\n\nconst findAssistiveMathML = (element: Element): string | null => {\n\tconst assistive = element.querySelector(\"mjx-assistive-mml math\");\n\treturn assistive?.outerHTML || null;\n};\n\nconst findCanonicalMathML = (element: Element): string | null => {\n\tif (element.tagName.toLowerCase() === \"math\") {\n\t\treturn canonicalizeMathML(element.outerHTML);\n\t}\n\tconst dataMath = getDataMathML(element);\n\tif (dataMath) {\n\t\tconst canonical = canonicalizeMathML(dataMath);\n\t\tif (canonical) return canonical;\n\t}\n\tif (isMathJaxElement(element)) {\n\t\tconst assistive = findAssistiveMathML(element);\n\t\tif (assistive) {\n\t\t\tconst canonical = canonicalizeMathML(assistive);\n\t\t\tif (canonical) return canonical;\n\t\t}\n\t}\n\treturn null;\n};\n\nconst isMathContainer = (element: Element): boolean =>\n\tBoolean(findCanonicalMathML(element));\n\nconst TEXT_CHUNK_SOURCE_TAGS = new Set([\n\t\"P\",\n\t\"H1\",\n\t\"H2\",\n\t\"H3\",\n\t\"H4\",\n\t\"H5\",\n\t\"H6\",\n\t\"LI\",\n\t\"TD\",\n\t\"TH\",\n\t\"DD\",\n\t\"DT\",\n\t\"FIGCAPTION\",\n\t\"LABEL\",\n\t\"BUTTON\",\n]);\n\nconst TEXT_CHUNK_SOURCE_ROLES = new Set([\n\t\"heading\",\n\t\"listitem\",\n\t\"option\",\n\t\"radio\",\n\t\"row\",\n\t\"cell\",\n\t\"columnheader\",\n\t\"rowheader\",\n]);\n\nconst resolveTextChunkSourceElement = (\n\ttextNode: Text,\n\troot: Element,\n): Element => {\n\tlet current = textNode.parentElement;\n\tlet best: Element | null = null;\n\twhile (current && current !== root) {\n\t\tconst role = (current.getAttribute(\"role\") || \"\").toLowerCase();\n\t\tconst tagName = current.tagName.toUpperCase();\n\t\tif (\n\t\t\tTEXT_CHUNK_SOURCE_TAGS.has(tagName) ||\n\t\t\tTEXT_CHUNK_SOURCE_ROLES.has(role)\n\t\t) {\n\t\t\tbest = current;\n\t\t\tbreak;\n\t\t}\n\t\tcurrent = current.parentElement;\n\t}\n\treturn best || root;\n};\n\nconst hasMathCandidate = (element: Element): boolean =>\n\tBoolean(\n\t\ttypeof element.querySelector === \"function\" &&\n\t\t\telement.querySelector(\n\t\t\t\t\"math, [data-mathml], .MathJax, mjx-container, mjx-assistive-mml\",\n\t\t\t),\n\t) ||\n\telement.tagName?.toLowerCase() === \"math\" ||\n\telement.hasAttribute?.(\"data-mathml\") ||\n\tisMathJaxElement(element);\n\nconst appendCharacter = (\n\tacc: TextAccumulator,\n\tcharacter: string,\n\tmapping: { node: Text; offset: number } | null,\n): void => {\n\tif (\n\t\t/[.,;:!?]/.test(character) &&\n\t\tacc.chars.length > 0 &&\n\t\tacc.chars[acc.chars.length - 1] === \" \"\n\t) {\n\t\tacc.chars.pop();\n\t\tacc.position--;\n\t\tacc.map.delete(acc.position);\n\t}\n\tacc.chars.push(character);\n\tif (mapping) {\n\t\tacc.map.set(acc.position, mapping);\n\t\tacc.lastMapped = mapping;\n\t} else if (acc.lastMapped) {\n\t\tacc.map.set(acc.position, acc.lastMapped);\n\t}\n\tacc.position++;\n};\n\nconst shouldInsertBoundarySpace = (\n\tacc: TextAccumulator,\n\tnextCharacter: string,\n): boolean => {\n\tif (\n\t\tacc.inLeadingWhitespace ||\n\t\tacc.lastCharWasWhitespace ||\n\t\tacc.chars.length === 0\n\t) {\n\t\treturn false;\n\t}\n\tconst previous = acc.chars[acc.chars.length - 1];\n\t// Defer to the shared locale-aware decision (Intl.Segmenter, falling back to\n\t// alnum) so adjacent text nodes split into words consistently with the main\n\t// visible-text collector, instead of an ASCII-only heuristic.\n\treturn shouldInsertWordBoundarySpace(previous, nextCharacter, acc.options);\n};\n\nconst appendTextNode = (acc: TextAccumulator, textNode: Text): void => {\n\tconst raw = textNode.textContent || \"\";\n\tlet appendedNonWhitespaceInNode = false;\n\tfor (let i = 0; i < raw.length; i++) {\n\t\tconst character = raw[i];\n\t\tconst isWhitespace = /\\s/.test(character);\n\t\tif (acc.inLeadingWhitespace) {\n\t\t\tif (!isWhitespace) {\n\t\t\t\tacc.inLeadingWhitespace = false;\n\t\t\t\tappendCharacter(acc, character, { node: textNode, offset: i });\n\t\t\t\tappendedNonWhitespaceInNode = true;\n\t\t\t\tacc.lastCharWasWhitespace = false;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (isWhitespace) {\n\t\t\tif (!acc.lastCharWasWhitespace) {\n\t\t\t\tappendCharacter(acc, \" \", { node: textNode, offset: i });\n\t\t\t}\n\t\t\tacc.lastCharWasWhitespace = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (\n\t\t\t!appendedNonWhitespaceInNode &&\n\t\t\tshouldInsertBoundarySpace(acc, character)\n\t\t) {\n\t\t\tappendCharacter(acc, \" \", { node: textNode, offset: i });\n\t\t}\n\t\tappendCharacter(acc, character, { node: textNode, offset: i });\n\t\tappendedNonWhitespaceInNode = true;\n\t\tacc.lastCharWasWhitespace = false;\n\t}\n};\n\nconst appendCollectedText = (\n\tacc: TextAccumulator,\n\tcollected: { text: string; map: NormalizedTextMap },\n): void => {\n\tfor (let i = 0; i < collected.text.length; i++) {\n\t\tconst character = collected.text[i];\n\t\tconst isWhitespace = /\\s/.test(character);\n\t\tconst mapping = collected.map.get(i) || null;\n\t\tconst syntheticNode = mapping?.node || acc.lastMapped?.node || null;\n\t\tconst syntheticOffset = mapping?.offset ?? acc.lastMapped?.offset ?? 0;\n\t\tconst normalizedMapping = mapping\n\t\t\t? { node: mapping.node, offset: mapping.offset }\n\t\t\t: syntheticNode\n\t\t\t\t? { node: syntheticNode, offset: syntheticOffset }\n\t\t\t\t: null;\n\t\tif (acc.inLeadingWhitespace) {\n\t\t\tif (isWhitespace) continue;\n\t\t\tacc.inLeadingWhitespace = false;\n\t\t}\n\t\tif (isWhitespace) {\n\t\t\tif (!acc.lastCharWasWhitespace) {\n\t\t\t\tappendCharacter(acc, \" \", normalizedMapping);\n\t\t\t}\n\t\t\tacc.lastCharWasWhitespace = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (i === 0 && shouldInsertBoundarySpace(acc, character)) {\n\t\t\tappendCharacter(acc, \" \", normalizedMapping);\n\t\t}\n\t\tappendCharacter(acc, character, normalizedMapping);\n\t\tacc.lastCharWasWhitespace = false;\n\t\tif (mapping) {\n\t\t\tacc.map.set(acc.position - 1, {\n\t\t\t\tnode: mapping.node,\n\t\t\t\toffset: mapping.offset,\n\t\t\t});\n\t\t\tacc.lastMapped = { node: mapping.node, offset: mapping.offset };\n\t\t} else if (syntheticNode) {\n\t\t\tacc.map.set(acc.position - 1, {\n\t\t\t\tnode: syntheticNode,\n\t\t\t\toffset: syntheticOffset,\n\t\t\t});\n\t\t}\n\t}\n};\n\nconst textFallbackFromMathML = (mathml: string): string => {\n\tif (typeof DOMParser === \"undefined\") return \"\";\n\tconst parsed = new DOMParser().parseFromString(mathml, \"application/xml\");\n\tif (parsed.getElementsByTagName(\"parsererror\").length > 0) return \"\";\n\tconst parts: string[] = [];\n\tconst visit = (node: Node): void => {\n\t\tif (node.nodeType === 3) {\n\t\t\tconst text = node.textContent || \"\";\n\t\t\tif (text.trim()) parts.push(text);\n\t\t\treturn;\n\t\t}\n\t\tif (node.nodeType !== 1) return;\n\t\tconst element = node as Element;\n\t\tif (isAnnotationElement(element)) return;\n\t\tfor (const child of Array.from(element.childNodes)) {\n\t\t\tvisit(child);\n\t\t}\n\t};\n\tvisit(parsed.documentElement);\n\treturn normalizeTextForSpeech(parts.join(\" \"));\n};\n\nconst collectVisibleMathFallback = (\n\telement: Element,\n\tcanonicalMathML: string,\n\toptions?: TextProcessingOptions,\n): { text: string; map: NormalizedTextMap } => {\n\tconst mathAcc = createAccumulator(options);\n\tconst visit = (node: Node): void => {\n\t\tif (isNodeHiddenForTTS(node, element)) return;\n\t\tif (node.nodeType === 3) {\n\t\t\tappendTextNode(mathAcc, node as Text);\n\t\t\treturn;\n\t\t}\n\t\tif (node.nodeType !== 1) return;\n\t\tconst childElement = node as Element;\n\t\tif (\n\t\t\tisAssistiveMathElement(childElement) ||\n\t\t\tisAnnotationElement(childElement)\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const child of Array.from(childElement.childNodes)) {\n\t\t\tvisit(child);\n\t\t}\n\t};\n\tvisit(element);\n\ttrimTrailingWhitespace(mathAcc);\n\tconst text = normalizeTextForSpeech(mathAcc.chars.join(\"\"));\n\tif (text) {\n\t\treturn { text, map: mathAcc.map };\n\t}\n\tconst fallbackText = textFallbackFromMathML(canonicalMathML);\n\tif (!fallbackText) {\n\t\treturn collectVisibleTextAndMap(element, options);\n\t}\n\treturn {\n\t\ttext: fallbackText,\n\t\tmap: new Map(),\n\t};\n};\n\nconst trimTrailingWhitespace = (acc: TextAccumulator): void => {\n\twhile (acc.chars.length > 0 && /\\s/.test(acc.chars[acc.chars.length - 1])) {\n\t\tacc.chars.pop();\n\t\tacc.position--;\n\t\tacc.map.delete(acc.position);\n\t}\n};\n\nconst collectMathAware = (\n\troot: Element,\n\toptions?: TextProcessingOptions,\n): MathAwareTextResult => {\n\tconst acc = createAccumulator(options);\n\tconst chunks: MathAwareSpeechChunk[] = [];\n\tlet textChunkStart = 0;\n\tlet textChunkSourceElement: Element | undefined;\n\tlet containsMathMarkup = false;\n\n\tconst flushTextChunk = () => {\n\t\tconst text = normalizeTextForSpeech(\n\t\t\tacc.chars.slice(textChunkStart, acc.chars.length).join(\"\"),\n\t\t);\n\t\tif (text) {\n\t\t\tchunks.push({\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext,\n\t\t\t\tsourceElement: textChunkSourceElement,\n\t\t\t});\n\t\t}\n\t\ttextChunkStart = acc.chars.length;\n\t\ttextChunkSourceElement = undefined;\n\t};\n\n\tconst processNode = (node: Node): void => {\n\t\tif (isNodeHiddenForTTS(node, root)) return;\n\t\tif (node.nodeType === Node.TEXT_NODE) {\n\t\t\tconst sourceElement = resolveTextChunkSourceElement(node as Text, root);\n\t\t\tif (textChunkSourceElement && sourceElement !== textChunkSourceElement) {\n\t\t\t\tflushTextChunk();\n\t\t\t}\n\t\t\ttextChunkSourceElement = sourceElement;\n\t\t\tappendTextNode(acc, node as Text);\n\t\t\treturn;\n\t\t}\n\t\tif (node.nodeType !== Node.ELEMENT_NODE) return;\n\t\tconst element = node as Element;\n\t\tif (isAssistiveMathElement(element)) return;\n\t\tconst canonicalMathML = findCanonicalMathML(element);\n\t\tif (canonicalMathML) {\n\t\t\tflushTextChunk();\n\t\t\tconst collected = collectVisibleMathFallback(\n\t\t\t\telement,\n\t\t\t\tcanonicalMathML,\n\t\t\t\toptions,\n\t\t\t);\n\t\t\tappendCollectedText(acc, collected);\n\t\t\tconst fallbackText = normalizeTextForSpeech(collected.text);\n\t\t\tchunks.push({\n\t\t\t\ttype: \"math\",\n\t\t\t\tmathml: canonicalMathML,\n\t\t\t\tfallbackText,\n\t\t\t\tsourceElement: element,\n\t\t\t});\n\t\t\ttextChunkStart = acc.chars.length;\n\t\t\tcontainsMathMarkup = true;\n\t\t\treturn;\n\t\t}\n\t\tfor (const child of Array.from(element.childNodes)) {\n\t\t\tprocessNode(child);\n\t\t}\n\t};\n\n\tfor (const child of Array.from(root.childNodes)) {\n\t\tprocessNode(child);\n\t}\n\n\ttrimTrailingWhitespace(acc);\n\tflushTextChunk();\n\tconst visibleText = acc.chars.join(\"\");\n\twhile (acc.map.has(visibleText.length)) {\n\t\tacc.map.delete(visibleText.length);\n\t}\n\treturn {\n\t\tvisibleText,\n\t\tmap: acc.map,\n\t\tchunks,\n\t\tcontainsMathMarkup,\n\t};\n};\n\nexport const collectMathAwareTextAndMap = (\n\telement: Element,\n\toptions?: TextProcessingOptions,\n): MathAwareTextResult => {\n\tif (!hasMathCandidate(element)) {\n\t\tconst { text, map } = collectVisibleTextAndMap(element, options);\n\t\treturn {\n\t\t\tvisibleText: text,\n\t\t\tmap,\n\t\t\tchunks: text ? [{ type: \"text\", text }] : [],\n\t\t\tcontainsMathMarkup: false,\n\t\t};\n\t}\n\treturn collectMathAware(element, options);\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"math-speech.js","sourceRoot":"","sources":["../../../src/services/tts/math-speech.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MathAwareSpeechChunk } from \"./math-aware-text-processing.js\";\nimport { normalizeTextForSpeech } from \"./text-processing.js\";\n\ninterface SpeechRuleEngineApi {\n\tsetupEngine?: (options: Record<string, unknown>) => Promise<void> | void;\n\tengineReady?: () => Promise<void>;\n\ttoSpeech: (mathml: string) => string;\n}\n\nexport interface ResolveMathSpeechOptions {\n\tlanguage?: string;\n\tloadSre?: () => Promise<SpeechRuleEngineApi>;\n\t/**\n\t * Optional SRE configuration supplied by the host. This deliberately exposes\n\t * SRE's own knobs instead of adding toolkit speech rewrites.\n\t */\n\tmathSpeech?: SREMathSpeechOptions;\n\t/**\n\t * Also produce SRE's SSML rendering (markup: \"ssml\") for the equation, for\n\t * the runtime generated-SSML path (PIE-623). Only populated when the input\n\t * is a single math chunk and SRE succeeds. The plain `speechText` is always\n\t * computed with markup: \"none\" so the plain path is unaffected.\n\t */\n\tproduceSsml?: boolean;\n}\n\nexport interface SREMathSpeechOptions {\n\t/** SRE domain, e.g. \"clearspeak\" or \"mathspeak\". Defaults by locale. */\n\tdomain?: string;\n\t/** Raw SRE style/preference string. ClearSpeak preferences are \":\"-joined. */\n\tstyle?: string;\n\t/** Extra SRE setupEngine options for hosts that need a newly exposed SRE knob. */\n\tengineOptions?: Record<string, unknown>;\n}\n\nexport interface ResolvedMathSpeech {\n\tspeechText: string;\n\tusedMathSpeech: boolean;\n\tusedFallback: boolean;\n\t/**\n\t * SRE SSML rendering of the equation (a self-contained `<speak>` document),\n\t * present only when `produceSsml` was requested for a single math chunk and\n\t * SRE produced trackable SSML.\n\t */\n\tssml?: string;\n}\n\nlet sreLoadPromise: Promise<SpeechRuleEngineApi> | null = null;\nlet sreOperationQueue: Promise<unknown> = Promise.resolve();\n\nconst normalizeLocale = (language?: string): string =>\n\t(language || \"en\").split(\"-\")[0].toLowerCase() || \"en\";\n\nconst defaultLoadSre = async (): Promise<SpeechRuleEngineApi> => {\n\tif (!sreLoadPromise) {\n\t\tsreLoadPromise = import(\"speech-rule-engine\").then((module) => {\n\t\t\tconst candidate =\n\t\t\t\t(module as { default?: SpeechRuleEngineApi }).default ||\n\t\t\t\t(module as unknown as SpeechRuleEngineApi);\n\t\t\tif (!candidate || typeof candidate.toSpeech !== \"function\") {\n\t\t\t\tthrow new Error(\"speech-rule-engine did not expose toSpeech\");\n\t\t\t}\n\t\t\treturn candidate;\n\t\t});\n\t}\n\treturn sreLoadPromise;\n};\n\n// SRE's ClearSpeak rule set is English-only; MathSpeak is localized to many more\n// locales. Use ClearSpeak for English (highest-quality math prose) and MathSpeak\n// elsewhere, so non-English content gets locale-appropriate voicing instead of\n// silently falling back to English ClearSpeak rules.\nconst domainForLocale = (locale: string): string =>\n\tlocale === \"en\" ? \"clearspeak\" : \"mathspeak\";\n\nconst isPlainRecord = (value: unknown): value is Record<string, unknown> =>\n\t!!value && typeof value === \"object\" && !Array.isArray(value);\n\nconst trimOption = (value: unknown): string | undefined =>\n\ttypeof value === \"string\" && value.trim() ? value.trim() : undefined;\n\nexport const normalizeSREMathSpeechOptions = (\n\toptions: unknown,\n): SREMathSpeechOptions | undefined => {\n\tif (!isPlainRecord(options)) return undefined;\n\tconst domain = trimOption(options.domain);\n\tconst style = trimOption(options.style);\n\tconst engineOptions = isPlainRecord(options.engineOptions)\n\t\t? options.engineOptions\n\t\t: undefined;\n\tif (!domain && !style && !engineOptions) return undefined;\n\treturn {\n\t\t...(domain ? { domain } : {}),\n\t\t...(style ? { style } : {}),\n\t\t...(engineOptions ? { engineOptions } : {}),\n\t};\n};\n\nconst setupSre = async (\n\tsre: SpeechRuleEngineApi,\n\tlanguage: string | undefined,\n\tmarkup: \"none\" | \"ssml\",\n\tmathSpeech?: SREMathSpeechOptions,\n): Promise<void> => {\n\tconst locale = normalizeLocale(language);\n\tconst normalizedMathSpeech = normalizeSREMathSpeechOptions(mathSpeech);\n\tawait sre.setupEngine?.({\n\t\t...(normalizedMathSpeech?.engineOptions || {}),\n\t\tlocale,\n\t\tdomain: normalizedMathSpeech?.domain || domainForLocale(locale),\n\t\t...(normalizedMathSpeech?.style\n\t\t\t? { style: normalizedMathSpeech.style }\n\t\t\t: {}),\n\t\tmodality: \"speech\",\n\t\tmarkup,\n\t});\n\tawait sre.engineReady?.();\n};\n\nconst runSerializedSreOperation = async <T>(\n\toperation: () => Promise<T>,\n): Promise<T> => {\n\tconst result = sreOperationQueue.then(operation, operation);\n\tsreOperationQueue = result.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\treturn result;\n};\n\nconst toSpeechWithSreSettings = async (\n\tsre: SpeechRuleEngineApi,\n\tmathml: string,\n\tlanguage: string | undefined,\n\tmarkup: \"none\" | \"ssml\",\n\tmathSpeech?: SREMathSpeechOptions,\n): Promise<string> =>\n\trunSerializedSreOperation(async () => {\n\t\tawait setupSre(sre, language, markup, mathSpeech);\n\t\treturn sre.toSpeech(mathml);\n\t});\n\n// SRE's SSML rendering wraps the spoken words in highlight-safe structural tags\n// (`speak`, `prosody`, `say-as interpret-as=\"character\"`, `break`), all handled\n// by `extractSpokenText`, so word-boundary offsets into the raw SSML still map\n// back to spoken tokens. SRE also prepends an XML prolog (`<?xml …?>`); AWS\n// Polly and Google Cloud TTS reject SSML that does not begin with `<speak>`, so\n// we strip the prolog here. The stripped string is the single source of truth:\n// it is both what the provider receives and what the alignment tokenizes, so\n// offsets stay consistent. We only accept output that contains a `<speak>`\n// element, so a bad/empty render degrades to plain text.\nconst stripXmlProlog = (ssml: string): string =>\n\tssml.replace(/^\\s*<\\?xml[^>]*\\?>\\s*/i, \"\");\n\nconst generateMathSsml = async (\n\tsre: SpeechRuleEngineApi,\n\tmathml: string,\n\tlanguage?: string,\n\tmathSpeech?: SREMathSpeechOptions,\n): Promise<string | null> => {\n\ttry {\n\t\tconst ssml = stripXmlProlog(\n\t\t\tawait toSpeechWithSreSettings(sre, mathml, language, \"ssml\", mathSpeech),\n\t\t);\n\t\treturn ssml.includes(\"<speak\") ? ssml : null;\n\t} catch (error) {\n\t\tconsole.debug(\"[TTSService] Math SSML generation failed; using plain\", {\n\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t});\n\t\treturn null;\n\t}\n};\n\nexport const resolveMathSpeechFromChunks = async (\n\tchunks: MathAwareSpeechChunk[],\n\toptions: ResolveMathSpeechOptions = {},\n): Promise<ResolvedMathSpeech> => {\n\tif (!chunks.some((chunk) => chunk.type === \"math\")) {\n\t\treturn {\n\t\t\tspeechText: normalizeTextForSpeech(\n\t\t\t\tchunks\n\t\t\t\t\t.map((chunk) =>\n\t\t\t\t\t\tchunk.type === \"text\" ? chunk.text : chunk.fallbackText,\n\t\t\t\t\t)\n\t\t\t\t\t.join(\" \"),\n\t\t\t),\n\t\t\tusedMathSpeech: false,\n\t\t\tusedFallback: false,\n\t\t};\n\t}\n\n\tlet sre: SpeechRuleEngineApi | null = null;\n\tlet usedMathSpeech = false;\n\tlet usedFallback = false;\n\tconst loadSre = options.loadSre || defaultLoadSre;\n\tconst speechParts: string[] = [];\n\n\tfor (const chunk of chunks) {\n\t\tif (chunk.type === \"text\") {\n\t\t\tspeechParts.push(chunk.text);\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tif (!sre) {\n\t\t\t\tsre = await loadSre();\n\t\t\t}\n\t\t\tconst speech = normalizeTextForSpeech(\n\t\t\t\tawait toSpeechWithSreSettings(\n\t\t\t\t\tsre,\n\t\t\t\t\tchunk.mathml,\n\t\t\t\t\toptions.language,\n\t\t\t\t\t\"none\",\n\t\t\t\t\toptions.mathSpeech,\n\t\t\t\t),\n\t\t\t);\n\t\t\tif (speech) {\n\t\t\t\tspeechParts.push(speech);\n\t\t\t\tusedMathSpeech = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tusedFallback = true;\n\t\t\tspeechParts.push(chunk.fallbackText);\n\t\t} catch (error) {\n\t\t\tusedFallback = true;\n\t\t\tconsole.debug(\n\t\t\t\t\"[TTSService] Math speech generation failed; using visible fallback\",\n\t\t\t\t{\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t},\n\t\t\t);\n\t\t\tspeechParts.push(chunk.fallbackText);\n\t\t}\n\t}\n\n\t// Generated-SSML path: only meaningful for a single math chunk (the runtime\n\t// composes one chunk per equation). Computed after the plain pass so the\n\t// markup: \"none\" `speechText` above is unchanged. The \"none\" → \"ssml\"\n\t// engine reconfiguration happens sequentially within this call.\n\tconst speechText = normalizeTextForSpeech(speechParts.join(\" \"));\n\tlet ssml: string | undefined;\n\tif (\n\t\toptions.produceSsml &&\n\t\tsre &&\n\t\tusedMathSpeech &&\n\t\tchunks.length === 1 &&\n\t\tchunks[0].type === \"math\"\n\t) {\n\t\tconst generated = await generateMathSsml(\n\t\t\tsre,\n\t\t\tchunks[0].mathml,\n\t\t\toptions.language,\n\t\t\toptions.mathSpeech,\n\t\t);\n\t\tssml = generated ?? undefined;\n\t}\n\n\treturn {\n\t\tspeechText,\n\t\tusedMathSpeech,\n\t\tusedFallback,\n\t\tssml,\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mathml-sanitization.js","sourceRoot":"","sources":["../../../src/services/tts/mathml-sanitization.ts"],"names":[],"mappings":"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","sourcesContent":["const MATHML_NAMESPACE = \"http://www.w3.org/1998/Math/MathML\";\n\nconst ALLOWED_MATHML_ELEMENTS = new Set([\n\t\"math\",\n\t\"maction\",\n\t\"menclose\",\n\t\"merror\",\n\t\"mfenced\",\n\t\"mfrac\",\n\t\"mi\",\n\t\"mmultiscripts\",\n\t\"mn\",\n\t\"mo\",\n\t\"mover\",\n\t\"mpadded\",\n\t\"mphantom\",\n\t\"mprescripts\",\n\t\"mroot\",\n\t\"mrow\",\n\t\"ms\",\n\t\"mspace\",\n\t\"msqrt\",\n\t\"mstyle\",\n\t\"msub\",\n\t\"msubsup\",\n\t\"msup\",\n\t\"mtable\",\n\t\"mtd\",\n\t\"mtext\",\n\t\"mtr\",\n\t\"munder\",\n\t\"munderover\",\n\t\"none\",\n\t\"semantics\",\n\t\"annotation\",\n\t\"annotation-xml\",\n]);\n\nconst ALLOWED_ATTRIBUTES = new Set([\n\t\"accent\",\n\t\"accentunder\",\n\t\"align\",\n\t\"bevelled\",\n\t\"class\",\n\t\"close\",\n\t\"columnalign\",\n\t\"columnlines\",\n\t\"columnspacing\",\n\t\"columnspan\",\n\t\"denomalign\",\n\t\"depth\",\n\t\"dir\",\n\t\"display\",\n\t\"encoding\",\n\t\"fence\",\n\t\"height\",\n\t\"href\",\n\t\"id\",\n\t\"largeop\",\n\t\"length\",\n\t\"linethickness\",\n\t\"lspace\",\n\t\"mathbackground\",\n\t\"mathcolor\",\n\t\"mathsize\",\n\t\"mathvariant\",\n\t\"maxsize\",\n\t\"minsize\",\n\t\"movablelimits\",\n\t\"notation\",\n\t\"numalign\",\n\t\"open\",\n\t\"rowalign\",\n\t\"rowlines\",\n\t\"rowspacing\",\n\t\"rowspan\",\n\t\"rspace\",\n\t\"scriptlevel\",\n\t\"scriptminsize\",\n\t\"scriptsizemultiplier\",\n\t\"selection\",\n\t\"separator\",\n\t\"separators\",\n\t\"stretchy\",\n\t\"subscriptshift\",\n\t\"supscriptshift\",\n\t\"symmetric\",\n\t\"width\",\n\t\"xmlns\",\n\t\"xml:lang\",\n]);\n\nconst escapeText = (value: string): string =>\n\tvalue.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\");\n\nconst escapeAttribute = (value: string): string =>\n\tescapeText(value).replace(/\"/g, \"&quot;\");\n\nconst isUnsafeAttribute = (name: string, value: string): boolean => {\n\tconst lowerName = name.toLowerCase();\n\tconst lowerValue = value.trim().toLowerCase();\n\tif (lowerName.startsWith(\"on\")) return true;\n\tif (!ALLOWED_ATTRIBUTES.has(lowerName)) return true;\n\tif (lowerValue.includes(\"javascript:\")) return true;\n\tif (lowerValue.includes(\"data:text/html\")) return true;\n\treturn false;\n};\n\nconst isAllowedMathElement = (element: Element): boolean => {\n\tconst localName = element.localName.toLowerCase();\n\tif (!ALLOWED_MATHML_ELEMENTS.has(localName)) return false;\n\tconst namespace = element.namespaceURI;\n\treturn (\n\t\t!namespace ||\n\t\tnamespace === MATHML_NAMESPACE ||\n\t\tnamespace === \"http://www.w3.org/1999/xhtml\"\n\t);\n};\n\nconst serializeMathNode = (node: Node): string | null => {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn escapeText(node.textContent || \"\");\n\t}\n\tif (node.nodeType === Node.COMMENT_NODE) {\n\t\treturn \"\";\n\t}\n\tif (node.nodeType !== Node.ELEMENT_NODE) {\n\t\treturn null;\n\t}\n\tconst element = node as Element;\n\tif (!isAllowedMathElement(element)) return null;\n\tconst tagName = element.localName.toLowerCase();\n\tconst attrs: string[] = [];\n\tfor (const attr of Array.from(element.attributes)) {\n\t\tconst name = attr.name.toLowerCase();\n\t\tif (isUnsafeAttribute(name, attr.value)) continue;\n\t\tif (name === \"xmlns\" && attr.value !== MATHML_NAMESPACE) return null;\n\t\tattrs.push(`${name}=\"${escapeAttribute(attr.value)}\"`);\n\t}\n\tif (tagName === \"math\" && !attrs.some((attr) => attr.startsWith(\"xmlns=\"))) {\n\t\tattrs.unshift(`xmlns=\"${MATHML_NAMESPACE}\"`);\n\t}\n\tconst children: string[] = [];\n\tfor (const child of Array.from(element.childNodes)) {\n\t\tconst serialized = serializeMathNode(child);\n\t\tif (serialized === null) return null;\n\t\tchildren.push(serialized);\n\t}\n\tconst attrText = attrs.length > 0 ? ` ${attrs.join(\" \")}` : \"\";\n\treturn `<${tagName}${attrText}>${children.join(\"\")}</${tagName}>`;\n};\n\nconst parseMathML = (input: string): Document | null => {\n\tif (typeof DOMParser !== \"undefined\") {\n\t\treturn new DOMParser().parseFromString(input, \"application/xml\");\n\t}\n\treturn null;\n};\n\nexport const canonicalizeMathML = (input: string): string | null => {\n\tconst trimmed = String(input || \"\").trim();\n\tif (!trimmed) return null;\n\tconst parsed = parseMathML(trimmed);\n\tif (!parsed) return null;\n\tif (parsed.getElementsByTagName(\"parsererror\").length > 0) return null;\n\tconst root = parsed.documentElement;\n\tif (!root || root.localName.toLowerCase() !== \"math\") return null;\n\treturn serializeMathNode(root);\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"spoken-text.js","sourceRoot":"","sources":["../../../../src/services/tts/ssml/spoken-text.ts"],"names":[],"mappings":"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","sourcesContent":["import { normalizeTextForSpeech } from \"../text-processing.js\";\n\n/**\n * Shared SSML → spoken-text extractor.\n *\n * Strips SSML markup to the words a TTS engine would actually voice, while\n * recording a `rawToSpokenOffsetMap` from raw-SSML character offsets to\n * normalized spoken-text offsets. This is the single source of truth for SSML\n * stripping used by:\n * - the catalog span aligner (`catalog-span-alignment.ts`),\n * - the math speech tokenizer (`math-alignment/speech-tokenizer.ts`),\n * - the runtime generated-speech path (PIE-623).\n *\n * Keeping one implementation guarantees provider word-boundary offsets (which\n * are into the raw SSML string) map to the same spoken text on every path.\n *\n * Tag handling:\n * - Structural tags (`speak`, `p`, `s`, `emphasis`, `prosody`, `say-as`,\n * `voice`, `lang`) are transparent; their text content flows through.\n * `p`/`s` open tags inject a word boundary space.\n * - `break` injects a space (a pause is a word boundary).\n * - `sub alias=\"…\"` substitutes the alias text (and skips the original).\n * `sub` with no alias marks the content unsupported for word tracking.\n * - Semantic tags that change *what* is spoken without leaving trackable\n * text (`audio`, `phoneme`) mark the content unsupported.\n * - Any other unknown element marks the content unsupported (degrade to\n * coarse region highlighting rather than mis-track).\n */\nexport interface ExtractedSpokenText {\n\tspokenText: string;\n\trawToSpokenOffsetMap: Map<number, number>;\n\tunsupportedSemantic: boolean;\n\thasMarkup: boolean;\n}\n\ninterface SourceChar {\n\tchar: string;\n\trawOffset: number | null;\n}\n\nconst STRUCTURAL_SSML_TAGS = new Set([\n\t\"speak\",\n\t\"p\",\n\t\"s\",\n\t\"emphasis\",\n\t\"prosody\",\n\t\"say-as\",\n\t\"voice\",\n\t\"lang\",\n]);\n\nconst UNSUPPORTED_SEMANTIC_SSML_TAGS = new Set([\"audio\", \"phoneme\"]);\n\nconst decodeXmlEntities = (value: string): string =>\n\tvalue\n\t\t.replace(/&quot;/g, '\"')\n\t\t.replace(/&apos;/g, \"'\")\n\t\t.replace(/&lt;/g, \"<\")\n\t\t.replace(/&gt;/g, \">\")\n\t\t.replace(/&nbsp;/g, \"\\u00a0\")\n\t\t.replace(/&#160;/g, \"\\u00a0\")\n\t\t.replace(/&amp;/g, \"&\");\n\nconst appendSourceText = (\n\tchars: SourceChar[],\n\tvalue: string,\n\trawOffset: number | null,\n): void => {\n\tfor (let i = 0; i < value.length; i++) {\n\t\tchars.push({\n\t\t\tchar: value[i],\n\t\t\trawOffset: rawOffset === null ? null : rawOffset + i,\n\t\t});\n\t}\n};\n\nconst appendDecodedXmlEntity = (\n\tchars: SourceChar[],\n\tsource: string,\n\trawOffset: number,\n): number => {\n\tconst match = source\n\t\t.slice(rawOffset)\n\t\t.match(/^&(quot|apos|lt|gt|nbsp|amp|#160);/i);\n\tif (!match) return 0;\n\tappendSourceText(chars, decodeXmlEntities(match[0]), rawOffset);\n\treturn match[0].length;\n};\n\nconst appendSpace = (chars: SourceChar[]): void => {\n\tchars.push({ char: \" \", rawOffset: null });\n};\n\nconst normalizeSourceChars = (\n\tchars: SourceChar[],\n): { text: string; rawToSpokenOffsetMap: Map<number, number> } => {\n\tconst rawToSpokenOffsetMap = new Map<number, number>();\n\tconst out: string[] = [];\n\tlet lastWasWhitespace = true;\n\n\tfor (const sourceChar of chars) {\n\t\tconst isWhitespace = /\\s/.test(sourceChar.char);\n\t\tif (isWhitespace) {\n\t\t\tif (!lastWasWhitespace && out.length > 0) {\n\t\t\t\tout.push(\" \");\n\t\t\t\tif (sourceChar.rawOffset !== null) {\n\t\t\t\t\trawToSpokenOffsetMap.set(sourceChar.rawOffset, out.length - 1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tlastWasWhitespace = true;\n\t\t\tcontinue;\n\t\t}\n\t\tout.push(sourceChar.char);\n\t\tif (sourceChar.rawOffset !== null) {\n\t\t\trawToSpokenOffsetMap.set(sourceChar.rawOffset, out.length - 1);\n\t\t}\n\t\tlastWasWhitespace = false;\n\t}\n\n\twhile (out.length > 0 && out[out.length - 1] === \" \") {\n\t\tout.pop();\n\t}\n\n\t// Drop any raw→spoken entries that now point past the trimmed end; otherwise\n\t// a boundary at a trailing space would map to an out-of-range spoken offset.\n\tfor (const [rawOffset, spokenOffset] of rawToSpokenOffsetMap) {\n\t\tif (spokenOffset >= out.length) rawToSpokenOffsetMap.delete(rawOffset);\n\t}\n\n\treturn { text: out.join(\"\"), rawToSpokenOffsetMap };\n};\n\nconst getTagName = (tagSource: string): string => {\n\tconst match = tagSource.match(/^<\\/?\\s*([A-Za-z0-9:-]+)/);\n\treturn match?.[1]?.toLowerCase() || \"\";\n};\n\nconst getAttribute = (tagSource: string, name: string): string | null => {\n\tconst pattern = new RegExp(`${name}\\\\s*=\\\\s*(\"([^\"]*)\"|'([^']*)')`, \"i\");\n\tconst match = tagSource.match(pattern);\n\treturn decodeXmlEntities(match?.[2] ?? match?.[3] ?? \"\") || null;\n};\n\nexport const extractSpokenText = (speechText: string): ExtractedSpokenText => {\n\tconst sourceChars: SourceChar[] = [];\n\tlet unsupportedSemantic = false;\n\tlet hasMarkup = false;\n\tlet index = 0;\n\n\twhile (index < speechText.length) {\n\t\tconst char = speechText[index];\n\t\tif (char !== \"<\") {\n\t\t\tif (char === \"&\") {\n\t\t\t\tconst consumed = appendDecodedXmlEntity(sourceChars, speechText, index);\n\t\t\t\tif (consumed > 0) {\n\t\t\t\t\tindex += consumed;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t\tappendSourceText(sourceChars, char, index);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst closeIndex = speechText.indexOf(\">\", index + 1);\n\t\tif (closeIndex < 0) {\n\t\t\tappendSourceText(sourceChars, char, index);\n\t\t\tindex++;\n\t\t\tcontinue;\n\t\t}\n\n\t\thasMarkup = true;\n\t\tconst tagSource = speechText.slice(index, closeIndex + 1);\n\t\tconst tagName = getTagName(tagSource);\n\t\tconst isClosing = /^<\\s*\\//.test(tagSource);\n\t\tconst isSelfClosing = /\\/\\s*>$/.test(tagSource);\n\n\t\tif (!isClosing && tagName === \"sub\") {\n\t\t\tconst alias = getAttribute(tagSource, \"alias\");\n\t\t\tif (!alias) {\n\t\t\t\tunsupportedSemantic = true;\n\t\t\t\tindex = closeIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tappendSourceText(sourceChars, alias, null);\n\t\t\tconst closingSub = speechText\n\t\t\t\t.toLowerCase()\n\t\t\t\t.indexOf(\"</sub>\", closeIndex + 1);\n\t\t\tindex = closingSub >= 0 ? closingSub + \"</sub>\".length : closeIndex + 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!isClosing && tagName === \"break\") {\n\t\t\tappendSpace(sourceChars);\n\t\t\tindex = closeIndex + 1;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!isClosing && UNSUPPORTED_SEMANTIC_SSML_TAGS.has(tagName)) {\n\t\t\tunsupportedSemantic = true;\n\t\t\tif (isSelfClosing) {\n\t\t\t\tindex = closeIndex + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (\n\t\t\ttagName &&\n\t\t\t!STRUCTURAL_SSML_TAGS.has(tagName) &&\n\t\t\t!UNSUPPORTED_SEMANTIC_SSML_TAGS.has(tagName) &&\n\t\t\ttagName !== \"break\" &&\n\t\t\ttagName !== \"sub\"\n\t\t) {\n\t\t\tunsupportedSemantic = true;\n\t\t}\n\n\t\tif (!isClosing && (tagName === \"p\" || tagName === \"s\")) {\n\t\t\tappendSpace(sourceChars);\n\t\t}\n\t\tindex = closeIndex + 1;\n\t}\n\n\tconst normalized = normalizeSourceChars(sourceChars);\n\treturn {\n\t\tspokenText: normalizeTextForSpeech(normalized.text),\n\t\trawToSpokenOffsetMap: normalized.rawToSpokenOffsetMap,\n\t\tunsupportedSemantic,\n\t\thasMarkup,\n\t};\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"text-processing.js","sourceRoot":"","sources":["../../../src/services/tts/text-processing.ts"],"names":[],"mappings":"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","sourcesContent":["export type NormalizedTextMap = Map<number, { node: Text; offset: number }>;\nexport type BoundarySpacingMode = \"segmenterPreferred\" | \"alnum\" | \"none\";\n\nexport interface TextProcessingOptions {\n\tlocale?: string;\n\tboundarySpacingMode?: BoundarySpacingMode;\n}\n\nexport const normalizeTextForSpeech = (text: string): string =>\n\ttext.trim().replace(/\\s+/g, \" \");\n\n// Single source of truth for aligning spoken text to visible / math text.\n// Letters are matched with `\\p{L}` so the tokenizer is Unicode-aware — accented\n// Latin (\"café\") and non-Latin scripts stay one token instead of being split\n// or dropped, which is what previously limited word-level highlight alignment\n// to ASCII. Numbers cover integers and decimals; the trailing class lists the\n// math glyphs a TTS engine may surface as a single visible token. A fresh\n// RegExp is returned each call because the global flag carries mutable\n// `lastIndex` state that must not be shared across call sites.\nexport const createSpeechAlignmentTokenPattern = (): RegExp =>\n\t/[\\p{L}]+|\\d+(?:\\.\\d+)?|[±√=+\\-*/()²³^×÷≤≥≠≈<>|%°'′\\u2062]/gu;\n\nexport const isElementHiddenForTTS = (element: Element): boolean => {\n\tif ((element as HTMLElement).hidden) return true;\n\tif (element.tagName?.toLowerCase() === \"mjx-assistive-mml\") return true;\n\tif (element.getAttribute(\"aria-hidden\") === \"true\") return true;\n\tif (element.hasAttribute(\"inert\")) return true;\n\tconst classList = (element as HTMLElement).classList;\n\tif (\n\t\tclassList?.contains(\"sr-only\") ||\n\t\tclassList?.contains(\"pie-sr-only\") ||\n\t\tclassList?.contains(\"visually-hidden\")\n\t) {\n\t\treturn true;\n\t}\n\tif (typeof window !== \"undefined\") {\n\t\tconst style = window.getComputedStyle(element);\n\t\tif (style.display === \"none\" || style.visibility === \"hidden\") {\n\t\t\treturn true;\n\t\t}\n\t\tconst width = Number.parseFloat(style.width || \"0\");\n\t\tconst height = Number.parseFloat(style.height || \"0\");\n\t\tconst isTiny =\n\t\t\tNumber.isFinite(width) &&\n\t\t\tNumber.isFinite(height) &&\n\t\t\twidth <= 1 &&\n\t\t\theight <= 1;\n\t\tconst isOffscreenClip =\n\t\t\tstyle.clipPath.includes(\"inset(50%\") ||\n\t\t\tstyle.clipPath.includes(\"inset(100%\") ||\n\t\t\tstyle.clip.includes(\"rect(0px, 0px, 0px, 0px)\") ||\n\t\t\tstyle.clip.includes(\"rect(0 0 0 0)\");\n\t\tif (\n\t\t\t(style.position === \"absolute\" || style.position === \"fixed\") &&\n\t\t\tisTiny &&\n\t\t\t(style.overflow === \"hidden\" || isOffscreenClip)\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tif (style.opacity === \"0\" && isTiny) {\n\t\t\treturn true;\n\t\t}\n\t}\n\treturn false;\n};\n\nexport const isNodeHiddenForTTS = (\n\tnode: Node,\n\troot?: Element | null,\n): boolean => {\n\tlet current =\n\t\tnode.nodeType === 1\n\t\t\t? (node as Element)\n\t\t\t: (node.parentElement as Element | null);\n\twhile (current) {\n\t\tif (isElementHiddenForTTS(current)) return true;\n\t\tif (root && current === root) break;\n\t\tcurrent = current.parentElement;\n\t}\n\treturn false;\n};\n\nexport const shouldInsertWordBoundarySpace = (\n\tpreviousChar: string | null,\n\tnextChar: string | null,\n\toptions?: TextProcessingOptions,\n): boolean => {\n\tif (!previousChar || !nextChar) return false;\n\tif (/\\s/.test(previousChar) || /\\s/.test(nextChar)) return false;\n\tconst mode = options?.boundarySpacingMode ?? \"segmenterPreferred\";\n\tif (mode === \"none\") return false;\n\tif (mode === \"segmenterPreferred\") {\n\t\ttry {\n\t\t\tconst Segmenter = globalThis.Intl?.Segmenter;\n\t\t\tif (typeof Segmenter === \"function\") {\n\t\t\t\tconst segmenter = new Segmenter(options?.locale, {\n\t\t\t\t\tgranularity: \"word\",\n\t\t\t\t});\n\t\t\t\tconst segments = Array.from(\n\t\t\t\t\tsegmenter.segment(`${previousChar}${nextChar}`),\n\t\t\t\t);\n\t\t\t\tconst wordLikeCount = segments.filter(\n\t\t\t\t\t(segment) =>\n\t\t\t\t\t\t(segment as { isWordLike?: boolean }).isWordLike !== false &&\n\t\t\t\t\t\tsegment.segment.trim().length > 0,\n\t\t\t\t).length;\n\t\t\t\tif (wordLikeCount >= 2) return true;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Fall back to alnum heuristics below.\n\t\t}\n\t}\n\tconst prevIsAlnum = /[A-Za-z0-9]/.test(previousChar);\n\tconst nextIsAlnum = /[A-Za-z0-9]/.test(nextChar);\n\treturn prevIsAlnum && nextIsAlnum;\n};\n\nexport const collectVisibleTextAndMap = (\n\telement: Element,\n\toptions?: TextProcessingOptions,\n): { text: string; map: NormalizedTextMap } => {\n\tconst map: NormalizedTextMap = new Map();\n\tif (\n\t\ttypeof document === \"undefined\" ||\n\t\ttypeof (document as { createTreeWalker?: unknown }).createTreeWalker !==\n\t\t\t\"function\" ||\n\t\ttypeof NodeFilter === \"undefined\"\n\t) {\n\t\treturn { text: \"\", map };\n\t}\n\tconst outChars: string[] = [];\n\tlet outPos = 0;\n\tlet inLeadingWhitespace = true;\n\tlet lastCharWasWhitespace = false;\n\tlet lastMapped: { node: Text; offset: number } | null = null;\n\tlet previousVisibleChar: string | null = null;\n\n\tconst walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);\n\tlet current = walker.nextNode();\n\twhile (current) {\n\t\tconst textNode = current as Text;\n\t\tconst parent = textNode.parentElement;\n\t\tif (parent && !isNodeHiddenForTTS(textNode, element)) {\n\t\t\tconst raw = textNode.textContent || \"\";\n\t\t\tconst firstVisibleMatch = raw.match(/\\S/);\n\t\t\tconst firstVisibleChar = firstVisibleMatch ? firstVisibleMatch[0] : null;\n\n\t\t\tif (\n\t\t\t\t!inLeadingWhitespace &&\n\t\t\t\t!lastCharWasWhitespace &&\n\t\t\t\tshouldInsertWordBoundarySpace(\n\t\t\t\t\tpreviousVisibleChar,\n\t\t\t\t\tfirstVisibleChar,\n\t\t\t\t\toptions,\n\t\t\t\t)\n\t\t\t) {\n\t\t\t\toutChars.push(\" \");\n\t\t\t\tif (lastMapped) {\n\t\t\t\t\tmap.set(outPos, lastMapped);\n\t\t\t\t}\n\t\t\t\toutPos++;\n\t\t\t\tlastCharWasWhitespace = true;\n\t\t\t}\n\n\t\t\tfor (let i = 0; i < raw.length; i++) {\n\t\t\t\tconst ch = raw[i];\n\t\t\t\tconst isWhitespace = /\\s/.test(ch);\n\n\t\t\t\tif (inLeadingWhitespace) {\n\t\t\t\t\tif (!isWhitespace) {\n\t\t\t\t\t\tinLeadingWhitespace = false;\n\t\t\t\t\t\toutChars.push(ch);\n\t\t\t\t\t\tmap.set(outPos, { node: textNode, offset: i });\n\t\t\t\t\t\tlastMapped = { node: textNode, offset: i };\n\t\t\t\t\t\toutPos++;\n\t\t\t\t\t\tlastCharWasWhitespace = false;\n\t\t\t\t\t\tpreviousVisibleChar = ch;\n\t\t\t\t\t}\n\t\t\t\t} else if (isWhitespace) {\n\t\t\t\t\tif (!lastCharWasWhitespace) {\n\t\t\t\t\t\toutChars.push(\" \");\n\t\t\t\t\t\tmap.set(outPos, { node: textNode, offset: i });\n\t\t\t\t\t\tlastMapped = { node: textNode, offset: i };\n\t\t\t\t\t\toutPos++;\n\t\t\t\t\t}\n\t\t\t\t\tlastCharWasWhitespace = true;\n\t\t\t\t} else {\n\t\t\t\t\toutChars.push(ch);\n\t\t\t\t\tmap.set(outPos, { node: textNode, offset: i });\n\t\t\t\t\tlastMapped = { node: textNode, offset: i };\n\t\t\t\t\toutPos++;\n\t\t\t\t\tlastCharWasWhitespace = false;\n\t\t\t\t\tpreviousVisibleChar = ch;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tcurrent = walker.nextNode();\n\t}\n\n\tconst text = outChars.join(\"\").trimEnd();\n\twhile (map.has(text.length)) {\n\t\tmap.delete(text.length);\n\t}\n\treturn { text, map };\n};\n\nexport const extractVisibleText = (\n\telement: Element,\n\toptions?: TextProcessingOptions,\n): string => collectVisibleTextAndMap(element, options).text;\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"text-segmentation.js","sourceRoot":"","sources":["../../../src/services/tts/text-segmentation.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC/B,IAAY,EACZ,OAAqC,EACjB,EAAE;IACtB,MAAM,oBAAoB,GAAG,OAAO,EAAE,oBAAoB,KAAK,KAAK,CAAC;IACrE,IAAI,CAAC;QACJ,IAAI,CAAC,oBAAoB,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QACjD,CAAC;QACD,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC;QAC7C,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE;gBAChD,WAAW,EAAE,UAAU;aACvB,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;iBAChD,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBAClB,IAAI,EAAE,OAAO,CAAC,OAAO;gBACrB,MAAM,EAAE,OAAO,CAAC,KAAK;aACrB,CAAC,CAAC;iBACF,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACtD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,OAAO,MAAM,CAAC;QACtC,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,sCAAsC;IACvC,CAAC;IAED,MAAM,aAAa,GAAG,sBAAsB,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,MAAM,GAAsB,EAAE,CAAC;IACrC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,QAAQ,IAAI,GAAG,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACjD,IAAI,MAAM,KAAK,CAAC,CAAC;YAAE,SAAS;QAC5B,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QACxC,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;IACtC,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;AAC3D,CAAC,CAAC","sourcesContent":["export interface SentenceSegment {\n\ttext: string;\n\toffset: number;\n}\n\nexport interface SentenceSegmentationOptions {\n\tlocale?: string;\n\tuseSentenceSegmenter?: boolean;\n}\n\nexport const segmentSentences = (\n\ttext: string,\n\toptions?: SentenceSegmentationOptions,\n): SentenceSegment[] => {\n\tconst useSentenceSegmenter = options?.useSentenceSegmenter !== false;\n\ttry {\n\t\tif (!useSentenceSegmenter) {\n\t\t\tthrow new Error(\"Segmenter disabled by policy\");\n\t\t}\n\t\tconst Segmenter = globalThis.Intl?.Segmenter;\n\t\tif (typeof Segmenter === \"function\") {\n\t\t\tconst segmenter = new Segmenter(options?.locale, {\n\t\t\t\tgranularity: \"sentence\",\n\t\t\t});\n\t\t\tconst parsed = Array.from(segmenter.segment(text))\n\t\t\t\t.map((segment) => ({\n\t\t\t\t\ttext: segment.segment,\n\t\t\t\t\toffset: segment.index,\n\t\t\t\t}))\n\t\t\t\t.filter((segment) => segment.text.trim().length > 0);\n\t\t\tif (parsed.length > 0) return parsed;\n\t\t}\n\t} catch {\n\t\t// Fall through to regex segmentation.\n\t}\n\n\tconst sentenceRegex = /[^.!?]+(?:[.!?]+|$)/g;\n\tconst raw = text.match(sentenceRegex) || [text];\n\tconst parsed: SentenceSegment[] = [];\n\tlet processed = 0;\n\tfor (const sentence of raw) {\n\t\tconst offset = text.indexOf(sentence, processed);\n\t\tif (offset === -1) continue;\n\t\tparsed.push({ text: sentence, offset });\n\t\tprocessed = offset + sentence.length;\n\t}\n\treturn parsed.length > 0 ? parsed : [{ text, offset: 0 }];\n};\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"visual-line-ranges.js","sourceRoot":"","sources":["../../../src/services/tts/visual-line-ranges.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Derive visual line box ranges inside a root element for TTS read-along.\n *\n * Cloud TTS (Polly, Google, etc.) only provides word/sentence character offsets.\n * “Lines” are layout-dependent (wrap, zoom, font size), so we compute them in\n * the browser from the same DOM Range already used for word highlighting.\n */\n\nconst LINE_TOP_EPSILON = 2;\nconst CARET_INSET = 1;\nconst HORIZONTAL_PROBE_STEP = 3;\nconst MAX_HORIZONTAL_PROBES = 400;\nconst MAX_LINE_RANGES = 48;\n\ntype DocumentWithCaret = Document & {\n\tcaretRangeFromPoint?: (x: number, y: number) => Range | null;\n\tcaretPositionFromPoint?: (\n\t\tx: number,\n\t\ty: number,\n\t) => { offsetNode: Node; offset: number } | null;\n};\n\nfunction caretRangeAtPoint(doc: Document, x: number, y: number): Range | null {\n\tconst d = doc as DocumentWithCaret;\n\tif (typeof d.caretRangeFromPoint === \"function\") {\n\t\ttry {\n\t\t\treturn d.caretRangeFromPoint(x, y);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst pos = d.caretPositionFromPoint?.(x, y);\n\tif (\n\t\tpos?.offsetNode?.nodeType === Node.TEXT_NODE ||\n\t\tpos?.offsetNode?.nodeType === Node.ELEMENT_NODE\n\t) {\n\t\tconst r = doc.createRange();\n\t\ttry {\n\t\t\tr.setStart(pos.offsetNode, pos.offset);\n\t\t\tr.collapse(true);\n\t\t\treturn r;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\nfunction collapsedRangeTop(r: Range): number | null {\n\tconst rects = r.getClientRects();\n\tif (rects.length === 0) return null;\n\treturn rects[0].top;\n}\n\n/**\n * Intersect `range` with the contents of `root` (document order).\n * Returns null if there is no non-empty intersection.\n */\nexport function intersectRangeWithElement(\n\trange: Range,\n\troot: Element,\n): Range | null {\n\tif (typeof document === \"undefined\") return null;\n\tif (typeof range.compareBoundaryPoints !== \"function\") return null;\n\ttry {\n\t\tconst doc = range.startContainer.ownerDocument || document;\n\t\tconst rootRange = doc.createRange();\n\t\trootRange.selectNodeContents(root);\n\n\t\tif (range.compareBoundaryPoints(Range.END_TO_START, rootRange) <= 0) {\n\t\t\treturn null;\n\t\t}\n\t\tif (range.compareBoundaryPoints(Range.START_TO_END, rootRange) >= 0) {\n\t\t\treturn null;\n\t\t}\n\n\t\tconst out = doc.createRange();\n\t\tif (range.compareBoundaryPoints(Range.START_TO_START, rootRange) < 0) {\n\t\t\tout.setStart(rootRange.startContainer, rootRange.startOffset);\n\t\t} else {\n\t\t\tout.setStart(range.startContainer, range.startOffset);\n\t\t}\n\t\tif (range.compareBoundaryPoints(Range.END_TO_END, rootRange) > 0) {\n\t\t\tout.setEnd(rootRange.endContainer, rootRange.endOffset);\n\t\t} else {\n\t\t\tout.setEnd(range.endContainer, range.endOffset);\n\t\t}\n\t\tif (out.collapsed) return null;\n\t\treturn out;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction lineKeyForRect(top: number): number {\n\treturn Math.round(top / LINE_TOP_EPSILON);\n}\n\nfunction extendLineEdge(\n\tdoc: Document,\n\troot: Element,\n\trefTop: number,\n\tmidY: number,\n\tstartX: number,\n\trootLeft: number,\n\trootRight: number,\n\tdirection: \"left\" | \"right\",\n): Range | null {\n\tlet lastGood: Range | null = null;\n\tlet x = startX;\n\tif (direction === \"left\") {\n\t\tfor (\n\t\t\tlet i = 0;\n\t\t\ti < MAX_HORIZONTAL_PROBES && x >= rootLeft + CARET_INSET;\n\t\t\ti++\n\t\t) {\n\t\t\tconst cr = caretRangeAtPoint(doc, x, midY);\n\t\t\tif (!cr || !rootContainsNode(root, cr.startContainer)) break;\n\t\t\tconst t = collapsedRangeTop(cr);\n\t\t\tif (t === null || Math.abs(t - refTop) > LINE_TOP_EPSILON) break;\n\t\t\tlastGood = cr.cloneRange();\n\t\t\tx -= HORIZONTAL_PROBE_STEP;\n\t\t}\n\t} else {\n\t\tfor (\n\t\t\tlet i = 0;\n\t\t\ti < MAX_HORIZONTAL_PROBES && x <= rootRight - CARET_INSET;\n\t\t\ti++\n\t\t) {\n\t\t\tconst cr = caretRangeAtPoint(doc, x, midY);\n\t\t\tif (!cr || !rootContainsNode(root, cr.startContainer)) break;\n\t\t\tconst t = collapsedRangeTop(cr);\n\t\t\tif (t === null || Math.abs(t - refTop) > LINE_TOP_EPSILON) break;\n\t\t\tlastGood = cr.cloneRange();\n\t\t\tx += HORIZONTAL_PROBE_STEP;\n\t\t}\n\t}\n\treturn lastGood;\n}\n\nfunction rootContainsNode(root: Element, node: Node): boolean {\n\tif (typeof root.contains !== \"function\") return false;\n\ttry {\n\t\treturn root.contains(node);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction fallbackSingleRangeFromFocus(focus: Range): Range[] {\n\ttry {\n\t\tconst doc = focus.startContainer.ownerDocument || document;\n\t\tconst r = doc.createRange();\n\t\tr.setStart(focus.startContainer, focus.startOffset);\n\t\tr.setEnd(focus.endContainer, focus.endOffset);\n\t\treturn [r];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Returns one Range per visual line that intersects `focus`, expanded to the\n * full width of each line within `root`. Falls back to `[intersectedFocus]`\n * when layout or caret APIs are unavailable.\n */\nexport function visualLineRangesForFocusInRoot(\n\troot: Element,\n\tfocus: Range,\n): Range[] {\n\tif (typeof document === \"undefined\") return [];\n\tif (!focus.startContainer) {\n\t\treturn [];\n\t}\n\n\tconst doc = focus.startContainer.ownerDocument || document;\n\tconst intersected = intersectRangeWithElement(focus, root);\n\tif (!intersected) {\n\t\treturn fallbackSingleRangeFromFocus(focus);\n\t}\n\n\tlet rects: DOMRect[];\n\ttry {\n\t\trects = Array.from(intersected.getClientRects()).filter(\n\t\t\t(r) => r.width > 0 && r.height > 0,\n\t\t);\n\t} catch {\n\t\treturn [intersected.cloneRange()];\n\t}\n\tif (rects.length === 0) {\n\t\treturn [intersected.cloneRange()];\n\t}\n\n\tlet rootBounds: DOMRect;\n\ttry {\n\t\trootBounds = root.getBoundingClientRect();\n\t} catch {\n\t\treturn [intersected.cloneRange()];\n\t}\n\tconst groups = new Map<number, DOMRect[]>();\n\tfor (const rect of rects) {\n\t\tconst key = lineKeyForRect(rect.top);\n\t\tconst list = groups.get(key);\n\t\tif (list) list.push(rect);\n\t\telse groups.set(key, [rect]);\n\t}\n\n\tconst sortedKeys = Array.from(groups.keys()).sort((a, b) => a - b);\n\tconst out: Range[] = [];\n\n\tfor (const key of sortedKeys) {\n\t\tif (out.length >= MAX_LINE_RANGES) break;\n\t\tconst group = groups.get(key);\n\t\tif (!group?.length) continue;\n\n\t\tlet sumTop = 0;\n\t\tlet sumMid = 0;\n\t\tlet minL = Infinity;\n\t\tlet maxR = -Infinity;\n\t\tfor (const r of group) {\n\t\t\tsumTop += r.top;\n\t\t\tsumMid += r.top + r.height / 2;\n\t\t\tminL = Math.min(minL, r.left);\n\t\t\tmaxR = Math.max(maxR, r.right);\n\t\t}\n\t\tconst refTop = sumTop / group.length;\n\t\tconst midY = sumMid / group.length;\n\n\t\tconst leftEdge = extendLineEdge(\n\t\t\tdoc,\n\t\t\troot,\n\t\t\trefTop,\n\t\t\tmidY,\n\t\t\tminL,\n\t\t\trootBounds.left,\n\t\t\trootBounds.right,\n\t\t\t\"left\",\n\t\t);\n\t\tconst rightEdge = extendLineEdge(\n\t\t\tdoc,\n\t\t\troot,\n\t\t\trefTop,\n\t\t\tmidY,\n\t\t\tmaxR,\n\t\t\trootBounds.left,\n\t\t\trootBounds.right,\n\t\t\t\"right\",\n\t\t);\n\n\t\tif (!leftEdge || !rightEdge) {\n\t\t\tconst fallback = intersected.cloneRange();\n\t\t\treturn [fallback];\n\t\t}\n\n\t\tconst lineRange = doc.createRange();\n\t\ttry {\n\t\t\tlineRange.setStart(leftEdge.startContainer, leftEdge.startOffset);\n\t\t\tlineRange.setEnd(rightEdge.startContainer, rightEdge.startOffset);\n\t\t} catch {\n\t\t\treturn [intersected.cloneRange()];\n\t\t}\n\n\t\tif (lineRange.collapsed) {\n\t\t\treturn [intersected.cloneRange()];\n\t\t}\n\n\t\tconst clipped = intersectRangeWithElement(lineRange, root);\n\t\tif (clipped && !clipped.collapsed) {\n\t\t\tout.push(clipped);\n\t\t} else {\n\t\t\treturn [intersected.cloneRange()];\n\t\t}\n\t}\n\n\treturn out.length > 0 ? out : [intersected.cloneRange()];\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"tts-control-events.js","sourceRoot":"","sources":["../../src/services/tts-control-events.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,yBAAyB,CAAC","sourcesContent":["/**\n * Shared orchestration events for TTS tool UI controls.\n *\n * These events are intentionally UI-level coordination hints. They do not replace\n * playback controls such as stop/pause/resume.\n */\nexport const PIE_TTS_CONTROL_HANDOFF_EVENT = \"pie-tts-control-handoff\";\n\nexport type TTSControlHandoffDetail = {\n\t/**\n\t * Optional source identifier for host-orchestrated handoff tracing.\n\t */\n\tsource?: \"host\";\n};\n"]}