@pie-players/pie-assessment-toolkit 0.3.73 → 0.3.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (274) hide show
  1. package/README.md +469 -318
  2. package/dist/attempt/AssessmentSession.d.ts +1 -5
  3. package/dist/attempt/AssessmentSession.js +0 -29
  4. package/dist/attempt/TestSession.d.ts +1 -20
  5. package/dist/attempt/TestSession.js +8 -99
  6. package/dist/components/ItemScope.custom-element.d.ts +1 -0
  7. package/dist/components/ItemScope.custom-element.js +1 -0
  8. package/dist/components/ItemToolBar.custom-element.d.ts +1 -0
  9. package/dist/components/ItemToolBar.custom-element.js +1 -1
  10. package/dist/components/PieAssessmentToolkit.custom-element.d.ts +1 -0
  11. package/dist/components/PieAssessmentToolkit.custom-element.js +13 -13
  12. package/dist/components/SectionToolBar.custom-element.d.ts +1 -0
  13. package/dist/components/SectionToolBar.custom-element.js +1 -1
  14. package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
  15. package/dist/components/chunks/ItemToolBar-97hx60z4.js +2 -0
  16. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
  17. package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
  18. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
  19. package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
  20. package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
  21. package/dist/components/item-scope-element.d.ts +2 -0
  22. package/dist/components/item-scope-element.js +2 -0
  23. package/dist/context/assessment-toolkit-context.d.ts +32 -6
  24. package/dist/index.d.ts +23 -48
  25. package/dist/index.js +12 -37
  26. package/dist/policy/core/PolicySource.d.ts +1 -1
  27. package/dist/policy/core/ToolPolicyEngine.d.ts +85 -34
  28. package/dist/policy/core/ToolPolicyEngine.js +148 -46
  29. package/dist/policy/core/compose-decision.d.ts +28 -7
  30. package/dist/policy/core/compose-decision.js +75 -54
  31. package/dist/policy/core/decision-types.d.ts +65 -34
  32. package/dist/policy/core/feature-decision.d.ts +16 -9
  33. package/dist/policy/core/feature-decision.js +14 -8
  34. package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
  35. package/dist/policy/core/pnp-policy-inputs.js +28 -56
  36. package/dist/policy/core/provenance.d.ts +9 -4
  37. package/dist/policy/core/provenance.js +1 -1
  38. package/dist/policy/engine.d.ts +7 -23
  39. package/dist/policy/engine.js +5 -20
  40. package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
  41. package/dist/policy/sources/PnpPolicySource.js +117 -118
  42. package/dist/runtime/SectionControllerBinding.d.ts +279 -0
  43. package/dist/runtime/SectionControllerBinding.js +450 -0
  44. package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
  45. package/dist/runtime/SectionRuntimeEngine.js +30 -372
  46. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
  47. package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
  48. package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
  49. package/dist/runtime/adapter/dom-event-bridge.js +17 -44
  50. package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
  51. package/dist/runtime/adapter/subscriber-fanout.js +1 -2
  52. package/dist/runtime/catalog-context.d.ts +19 -0
  53. package/dist/runtime/catalog-context.js +28 -0
  54. package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
  55. package/dist/runtime/composition-emit-scheduler.js +9 -15
  56. package/dist/runtime/content-language.d.ts +32 -0
  57. package/dist/runtime/content-language.js +46 -0
  58. package/dist/runtime/content-region.d.ts +17 -0
  59. package/dist/runtime/content-region.js +29 -0
  60. package/dist/runtime/content-registration.d.ts +13 -0
  61. package/dist/runtime/content-registration.js +25 -0
  62. package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
  63. package/dist/runtime/core/SectionEngineCore.js +2 -2
  64. package/dist/runtime/core/cohort.d.ts +1 -1
  65. package/dist/runtime/core/cohort.js +1 -1
  66. package/dist/runtime/core/engine-input.d.ts +18 -30
  67. package/dist/runtime/core/engine-input.js +11 -12
  68. package/dist/runtime/core/engine-output.d.ts +16 -40
  69. package/dist/runtime/core/engine-output.js +9 -21
  70. package/dist/runtime/core/engine-readiness.d.ts +5 -37
  71. package/dist/runtime/core/engine-readiness.js +8 -41
  72. package/dist/runtime/core/engine-resolver.d.ts +46 -74
  73. package/dist/runtime/core/engine-resolver.js +18 -43
  74. package/dist/runtime/core/engine-state.d.ts +28 -36
  75. package/dist/runtime/core/engine-state.js +18 -21
  76. package/dist/runtime/core/engine-transition.d.ts +14 -19
  77. package/dist/runtime/core/engine-transition.js +71 -51
  78. package/dist/runtime/engine.d.ts +16 -19
  79. package/dist/runtime/engine.js +12 -19
  80. package/dist/runtime/page-warnings.d.ts +13 -0
  81. package/dist/runtime/page-warnings.js +22 -0
  82. package/dist/runtime/policy-input-forwarding.d.ts +25 -0
  83. package/dist/runtime/policy-input-forwarding.js +25 -0
  84. package/dist/runtime/registration-events.d.ts +14 -0
  85. package/dist/runtime/runtime-event-claim.d.ts +8 -0
  86. package/dist/runtime/runtime-event-claim.js +15 -0
  87. package/dist/runtime/shell-event-bridge.d.ts +45 -0
  88. package/dist/runtime/shell-event-bridge.js +127 -0
  89. package/dist/runtime/shell-registration.d.ts +73 -0
  90. package/dist/runtime/shell-registration.js +95 -0
  91. package/dist/runtime/shell-scope.d.ts +68 -0
  92. package/dist/runtime/shell-scope.js +188 -0
  93. package/dist/runtime/tool-host-contract.d.ts +28 -10
  94. package/dist/runtime/tool-host-contract.js +31 -10
  95. package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
  96. package/dist/runtime/unclaimed-registration-watch.js +23 -0
  97. package/dist/services/ElementToolStateStore.d.ts +14 -10
  98. package/dist/services/ElementToolStateStore.js +37 -13
  99. package/dist/services/HighlightCoordinator.d.ts +22 -28
  100. package/dist/services/HighlightCoordinator.js +431 -327
  101. package/dist/services/RangeSerializer.d.ts +10 -1
  102. package/dist/services/RangeSerializer.js +87 -14
  103. package/dist/services/TTSService.d.ts +130 -69
  104. package/dist/services/TTSService.js +713 -570
  105. package/dist/services/ToolCoordinator.d.ts +43 -66
  106. package/dist/services/ToolCoordinator.js +164 -208
  107. package/dist/services/ToolRegistry.d.ts +78 -54
  108. package/dist/services/ToolRegistry.js +107 -105
  109. package/dist/services/ToolkitCoordinator.d.ts +261 -175
  110. package/dist/services/ToolkitCoordinator.js +667 -433
  111. package/dist/services/audio-handoff.d.ts +6 -7
  112. package/dist/services/audio-handoff.js +20 -14
  113. package/dist/services/catalog-owner.d.ts +11 -1
  114. package/dist/services/catalog-owner.js +25 -0
  115. package/dist/services/create-tools-config.d.ts +2 -2
  116. package/dist/services/framework-error-bus.d.ts +3 -6
  117. package/dist/services/framework-error-bus.js +3 -6
  118. package/dist/services/framework-error.d.ts +30 -8
  119. package/dist/services/framework-error.js +9 -7
  120. package/dist/services/interfaces.d.ts +158 -129
  121. package/dist/services/section-controller-types.d.ts +15 -0
  122. package/dist/services/section-session-assignment.d.ts +21 -0
  123. package/dist/services/section-session-assignment.js +82 -0
  124. package/dist/services/spoken-audio-cards.d.ts +0 -2
  125. package/dist/services/spoken-audio-cards.js +1 -3
  126. package/dist/services/tool-config-defaults.d.ts +3 -3
  127. package/dist/services/tool-config-defaults.js +3 -3
  128. package/dist/services/tool-config-validation.d.ts +14 -4
  129. package/dist/services/tool-config-validation.js +63 -71
  130. package/dist/services/tool-context.d.ts +0 -8
  131. package/dist/services/tool-context.js +29 -25
  132. package/dist/services/tool-icons.d.ts +4 -3
  133. package/dist/services/tool-icons.js +4 -3
  134. package/dist/services/tool-instance-id.d.ts +0 -1
  135. package/dist/services/tool-instance-id.js +0 -6
  136. package/dist/services/tool-providers/TTSToolProvider.d.ts +41 -80
  137. package/dist/services/tool-providers/TTSToolProvider.js +81 -40
  138. package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
  139. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +32 -28
  140. package/dist/services/tool-providers/ToolProviderRegistry.js +121 -107
  141. package/dist/services/tool-providers/index.d.ts +0 -9
  142. package/dist/services/tool-providers/index.js +2 -4
  143. package/dist/services/tool-request.d.ts +27 -12
  144. package/dist/services/tool-request.js +24 -14
  145. package/dist/services/toolbar-decided-tools.d.ts +15 -0
  146. package/dist/services/toolbar-decided-tools.js +28 -0
  147. package/dist/services/tools-config-normalizer.d.ts +37 -23
  148. package/dist/services/tools-config-normalizer.js +2 -10
  149. package/dist/services/tts/browser-provider.d.ts +38 -2
  150. package/dist/services/tts/browser-provider.js +276 -140
  151. package/dist/services/tts/catalog-span-alignment.d.ts +2 -17
  152. package/dist/services/tts/catalog-span-alignment.js +17 -142
  153. package/dist/services/tts/flat-tree.d.ts +71 -0
  154. package/dist/services/tts/flat-tree.js +269 -0
  155. package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
  156. package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
  157. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
  158. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
  159. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
  160. package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
  161. package/dist/services/tts/generated-speech/types.d.ts +0 -12
  162. package/dist/services/tts/highlight-pipeline/boundary-normalizer.js +12 -62
  163. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +38 -5
  164. package/dist/services/tts/highlight-pipeline/normalize-speech-chunks.js +3 -6
  165. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
  166. package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
  167. package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
  168. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
  169. package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
  170. package/dist/services/tts/math-alignment/index.d.ts +0 -5
  171. package/dist/services/tts/math-alignment/index.js +5 -25
  172. package/dist/services/tts/math-alignment/speech-tokenizer.d.ts +9 -1
  173. package/dist/services/tts/math-alignment/speech-tokenizer.js +38 -36
  174. package/dist/services/tts/math-alignment/types.d.ts +4 -2
  175. package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
  176. package/dist/services/tts/math-aware-text-processing.js +74 -18
  177. package/dist/services/tts/math-control-names.d.ts +21 -0
  178. package/dist/services/tts/math-control-names.js +142 -0
  179. package/dist/services/tts/math-speech.d.ts +16 -1
  180. package/dist/services/tts/math-speech.js +55 -19
  181. package/dist/services/tts/provider-options.d.ts +41 -0
  182. package/dist/services/tts/provider-options.js +1 -0
  183. package/dist/services/tts/sre-engine.d.ts +7 -0
  184. package/dist/services/tts/sre-engine.js +11 -0
  185. package/dist/services/tts/sre-locales.d.ts +14 -0
  186. package/dist/services/tts/sre-locales.js +57 -0
  187. package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
  188. package/dist/services/tts/ssml/spoken-text.js +29 -0
  189. package/dist/services/tts/start-failure.d.ts +11 -0
  190. package/dist/services/tts/start-failure.js +19 -0
  191. package/dist/services/tts/text-processing.d.ts +9 -1
  192. package/dist/services/tts/text-processing.js +46 -34
  193. package/dist/services/tts/text-segmentation.d.ts +7 -0
  194. package/dist/services/tts/text-segmentation.js +47 -0
  195. package/dist/services/tts-runtime-config.d.ts +44 -18
  196. package/dist/services/tts-runtime-config.js +22 -93
  197. package/dist/tools/content-capability-resolution.d.ts +7 -9
  198. package/dist/tools/content-capability-resolution.js +8 -45
  199. package/dist/tools/registration.d.ts +58 -0
  200. package/dist/tools/registration.js +54 -0
  201. package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
  202. package/dist/tools/tool-surface-host.d.ts +6 -10
  203. package/dist/tools/tool-surface-host.js +44 -42
  204. package/dist/tools/tool-tag-map.d.ts +5 -15
  205. package/dist/tools/tool-tag-map.js +6 -20
  206. package/dist/utils/structural-equality.d.ts +11 -0
  207. package/dist/utils/structural-equality.js +47 -0
  208. package/package.json +24 -59
  209. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
  210. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
  211. package/dist/components/chunks/ItemToolBar-byq80z42.js +0 -40
  212. package/dist/components/chunks/ItemToolBar-d36g3aw4.js +0 -49
  213. package/dist/context/runtime-context-consumer.d.ts +0 -13
  214. package/dist/context/runtime-context-consumer.js +0 -51
  215. package/dist/item-loader.d.ts +0 -44
  216. package/dist/item-loader.js +0 -61
  217. package/dist/policy/core/engine-context.d.ts +0 -29
  218. package/dist/policy/core/engine-context.js +0 -27
  219. package/dist/policy/core/policy-source-tag.d.ts +0 -31
  220. package/dist/policy/core/policy-source-tag.js +0 -28
  221. package/dist/policy/internal.d.ts +0 -25
  222. package/dist/policy/internal.js +0 -25
  223. package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
  224. package/dist/runtime/adapter/coordinator-bridge.js +0 -126
  225. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
  226. package/dist/runtime/adapter/framework-error-bridge.js +0 -46
  227. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
  228. package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
  229. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
  230. package/dist/runtime/core/engine-stage-derivation.js +0 -29
  231. package/dist/runtime/engine-context.d.ts +0 -39
  232. package/dist/runtime/engine-context.js +0 -31
  233. package/dist/runtime/internal.d.ts +0 -43
  234. package/dist/runtime/internal.js +0 -45
  235. package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -80
  236. package/dist/runtime/section-runtime-engine-host-context.js +0 -95
  237. package/dist/runtime/stage-emit-gate.d.ts +0 -92
  238. package/dist/runtime/stage-emit-gate.js +0 -82
  239. package/dist/services/I18nService.d.ts +0 -47
  240. package/dist/services/I18nService.js +0 -84
  241. package/dist/services/ThemeProvider.d.ts +0 -95
  242. package/dist/services/ThemeProvider.js +0 -334
  243. package/dist/services/ToolConfigResolver.d.ts +0 -161
  244. package/dist/services/ToolConfigResolver.js +0 -229
  245. package/dist/services/catalog-media.d.ts +0 -56
  246. package/dist/services/catalog-media.js +0 -190
  247. package/dist/services/createDefaultToolRegistry.d.ts +0 -53
  248. package/dist/services/createDefaultToolRegistry.js +0 -45
  249. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
  250. package/dist/services/defaultPersonalNeedsProfile.js +0 -26
  251. package/dist/services/pnp-standard-features.d.ts +0 -238
  252. package/dist/services/pnp-standard-features.js +0 -338
  253. package/dist/services/tool-providers/CortexToolProvider.d.ts +0 -13
  254. package/dist/services/tool-providers/CortexToolProvider.js +0 -32
  255. package/dist/services/tool-providers/DesmosToolProvider.d.ts +0 -14
  256. package/dist/services/tool-providers/DesmosToolProvider.js +0 -32
  257. package/dist/services/tool-providers/GeoGebraToolProvider.d.ts +0 -16
  258. package/dist/services/tool-providers/GeoGebraToolProvider.js +0 -32
  259. package/dist/services/tool-providers/LazyCalculatorToolProvider.d.ts +0 -35
  260. package/dist/services/tool-providers/LazyCalculatorToolProvider.js +0 -95
  261. package/dist/services/tts/visual-line-ranges.d.ts +0 -18
  262. package/dist/services/tts/visual-line-ranges.js +0 -226
  263. package/dist/tools/client.d.ts +0 -12
  264. package/dist/tools/client.js +0 -14
  265. package/dist/tools/index.d.ts +0 -8
  266. package/dist/tools/index.js +0 -10
  267. package/dist/tools/internal.d.ts +0 -43
  268. package/dist/tools/internal.js +0 -41
  269. package/dist/tools/response-discovery.d.ts +0 -61
  270. package/dist/tools/response-discovery.js +0 -113
  271. package/dist/tools/types.d.ts +0 -337
  272. package/dist/tools/types.js +0 -12
  273. package/dist/tools/variant-resolver.d.ts +0 -41
  274. package/dist/tools/variant-resolver.js +0 -207
