@pie-players/pie-assessment-toolkit 0.3.43 → 0.3.45

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 (441) hide show
  1. package/README.md +54 -69
  2. package/dist/attempt/AssessmentSession.d.ts +0 -1
  3. package/dist/attempt/AssessmentSession.js.map +1 -1
  4. package/dist/attempt/TestSession.d.ts +0 -1
  5. package/dist/attempt/TestSession.js.map +1 -1
  6. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -1
  7. package/dist/attempt/adapters/activity-to-test-attempt-session.js.map +1 -1
  8. package/dist/components/ItemToolBar.custom-element.js +1607 -428
  9. package/dist/components/PieAssessmentToolkit.custom-element.js +19498 -3590
  10. package/dist/components/SectionToolBar.custom-element.js +1887 -611
  11. package/dist/components/item-toolbar-element.d.ts +0 -1
  12. package/dist/components/item-toolbar-element.js.map +1 -1
  13. package/dist/components/pie-assessment-toolkit-element.d.ts +0 -1
  14. package/dist/components/pie-assessment-toolkit-element.js.map +1 -1
  15. package/dist/components/section-toolbar-element.d.ts +0 -1
  16. package/dist/components/section-toolbar-element.js.map +1 -1
  17. package/dist/components/vendor/nds/README.md +110 -0
  18. package/dist/components/vendor/nds/nds-icon-button.js +826 -0
  19. package/dist/context/assessment-toolkit-context.d.ts +0 -1
  20. package/dist/context/assessment-toolkit-context.js.map +1 -1
  21. package/dist/context/runtime-context-consumer.d.ts +0 -1
  22. package/dist/context/runtime-context-consumer.js.map +1 -1
  23. package/dist/index.d.ts +3 -7
  24. package/dist/index.js +1 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/item-loader.d.ts +0 -1
  27. package/dist/item-loader.js.map +1 -1
  28. package/dist/policy/core/PolicySource.d.ts +0 -1
  29. package/dist/policy/core/PolicySource.js.map +1 -1
  30. package/dist/policy/core/ToolPolicyEngine.d.ts +1 -3
  31. package/dist/policy/core/ToolPolicyEngine.js +1 -2
  32. package/dist/policy/core/ToolPolicyEngine.js.map +1 -1
  33. package/dist/policy/core/compose-decision.d.ts +0 -1
  34. package/dist/policy/core/compose-decision.js.map +1 -1
  35. package/dist/policy/core/decision-types.d.ts +0 -1
  36. package/dist/policy/core/decision-types.js.map +1 -1
  37. package/dist/policy/core/engine-context.d.ts +0 -1
  38. package/dist/policy/core/engine-context.js.map +1 -1
  39. package/dist/policy/core/pnp-policy-inputs.d.ts +2 -4
  40. package/dist/policy/core/pnp-policy-inputs.js +2 -3
  41. package/dist/policy/core/pnp-policy-inputs.js.map +1 -1
  42. package/dist/policy/core/policy-source-tag.d.ts +0 -1
  43. package/dist/policy/core/policy-source-tag.js.map +1 -1
  44. package/dist/policy/core/provenance.d.ts +7 -35
  45. package/dist/policy/core/provenance.js +4 -21
  46. package/dist/policy/core/provenance.js.map +1 -1
  47. package/dist/policy/engine.d.ts +0 -1
  48. package/dist/policy/engine.js.map +1 -1
  49. package/dist/policy/internal.d.ts +0 -1
  50. package/dist/policy/internal.js.map +1 -1
  51. package/dist/policy/sources/PnpPolicySource.d.ts +6 -20
  52. package/dist/policy/sources/PnpPolicySource.js +6 -19
  53. package/dist/policy/sources/PnpPolicySource.js.map +1 -1
  54. package/dist/runtime/RuntimeRegistry.d.ts +0 -1
  55. package/dist/runtime/RuntimeRegistry.js.map +1 -1
  56. package/dist/runtime/SectionRuntimeEngine.d.ts +13 -29
  57. package/dist/runtime/SectionRuntimeEngine.js +13 -27
  58. package/dist/runtime/SectionRuntimeEngine.js.map +1 -1
  59. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +2 -3
  60. package/dist/runtime/adapter/SectionEngineAdapter.js +2 -2
  61. package/dist/runtime/adapter/SectionEngineAdapter.js.map +1 -1
  62. package/dist/runtime/adapter/coordinator-bridge.d.ts +1 -2
  63. package/dist/runtime/adapter/coordinator-bridge.js +1 -1
  64. package/dist/runtime/adapter/coordinator-bridge.js.map +1 -1
  65. package/dist/runtime/adapter/dom-event-bridge.d.ts +1 -2
  66. package/dist/runtime/adapter/dom-event-bridge.js +1 -1
  67. package/dist/runtime/adapter/dom-event-bridge.js.map +1 -1
  68. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -1
  69. package/dist/runtime/adapter/framework-error-bridge.js.map +1 -1
  70. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -1
  71. package/dist/runtime/adapter/instrumentation-bridge.js.map +1 -1
  72. package/dist/runtime/adapter/subscriber-fanout.d.ts +0 -1
  73. package/dist/runtime/adapter/subscriber-fanout.js.map +1 -1
  74. package/dist/runtime/catalog-registration.d.ts +12 -0
  75. package/dist/runtime/catalog-registration.js +54 -0
  76. package/dist/runtime/catalog-registration.js.map +1 -0
  77. package/dist/runtime/core/SectionEngineCore.d.ts +0 -1
  78. package/dist/runtime/core/SectionEngineCore.js.map +1 -1
  79. package/dist/runtime/core/cohort.d.ts +0 -1
  80. package/dist/runtime/core/cohort.js.map +1 -1
  81. package/dist/runtime/core/engine-input.d.ts +0 -1
  82. package/dist/runtime/core/engine-input.js.map +1 -1
  83. package/dist/runtime/core/engine-output.d.ts +2 -3
  84. package/dist/runtime/core/engine-output.js +2 -2
  85. package/dist/runtime/core/engine-output.js.map +1 -1
  86. package/dist/runtime/core/engine-readiness.d.ts +2 -3
  87. package/dist/runtime/core/engine-readiness.js.map +1 -1
  88. package/dist/runtime/core/engine-resolver.d.ts +13 -42
  89. package/dist/runtime/core/engine-resolver.js +19 -76
  90. package/dist/runtime/core/engine-resolver.js.map +1 -1
  91. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -1
  92. package/dist/runtime/core/engine-stage-derivation.js.map +1 -1
  93. package/dist/runtime/core/engine-state.d.ts +0 -1
  94. package/dist/runtime/core/engine-state.js.map +1 -1
  95. package/dist/runtime/core/engine-transition.d.ts +1 -2
  96. package/dist/runtime/core/engine-transition.js +1 -1
  97. package/dist/runtime/core/engine-transition.js.map +1 -1
  98. package/dist/runtime/engine-context.d.ts +0 -1
  99. package/dist/runtime/engine-context.js.map +1 -1
  100. package/dist/runtime/engine.d.ts +1 -2
  101. package/dist/runtime/engine.js.map +1 -1
  102. package/dist/runtime/internal.d.ts +0 -1
  103. package/dist/runtime/internal.js.map +1 -1
  104. package/dist/runtime/registration-events.d.ts +0 -1
  105. package/dist/runtime/registration-events.js.map +1 -1
  106. package/dist/runtime/runtime-id.d.ts +0 -1
  107. package/dist/runtime/runtime-id.js.map +1 -1
  108. package/dist/runtime/section-runtime-engine-host-context.d.ts +22 -16
  109. package/dist/runtime/section-runtime-engine-host-context.js +10 -10
  110. package/dist/runtime/section-runtime-engine-host-context.js.map +1 -1
  111. package/dist/runtime/session-event-emitter-policy.d.ts +0 -1
  112. package/dist/runtime/session-event-emitter-policy.js.map +1 -1
  113. package/dist/runtime/stage-emit-gate.d.ts +4 -5
  114. package/dist/runtime/stage-emit-gate.js +3 -3
  115. package/dist/runtime/stage-emit-gate.js.map +1 -1
  116. package/dist/runtime/tool-host-contract.d.ts +0 -1
  117. package/dist/runtime/tool-host-contract.js.map +1 -1
  118. package/dist/services/AccessibilityCatalogResolver.d.ts +23 -1
  119. package/dist/services/AccessibilityCatalogResolver.js +200 -28
  120. package/dist/services/AccessibilityCatalogResolver.js.map +1 -1
  121. package/dist/services/ContextVariableStore.d.ts +0 -1
  122. package/dist/services/ContextVariableStore.js.map +1 -1
  123. package/dist/services/ElementToolStateStore.d.ts +0 -1
  124. package/dist/services/ElementToolStateStore.js.map +1 -1
  125. package/dist/services/HighlightCoordinator.d.ts +25 -1
  126. package/dist/services/HighlightCoordinator.js +125 -0
  127. package/dist/services/HighlightCoordinator.js.map +1 -1
  128. package/dist/services/I18nService.d.ts +0 -1
  129. package/dist/services/I18nService.js.map +1 -1
  130. package/dist/services/RangeSerializer.d.ts +0 -1
  131. package/dist/services/RangeSerializer.js.map +1 -1
  132. package/dist/services/SSMLExtractor.d.ts +16 -2
  133. package/dist/services/SSMLExtractor.js +86 -7
  134. package/dist/services/SSMLExtractor.js.map +1 -1
  135. package/dist/services/TTSService.d.ts +30 -5
  136. package/dist/services/TTSService.js +652 -159
  137. package/dist/services/TTSService.js.map +1 -1
  138. package/dist/services/ThemeProvider.d.ts +0 -1
  139. package/dist/services/ThemeProvider.js.map +1 -1
  140. package/dist/services/ToolConfigResolver.d.ts +0 -1
  141. package/dist/services/ToolConfigResolver.js.map +1 -1
  142. package/dist/services/ToolCoordinator.d.ts +0 -1
  143. package/dist/services/ToolCoordinator.js.map +1 -1
  144. package/dist/services/ToolRegistry.d.ts +2 -3
  145. package/dist/services/ToolRegistry.js +1 -1
  146. package/dist/services/ToolRegistry.js.map +1 -1
  147. package/dist/services/ToolkitCoordinator.d.ts +16 -74
  148. package/dist/services/ToolkitCoordinator.js +34 -113
  149. package/dist/services/ToolkitCoordinator.js.map +1 -1
  150. package/dist/services/create-tools-config.d.ts +0 -1
  151. package/dist/services/create-tools-config.js.map +1 -1
  152. package/dist/services/createDefaultToolRegistry.d.ts +0 -1
  153. package/dist/services/createDefaultToolRegistry.js.map +1 -1
  154. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -1
  155. package/dist/services/defaultPersonalNeedsProfile.js.map +1 -1
  156. package/dist/services/framework-error-bus.d.ts +0 -1
  157. package/dist/services/framework-error-bus.js.map +1 -1
  158. package/dist/services/framework-error.d.ts +0 -1
  159. package/dist/services/framework-error.js.map +1 -1
  160. package/dist/services/interfaces.d.ts +20 -3
  161. package/dist/services/interfaces.js.map +1 -1
  162. package/dist/services/pnp-standard-features.d.ts +0 -1
  163. package/dist/services/pnp-standard-features.js.map +1 -1
  164. package/dist/services/section-controller-types.d.ts +0 -1
  165. package/dist/services/section-controller-types.js.map +1 -1
  166. package/dist/services/tool-config-defaults.d.ts +0 -1
  167. package/dist/services/tool-config-defaults.js.map +1 -1
  168. package/dist/services/tool-config-validation.d.ts +1 -2
  169. package/dist/services/tool-config-validation.js +7 -7
  170. package/dist/services/tool-config-validation.js.map +1 -1
  171. package/dist/services/tool-context.d.ts +0 -1
  172. package/dist/services/tool-context.js.map +1 -1
  173. package/dist/services/tool-instance-id.d.ts +0 -1
  174. package/dist/services/tool-instance-id.js +1 -1
  175. package/dist/services/tool-instance-id.js.map +1 -1
  176. package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -1
  177. package/dist/services/tool-providers/DesmosToolProvider.js.map +1 -1
  178. package/dist/services/tool-providers/TTSToolProvider.d.ts +0 -1
  179. package/dist/services/tool-providers/TTSToolProvider.js +3 -3
  180. package/dist/services/tool-providers/TTSToolProvider.js.map +1 -1
  181. package/dist/services/tool-providers/ToolProviderApi.d.ts +0 -1
  182. package/dist/services/tool-providers/ToolProviderApi.js.map +1 -1
  183. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +0 -1
  184. package/dist/services/tool-providers/ToolProviderRegistry.js.map +1 -1
  185. package/dist/services/tool-providers/index.d.ts +0 -1
  186. package/dist/services/tool-providers/index.js.map +1 -1
  187. package/dist/services/toolbar-items.d.ts +0 -1
  188. package/dist/services/toolbar-items.js.map +1 -1
  189. package/dist/services/tools-config-normalizer.d.ts +13 -3
  190. package/dist/services/tools-config-normalizer.js +3 -8
  191. package/dist/services/tools-config-normalizer.js.map +1 -1
  192. package/dist/services/tts/browser-provider.d.ts +0 -1
  193. package/dist/services/tts/browser-provider.js +2 -0
  194. package/dist/services/tts/browser-provider.js.map +1 -1
  195. package/dist/services/tts/catalog-span-alignment.d.ts +39 -0
  196. package/dist/services/tts/catalog-span-alignment.js +300 -0
  197. package/dist/services/tts/catalog-span-alignment.js.map +1 -0
  198. package/dist/services/tts/generated-speech/assemble-plan.d.ts +33 -0
  199. package/dist/services/tts/generated-speech/assemble-plan.js +111 -0
  200. package/dist/services/tts/generated-speech/assemble-plan.js.map +1 -0
  201. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +25 -0
  202. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +66 -0
  203. package/dist/services/tts/generated-speech/dom/build-dom-plan.js.map +1 -0
  204. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.d.ts +19 -0
  205. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js +71 -0
  206. package/dist/services/tts/generated-speech/dom/to-chunk-inputs.js.map +1 -0
  207. package/dist/services/tts/generated-speech/dom/types.d.ts +37 -0
  208. package/dist/services/tts/generated-speech/dom/types.js +2 -0
  209. package/dist/services/tts/generated-speech/dom/types.js.map +1 -0
  210. package/dist/services/tts/generated-speech/index.d.ts +17 -0
  211. package/dist/services/tts/generated-speech/index.js +17 -0
  212. package/dist/services/tts/generated-speech/index.js.map +1 -0
  213. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +15 -0
  214. package/dist/services/tts/generated-speech/math-speech-cache.js +78 -0
  215. package/dist/services/tts/generated-speech/math-speech-cache.js.map +1 -0
  216. package/dist/services/tts/generated-speech/types.d.ts +104 -0
  217. package/dist/services/tts/generated-speech/types.js +18 -0
  218. package/dist/services/tts/generated-speech/types.js.map +1 -0
  219. package/dist/services/tts/highlight-pipeline/boundary-normalizer.d.ts +2 -0
  220. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +106 -0
  221. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js.map +1 -0
  222. package/dist/services/tts/highlight-pipeline/create-highlight-plan.d.ts +6 -0
  223. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +217 -0
  224. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js.map +1 -0
  225. package/dist/services/tts/highlight-pipeline/highlight-policy.d.ts +2 -0
  226. package/dist/services/tts/highlight-pipeline/highlight-policy.js +32 -0
  227. package/dist/services/tts/highlight-pipeline/highlight-policy.js.map +1 -0
  228. package/dist/services/tts/highlight-pipeline/index.d.ts +9 -0
  229. package/dist/services/tts/highlight-pipeline/index.js +9 -0
  230. package/dist/services/tts/highlight-pipeline/index.js.map +1 -0
  231. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.d.ts +3 -0
  232. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js +32 -0
  233. package/dist/services/tts/highlight-pipeline/math-semantic-resolver.js.map +1 -0
  234. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.d.ts +29 -0
  235. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +44 -0
  236. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js.map +1 -0
  237. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.d.ts +2 -0
  238. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +20 -0
  239. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js.map +1 -0
  240. package/dist/services/tts/highlight-pipeline/readable-region.d.ts +1 -0
  241. package/dist/services/tts/highlight-pipeline/readable-region.js +19 -0
  242. package/dist/services/tts/highlight-pipeline/readable-region.js.map +1 -0
  243. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.d.ts +6 -0
  244. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js +198 -0
  245. package/dist/services/tts/highlight-pipeline/rendered-math-target-resolver.js.map +1 -0
  246. package/dist/services/tts/highlight-pipeline/types.d.ts +72 -0
  247. package/dist/services/tts/highlight-pipeline/types.js +2 -0
  248. package/dist/services/tts/highlight-pipeline/types.js.map +1 -0
  249. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +2 -0
  250. package/dist/services/tts/highlight-pipeline/visible-map-range.js +22 -0
  251. package/dist/services/tts/highlight-pipeline/visible-map-range.js.map +1 -0
  252. package/dist/services/tts/math-alignment/index.d.ts +30 -0
  253. package/dist/services/tts/math-alignment/index.js +66 -0
  254. package/dist/services/tts/math-alignment/index.js.map +1 -0
  255. package/dist/services/tts/math-alignment/mathml-tokenizer.d.ts +2 -0
  256. package/dist/services/tts/math-alignment/mathml-tokenizer.js +317 -0
  257. package/dist/services/tts/math-alignment/mathml-tokenizer.js.map +1 -0
  258. package/dist/services/tts/math-alignment/range-resolver.d.ts +10 -0
  259. package/dist/services/tts/math-alignment/range-resolver.js +13 -0
  260. package/dist/services/tts/math-alignment/range-resolver.js.map +1 -0
  261. package/dist/services/tts/math-alignment/sequence-aligner.d.ts +7 -0
  262. package/dist/services/tts/math-alignment/sequence-aligner.js +119 -0
  263. package/dist/services/tts/math-alignment/sequence-aligner.js.map +1 -0
  264. package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +31 -0
  265. package/dist/services/tts/math-alignment/speech-tokenizer.js +143 -0
  266. package/dist/services/tts/math-alignment/speech-tokenizer.js.map +1 -0
  267. package/dist/services/tts/math-alignment/types.d.ts +58 -0
  268. package/dist/services/tts/math-alignment/types.js +2 -0
  269. package/dist/services/tts/math-alignment/types.js.map +1 -0
  270. package/dist/services/tts/math-aware-text-processing.d.ts +18 -0
  271. package/dist/services/tts/math-aware-text-processing.js +348 -0
  272. package/dist/services/tts/math-aware-text-processing.js.map +1 -0
  273. package/dist/services/tts/math-speech.d.ts +44 -0
  274. package/dist/services/tts/math-speech.js +147 -0
  275. package/dist/services/tts/math-speech.js.map +1 -0
  276. package/dist/services/tts/mathml-sanitization.d.ts +1 -0
  277. package/dist/services/tts/mathml-sanitization.js +170 -0
  278. package/dist/services/tts/mathml-sanitization.js.map +1 -0
  279. package/dist/services/tts/ssml/spoken-text.d.ts +33 -0
  280. package/dist/services/tts/ssml/spoken-text.js +159 -0
  281. package/dist/services/tts/ssml/spoken-text.js.map +1 -0
  282. package/dist/services/tts/text-processing.d.ts +3 -1
  283. package/dist/services/tts/text-processing.js +30 -4
  284. package/dist/services/tts/text-processing.js.map +1 -1
  285. package/dist/services/tts/text-segmentation.d.ts +0 -1
  286. package/dist/services/tts/text-segmentation.js.map +1 -1
  287. package/dist/services/tts/visual-line-ranges.d.ts +0 -1
  288. package/dist/services/tts/visual-line-ranges.js.map +1 -1
  289. package/dist/services/tts-control-events.d.ts +0 -1
  290. package/dist/services/tts-control-events.js.map +1 -1
  291. package/dist/services/tts-runtime-config.d.ts +17 -2
  292. package/dist/services/tts-runtime-config.js +11 -1
  293. package/dist/services/tts-runtime-config.js.map +1 -1
  294. package/dist/tools/calculators/desmos-provider.d.ts +0 -1
  295. package/dist/tools/calculators/desmos-provider.js.map +1 -1
  296. package/dist/tools/client.d.ts +0 -1
  297. package/dist/tools/client.js.map +1 -1
  298. package/dist/tools/default-tool-module-loaders.d.ts +0 -1
  299. package/dist/tools/default-tool-module-loaders.js.map +1 -1
  300. package/dist/tools/index.d.ts +0 -1
  301. package/dist/tools/index.js.map +1 -1
  302. package/dist/tools/library-loader.d.ts +0 -1
  303. package/dist/tools/library-loader.js.map +1 -1
  304. package/dist/tools/registrations/accessibility-tools.d.ts +0 -1
  305. package/dist/tools/registrations/accessibility-tools.js +0 -1
  306. package/dist/tools/registrations/accessibility-tools.js.map +1 -1
  307. package/dist/tools/registrations/calculator.d.ts +0 -1
  308. package/dist/tools/registrations/calculator.js +41 -2
  309. package/dist/tools/registrations/calculator.js.map +1 -1
  310. package/dist/tools/registrations/interaction-tools.d.ts +0 -1
  311. package/dist/tools/registrations/interaction-tools.js.map +1 -1
  312. package/dist/tools/registrations/measurement-tools.d.ts +0 -1
  313. package/dist/tools/registrations/measurement-tools.js.map +1 -1
  314. package/dist/tools/registrations/subject-specific-tools.d.ts +0 -1
  315. package/dist/tools/registrations/subject-specific-tools.js.map +1 -1
  316. package/dist/tools/registrations/toolbar-registration-helpers.d.ts +0 -1
  317. package/dist/tools/registrations/toolbar-registration-helpers.js.map +1 -1
  318. package/dist/tools/registrations/tts.d.ts +0 -1
  319. package/dist/tools/registrations/tts.js.map +1 -1
  320. package/dist/tools/response-discovery.d.ts +0 -1
  321. package/dist/tools/response-discovery.js.map +1 -1
  322. package/dist/tools/tool-tag-map.d.ts +0 -1
  323. package/dist/tools/tool-tag-map.js +0 -1
  324. package/dist/tools/tool-tag-map.js.map +1 -1
  325. package/dist/tools/types.d.ts +0 -1
  326. package/dist/tools/types.js.map +1 -1
  327. package/dist/tools/variant-resolver.d.ts +0 -1
  328. package/dist/tools/variant-resolver.js.map +1 -1
  329. package/dist/utils/logger.d.ts +0 -1
  330. package/dist/utils/logger.js.map +1 -1
  331. package/package.json +8 -11
  332. package/dist/attempt/AssessmentSession.d.ts.map +0 -1
  333. package/dist/attempt/TestSession.d.ts.map +0 -1
  334. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts.map +0 -1
  335. package/dist/components/item-toolbar-element.d.ts.map +0 -1
  336. package/dist/components/pie-assessment-toolkit-element.d.ts.map +0 -1
  337. package/dist/components/section-toolbar-element.d.ts.map +0 -1
  338. package/dist/context/assessment-toolkit-context.d.ts.map +0 -1
  339. package/dist/context/runtime-context-consumer.d.ts.map +0 -1
  340. package/dist/index.d.ts.map +0 -1
  341. package/dist/item-loader.d.ts.map +0 -1
  342. package/dist/policy/core/PolicySource.d.ts.map +0 -1
  343. package/dist/policy/core/ToolPolicyEngine.d.ts.map +0 -1
  344. package/dist/policy/core/compose-decision.d.ts.map +0 -1
  345. package/dist/policy/core/decision-types.d.ts.map +0 -1
  346. package/dist/policy/core/engine-context.d.ts.map +0 -1
  347. package/dist/policy/core/pnp-policy-inputs.d.ts.map +0 -1
  348. package/dist/policy/core/policy-source-tag.d.ts.map +0 -1
  349. package/dist/policy/core/provenance.d.ts.map +0 -1
  350. package/dist/policy/engine.d.ts.map +0 -1
  351. package/dist/policy/internal.d.ts.map +0 -1
  352. package/dist/policy/sources/PnpPolicySource.d.ts.map +0 -1
  353. package/dist/runtime/RuntimeRegistry.d.ts.map +0 -1
  354. package/dist/runtime/SectionRuntimeEngine.d.ts.map +0 -1
  355. package/dist/runtime/adapter/SectionEngineAdapter.d.ts.map +0 -1
  356. package/dist/runtime/adapter/coordinator-bridge.d.ts.map +0 -1
  357. package/dist/runtime/adapter/dom-event-bridge.d.ts.map +0 -1
  358. package/dist/runtime/adapter/framework-error-bridge.d.ts.map +0 -1
  359. package/dist/runtime/adapter/instrumentation-bridge.d.ts.map +0 -1
  360. package/dist/runtime/adapter/subscriber-fanout.d.ts.map +0 -1
  361. package/dist/runtime/core/SectionEngineCore.d.ts.map +0 -1
  362. package/dist/runtime/core/cohort.d.ts.map +0 -1
  363. package/dist/runtime/core/engine-input.d.ts.map +0 -1
  364. package/dist/runtime/core/engine-output.d.ts.map +0 -1
  365. package/dist/runtime/core/engine-readiness.d.ts.map +0 -1
  366. package/dist/runtime/core/engine-resolver.d.ts.map +0 -1
  367. package/dist/runtime/core/engine-stage-derivation.d.ts.map +0 -1
  368. package/dist/runtime/core/engine-state.d.ts.map +0 -1
  369. package/dist/runtime/core/engine-transition.d.ts.map +0 -1
  370. package/dist/runtime/engine-context.d.ts.map +0 -1
  371. package/dist/runtime/engine.d.ts.map +0 -1
  372. package/dist/runtime/internal.d.ts.map +0 -1
  373. package/dist/runtime/registration-events.d.ts.map +0 -1
  374. package/dist/runtime/runtime-id.d.ts.map +0 -1
  375. package/dist/runtime/section-runtime-engine-host-context.d.ts.map +0 -1
  376. package/dist/runtime/session-event-emitter-policy.d.ts.map +0 -1
  377. package/dist/runtime/stage-emit-gate.d.ts.map +0 -1
  378. package/dist/runtime/tool-host-contract.d.ts.map +0 -1
  379. package/dist/services/AccessibilityCatalogResolver.d.ts.map +0 -1
  380. package/dist/services/ContextVariableStore.d.ts.map +0 -1
  381. package/dist/services/ElementToolStateStore.d.ts.map +0 -1
  382. package/dist/services/HighlightCoordinator.d.ts.map +0 -1
  383. package/dist/services/I18nService.d.ts.map +0 -1
  384. package/dist/services/PNPToolResolver.d.ts +0 -281
  385. package/dist/services/PNPToolResolver.d.ts.map +0 -1
  386. package/dist/services/PNPToolResolver.js +0 -514
  387. package/dist/services/PNPToolResolver.js.map +0 -1
  388. package/dist/services/RangeSerializer.d.ts.map +0 -1
  389. package/dist/services/SSMLExtractor.d.ts.map +0 -1
  390. package/dist/services/TTSService.d.ts.map +0 -1
  391. package/dist/services/ThemeProvider.d.ts.map +0 -1
  392. package/dist/services/ToolConfigResolver.d.ts.map +0 -1
  393. package/dist/services/ToolCoordinator.d.ts.map +0 -1
  394. package/dist/services/ToolRegistry.d.ts.map +0 -1
  395. package/dist/services/ToolkitCoordinator.d.ts.map +0 -1
  396. package/dist/services/create-tools-config.d.ts.map +0 -1
  397. package/dist/services/createDefaultToolRegistry.d.ts.map +0 -1
  398. package/dist/services/defaultPersonalNeedsProfile.d.ts.map +0 -1
  399. package/dist/services/framework-error-bus.d.ts.map +0 -1
  400. package/dist/services/framework-error.d.ts.map +0 -1
  401. package/dist/services/interfaces.d.ts.map +0 -1
  402. package/dist/services/pnp-provenance.d.ts +0 -167
  403. package/dist/services/pnp-provenance.d.ts.map +0 -1
  404. package/dist/services/pnp-provenance.js +0 -231
  405. package/dist/services/pnp-provenance.js.map +0 -1
  406. package/dist/services/pnp-standard-features.d.ts.map +0 -1
  407. package/dist/services/section-controller-types.d.ts.map +0 -1
  408. package/dist/services/tool-config-defaults.d.ts.map +0 -1
  409. package/dist/services/tool-config-validation.d.ts.map +0 -1
  410. package/dist/services/tool-context.d.ts.map +0 -1
  411. package/dist/services/tool-instance-id.d.ts.map +0 -1
  412. package/dist/services/tool-providers/DesmosToolProvider.d.ts.map +0 -1
  413. package/dist/services/tool-providers/TTSToolProvider.d.ts.map +0 -1
  414. package/dist/services/tool-providers/ToolProviderApi.d.ts.map +0 -1
  415. package/dist/services/tool-providers/ToolProviderRegistry.d.ts.map +0 -1
  416. package/dist/services/tool-providers/index.d.ts.map +0 -1
  417. package/dist/services/toolbar-items.d.ts.map +0 -1
  418. package/dist/services/tools-config-normalizer.d.ts.map +0 -1
  419. package/dist/services/tts/browser-provider.d.ts.map +0 -1
  420. package/dist/services/tts/text-processing.d.ts.map +0 -1
  421. package/dist/services/tts/text-segmentation.d.ts.map +0 -1
  422. package/dist/services/tts/visual-line-ranges.d.ts.map +0 -1
  423. package/dist/services/tts-control-events.d.ts.map +0 -1
  424. package/dist/services/tts-runtime-config.d.ts.map +0 -1
  425. package/dist/tools/calculators/desmos-provider.d.ts.map +0 -1
  426. package/dist/tools/client.d.ts.map +0 -1
  427. package/dist/tools/default-tool-module-loaders.d.ts.map +0 -1
  428. package/dist/tools/index.d.ts.map +0 -1
  429. package/dist/tools/library-loader.d.ts.map +0 -1
  430. package/dist/tools/registrations/accessibility-tools.d.ts.map +0 -1
  431. package/dist/tools/registrations/calculator.d.ts.map +0 -1
  432. package/dist/tools/registrations/interaction-tools.d.ts.map +0 -1
  433. package/dist/tools/registrations/measurement-tools.d.ts.map +0 -1
  434. package/dist/tools/registrations/subject-specific-tools.d.ts.map +0 -1
  435. package/dist/tools/registrations/toolbar-registration-helpers.d.ts.map +0 -1
  436. package/dist/tools/registrations/tts.d.ts.map +0 -1
  437. package/dist/tools/response-discovery.d.ts.map +0 -1
  438. package/dist/tools/tool-tag-map.d.ts.map +0 -1
  439. package/dist/tools/types.d.ts.map +0 -1
  440. package/dist/tools/variant-resolver.d.ts.map +0 -1
  441. package/dist/utils/logger.d.ts.map +0 -1
