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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +349 -313
  2. package/dist/attempt/TestSession.d.ts +1 -20
  3. package/dist/attempt/TestSession.js +8 -99
  4. package/dist/components/ItemScope.custom-element.d.ts +1 -0
  5. package/dist/components/ItemScope.custom-element.js +1 -0
  6. package/dist/components/ItemToolBar.custom-element.js +1 -1
  7. package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
  8. package/dist/components/SectionToolBar.custom-element.js +1 -1
  9. package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
  10. package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-97hx60z4.js} +1 -1
  11. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
  12. package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
  13. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
  14. package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
  15. package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
  16. package/dist/components/item-scope-element.d.ts +2 -0
  17. package/dist/components/item-scope-element.js +2 -0
  18. package/dist/context/assessment-toolkit-context.d.ts +27 -6
  19. package/dist/index.d.ts +22 -47
  20. package/dist/index.js +12 -36
  21. package/dist/policy/core/PolicySource.d.ts +1 -1
  22. package/dist/policy/core/ToolPolicyEngine.d.ts +77 -32
  23. package/dist/policy/core/ToolPolicyEngine.js +136 -46
  24. package/dist/policy/core/compose-decision.d.ts +28 -7
  25. package/dist/policy/core/compose-decision.js +75 -54
  26. package/dist/policy/core/decision-types.d.ts +65 -34
  27. package/dist/policy/core/feature-decision.d.ts +16 -9
  28. package/dist/policy/core/feature-decision.js +14 -8
  29. package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
  30. package/dist/policy/core/pnp-policy-inputs.js +28 -56
  31. package/dist/policy/core/provenance.d.ts +9 -4
  32. package/dist/policy/core/provenance.js +1 -1
  33. package/dist/policy/engine.d.ts +7 -23
  34. package/dist/policy/engine.js +5 -20
  35. package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
  36. package/dist/policy/sources/PnpPolicySource.js +117 -118
  37. package/dist/runtime/SectionControllerBinding.d.ts +279 -0
  38. package/dist/runtime/SectionControllerBinding.js +450 -0
  39. package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
  40. package/dist/runtime/SectionRuntimeEngine.js +30 -372
  41. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
  42. package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
  43. package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
  44. package/dist/runtime/adapter/dom-event-bridge.js +17 -44
  45. package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
  46. package/dist/runtime/adapter/subscriber-fanout.js +1 -2
  47. package/dist/runtime/catalog-context.d.ts +19 -0
  48. package/dist/runtime/catalog-context.js +28 -0
  49. package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
  50. package/dist/runtime/composition-emit-scheduler.js +9 -15
  51. package/dist/runtime/content-language.d.ts +32 -0
  52. package/dist/runtime/content-language.js +46 -0
  53. package/dist/runtime/content-region.d.ts +17 -0
  54. package/dist/runtime/content-region.js +29 -0
  55. package/dist/runtime/content-registration.d.ts +13 -0
  56. package/dist/runtime/content-registration.js +25 -0
  57. package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
  58. package/dist/runtime/core/SectionEngineCore.js +2 -2
  59. package/dist/runtime/core/cohort.d.ts +1 -1
  60. package/dist/runtime/core/cohort.js +1 -1
  61. package/dist/runtime/core/engine-input.d.ts +18 -30
  62. package/dist/runtime/core/engine-input.js +11 -12
  63. package/dist/runtime/core/engine-output.d.ts +16 -40
  64. package/dist/runtime/core/engine-output.js +9 -21
  65. package/dist/runtime/core/engine-readiness.d.ts +5 -37
  66. package/dist/runtime/core/engine-readiness.js +8 -41
  67. package/dist/runtime/core/engine-resolver.d.ts +46 -74
  68. package/dist/runtime/core/engine-resolver.js +18 -43
  69. package/dist/runtime/core/engine-state.d.ts +28 -36
  70. package/dist/runtime/core/engine-state.js +18 -21
  71. package/dist/runtime/core/engine-transition.d.ts +14 -19
  72. package/dist/runtime/core/engine-transition.js +71 -51
  73. package/dist/runtime/engine.d.ts +16 -19
  74. package/dist/runtime/engine.js +12 -19
  75. package/dist/runtime/page-warnings.d.ts +13 -0
  76. package/dist/runtime/page-warnings.js +22 -0
  77. package/dist/runtime/policy-input-forwarding.d.ts +25 -0
  78. package/dist/runtime/policy-input-forwarding.js +25 -0
  79. package/dist/runtime/registration-events.d.ts +11 -0
  80. package/dist/runtime/runtime-event-claim.d.ts +8 -0
  81. package/dist/runtime/runtime-event-claim.js +15 -0
  82. package/dist/runtime/shell-event-bridge.d.ts +45 -0
  83. package/dist/runtime/shell-event-bridge.js +127 -0
  84. package/dist/runtime/shell-registration.d.ts +73 -0
  85. package/dist/runtime/shell-registration.js +95 -0
  86. package/dist/runtime/shell-scope.d.ts +68 -0
  87. package/dist/runtime/shell-scope.js +188 -0
  88. package/dist/runtime/tool-host-contract.d.ts +8 -10
  89. package/dist/runtime/tool-host-contract.js +11 -11
  90. package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
  91. package/dist/runtime/unclaimed-registration-watch.js +23 -0
  92. package/dist/services/ElementToolStateStore.d.ts +14 -10
  93. package/dist/services/ElementToolStateStore.js +37 -13
  94. package/dist/services/HighlightCoordinator.d.ts +21 -21
  95. package/dist/services/HighlightCoordinator.js +431 -325
  96. package/dist/services/RangeSerializer.d.ts +10 -1
  97. package/dist/services/RangeSerializer.js +87 -14
  98. package/dist/services/TTSService.d.ts +117 -70
  99. package/dist/services/TTSService.js +668 -578
  100. package/dist/services/ToolCoordinator.d.ts +43 -60
  101. package/dist/services/ToolCoordinator.js +164 -206
  102. package/dist/services/ToolRegistry.d.ts +74 -57
  103. package/dist/services/ToolRegistry.js +104 -114
  104. package/dist/services/ToolkitCoordinator.d.ts +183 -189
  105. package/dist/services/ToolkitCoordinator.js +477 -431
  106. package/dist/services/audio-handoff.d.ts +6 -7
  107. package/dist/services/audio-handoff.js +20 -14
  108. package/dist/services/catalog-owner.d.ts +0 -1
  109. package/dist/services/framework-error-bus.d.ts +3 -6
  110. package/dist/services/framework-error-bus.js +3 -6
  111. package/dist/services/framework-error.d.ts +22 -7
  112. package/dist/services/framework-error.js +9 -7
  113. package/dist/services/interfaces.d.ts +158 -129
  114. package/dist/services/section-session-assignment.d.ts +21 -0
  115. package/dist/services/section-session-assignment.js +82 -0
  116. package/dist/services/spoken-audio-cards.d.ts +0 -2
  117. package/dist/services/spoken-audio-cards.js +0 -2
  118. package/dist/services/tool-config-defaults.d.ts +3 -3
  119. package/dist/services/tool-config-defaults.js +3 -3
  120. package/dist/services/tool-config-validation.d.ts +4 -5
  121. package/dist/services/tool-config-validation.js +36 -61
  122. package/dist/services/tool-context.d.ts +0 -8
  123. package/dist/services/tool-context.js +0 -12
  124. package/dist/services/tool-icons.d.ts +4 -3
  125. package/dist/services/tool-icons.js +4 -3
  126. package/dist/services/tool-instance-id.d.ts +0 -1
  127. package/dist/services/tool-instance-id.js +0 -6
  128. package/dist/services/tool-providers/TTSToolProvider.d.ts +18 -19
  129. package/dist/services/tool-providers/TTSToolProvider.js +37 -34
  130. package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
  131. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +30 -26
  132. package/dist/services/tool-providers/ToolProviderRegistry.js +119 -93
  133. package/dist/services/tool-request.d.ts +27 -12
  134. package/dist/services/tool-request.js +24 -14
  135. package/dist/services/toolbar-decided-tools.d.ts +15 -0
  136. package/dist/services/toolbar-decided-tools.js +28 -0
  137. package/dist/services/tools-config-normalizer.d.ts +17 -22
  138. package/dist/services/tools-config-normalizer.js +2 -10
  139. package/dist/services/tts/browser-provider.d.ts +38 -2
  140. package/dist/services/tts/browser-provider.js +276 -140
  141. package/dist/services/tts/flat-tree.d.ts +71 -0
  142. package/dist/services/tts/flat-tree.js +269 -0
  143. package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
  144. package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
  145. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
  146. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
  147. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
  148. package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
  149. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +12 -4
  150. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
  151. package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
  152. package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
  153. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
  154. package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
  155. package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
  156. package/dist/services/tts/math-aware-text-processing.js +32 -18
  157. package/dist/services/tts/math-control-names.d.ts +21 -0
  158. package/dist/services/tts/math-control-names.js +142 -0
  159. package/dist/services/tts/math-speech.d.ts +11 -0
  160. package/dist/services/tts/math-speech.js +51 -18
  161. package/dist/services/tts/provider-options.d.ts +41 -0
  162. package/dist/services/tts/provider-options.js +1 -0
  163. package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
  164. package/dist/services/tts/ssml/spoken-text.js +29 -0
  165. package/dist/services/tts/start-failure.d.ts +11 -0
  166. package/dist/services/tts/start-failure.js +19 -0
  167. package/dist/services/tts/text-processing.d.ts +9 -0
  168. package/dist/services/tts/text-processing.js +46 -33
  169. package/dist/services/tts/text-segmentation.d.ts +7 -0
  170. package/dist/services/tts/text-segmentation.js +47 -0
  171. package/dist/services/tts-runtime-config.d.ts +25 -17
  172. package/dist/services/tts-runtime-config.js +16 -95
  173. package/dist/tools/content-capability-resolution.d.ts +7 -9
  174. package/dist/tools/content-capability-resolution.js +8 -45
  175. package/dist/tools/registration.d.ts +58 -0
  176. package/dist/tools/registration.js +54 -0
  177. package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
  178. package/dist/tools/tool-surface-host.d.ts +6 -10
  179. package/dist/tools/tool-surface-host.js +44 -42
  180. package/dist/tools/tool-tag-map.d.ts +5 -15
  181. package/dist/tools/tool-tag-map.js +6 -20
  182. package/dist/utils/structural-equality.d.ts +11 -0
  183. package/dist/utils/structural-equality.js +47 -0
  184. package/package.json +13 -29
  185. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
  186. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
  187. package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
  188. package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
  189. package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
  190. package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
  191. package/dist/context/runtime-context-consumer.d.ts +0 -13
  192. package/dist/context/runtime-context-consumer.js +0 -18
  193. package/dist/item-loader.d.ts +0 -44
  194. package/dist/item-loader.js +0 -61
  195. package/dist/policy/core/engine-context.d.ts +0 -29
  196. package/dist/policy/core/engine-context.js +0 -27
  197. package/dist/policy/core/policy-source-tag.d.ts +0 -31
  198. package/dist/policy/core/policy-source-tag.js +0 -28
  199. package/dist/policy/internal.d.ts +0 -25
  200. package/dist/policy/internal.js +0 -25
  201. package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
  202. package/dist/runtime/adapter/coordinator-bridge.js +0 -126
  203. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
  204. package/dist/runtime/adapter/framework-error-bridge.js +0 -46
  205. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
  206. package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
  207. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
  208. package/dist/runtime/core/engine-stage-derivation.js +0 -29
  209. package/dist/runtime/engine-context.d.ts +0 -39
  210. package/dist/runtime/engine-context.js +0 -31
  211. package/dist/runtime/internal.d.ts +0 -43
  212. package/dist/runtime/internal.js +0 -45
  213. package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
  214. package/dist/runtime/section-runtime-engine-host-context.js +0 -57
  215. package/dist/runtime/stage-emit-gate.d.ts +0 -92
  216. package/dist/runtime/stage-emit-gate.js +0 -82
  217. package/dist/services/I18nService.d.ts +0 -47
  218. package/dist/services/I18nService.js +0 -84
  219. package/dist/services/ThemeProvider.d.ts +0 -95
  220. package/dist/services/ThemeProvider.js +0 -334
  221. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
  222. package/dist/services/defaultPersonalNeedsProfile.js +0 -26
  223. package/dist/services/pnp-standard-features.d.ts +0 -238
  224. package/dist/services/pnp-standard-features.js +0 -338
  225. package/dist/tools/client.d.ts +0 -10
  226. package/dist/tools/client.js +0 -10
  227. package/dist/tools/internal.d.ts +0 -44
  228. package/dist/tools/internal.js +0 -39