@@ -5,7 +5,9 @@
5
5
  * Used by both TTS (for timing data) and annotations (for persistence).
6
6
  *
7
7
  * Uses CSS selector paths and node indices for robust serialization that
8
- * survives content changes when possible.
8
+ * survives content changes when possible. A path into an open shadow root names
9
+ * its host, then ` >>> `, then the path inside the root, so ranges in content
10
+ * that renders into shadow roots round-trip; paths without one read as before.
9
11
  */
10
12
  /**
11
13
  * Serialized form of a Range suitable for storage.
@@ -91,6 +93,13 @@ export declare class RangeSerializer {
91
93
  * @returns CSS selector path
92
94
  */
93
95
  private getElementPath;
96
+ /** The path to `shadowRoot` itself: its host's path and an empty step into it. */
97
+ private getShadowRootPath;
98
+ /**
99
+ * The element, or shadow root, an element path names: each step after a
100
+ * {@link SHADOW_STEP} continues in the shadow root of what the step before named.
101
+ */
102
+ private resolveElementPath;
94
103
  /**
95
104
  * Find a node by its path from root.
96
105
  *
@@ -5,8 +5,13 @@
5
5
  * Used by both TTS (for timing data) and annotations (for persistence).
6
6
  *
7
7
  * Uses CSS selector paths and node indices for robust serialization that
8
- * survives content changes when possible.
8
+ * survives content changes when possible. A path into an open shadow root names
9
+ * its host, then ` >>> `, then the path inside the root, so ranges in content
10
+ * that renders into shadow roots round-trip; paths without one read as before.
9
11
  */