@@ -0,0 +1,143 @@
1
+ import { extractSpokenText } from "../ssml/spoken-text.js";
2
+ import { createSpeechAlignmentTokenPattern } from "../text-processing.js";
3
+ // Shared Unicode-aware tokenizer, identical to the catalog span aligner's, so
4
+ // spoken text tokenizes the same way on both paths (see
5
+ // `createSpeechAlignmentTokenPattern`).
6
+ const TOKEN_PATTERN = createSpeechAlignmentTokenPattern();
7
+ const NUMERIC_WORDS = new Map([
8
+ ["zero", "0"],
9
+ ["one", "1"],
10
+ ["two", "2"],
11
+ ["three", "3"],
12
+ ["four", "4"],
13
+ ["five", "5"],
14
+ ["six", "6"],
15
+ ["seven", "7"],
16
+ ["eight", "8"],
17
+ ["nine", "9"],
18
+ ["ten", "10"],
19
+ ]);
20
+ const normalizedTokenValue = (value) => {
21
+ const normalized = value.toLowerCase();
22
+ return NUMERIC_WORDS.get(normalized) || normalized;
23
+ };
24
+ const tokenizeSpokenText = (spokenText) => {
25
+ const tokens = [];
26
+ for (const match of spokenText.matchAll(TOKEN_PATTERN)) {
27
+ const text = match[0];
28
+ const start = match.index ?? 0;
29
+ tokens.push({
30
+ id: `speech-token-${tokens.length}`,
31
+ text,
32
+ normalized: normalizedTokenValue(text),
33
+ start,
34
+ end: start + text.length,
35
+ sourceStart: start,
36
+ sourceEnd: start + text.length,
37
+ coordinateSystem: "normalized-speech",
38
+ });
39
+ }
40
+ return tokens;
41
+ };
42
+ export const tokenizeSpeechSource = (args) => {
43
+ const extracted = extractSpokenText(args.speechText);
44
+ return {
45
+ speechText: args.speechText,
46
+ spokenText: extracted.spokenText,
47
+ tokens: tokenizeSpokenText(extracted.spokenText),
48
+ rawToSpokenOffsetMap: extracted.rawToSpokenOffsetMap,
49
+ boundaryOffsetSpace: extracted.unsupportedSemantic
50
+ ? "unsupported"
51
+ : extracted.hasMarkup
52
+ ? "raw-ssml"
53
+ : "plain-spoken-text",
54
+ unsupportedSemantic: extracted.unsupportedSemantic,
55
+ hasMarkup: extracted.hasMarkup,
56
+ };
57
+ };
58
+ const normalizeBoundaryWord = (word) => {
59
+ if (!word)
60
+ return null;
61
+ if (/^<[^>]+>$/.test(word.trim()))
62
+ return null;
63
+ const match = word.match(TOKEN_PATTERN);
64
+ if (!match?.[0])
65
+ return null;
66
+ return normalizedTokenValue(match[0]);
67
+ };
68
+ const findTokenAtOffset = (tokens, offset) => tokens.find((token) => token.start <= offset && offset < token.end) || null;
69
+ const mapRawOffsetToSpokenOffset = (tokenization, position) => {
70
+ const direct = tokenization.rawToSpokenOffsetMap.get(position);
71
+ if (direct !== undefined)
72
+ return direct;
73
+ for (let offset = position; offset < tokenization.speechText.length; offset++) {
74
+ const mapped = tokenization.rawToSpokenOffsetMap.get(offset);
75
+ if (mapped !== undefined)
76
+ return mapped;
77
+ if (tokenization.speechText[offset] === ">")
78
+ break;
79
+ }
80
+ return null;
81
+ };
82
+ const candidateForOffset = (tokenization, offset, length) => {
83
+ if (offset === null)
84
+ return null;
85
+ const token = findTokenAtOffset(tokenization.tokens, offset);
86
+ if (!token)
87
+ return null;
88
+ return {
89
+ token,
90
+ start: offset,
91
+ length: Math.max(1, Math.min(length, token.end - offset)),
92
+ confidence: 0.75,
93
+ };
94
+ };
95
+ const candidateMatchesWord = (candidate, boundaryWord) => {
96
+ const normalizedBoundary = normalizeBoundaryWord(boundaryWord);
97
+ return Boolean(candidate &&
98
+ normalizedBoundary &&
99
+ candidate.token.normalized === normalizedBoundary);
100
+ };
101
+ export const resolveBoundaryToSpeechToken = (args) => {
102
+ if (args.tokenization.boundaryOffsetSpace === "unsupported" ||
103
+ !Number.isFinite(args.position) ||
104
+ (args.boundaryWord && /^<[^>]+>$/.test(args.boundaryWord.trim()))) {
105
+ return null;
106
+ }
107
+ const safeLength = Math.max(1, Number.isFinite(args.length) ? args.length : 1);
108
+ const rawCandidate = candidateForOffset(args.tokenization, mapRawOffsetToSpokenOffset(args.tokenization, args.position), safeLength);
109
+ const plainCandidate = candidateForOffset(args.tokenization, args.position >= 0 && args.position < args.tokenization.spokenText.length
110
+ ? args.position
111
+ : null, safeLength);
112
+ if (candidateMatchesWord(rawCandidate, args.boundaryWord)) {
113
+ return { ...rawCandidate, confidence: 1 };
114
+ }
115
+ if (candidateMatchesWord(plainCandidate, args.boundaryWord)) {
116
+ return { ...plainCandidate, confidence: 1 };
117
+ }
118
+ if (args.boundaryWord && normalizeBoundaryWord(args.boundaryWord)) {
119
+ return null;
120
+ }
121
+ return args.tokenization.boundaryOffsetSpace === "raw-ssml"
122
+ ? rawCandidate || plainCandidate
123
+ : plainCandidate || rawCandidate;
124
+ };
125
+ export const resolveSpokenOffsetToSpeechToken = (args) => {
126
+ if (args.tokenization.boundaryOffsetSpace === "unsupported" ||
127
+ !Number.isFinite(args.position) ||
128
+ (args.boundaryWord && /^<[^>]+>$/.test(args.boundaryWord.trim()))) {
129
+ return null;
130
+ }
131
+ const safeLength = Math.max(1, Number.isFinite(args.length) ? args.length : 1);
132
+ const spokenCandidate = candidateForOffset(args.tokenization, args.position >= 0 && args.position < args.tokenization.spokenText.length
133
+ ? args.position
134
+ : null, safeLength);
135
+ if (candidateMatchesWord(spokenCandidate, args.boundaryWord)) {
136
+ return { ...spokenCandidate, confidence: 1 };
137
+ }
138
+ if (args.boundaryWord && normalizeBoundaryWord(args.boundaryWord)) {
139
+ return null;
140
+ }
141
+ return spokenCandidate;
142
+ };
143
+ //# sourceMappingURL=speech-tokenizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"speech-tokenizer.js","sourceRoot":"","sources":["../../../../src/services/tts/math-alignment/speech-tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAK3D,OAAO,EAAE,iCAAiC,EAAE,MAAM,uBAAuB,CAAC;AAmB1E,8EAA8E;AAC9E,wDAAwD;AACxD,wCAAwC;AACxC,MAAM,aAAa,GAAG,iCAAiC,EAAE,CAAC;AAE1D,MAAM,aAAa,GAAG,IAAI,GAAG,CAAiB;IAC7C,CAAC,MAAM,EAAE,GAAG,CAAC;IACb,CAAC,KAAK,EAAE,GAAG,CAAC;IACZ,CAAC,KAAK,EAAE,GAAG,CAAC;IACZ,CAAC,OAAO,EAAE,GAAG,CAAC;IACd,CAAC,MAAM,EAAE,GAAG,CAAC;IACb,CAAC,MAAM,EAAE,GAAG,CAAC;IACb,CAAC,KAAK,EAAE,GAAG,CAAC;IACZ,CAAC,OAAO,EAAE,GAAG,CAAC;IACd,CAAC,OAAO,EAAE,GAAG,CAAC;IACd,CAAC,MAAM,EAAE,GAAG,CAAC;IACb,CAAC,KAAK,EAAE,IAAI,CAAC;CACb,CAAC,CAAC;AAEH,MAAM,oBAAoB,GAAG,CAAC,KAAa,EAAU,EAAE;IACtD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IACvC,OAAO,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC;AACpD,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAAC,UAAkB,EAA0B,EAAE;IACzE,MAAM,MAAM,GAA2B,EAAE,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,UAAU,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC;QAC/B,MAAM,CAAC,IAAI,CAAC;YACX,EAAE,EAAE,gBAAgB,MAAM,CAAC,MAAM,EAAE;YACnC,IAAI;YACJ,UAAU,EAAE,oBAAoB,CAAC,IAAI,CAAC;YACtC,KAAK;YACL,GAAG,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM;YACxB,WAAW,EAAE,KAAK;YAClB,SAAS,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM;YAC9B,gBAAgB,EAAE,mBAAmB;SACrC,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AACf,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAEpC,EAA4B,EAAE;IAC9B,MAAM,SAAS,GAAG,iBAAiB,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACrD,OAAO;QACN,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,UAAU,EAAE,SAAS,CAAC,UAAU;QAChC,MAAM,EAAE,kBAAkB,CAAC,SAAS,CAAC,UAAU,CAAC;QAChD,oBAAoB,EAAE,SAAS,CAAC,oBAAoB;QACpD,mBAAmB,EAAE,SAAS,CAAC,mBAAmB;YACjD,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,SAAS,CAAC,SAAS;gBACpB,CAAC,CAAC,UAAU;gBACZ,CAAC,CAAC,mBAAmB;QACvB,mBAAmB,EAAE,SAAS,CAAC,mBAAmB;QAClD,SAAS,EAAE,SAAS,CAAC,SAAS;KAC9B,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,IAAa,EAAiB,EAAE;IAC9D,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACvC,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACzB,MAA8B,EAC9B,MAAc,EACgB,EAAE,CAChC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;AAE7E,MAAM,0BAA0B,GAAG,CAClC,YAAsC,EACtC,QAAgB,EACA,EAAE;IAClB,MAAM,MAAM,GAAG,YAAY,CAAC,oBAAoB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC/D,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,KAAK,IAAI,MAAM,GAAG,QAAQ,EAAE,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC;QAC/E,MAAM,MAAM,GAAG,YAAY,CAAC,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7D,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACxC,IAAI,YAAY,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,GAAG;YAAE,MAAM;IACpD,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,CAC1B,YAAsC,EACtC,MAAqB,EACrB,MAAc,EACkB,EAAE;IAClC,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,KAAK,GAAG,iBAAiB,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7D,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO;QACN,KAAK;QACL,KAAK,EAAE,MAAM;QACb,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;QACzD,UAAU,EAAE,IAAI;KAChB,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,CAC5B,SAAwC,EACxC,YAAqB,EACX,EAAE;IACZ,MAAM,kBAAkB,GAAG,qBAAqB,CAAC,YAAY,CAAC,CAAC;IAC/D,OAAO,OAAO,CACb,SAAS;QACR,kBAAkB;QAClB,SAAS,CAAC,KAAK,CAAC,UAAU,KAAK,kBAAkB,CAClD,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,IAK5C,EAAiC,EAAE;IACnC,IACC,IAAI,CAAC,YAAY,CAAC,mBAAmB,KAAK,aAAa;QACvD,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC/B,CAAC,IAAI,CAAC,YAAY,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,EAChE,CAAC;QACF,OAAO,IAAI,CAAC;IACb,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChF,MAAM,YAAY,GAAG,kBAAkB,CACtC,IAAI,CAAC,YAAY,EACjB,0BAA0B,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,EAC5D,UAAU,CACV,CAAC;IACF,MAAM,cAAc,GAAG,kBAAkB,CACxC,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM;QACxE,CAAC,CAAC,IAAI,CAAC,QAAQ;QACf,CAAC,CAAC,IAAI,EACP,UAAU,CACV,CAAC;IAEF,IAAI,oBAAoB,CAAC,YAAY,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,GAAG,YAAa,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IAC5C,CAAC;IACD,IAAI,oBAAoB,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QAC7D,OAAO,EAAE,GAAG,cAAe,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IAC9C,CAAC;IACD,IAAI,IAAI,CAAC,YAAY,IAAI,qBAAqB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO,IAAI,CAAC,YAAY,CAAC,mBAAmB,KAAK,UAAU;QAC1D,CAAC,CAAC,YAAY,IAAI,cAAc;QAChC,CAAC,CAAC,cAAc,IAAI,YAAY,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,IAKhD,EAAiC,EAAE;IACnC,IACC,IAAI,CAAC,YAAY,CAAC,mBAAmB,KAAK,aAAa;QACvD,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC;QAC/B,CAAC,IAAI,CAAC,YAAY,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC,CAAC,EAChE,CAAC;QACF,OAAO,IAAI,CAAC;IACb,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChF,MAAM,eAAe,GAAG,kBAAkB,CACzC,IAAI,CAAC,YAAY,EACjB,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,MAAM;QACxE,CAAC,CAAC,IAAI,CAAC,QAAQ;QACf,CAAC,CAAC,IAAI,EACP,UAAU,CACV,CAAC;IACF,IAAI,oBAAoB,CAAC,eAAe,EAAE,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,GAAG,eAAgB,EAAE,UAAU,EAAE,CAAC,EAAE,CAAC;IAC/C,CAAC;IACD,IAAI,IAAI,CAAC,YAAY,IAAI,qBAAqB,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,eAAe,CAAC;AACxB,CAAC,CAAC","sourcesContent":["import { extractSpokenText } from \"../ssml/spoken-text.js\";\nimport type {\n\tBoundaryOffsetSpace,\n\tSpeechAlignmentToken,\n} 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 (let offset = position; offset < tokenization.speechText.length; offset++) {\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(1, Number.isFinite(args.length) ? args.length! : 1);\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(1, Number.isFinite(args.length) ? args.length! : 1);\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"]}
@@ -0,0 +1,58 @@
1
+ export type HighlightTargetQuality = "exact-word" | "element-range" | "region-fallback";
2
+ export type HighlightTarget = {
3
+ type: "text-range";
4
+ quality: "exact-word";
5
+ node: Text;
6
+ startOffset: number;
7
+ endOffset: number;
8
+ } | {
9
+ type: "element-range";
10
+ quality: Exclude<HighlightTargetQuality, "exact-word">;
11
+ element: Element;
12
+ } | {
13
+ type: "region";
14
+ quality: "region-fallback";
15
+ element: Element;
16
+ };
17
+ export type MathAlignmentTokenKind = "identifier" | "number" | "operator" | "text" | "space" | "structure" | "layout" | "unknown";
18
+ export interface MathAlignmentToken {
19
+ id: string;
20
+ kind: MathAlignmentTokenKind;
21
+ role: string;
22
+ text: string;
23
+ normalized: string;
24
+ spokenAliases: string[];
25
+ sourceElement: Element;
26
+ path: string;
27
+ target: HighlightTarget;
28
+ optional?: boolean;
29
+ }
30
+ export interface MathMLTokenizationResult {
31
+ tokens: MathAlignmentToken[];
32
+ layoutTargets: MathAlignmentToken[];
33
+ expressionTarget: HighlightTarget;
34
+ }
35
+ export type BoundaryCoordinateSystem = "normalized-speech" | "raw-ssml" | "provider-speech-marks";
36
+ export type BoundaryOffsetSpace = "plain-spoken-text" | "raw-ssml" | "unsupported";
37
+ export interface SpeechAlignmentToken {
38
+ id: string;
39
+ text: string;
40
+ normalized: string;
41
+ start: number;
42
+ end: number;
43
+ sourceStart: number;
44
+ sourceEnd: number;
45
+ coordinateSystem: BoundaryCoordinateSystem;
46
+ }
47
+ export interface AlignmentSegment {
48
+ speechTokenIds: string[];
49
+ mathTokenIds: string[];
50
+ score: number;
51
+ confidence: number;
52
+ target: HighlightTarget;
53
+ }
54
+ export interface AlignmentResult {
55
+ segments: AlignmentSegment[];
56
+ confidence: number;
57
+ fallbackTarget: HighlightTarget;
58
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,18 @@
1
+ import { type NormalizedTextMap, type TextProcessingOptions } from "./text-processing.js";
2
+ export type MathAwareSpeechChunk = {
3
+ type: "text";
4
+ text: string;
5
+ sourceElement?: Element;
6
+ } | {
7
+ type: "math";
8
+ mathml: string;
9
+ fallbackText: string;
10
+ sourceElement?: Element;
11
+ };
12
+ export interface MathAwareTextResult {
13
+ visibleText: string;
14
+ map: NormalizedTextMap;
15
+ chunks: MathAwareSpeechChunk[];
16
+ containsMathMarkup: boolean;
17
+ }
18
+ export declare const collectMathAwareTextAndMap: (element: Element, options?: TextProcessingOptions) => MathAwareTextResult;
@@ -0,0 +1,348 @@
1
+ import { canonicalizeMathML } from "./mathml-sanitization.js";
2
+ import { collectVisibleTextAndMap, isNodeHiddenForTTS, normalizeTextForSpeech, shouldInsertWordBoundarySpace, } from "./text-processing.js";
3
+ const createAccumulator = (options) => ({
4
+ chars: [],
5
+ map: new Map(),
6
+ position: 0,
7
+ inLeadingWhitespace: true,
8
+ lastCharWasWhitespace: false,
9
+ lastMapped: null,
10
+ options,
11
+ });
12
+ const isMathJaxElement = (element) => element.classList?.contains("MathJax") ||
13
+ element.tagName?.toLowerCase() === "mjx-container";
14
+ const isAssistiveMathElement = (element) => element.tagName?.toLowerCase() === "mjx-assistive-mml";
15
+ const isAnnotationElement = (element) => {
16
+ const tagName = element.tagName?.toLowerCase();
17
+ return tagName === "annotation" || tagName === "annotation-xml";
18
+ };
19
+ const getDataMathML = (element) => {
20
+ const value = element.getAttribute("data-mathml");
21
+ return value && value.trim() ? value : null;
22
+ };
23
+ const findAssistiveMathML = (element) => {
24
+ const assistive = element.querySelector("mjx-assistive-mml math");
25
+ return assistive?.outerHTML || null;
26
+ };
27
+ const findCanonicalMathML = (element) => {
28
+ if (element.tagName.toLowerCase() === "math") {
29
+ return canonicalizeMathML(element.outerHTML);
30
+ }
31
+ const dataMath = getDataMathML(element);
32
+ if (dataMath) {
33
+ const canonical = canonicalizeMathML(dataMath);
34
+ if (canonical)
35
+ return canonical;
36
+ }
37
+ if (isMathJaxElement(element)) {
38
+ const assistive = findAssistiveMathML(element);
39
+ if (assistive) {
40
+ const canonical = canonicalizeMathML(assistive);
41
+ if (canonical)
42
+ return canonical;
43
+ }
44
+ }
45
+ return null;
46
+ };
47
+ const isMathContainer = (element) => Boolean(findCanonicalMathML(element));
48
+ const TEXT_CHUNK_SOURCE_TAGS = new Set([
49
+ "P",
50
+ "H1",
51
+ "H2",
52
+ "H3",
53
+ "H4",
54
+ "H5",
55
+ "H6",
56
+ "LI",
57
+ "TD",
58
+ "TH",
59
+ "DD",
60
+ "DT",
61
+ "FIGCAPTION",
62
+ "LABEL",
63
+ "BUTTON",
64
+ ]);
65
+ const TEXT_CHUNK_SOURCE_ROLES = new Set([
66
+ "heading",
67
+ "listitem",
68
+ "option",
69
+ "radio",
70
+ "row",
71
+ "cell",
72
+ "columnheader",
73
+ "rowheader",
74
+ ]);
75
+ const resolveTextChunkSourceElement = (textNode, root) => {
76
+ let current = textNode.parentElement;
77
+ let best = null;
78
+ while (current && current !== root) {
79
+ const role = (current.getAttribute("role") || "").toLowerCase();
80
+ const tagName = current.tagName.toUpperCase();
81
+ if (TEXT_CHUNK_SOURCE_TAGS.has(tagName) ||
82
+ TEXT_CHUNK_SOURCE_ROLES.has(role)) {
83
+ best = current;
84
+ break;
85
+ }
86
+ current = current.parentElement;
87
+ }
88
+ return best || root;
89
+ };
90
+ const hasMathCandidate = (element) => Boolean(typeof element.querySelector === "function" &&
91
+ element.querySelector("math, [data-mathml], .MathJax, mjx-container, mjx-assistive-mml")) ||
92
+ element.tagName?.toLowerCase() === "math" ||
93
+ element.hasAttribute?.("data-mathml") ||
94
+ isMathJaxElement(element);
95
+ const appendCharacter = (acc, character, mapping) => {
96
+ if (/[.,;:!?]/.test(character) &&
97
+ acc.chars.length > 0 &&
98
+ acc.chars[acc.chars.length - 1] === " ") {
99
+ acc.chars.pop();
100
+ acc.position--;
101
+ acc.map.delete(acc.position);
102
+ }
103
+ acc.chars.push(character);
104
+ if (mapping) {
105
+ acc.map.set(acc.position, mapping);
106
+ acc.lastMapped = mapping;
107
+ }
108
+ else if (acc.lastMapped) {
109
+ acc.map.set(acc.position, acc.lastMapped);
110
+ }
111
+ acc.position++;
112
+ };
113
+ const shouldInsertBoundarySpace = (acc, nextCharacter) => {
114
+ if (acc.inLeadingWhitespace ||
115
+ acc.lastCharWasWhitespace ||
116
+ acc.chars.length === 0) {
117
+ return false;
118
+ }
119
+ const previous = acc.chars[acc.chars.length - 1];
120
+ // Defer to the shared locale-aware decision (Intl.Segmenter, falling back to
121
+ // alnum) so adjacent text nodes split into words consistently with the main
122
+ // visible-text collector, instead of an ASCII-only heuristic.
123
+ return shouldInsertWordBoundarySpace(previous, nextCharacter, acc.options);
124
+ };
125
+ const appendTextNode = (acc, textNode) => {
126
+ const raw = textNode.textContent || "";
127
+ let appendedNonWhitespaceInNode = false;
128
+ for (let i = 0; i < raw.length; i++) {
129
+ const character = raw[i];
130
+ const isWhitespace = /\s/.test(character);
131
+ if (acc.inLeadingWhitespace) {
132
+ if (!isWhitespace) {
133
+ acc.inLeadingWhitespace = false;
134
+ appendCharacter(acc, character, { node: textNode, offset: i });
135
+ appendedNonWhitespaceInNode = true;
136
+ acc.lastCharWasWhitespace = false;
137
+ }
138
+ continue;
139
+ }
140
+ if (isWhitespace) {
141
+ if (!acc.lastCharWasWhitespace) {
142
+ appendCharacter(acc, " ", { node: textNode, offset: i });
143
+ }
144
+ acc.lastCharWasWhitespace = true;
145
+ continue;
146
+ }
147
+ if (!appendedNonWhitespaceInNode &&
148
+ shouldInsertBoundarySpace(acc, character)) {
149
+ appendCharacter(acc, " ", { node: textNode, offset: i });
150
+ }
151
+ appendCharacter(acc, character, { node: textNode, offset: i });
152
+ appendedNonWhitespaceInNode = true;
153
+ acc.lastCharWasWhitespace = false;
154
+ }
155
+ };
156
+ const appendCollectedText = (acc, collected) => {
157
+ for (let i = 0; i < collected.text.length; i++) {
158
+ const character = collected.text[i];
159
+ const isWhitespace = /\s/.test(character);
160
+ const mapping = collected.map.get(i) || null;
161
+ const syntheticNode = mapping?.node || acc.lastMapped?.node || null;
162
+ const syntheticOffset = mapping?.offset ?? acc.lastMapped?.offset ?? 0;
163
+ const normalizedMapping = mapping
164
+ ? { node: mapping.node, offset: mapping.offset }
165
+ : syntheticNode
166
+ ? { node: syntheticNode, offset: syntheticOffset }
167
+ : null;
168
+ if (acc.inLeadingWhitespace) {
169
+ if (isWhitespace)
170
+ continue;
171
+ acc.inLeadingWhitespace = false;
172
+ }
173
+ if (isWhitespace) {
174
+ if (!acc.lastCharWasWhitespace) {
175
+ appendCharacter(acc, " ", normalizedMapping);
176
+ }
177
+ acc.lastCharWasWhitespace = true;
178
+ continue;
179
+ }
180
+ if (i === 0 && shouldInsertBoundarySpace(acc, character)) {
181
+ appendCharacter(acc, " ", normalizedMapping);
182
+ }
183
+ appendCharacter(acc, character, normalizedMapping);
184
+ acc.lastCharWasWhitespace = false;
185
+ if (mapping) {
186
+ acc.map.set(acc.position - 1, {
187
+ node: mapping.node,
188
+ offset: mapping.offset,
189
+ });
190
+ acc.lastMapped = { node: mapping.node, offset: mapping.offset };
191
+ }
192
+ else if (syntheticNode) {
193
+ acc.map.set(acc.position - 1, {
194
+ node: syntheticNode,
195
+ offset: syntheticOffset,
196
+ });
197
+ }
198
+ }
199
+ };
200
+ const textFallbackFromMathML = (mathml) => {
201
+ if (typeof DOMParser === "undefined")
202
+ return "";
203
+ const parsed = new DOMParser().parseFromString(mathml, "application/xml");
204
+ if (parsed.getElementsByTagName("parsererror").length > 0)
205
+ return "";
206
+ const parts = [];
207
+ const visit = (node) => {
208
+ if (node.nodeType === 3) {
209
+ const text = node.textContent || "";
210
+ if (text.trim())
211
+ parts.push(text);
212
+ return;
213
+ }
214
+ if (node.nodeType !== 1)
215
+ return;
216
+ const element = node;
217
+ if (isAnnotationElement(element))
218
+ return;
219
+ for (const child of Array.from(element.childNodes)) {
220
+ visit(child);
221
+ }
222
+ };
223
+ visit(parsed.documentElement);
224
+ return normalizeTextForSpeech(parts.join(" "));
225
+ };
226
+ const collectVisibleMathFallback = (element, canonicalMathML, options) => {
227
+ const mathAcc = createAccumulator(options);
228
+ const visit = (node) => {
229
+ if (isNodeHiddenForTTS(node, element))
230
+ return;
231
+ if (node.nodeType === 3) {
232
+ appendTextNode(mathAcc, node);
233
+ return;
234
+ }
235
+ if (node.nodeType !== 1)
236
+ return;
237
+ const childElement = node;
238
+ if (isAssistiveMathElement(childElement) ||
239
+ isAnnotationElement(childElement)) {
240
+ return;
241
+ }
242
+ for (const child of Array.from(childElement.childNodes)) {
243
+ visit(child);
244
+ }
245
+ };
246
+ visit(element);
247
+ trimTrailingWhitespace(mathAcc);
248
+ const text = normalizeTextForSpeech(mathAcc.chars.join(""));
249
+ if (text) {
250
+ return { text, map: mathAcc.map };
251
+ }
252
+ const fallbackText = textFallbackFromMathML(canonicalMathML);
253
+ if (!fallbackText) {
254
+ return collectVisibleTextAndMap(element, options);
255
+ }
256
+ return {
257
+ text: fallbackText,
258
+ map: new Map(),
259
+ };
260
+ };
261
+ const trimTrailingWhitespace = (acc) => {
262
+ while (acc.chars.length > 0 && /\s/.test(acc.chars[acc.chars.length - 1])) {
263
+ acc.chars.pop();
264
+ acc.position--;
265
+ acc.map.delete(acc.position);
266
+ }
267
+ };
268
+ const collectMathAware = (root, options) => {
269
+ const acc = createAccumulator(options);
270
+ const chunks = [];
271
+ let textChunkStart = 0;
272
+ let textChunkSourceElement;
273
+ let containsMathMarkup = false;
274
+ const flushTextChunk = () => {
275
+ const text = normalizeTextForSpeech(acc.chars.slice(textChunkStart, acc.chars.length).join(""));
276
+ if (text) {
277
+ chunks.push({ type: "text", text, sourceElement: textChunkSourceElement });
278
+ }
279
+ textChunkStart = acc.chars.length;
280
+ textChunkSourceElement = undefined;
281
+ };
282
+ const processNode = (node) => {
283
+ if (isNodeHiddenForTTS(node, root))
284
+ return;
285
+ if (node.nodeType === Node.TEXT_NODE) {
286
+ const sourceElement = resolveTextChunkSourceElement(node, root);
287
+ if (textChunkSourceElement &&
288
+ sourceElement !== textChunkSourceElement) {
289
+ flushTextChunk();
290
+ }
291
+ textChunkSourceElement = sourceElement;
292
+ appendTextNode(acc, node);
293
+ return;
294
+ }
295
+ if (node.nodeType !== Node.ELEMENT_NODE)
296
+ return;
297
+ const element = node;
298
+ if (isAssistiveMathElement(element))
299
+ return;
300
+ const canonicalMathML = findCanonicalMathML(element);
301
+ if (canonicalMathML) {
302
+ flushTextChunk();
303
+ const collected = collectVisibleMathFallback(element, canonicalMathML, options);
304
+ appendCollectedText(acc, collected);
305
+ const fallbackText = normalizeTextForSpeech(collected.text);
306
+ chunks.push({
307
+ type: "math",
308
+ mathml: canonicalMathML,
309
+ fallbackText,
310
+ sourceElement: element,
311
+ });
312
+ textChunkStart = acc.chars.length;
313
+ containsMathMarkup = true;
314
+ return;
315
+ }
316
+ for (const child of Array.from(element.childNodes)) {
317
+ processNode(child);
318
+ }
319
+ };
320
+ for (const child of Array.from(root.childNodes)) {
321
+ processNode(child);
322
+ }
323
+ trimTrailingWhitespace(acc);
324
+ flushTextChunk();
325
+ const visibleText = acc.chars.join("");
326
+ while (acc.map.has(visibleText.length)) {
327
+ acc.map.delete(visibleText.length);
328
+ }
329
+ return {
330
+ visibleText,
331
+ map: acc.map,
332
+ chunks,
333
+ containsMathMarkup,
334
+ };
335
+ };
336
+ export const collectMathAwareTextAndMap = (element, options) => {
337
+ if (!hasMathCandidate(element)) {
338
+ const { text, map } = collectVisibleTextAndMap(element, options);
339
+ return {
340
+ visibleText: text,
341
+ map,
342
+ chunks: text ? [{ type: "text", text }] : [],
343
+ containsMathMarkup: false,
344
+ };
345
+ }
346
+ return collectMathAware(element, options);
347
+ };
348
+ //# sourceMappingURL=math-aware-text-processing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"math-aware-text-processing.js","sourceRoot":"","sources":["../../../src/services/tts/math-aware-text-processing.ts"],"names":[],"mappings":"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","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({ type: \"text\", text, sourceElement: textChunkSourceElement });\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 (\n\t\t\t\ttextChunkSourceElement &&\n\t\t\t\tsourceElement !== textChunkSourceElement\n\t\t\t) {\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"]}