@@ -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;
@@ -70,6 +67,8 @@ export declare class TTSService {
70
67
  private playbackStartDeferredRunId;
71
68
  private pendingPlaybackStartHighlights;
72
69
  private playbackStartBarrierCleanup;
70
+ private pauseHold;
71
+ private structuralPause;
73
72
  private playbackRateWriteQueue;
74
73
  private playbackRateRequestId;
75
74
  private pendingPlaybackRateRequest;
@@ -78,13 +77,31 @@ export declare class TTSService {
78
77
  private lastRenderedRegionTarget;
79
78
  private telemetryReporter;
80
79
  private readonly generatedMathSpeechResolver;
81
- private static readonly debugEnabled;
82
80
  constructor();
83
- private debugLog;
84
81
  private clearPlaybackStartBarrier;
85
82
  private notifyPlaybackStarted;
86
83
  private installPlaybackStartBarrier;
87
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;
88
105
  private emitTelemetry;
89
106
  /**
90
107
  * Initialize TTS service with a provider
@@ -94,18 +111,27 @@ export declare class TTSService {
94
111
  */
95
112
  initialize(provider: ITTSProvider, config?: Partial<TTSConfig>): Promise<void>;
96
113
  /**
97
- * Update TTS settings dynamically without full reinitialization
98
- *
99
- * This allows changing rate, pitch, and voice on the fly.
100
- * Note: Some providers may require reinitialization for voice changes.
101
- *
102
- * @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.
103
116
  */
104
117
  updateSettings(settings: Partial<TTSConfig>): Promise<void>;
105
118
  /**
106
119
  * Set highlight coordinator for word highlighting
107
120
  */
108
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;
109
135
  /**
110
136
  * Set a late-bound provider for optional host TTS highlight target remapping.
111
137
  *
@@ -131,18 +157,6 @@ export declare class TTSService {
131
157
  * @param resolver AccessibilityCatalogResolver instance
132
158
  */
133
159
  setCatalogResolver(resolver: AccessibilityCatalogResolver): void;
134
- /**
135
- * Whether a catalog holds spoken content this service could actually speak.
136
- *
137
- * Exists because `data-catalog-idref` is one attribute with several readers:
138
- * a node can be docked to a catalog that carries only a signing card, and the
139
- * nearest docked ancestor of a selection is therefore not necessarily the one
140
- * holding its authored SSML. A caller resolving a selection climbs ancestors
141
- * and asks this before settling on an id — see the TTS tool's
142
- * `findSpokenCatalogId`. Without it, a signing card docked on an inner node
143
- * silently shadows authored speech on an outer one.
144
- */
145
- hasSpokenAlternate(catalogId: string, language?: string): boolean;
146
160
  private getHighlightResolverRuntime;
147
161
  private getNodeElement;
148
162
  private isElementWithinScope;
@@ -150,18 +164,45 @@ export declare class TTSService {
150
164
  private resolveWordHighlightRange;
151
165
  private resolveSentenceHighlightTargets;
152
166
  private createTextRange;
153
- 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;
154
173
  private paintTTSSentenceRanges;
155
174
  /**
156
175
  * Get provider capabilities
157
176
  */
158
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;
159
189
  /**
160
190
  * Build a character-by-character map from normalized text positions to DOM positions
161
191
  * This handles the complexity of whitespace normalization
162
192
  */
163
193
  private getTextProcessingOptions;
164
- 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;
165
206
  private buildPositionMap;
166
207
  private getStructuralPauseProfile;
167
208
  private resolvePauseMsFromUnits;
@@ -195,22 +236,30 @@ export declare class TTSService {
195
236
  private getSegmentIndexForOffset;
196
237
  private highlightSentenceForOffset;
197
238
  /**
198
- * 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.
199
243
  *
200
- * @param charIndex Character position in normalized/spoken text
201
- * @param length Length of the word
202
- * @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.
203
247
  */
204
- private findHighlightRange;
248
+ speak(target: Range | Element, options?: SpeakOptions): Promise<void>;
205
249
  /**
206
- * Speak text with optional catalog support
207
- *
208
- * @param text Text to speak (will be normalized: trimmed and whitespace collapsed)
209
- * @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.
210
262
  */
211
- speak(text: string, options?: SpeakOptions): Promise<void>;
212
- private switchProviderToBrowser;
213
- private isBrowserSpeechFallbackAvailable;
214
263
  private applyLanguageSettings;
215
264
  private resolveSpeechContent;
216
265
  private resolveCatalogComposedSpeechContent;
@@ -226,6 +275,10 @@ export declare class TTSService {
226
275
  * defensive net if a capable provider still rejects a given SSML payload.
227
276
  */
228
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
+ */
229
282
  private resolveGeneratedSpeechContent;
230
283
  private logResolvedSpeechContent;
231
284
  private initializeSpeakTracking;
@@ -233,7 +286,6 @@ export declare class TTSService {
233
286
  private configureWordBoundaryHighlighting;
234
287
  private clearWordBoundaryHighlighting;
235
288
  private highlightCatalogRegion;
236
- private highlightCatalogActiveRange;
237
289
  private highlightRenderableRegionTarget;
238
290
  private highlightRenderableActiveTarget;
239
291
  private renderHighlightDecision;
@@ -253,28 +305,19 @@ export declare class TTSService {
253
305
  private speakCatalogChunkOnce;
254
306
  private executeSpeakPlayback;
255
307
  private clearHighlightsAndTracking;
256
- /**
257
- * Speak a text range with accurate word highlighting
258
- *
259
- * This calculates the offset of the range within its parent element
260
- * to ensure word highlighting aligns correctly with the selected text.
261
- *
262
- * @param range DOM Range to speak
263
- */
264
308
  /**
265
309
  * The selected part of `root`'s normalized visible text and where it starts, or
266
310
  * null when the map is unavailable or holds no selected character.
267
311
  */
268
312
  private selectMappedRangeText;
269
- speakRange(range: Range, options?: {
270
- contentRoot?: Element | null;
271
- }): Promise<void>;
272
313
  /**
273
- * Pause playback
314
+ * Pause playback. A pause while the read is loading holds it: its audio does
315
+ * not start until {@link resume}.
274
316
  */
275
317
  pause(): void;
276
318
  /**
277
- * Resume playback
319
+ * Resume playback, back to loading when the pause came before the audio
320
+ * started.
278
321
  */
279
322
  resume(): void;
280
323
  private normalizePlaybackRate;
@@ -287,6 +330,16 @@ export declare class TTSService {
287
330
  * Stop playback
288
331
  */
289
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;
290
343
  /**
291
344
  * Request UI-level TTS controls to hand off/deactivate.
292
345
  *
@@ -316,16 +369,10 @@ export declare class TTSService {
316
369
  */
317
370
  getLastError(): string | null;
318
371
  /**
319
- * Subscribe to state changes
320
- *
321
- * @param id Unique listener ID
322
- * @param callback Function to call on state change
323
- */
324
- onStateChange(id: string, callback: (state: PlaybackState) => void): void;
325
- /**
326
- * 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.
327
374
  */
328
- offStateChange(id: string, callback: (state: PlaybackState) => void): void;
375
+ onStateChange(callback: (state: PlaybackState) => void): () => void;
329
376
  /**
330
377
  * Set state and notify listeners
331
378
  */