12
+ import { isShadowRootNode } from "./tts/flat-tree.js";
13
+ /** Separates a shadow host's path from the path inside its shadow root. */
14
+ const SHADOW_STEP = " >>> ";
10
15
  /**
11
16
  * RangeSerializer - Serialize and deserialize Range objects.
12
17
  */
@@ -94,20 +99,25 @@ export class RangeSerializer {
94
99
  }
95
100
  // Handle text nodes
96
101
  if (node.nodeType === Node.TEXT_NODE) {
97
- const parent = node.parentElement;
102
+ const parent = node.parentNode;
98
103
  if (!parent) {
99
104
  throw new Error("Text node has no parent");
100
105
  }
101
106
  // Get index of this text node among its siblings
102
107
  const textNodes = Array.from(parent.childNodes).filter((n) => n.nodeType === Node.TEXT_NODE);
103
108
  const index = textNodes.indexOf(node);
104
- const parentPath = this.getElementPath(parent, root);
109
+ const parentPath = isShadowRootNode(parent)
110
+ ? this.getShadowRootPath(parent, root)
111
+ : this.getElementPath(parent, root);
105
112
  return `${parentPath}::text[${index}]`;
106
113
  }
107
114
  // Handle element nodes
108
115
  if (node.nodeType === Node.ELEMENT_NODE) {
109
116
  return this.getElementPath(node, root);
110
117
  }
118
+ if (isShadowRootNode(node)) {
119
+ return this.getShadowRootPath(node, root);
120
+ }
111
121
  throw new Error(`Unsupported node type: ${node.nodeType}`);
112
122
  }
113
123
  /**
@@ -121,27 +131,81 @@ export class RangeSerializer {
121
131
  if (element === root) {
122
132
  return "";
123
133
  }
124
- const path = [];
134
+ const rootTree = root.getRootNode?.();
135
+ // One path per tree, outermost first.
136
+ const trees = [];
137
+ let path = [];
125
138
  let current = element;
139
+ // Ends `current`'s tree at the shadow root holding it, continuing at the host.
140
+ const leaveShadowRoot = (shadowRoot) => {
141
+ trees.unshift(path.join(" > "));
142
+ path = [];
143
+ current = shadowRoot.host;
144
+ };
126
145
  while (current && current !== root) {
127
- // Use ID if available (more stable)
146
+ // Use ID if available (more stable). An id is unique within its tree, so
147
+ // the path still names the shadow host above it.
128
148
  if (current.id) {
129
149
  path.unshift(`#${current.id}`);
150
+ const tree = current.getRootNode?.();
151
+ if (isShadowRootNode(tree) && tree !== rootTree) {
152
+ leaveShadowRoot(tree);
153
+ continue;
154
+ }
130
155
  break;
131
156
  }
132
157
  // Otherwise use tag + nth-of-type
133
158
  const parent = current.parentElement;
134
- if (!parent)
159
+ const parentNode = current.parentNode;
160
+ const siblingsParent = parent ?? (isShadowRootNode(parentNode) ? parentNode : null);
161
+ if (!siblingsParent)
135
162
  break;
136
- const siblings = Array.from(parent.children).filter((el) => el.tagName === current.tagName);
163
+ const tagName = current.tagName;
164
+ const siblings = Array.from(siblingsParent.children).filter((el) => el.tagName === tagName);
137
165
  const index = siblings.indexOf(current);
138
166
  const selector = siblings.length > 1
139
- ? `${current.tagName.toLowerCase()}:nth-of-type(${index + 1})`
140
- : current.tagName.toLowerCase();
167
+ ? `${tagName.toLowerCase()}:nth-of-type(${index + 1})`
168
+ : tagName.toLowerCase();
141
169
  path.unshift(selector);
142
- current = parent;
170
+ if (parent) {
171
+ current = parent;
172
+ }
173
+ else {
174
+ leaveShadowRoot(siblingsParent);
175
+ }
143
176
  }
144
- return path.join(" > ");
177
+ trees.unshift(path.join(" > "));
178
+ return trees.join(SHADOW_STEP);
179
+ }
180
+ /** The path to `shadowRoot` itself: its host's path and an empty step into it. */
181
+ getShadowRootPath(shadowRoot, root) {
182
+ return `${this.getElementPath(shadowRoot.host, root)}${SHADOW_STEP}`;
183
+ }
184
+ /**
185
+ * The element, or shadow root, an element path names: each step after a
186
+ * {@link SHADOW_STEP} continues in the shadow root of what the step before named.
187
+ */
188
+ resolveElementPath(path, root) {
189
+ let scope = root;
190
+ const steps = path.split(SHADOW_STEP);
191
+ for (let index = 0; index < steps.length; index++) {
192
+ if (index > 0) {
193
+ const shadowRoot = isShadowRootNode(scope)
194
+ ? null
195
+ : scope.shadowRoot;
196
+ if (!shadowRoot)
197
+ return null;
198
+ scope = shadowRoot;
199
+ }
200
+ const selector = steps[index].trim();
201
+ if (!selector)
202
+ continue;
203
+ const found = scope.querySelector(selector);
204
+ if (!found)
205
+ return null;
206
+ scope = found;
207
+ }
208
+ return scope;
145
209
  }
146
210
  /**
147
211
  * Find a node by its path from root.
@@ -156,10 +220,19 @@ export class RangeSerializer {
156
220
  }
157
221
  // Handle text node paths
158
222
  if (path.includes("::text[")) {
159
- const [elementPath, textPart] = path.split("::text[");
223
+ const marker = path.lastIndexOf("::text[");
224
+ const elementPath = path.slice(0, marker);
225
+ const textPart = path.slice(marker + "::text[".length);
160
226
  const textIndex = Number.parseInt(textPart.replace("]", ""), 10);
161
227
  // Find parent element
162
- const parent = elementPath ? root.querySelector(elementPath) : root;
228
+ let parent;
229
+ try {
230
+ parent = this.resolveElementPath(elementPath, root);
231
+ }
232
+ catch (error) {
233
+ console.warn("Invalid selector path:", path, error);
234
+ return null;
235
+ }
163
236
  if (!parent)
164
237
  return null;
165
238
  // Find text node by index
@@ -168,7 +241,7 @@ export class RangeSerializer {
168
241
  }
169
242
  // Handle element paths
170
243
  try {
171
- return root.querySelector(path);
244
+ return this.resolveElementPath(path, root);
172
245
  }
173
246
  catch (error) {
174
247
  console.warn("Invalid selector path:", path, error);
@@ -16,10 +16,11 @@
16
16
  * Part of PIE Assessment Toolkit.
17
17
  */
18
18
  import type { ITTSProvider, TTSConfig, TTSProviderCapabilities } from "@pie-players/pie-tts";
19
- import type { AccessibilityCatalogResolver, CatalogLookupContext } from "./AccessibilityCatalogResolver.js";
20
- import type { HighlightCoordinatorApi } from "./interfaces.js";
19
+ import type { AccessibilityCatalogResolver } from "./AccessibilityCatalogResolver.js";
20
+ import type { HighlightCoordinatorApi, SpeakOptions } from "./interfaces.js";
21
21
  import type { TTSHighlightTargetResolverProvider } from "./tts/highlight-target-resolver.js";
22
- export type { ITTSProvider, ITTSProviderImplementation, TTSConfig, TTSFeature, TTSProviderCapabilities, } from "@pie-players/pie-tts";
22
+ import { type SREMathSpeechOptions } from "./tts/math-speech.js";
23
+ export type { ITTSProvider, ITTSProviderImplementation, TTSConfig, TTSProviderCapabilities, } from "@pie-players/pie-tts";
23
24
  /**
24
25
  * Playback state
25
26
  */
@@ -30,16 +31,6 @@ export declare enum PlaybackState {
30
31
  PAUSED = "paused",
31
32
  ERROR = "error"
32
33
  }
33
- type HighlightMode = "word" | "sentence";
34
- interface SpeakOptions {
35
- catalogId?: string;
36
- catalogContext?: CatalogLookupContext;
37
- ignoreCatalogs?: boolean;
38
- language?: string;
39
- contentElement?: Element;
40
- wordBoundaryOffset?: number;
41
- highlightModeOverride?: HighlightMode;
42
- }
43
34
  /**
44
35
  * TTSService
45
36
  *
@@ -53,7 +44,13 @@ export declare class TTSService {
53
44
  private highlightTargetResolverProvider;
54
45
  private catalogResolver;
55
46
  private state;
47
+ private resumeState;
56
48
  private ttsConfig;
49
+ private hostLocales;
50
+ private readLocale;
51
+ private disposed;
52
+ private readinessGate;
53
+ private mathSpeechSource;
57
54
  private currentText;
58
55
  private currentContentElement;
59
56
  private normalizedToDOM;
@@ -66,11 +63,12 @@ export declare class TTSService {
66
63
  private playbackChunks;
67
64
  private activeRecordedAudio;
68
65
  private sentenceHighlightSegments;
69
- private currentSeekSegmentIndex;
70
66
  private activeSentenceStartOffset;
71
67
  private playbackStartDeferredRunId;
72
68
  private pendingPlaybackStartHighlights;
73
69
  private playbackStartBarrierCleanup;
70
+ private pauseHold;
71
+ private structuralPause;
74
72
  private playbackRateWriteQueue;
75
73
  private playbackRateRequestId;
76
74
  private pendingPlaybackRateRequest;
@@ -79,13 +77,31 @@ export declare class TTSService {
79
77
  private lastRenderedRegionTarget;
80
78
  private telemetryReporter;
81
79
  private readonly generatedMathSpeechResolver;
82
- private static readonly debugEnabled;
83
80
  constructor();
84
- private debugLog;
85
81
  private clearPlaybackStartBarrier;
86
82
  private notifyPlaybackStarted;
87
83
  private installPlaybackStartBarrier;
88
84
  private runWhenPlaybackStarts;
85
+ /** Resolves when run `runId` may start its next part: at once unless paused. */
86
+ private waitWhilePaused;
87
+ private releasePauseHold;
88
+ private waitStructuralPause;
89
+ private cancelStructuralPause;
90
+ /**
91
+ * Release what a superseded run still holds: its start barrier, a recorded
92
+ * clip still playing, a structural gap and a pause hold. Its loops then see
93
+ * the new run id and return.
94
+ */
95
+ private abandonRun;
96
+ /**
97
+ * Start a run and enter loading. From here a pause or stop applies to the
98
+ * run, whatever it is still waiting for: the provider, its content or the
99
+ * provider's audio.
100
+ */
101
+ private beginRun;
102
+ /** The run's audio is sounding; under a pause, it sounds again on resume. */
103
+ private markPlaying;
104
+ private assertNotDisposed;
89
105
  private emitTelemetry;
90
106
  /**
91
107
  * Initialize TTS service with a provider
@@ -95,18 +111,27 @@ export declare class TTSService {
95
111
  */
96
112
  initialize(provider: ITTSProvider, config?: Partial<TTSConfig>): Promise<void>;
97
113
  /**
98
- * Update TTS settings dynamically without full reinitialization
99
- *
100
- * This allows changing rate, pitch, and voice on the fly.
101
- * Note: Some providers may require reinitialization for voice changes.
102
- *
103
- * @param settings Partial settings to update (rate, pitch, voice)
114
+ * Update rate, pitch, voice or provider options without reinitializing.
115
+ * `providerOptions` merge over the ones already configured.
104
116
  */
105
117
  updateSettings(settings: Partial<TTSConfig>): Promise<void>;
106
118
  /**
107
119
  * Set highlight coordinator for word highlighting
108
120
  */
109
121
  setHighlightCoordinator(coordinator: HighlightCoordinatorApi): void;
122
+ /**
123
+ * Starts the service when a caller speaks before it is initialized; a gate
124
+ * that rejects fails the speak with its error. The coordinator installs one
125
+ * that runs its own text-to-speech start.
126
+ */
127
+ setReadinessGate(gate: (() => Promise<void>) | null): void;
128
+ /**
129
+ * Where the configured math speech options come from before, and apart from,
130
+ * initialization, so math names do not depend on when speech started.
131
+ */
132
+ setMathSpeechSource(source: (() => unknown) | null): void;
133
+ /** The start a speak must wait for, or null when it can speak now without yielding. */
134
+ private pendingReadiness;
110
135
  /**
111
136
  * Set a late-bound provider for optional host TTS highlight target remapping.
112
137
  *
@@ -132,18 +157,6 @@ export declare class TTSService {
132
157
  * @param resolver AccessibilityCatalogResolver instance
133
158
  */
134
159
  setCatalogResolver(resolver: AccessibilityCatalogResolver): void;
135
- /**
136
- * Whether a catalog holds spoken content this service could actually speak.
137
- *
138
- * Exists because `data-catalog-idref` is one attribute with several readers:
139
- * a node can be docked to a catalog that carries only a signing card, and the
140
- * nearest docked ancestor of a selection is therefore not necessarily the one
141
- * holding its authored SSML. A caller resolving a selection climbs ancestors
142
- * and asks this before settling on an id — see the TTS tool's
143
- * `findSpokenCatalogId`. Without it, a signing card docked on an inner node
144
- * silently shadows authored speech on an outer one.
145
- */
146
- hasSpokenAlternate(catalogId: string, language?: string): boolean;
147
160
  private getHighlightResolverRuntime;
148
161
  private getNodeElement;
149
162
  private isElementWithinScope;
@@ -151,18 +164,45 @@ export declare class TTSService {
151
164
  private resolveWordHighlightRange;
152
165
  private resolveSentenceHighlightTargets;
153
166
  private createTextRange;
154
- private paintTTSWordRange;
167
+ /**
168
+ * Paint the spoken word, each range offered to the host's resolver. A word
169
+ * split across inline elements is one range over its text nodes; across
170
+ * shadow trees, one range per tree.
171
+ */
172
+ private paintTTSWord;
155
173
  private paintTTSSentenceRanges;
156
174
  /**
157
175
  * Get provider capabilities
158
176
  */
159
177
  getCapabilities(): TTSProviderCapabilities | null;
178
+ private providerOptions;
179
+ /** The longest text the provider accepts in one speak, when it declares one. */
180
+ private maxTextLength;
181
+ /**
182
+ * Speak `text`, in pieces when it exceeds the provider's `maxTextLength`. Word
183
+ * boundaries keep indexing the whole text. An SSML document cannot be cut, so
184
+ * one over the limit fails and the chunk's plain fallback reads instead.
185
+ */
186
+ private speakWithinLimit;
187
+ /** Segments no longer than the provider's `maxTextLength`, offsets kept. */
188
+ private segmentsWithinLimit;
160
189
  /**
161
190
  * Build a character-by-character map from normalized text positions to DOM positions
162
191
  * This handles the complexity of whitespace normalization
163
192
  */
164
193
  private getTextProcessingOptions;
165
- private getMathSpeechOptions;
194
+ /**
195
+ * The language a read of `node` is in. Its content language is the nearest
196
+ * `lang` between it and its shell, else the language the speak names, else a
197
+ * pinned `lang_id`. Its locale is that content language, else the host's
198
+ * configured locale, else en-US.
199
+ */
200
+ private resolveReadLanguage;
201
+ /**
202
+ * The host's SRE options for math speech: from the installed source, else as
203
+ * this service was initialized with.
204
+ */
205
+ getMathSpeechOptions(): SREMathSpeechOptions | undefined;
166
206
  private buildPositionMap;
167
207
  private getStructuralPauseProfile;
168
208
  private resolvePauseMsFromUnits;
@@ -171,6 +211,11 @@ export declare class TTSService {
171
211
  private isElementHidden;
172
212
  private getBoundaryStrength;
173
213
  private getBoundaryAnchor;
214
+ /**
215
+ * `textOffset` is where `normalizedText` starts in the element's visible text:
216
+ * boundaries are collected over the whole element, and a selection speaks only
217
+ * part of it.
218
+ */
174
219
  private createSpeechPlan;
175
220
  private collectSpeechPlanBoundaries;
176
221
  private createNodeStartOffsets;
@@ -182,26 +227,39 @@ export declare class TTSService {
182
227
  private createSeekSegmentsFromSpeechChunks;
183
228
  private getCurrentSeekSegmentIndex;
184
229
  private speakWithPlan;
230
+ /**
231
+ * `startOffset` indexes the spoken text; the position map indexes the whole
232
+ * content element, which for a selection starts `activeWordBoundaryOffset`
233
+ * characters earlier.
234
+ */
185
235
  private highlightSentenceSegment;
186
236
  private getSegmentIndexForOffset;
187
237
  private highlightSentenceForOffset;
188
238
  /**
189
- * Find text node and offsets for highlighting a word
239
+ * Read `target` aloud: a range reads the text it selects, an element its
240
+ * content. A node with a spoken card that the target holds whole reads its
241
+ * card, `catalogId` names the card of the content root, and math reads as
242
+ * math speech.
190
243
  *
191
- * @param charIndex Character position in normalized/spoken text
192
- * @param length Length of the word
193
- * @returns Text node and local offsets, or null if not found
244
+ * Content marked not-to-be-spoken is never read, a named card included. When
245
+ * the target holds nothing speakable, nothing is spoken and playback already
246
+ * running continues.
194
247
  */
195
- private findHighlightRange;
248
+ speak(target: Range | Element, options?: SpeakOptions): Promise<void>;
196
249
  /**
197
- * Speak text with optional catalog support
198
- *
199
- * @param text Text to speak (will be normalized: trimmed and whitespace collapsed)
200
- * @param options Optional catalog ID, language, and content element for highlighting
250
+ * Suppression is read from the live ancestors of what is read, because the
251
+ * element carrying it may sit above the content root.
252
+ */
253
+ private isSuppressedTarget;
254
+ private resolveElementTarget;
255
+ private resolveRangeTarget;
256
+ private speakContent;
257
+ /**
258
+ * Hand the read's language to the provider. A read in a named content language
259
+ * sets the provider's locales and `contentLanguage` to it, so a browser voice
260
+ * follows it; a read naming none restores the host's locales and clears
261
+ * `contentLanguage`, so the browser voice follows the browser's language.
201
262
  */
202
- speak(text: string, options?: SpeakOptions): Promise<void>;
203
- private switchProviderToBrowser;
204
- private isBrowserSpeechFallbackAvailable;
205
263
  private applyLanguageSettings;
206
264
  private resolveSpeechContent;
207
265
  private resolveCatalogComposedSpeechContent;
@@ -217,6 +275,10 @@ export declare class TTSService {
217
275
  * defensive net if a capable provider still rejects a given SSML payload.
218
276
  */
219
277
  private resolveGeneratedPlaybackFormat;
278
+ /**
279
+ * With `range`, the selection only: an equation it holds whole reads as math
280
+ * speech, and a selection without one reads the selected text, `normalizedInputText`.
281
+ */
220
282
  private resolveGeneratedSpeechContent;
221
283
  private logResolvedSpeechContent;
222
284
  private initializeSpeakTracking;
@@ -224,7 +286,6 @@ export declare class TTSService {
224
286
  private configureWordBoundaryHighlighting;
225
287
  private clearWordBoundaryHighlighting;
226
288
  private highlightCatalogRegion;
227
- private highlightCatalogActiveRange;
228
289
  private highlightRenderableRegionTarget;
229
290
  private highlightRenderableActiveTarget;
230
291
  private renderHighlightDecision;
@@ -245,22 +306,18 @@ export declare class TTSService {
245
306
  private executeSpeakPlayback;
246
307
  private clearHighlightsAndTracking;
247
308
  /**
248
- * Speak a text range with accurate word highlighting
249
- *
250
- * This calculates the offset of the range within its parent element
251
- * to ensure word highlighting aligns correctly with the selected text.
252
- *
253
- * @param range DOM Range to speak
309
+ * The selected part of `root`'s normalized visible text and where it starts, or
310
+ * null when the map is unavailable or holds no selected character.
254
311
  */
255
- speakRange(range: Range, options?: {
256
- contentRoot?: Element | null;
257
- }): Promise<void>;
312
+ private selectMappedRangeText;
258
313
  /**
259
- * Pause playback
314
+ * Pause playback. A pause while the read is loading holds it: its audio does
315
+ * not start until {@link resume}.
260
316
  */
261
317
  pause(): void;
262
318
  /**
263
- * Resume playback
319
+ * Resume playback, back to loading when the pause came before the audio
320
+ * started.
264
321
  */
265
322
  resume(): void;
266
323
  private normalizePlaybackRate;
@@ -273,6 +330,16 @@ export declare class TTSService {
273
330
  * Stop playback
274
331
  */
275
332
  stop(): void;
333
+ /**
334
+ * Stop playback and drop the provider, whose owner destroys it. A later speak
335
+ * waits on the readiness gate, as before the first {@link initialize}.
336
+ */
337
+ releaseProvider(): void;
338
+ /**
339
+ * Stop, release the provider and drop every listener, hook and pending timer.
340
+ * A disposed service cannot speak or be initialized again.
341
+ */
342
+ dispose(): void;
276
343
  /**
277
344
  * Request UI-level TTS controls to hand off/deactivate.
278
345
  *
@@ -302,16 +369,10 @@ export declare class TTSService {
302
369
  */
303
370
  getLastError(): string | null;
304
371
  /**
305
- * Subscribe to state changes
306
- *
307
- * @param id Unique listener ID
308
- * @param callback Function to call on state change
309
- */
310
- onStateChange(id: string, callback: (state: PlaybackState) => void): void;
311
- /**
312
- * Unsubscribe from state changes
372
+ * Subscribe to playback state changes. The returned function unsubscribes;
373
+ * each subscription is its own, so the same callback can be subscribed twice.
313
374
  */
314
- offStateChange(id: string, callback: (state: PlaybackState) => void): void;
375
+ onStateChange(callback: (state: PlaybackState) => void): () => void;
315
376
  /**
316
377
  * Set state and notify listeners
317
378
  */