@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
@@ -8,48 +8,26 @@
8
8
  * Part of PIE Assessment Toolkit.
9
9
  */
10
10
  import type { I18nServiceApi } from "@pie-players/pie-players-shared/i18n";
11
- import type { AccessibilityCatalog } from "@pie-players/pie-players-shared/types";
11
+ import type { AccessibilityCatalog, SectionControllerSessionState } from "@pie-players/pie-players-shared/types";
12
12
  import type { AccessibilityCatalogResolver, CatalogChangeListener, CatalogLookupContext, CatalogLookupOptions, CatalogOwnerRegistration, CatalogOwnerView, CatalogStatistics, CatalogType, ResolvedCatalog } from "./AccessibilityCatalogResolver.js";
13
13
  import type { CatalogOwnerContext } from "./catalog-owner.js";
14
14
  import type { FrameworkErrorListener } from "./framework-error-bus.js";
15
15
  import type { FrameworkErrorModel } from "./framework-error.js";
16
- import type { HighlightColor, HighlightType } from "./HighlightCoordinator.js";
17
- import type { SectionControllerHandle, SectionItemEventSubscriptionArgs, SectionScopedEventSubscriptionArgs, SectionEventSubscriptionArgs, ToolkitCoordinatorHooks, ToolkitInitStatus } from "./ToolkitCoordinator.js";
18
- import type { ThemeConfig } from "./ThemeProvider.js";
16
+ import type { Annotation, HighlightColor, HighlightType } from "./HighlightCoordinator.js";
17
+ import type { SerializedRange } from "./RangeSerializer.js";
18
+ import type { SectionControllerHandle, SectionItemEventSubscriptionArgs, SectionScopedEventSubscriptionArgs, SectionEventSubscriptionArgs, ToolkitCoordinatorHooks } from "./ToolkitCoordinator.js";
19
19
  import type { ZIndexLayer } from "./ToolCoordinator.js";
20
20
  import type { PlaybackState, TTSConfig } from "./TTSService.js";
21
21
  import type { TTSHighlightTargetResolverProvider } from "./tts/highlight-target-resolver.js";
22
- import type { ToolPlacementConfig, ToolPlacementLevel, ToolProviderConfig } from "./tools-config-normalizer.js";
22
+ import type { TextToSpeechToolProviderConfig, ToolPlacementConfig, ToolProviderConfig } from "./tools-config-normalizer.js";
23
+ import type { ToolProviderApi } from "./tool-providers/ToolProviderApi.js";
23
24
  import type { ToolProviderRegistry } from "./tool-providers/ToolProviderRegistry.js";
24
- import type { FeaturePolicyDecision, PolicySource, PnpEnforcementMode, ResolvedEngineInputs, ToolPolicyChangeListener, ToolPolicyDecision, ToolPolicyDecisionRequest } from "../policy/engine.js";
25
- import type { AssessmentEntity, AssessmentItemRef } from "@pie-players/pie-players-shared/types";
25
+ import type { FeaturePolicyDecision, PolicySource, PnpEnforcementMode, ResolvedEngineInputs, ToolPolicyChangeListener, ToolPolicyDecision, ToolPolicyDecisionRequest, ToolScope } from "../policy/engine.js";
26
+ import type { AssessmentEntity, ItemSettings } from "@pie-players/pie-players-shared/types";
26
27
  import type { ITTSProvider, TTSProviderCapabilities } from "@pie-players/pie-tts";
27
- import type { ResolvedToolContext, ToolContextResolver, ToolContextResolverContext, ToolContextResolverMap } from "./ToolRegistry.js";
28
+ import type { ResolvedToolContext, ToolContextResolverContext, ToolRegistry } from "./ToolRegistry.js";
28
29
  import type { ToolOpenRequest, ToolRequestTarget } from "./tool-request.js";
29
30
  export type { I18nServiceApi };
30
- /**
31
- * Theme provider interface
32
- *
33
- * Applies consistent accessibility theming across items and tools.
34
- */
35
- export interface ThemeProviderApi {
36
- /**
37
- * Apply theme configuration
38
- */
39
- applyTheme(config: ThemeConfig): void;
40
- /**
41
- * Get current theme configuration
42
- */
43
- getCurrentTheme(): Required<ThemeConfig>;
44
- /**
45
- * Reset to default theme
46
- */
47
- reset(): void;
48
- /**
49
- * Clean up and remove theme styles
50
- */
51
- destroy(): void;
52
- }
53
31
  /**
54
32
  * Highlight coordinator interface
55
33
  *
@@ -61,9 +39,11 @@ export interface HighlightCoordinatorApi {
61
39
  */
62
40
  highlightRange(range: Range, type: HighlightType, color: HighlightColor): void;
63
41
  /**
64
- * Highlight a word for TTS (temporary)
42
+ * Highlight the word being read (temporary): one range per tree the word
43
+ * spans, all painted, so a word split across inline elements highlights
44
+ * whole.
65
45
  */
66
- highlightTTSWord(textNode: Text, startOffset: number, endOffset: number): void;
46
+ highlightTTSWord(ranges: Range[]): void;
67
47
  /**
68
48
  * Highlight a single element as the active TTS word (temporary).
69
49
  *
@@ -73,20 +53,16 @@ export interface HighlightCoordinatorApi {
73
53
  * (e.g. `<mjx-mi><mjx-c/></mjx-mi>`) and whole-expression fallbacks. Painting
74
54
  * the element itself is what lets a resolved math token highlight as a token
75
55
  * rather than escalating to the full `<math>` / `<mjx-container>`.
76
- *
77
- * Optional so lightweight coordinator mocks can omit it.
78
56
  */
79
- highlightTTSWordElement?(element: Element): void;
57
+ highlightTTSWordElement(element: Element): void;
80
58
  /**
81
59
  * Highlight sentence(s) for TTS (background layer)
82
60
  */
83
61
  highlightTTSSentence(ranges: Range[]): void;
84
62
  /**
85
63
  * Highlight sentence/block element targets for TTS (background layer).
86
- *
87
- * Optional so lightweight coordinator mocks can omit it.
88
64
  */
89
- highlightTTSSentenceElements?(elements: Element[]): void;
65
+ highlightTTSSentenceElements(elements: Element[]): void;
90
66
  /**
91
67
  * Clear all TTS highlights (word and sentence)
92
68
  */
@@ -95,10 +71,6 @@ export interface HighlightCoordinatorApi {
95
71
  * Clear highlights of a specific type
96
72
  */
97
73
  clearHighlights(type: HighlightType): void;
98
- /**
99
- * Clear all highlights
100
- */
101
- clearAll(): void;
102
74
  /**
103
75
  * Check if highlighting is supported in current environment
104
76
  */
@@ -107,6 +79,46 @@ export interface HighlightCoordinatorApi {
107
79
  * Update TTS highlight style dynamically
108
80
  */
109
81
  updateTTSHighlightStyle(color: string, opacity: number): void;
82
+ /**
83
+ * Record a learner annotation over a range.
84
+ *
85
+ * @returns Annotation id for later lookup or removal
86
+ */
87
+ addAnnotation(range: Range, color?: HighlightColor): string;
88
+ /**
89
+ * Remove one annotation
90
+ */
91
+ removeAnnotation(id: string): void;
92
+ /**
93
+ * Remove all annotations
94
+ */
95
+ clearAnnotations(): void;
96
+ /**
97
+ * All recorded annotations
98
+ */
99
+ getAnnotations(): Annotation[];
100
+ /**
101
+ * One annotation by id
102
+ */
103
+ getAnnotation(id: string): Annotation | null;
104
+ /**
105
+ * Serialize annotations relative to `root` for persistence
106
+ */
107
+ exportAnnotations(root?: Element): Array<SerializedRange & {
108
+ id: string;
109
+ color: HighlightColor;
110
+ timestamp: number;
111
+ }>;
112
+ /**
113
+ * Restore annotations serialized by `exportAnnotations` against the same root
114
+ *
115
+ * @returns Number of annotations restored
116
+ */
117
+ importAnnotations(data: Array<SerializedRange & {
118
+ id?: string;
119
+ color: HighlightColor;
120
+ timestamp?: number;
121
+ }>, root?: Element): number;
110
122
  }
111
123
  /**
112
124
  * Tool state interface
@@ -118,6 +130,11 @@ export interface ToolState {
118
130
  element: HTMLElement | null;
119
131
  layer: ZIndexLayer;
120
132
  }
133
+ /** Narrows tool-state queries to the instances of one tool. */
134
+ export interface ToolStateFilter {
135
+ /** Base tool id, such as `calculator`, matching every scoped instance of it. */
136
+ baseId?: string;
137
+ }
121
138
  /**
122
139
  * Tool coordinator interface
123
140
  *
@@ -167,14 +184,37 @@ export interface ToolCoordinatorApi {
167
184
  */
168
185
  getToolState(id: string): ToolState | undefined;
169
186
  /**
170
- * Get all visible tools
187
+ * Every visible tool, or with `{ baseId }` every visible instance of one
188
+ * tool, so a host need not parse scoped ids.
189
+ */
190
+ getVisibleTools(filter?: ToolStateFilter): ToolState[];
191
+ /**
192
+ * Hide every visible tool, or every instance of one tool with `{ baseId }`.
171
193
  */
172
- getVisibleTools(): ToolState[];
194
+ hideAllTools(filter?: ToolStateFilter): void;
173
195
  /**
174
196
  * Subscribe to tool state changes
175
197
  */
176
198
  subscribe(listener: () => void): () => void;
177
199
  }
200
+ /** Options for {@link TtsServiceApi.speak}. */
201
+ export interface SpeakOptions {
202
+ /**
203
+ * The spoken card of the content root, read in place of its content when the
204
+ * target holds the whole root.
205
+ */
206
+ catalogId?: string;
207
+ /** The owner whose registered cards apply; without it only assessment-level cards do. */
208
+ catalogContext?: CatalogLookupContext;
209
+ /** BCP 47 language of the content read. */
210
+ language?: string;
211
+ /**
212
+ * For a range target, the content root that scopes highlighting and the
213
+ * offsets of the selection. Defaults to the range's nearest element. An
214
+ * element target is its own root.
215
+ */
216
+ contentRoot?: Element | null;
217
+ }
178
218
  /**
179
219
  * TTS service interface
180
220
  *
@@ -187,20 +227,13 @@ export interface TtsServiceApi {
187
227
  */
188
228
  initialize(provider: ITTSProvider, config?: Partial<TTSConfig>): Promise<void>;
189
229
  /**
190
- * Speak text with optional catalog support
191
- */
192
- speak(text: string, options?: {
193
- catalogId?: string;
194
- catalogContext?: CatalogLookupContext;
195
- language?: string;
196
- contentElement?: Element;
197
- }): Promise<void>;
198
- /**
199
- * Speak a text range
230
+ * Read `target` aloud: a range reads the text it selects, an element its
231
+ * content. A node with a spoken card that the target holds whole reads its
232
+ * card, and math reads as math speech. Content marked not-to-be-spoken is
233
+ * never read; when nothing speakable remains, nothing is spoken and playback
234
+ * already running continues.
200
235
  */
201
- speakRange(range: Range, options?: {
202
- contentRoot?: Element | null;
203
- }): Promise<void>;
236
+ speak(target: Range | Element, options?: SpeakOptions): Promise<void>;
204
237
  /**
205
238
  * Pause playback
206
239
  */
@@ -213,6 +246,11 @@ export interface TtsServiceApi {
213
246
  * Stop playback
214
247
  */
215
248
  stop(): void;
249
+ /**
250
+ * Stop, release the provider and drop every listener and timer. A disposed
251
+ * service cannot speak.
252
+ */
253
+ dispose(): void;
216
254
  /**
217
255
  * Request active TTS controls to hand off/deactivate their UI state.
218
256
  *
@@ -245,13 +283,9 @@ export interface TtsServiceApi {
245
283
  */
246
284
  getCurrentText(): string | null;
247
285
  /**
248
- * Subscribe to state changes
249
- */
250
- onStateChange(id: string, callback: (state: PlaybackState) => void): void;
251
- /**
252
- * Unsubscribe from state changes
286
+ * Subscribe to state changes. Returns the function that unsubscribes.
253
287
  */
254
- offStateChange(id: string, callback: (state: PlaybackState) => void): void;
288
+ onStateChange(callback: (state: PlaybackState) => void): () => void;
255
289
  /**
256
290
  * Get capabilities of current provider
257
291
  */
@@ -421,14 +455,6 @@ export interface ToolkitCoordinatorApi {
421
455
  * Assessment identifier
422
456
  */
423
457
  readonly assessmentId: string;
424
- /**
425
- * Configuration
426
- */
427
- readonly config: {
428
- tools?: {
429
- providers?: Record<string, ToolProviderConfig | undefined>;
430
- };
431
- };
432
458
  /**
433
459
  * TTS service
434
460
  */
@@ -453,25 +479,17 @@ export interface ToolkitCoordinatorApi {
453
479
  * Tool provider registry
454
480
  */
455
481
  readonly toolProviderRegistry: ToolProviderRegistry;
456
- /**
457
- * Get all services as a bundle
458
- */
459
- getServiceBundle(): {
460
- ttsService: TtsServiceApi;
461
- toolCoordinator: ToolCoordinatorApi;
462
- highlightCoordinator: HighlightCoordinatorApi;
463
- elementToolStateStore: ElementToolStateStoreApi;
464
- catalogResolver: AccessibilityCatalogResolverApi;
465
- toolProviderRegistry: ToolProviderRegistry;
466
- };
467
482
  /**
468
483
  * Ensure TTS service is initialized and ready.
469
484
  */
470
485
  ensureTTSReady(config?: Record<string, unknown>): Promise<void>;
471
486
  /**
472
- * Ensure a provider is initialized and ready.
487
+ * Ensure a tool's provider is initialized and ready. Providers register under
488
+ * their tool's id, so a tool passes its own base tool id. A tool starts its
489
+ * provider here, so that a failure meets the toolkit's tool failure policy:
490
+ * recoverable unless policy grants the tool.
473
491
  */
474
- ensureProviderReady(providerId: string): Promise<unknown>;
492
+ ensureProviderReady(toolId: string): Promise<ToolProviderApi>;
475
493
  /**
476
494
  * Wait until coordinator initialization is complete.
477
495
  */
@@ -481,25 +499,19 @@ export interface ToolkitCoordinatorApi {
481
499
  */
482
500
  isReady(): boolean;
483
501
  /**
484
- * Read current initialization status.
485
- */
486
- getInitStatus(): ToolkitInitStatus;
487
- /**
488
- * Check if a tool is enabled
502
+ * Subscribe to changes of {@link isReady}.
489
503
  */
490
- isToolEnabled(toolId: string): boolean;
504
+ onReadyChange(listener: () => void): () => void;
491
505
  /**
492
506
  * Get tool configuration
493
507
  */
508
+ getToolConfig(toolId: "textToSpeech"): TextToSpeechToolProviderConfig | null;
494
509
  getToolConfig(toolId: string): ToolProviderConfig | null;
495
510
  /**
496
511
  * Update tool configuration
497
512
  */
513
+ updateToolConfig(toolId: "textToSpeech", updates: Partial<TextToSpeechToolProviderConfig>): void;
498
514
  updateToolConfig(toolId: string, updates: Partial<ToolProviderConfig>): void;
499
- /**
500
- * Update the enabled tool list for one placement level.
501
- */
502
- updateToolPlacement(level: ToolPlacementLevel, toolIds: string[]): void;
503
515
  /**
504
516
  * Patch one or more placement levels in the canonical tools config.
505
517
  */
@@ -526,11 +538,12 @@ export interface ToolkitCoordinatorApi {
526
538
  * aggregate `section-loading-complete`, in the canonical order a
527
539
  * fresh subscriber would have observed.
528
540
  *
529
- * Throws if no active section cohort exists; host code must call
530
- * `getOrCreateSectionController(...)` at least once before
531
- * subscribing. (`toolkit-ready` alone is not sufficient — it fires
532
- * once toolkit state has loaded but before any section controller
533
- * has been created.) The typical pattern is to subscribe once
541
+ * Throws until the first `getOrCreateSectionController(...)` call; a
542
+ * listener added while a section is starting binds when that section
543
+ * becomes active. `runtime-ready` alone is not sufficient: it fires once
544
+ * the coordinator is bound, before any section controller exists. A
545
+ * `<pie-assessment-toolkit>` holding a section emits `toolkit-ready`
546
+ * after its controller resolves. The typical pattern is to subscribe once
534
547
  * immediately after the first `getOrCreateSectionController(...)`
535
548
  * resolves; the subscription then follows the active cohort across
536
549
  * all subsequent navigation without further wiring.
@@ -559,12 +572,18 @@ export interface ToolkitCoordinatorApi {
559
572
  subscribeSectionLifecycleEvents(args: SectionScopedEventSubscriptionArgs): () => void;
560
573
  /**
561
574
  * Create or reuse a section controller with single-flight deduplication.
575
+ *
576
+ * `initialSession` is a host-supplied session. A new controller applies it in
577
+ * replace mode in place of `hydrate()`, before it is published; an existing
578
+ * one applies it unless it equals the current session, keeping recorded
579
+ * responses that a response-free item session would replace.
562
580
  */
563
581
  getOrCreateSectionController(args: {
564
582
  sectionId: string;
565
583
  attemptId?: string;
566
584
  input?: unknown;
567
585
  updateExisting?: boolean;
586
+ initialSession?: SectionControllerSessionState | null;
568
587
  createDefaultController: () => SectionControllerHandle | Promise<SectionControllerHandle>;
569
588
  }): Promise<SectionControllerHandle>;
570
589
  /**
@@ -599,6 +618,12 @@ export interface ToolkitCoordinatorApi {
599
618
  * Optional so structural host coordinators remain assignable.
600
619
  */
601
620
  reportFrameworkError?(model: FrameworkErrorModel): void;
621
+ /**
622
+ * Report that a toolbar could not load a tool's module. The tool degrades
623
+ * unless policy grants it, in which case the failure is fatal. Optional so
624
+ * structural host coordinators remain assignable; a toolbar without it logs.
625
+ */
626
+ reportToolModuleFailure?(toolId: string, error: unknown): void;
602
627
  /**
603
628
  * Resolve the visible tool set for a given placement level + scope.
604
629
  * Returns the engine's full decision (visible tools, diagnostics,
@@ -607,22 +632,25 @@ export interface ToolkitCoordinatorApi {
607
632
  */
608
633
  decideToolPolicy(request: ToolPolicyDecisionRequest): ToolPolicyDecision;
609
634
  /**
610
- * Resolve eligibility for one PNP/AfA feature id through the six-level
635
+ * Resolve eligibility for one PNP/AfA feature id through the eight-level
611
636
  * precedence, independent of toolbar placement — for capabilities that
612
637
  * render as their own surface rather than a toolbar button (a signed
613
638
  * alternate's region, for example).
614
639
  *
640
+ * `scope` is the surface asking; an item's scope brings in that item's
641
+ * registered settings ({@link registerItemSettings}).
642
+ *
615
643
  * Optional so host-supplied coordinator stubs predating this method stay
616
644
  * assignable; call sites must feature-detect.
617
645
  */
618
- decideFeaturePolicy?(featureId: string): FeaturePolicyDecision;
646
+ decideFeaturePolicy?(featureId: string, scope?: ToolScope): FeaturePolicyDecision;
619
647
  /**
620
648
  * Subscribe to policy-engine change events. Fires whenever the
621
649
  * coordinator's bound policy inputs change (`updateToolConfig`,
622
- * `updateToolPlacement`, `updateAssessment`, `updateCurrentItemRef`,
623
- * `setPnpEnforcement`) or a custom `PolicySource` is registered /
624
- * removed. Listeners that need the new visible tool set should
625
- * call `decideToolPolicy(...)` with their level / scope.
650
+ * `updateToolsPlacement`, `updateAssessment`, `setPnpEnforcement`), an
651
+ * item's settings are registered or withdrawn, or a custom `PolicySource`
652
+ * is registered / removed. Listeners that need the new visible tool set
653
+ * should call `decideToolPolicy(...)` with their level / scope.
626
654
  */
627
655
  onPolicyChange(listener: ToolPolicyChangeListener): () => void;
628
656
  /**
@@ -645,30 +673,31 @@ export interface ToolkitCoordinatorApi {
645
673
  /**
646
674
  * Bind (or clear) the active assessment for PNP/profile policy decisions.
647
675
  *
648
- * Under auto-mode (no host override via {@link setPnpEnforcement}),
649
- * the engine flips to `pnpEnforcement: "on"` iff the assessment
650
- * carries profile precedence material (`personalNeedsProfile`,
651
- * `settings.districtPolicy`, `settings.testAdministration`) or the
652
- * currently-bound item ref carries item-level profile inputs. A bare
653
- * assessment record (just `id` / `name`) keeps `"off"`.
676
+ * Under auto-mode (no host override via {@link setPnpEnforcement}), a
677
+ * decision enforces PNP/profile policy iff the assessment carries profile
678
+ * precedence material (`personalNeedsProfile`, `settings.districtPolicy`,
679
+ * `settings.testAdministration`), or the decision is scoped to an item whose
680
+ * registered settings carry item-level policy inputs. A bare assessment
681
+ * record (just `id` / `name`) keeps `"off"`.
654
682
  *
655
683
  * The host override set via {@link setPnpEnforcement} is sticky
656
684
  * across assessment swaps.
657
685
  */
658
686
  updateAssessment(assessment: AssessmentEntity | null): void;
659
687
  /**
660
- * Bind (or clear) the current item reference for policy decisions.
661
- * Used by item-level profile gates (item `requiredTools` /
662
- * `restrictedTools` / `toolParameters`). Item-level profile material
663
- * also feeds the auto-mode helper — navigating to an item with
664
- * profile settings can flip auto-mode to `"on"` even when the parent
665
- * assessment carries no profile block of its own.
688
+ * File a mounted item's policy settings (`requiredTools`, `restrictedTools`,
689
+ * `toolParameters`) under its canonical id; the returned function withdraws
690
+ * them. They govern only decisions scoped to that item: its own item-level
691
+ * toolbar, and feature decisions asked with the item's scope. A section- or
692
+ * assessment-level toolbar ignores them, and reports each tool on it that
693
+ * they restrict or require as `tool-policy.itemSettingNotApplied`.
694
+ * `<pie-item-scope>` registers its `settings` property through this.
666
695
  */
667
- updateCurrentItemRef(itemRef: AssessmentItemRef | null): void;
696
+ registerItemSettings(itemId: string, settings: ItemSettings): () => void;
668
697
  /**
669
698
  * Override the auto-mode PNP/profile enforcement decision. Pass `"on"` /
670
699
  * `"off"` to pin the mode, or `null` to clear the override and
671
- * return to auto-mode (`"on"` iff the bound assessment / item ref
700
+ * return to auto-mode (`"on"` for a decision whose assessment or item
672
701
  * carries profile material, otherwise `"off"`).
673
702
  */
674
703
  setPnpEnforcement(mode: PnpEnforcementMode | null): void;
@@ -683,14 +712,6 @@ export interface ToolkitCoordinatorApi {
683
712
  * (the returned function detaches).
684
713
  */
685
714
  registerPolicySource(source: PolicySource): () => void;
686
- /**
687
- * Register a host-owned resolver for scoped tool render context.
688
- */
689
- registerToolContextResolver(toolId: string, resolver: ToolContextResolver): () => void;
690
- /**
691
- * Replace all host-owned render-context resolvers.
692
- */
693
- setToolContextResolvers(resolvers: ToolContextResolverMap | null | undefined): void;
694
715
  /**
695
716
  * Whether a host resolver is registered for this tool.
696
717
  */
@@ -703,6 +724,14 @@ export interface ToolkitCoordinatorApi {
703
724
  * Subscribe to resolver registration/removal changes.
704
725
  */
705
726
  onToolContextResolverChange(listener: () => void): () => void;
727
+ /**
728
+ * The registry this coordinator's providers and policy resolve against: the
729
+ * one it was built with or adopted, else the empty one it started with. A
730
+ * toolbar that is not handed a registry reads it here, again on each
731
+ * {@link onPolicyChange}, which an adoption fires. Optional for the same reason
732
+ * as the request seam below: a host-supplied coordinator may predate it.
733
+ */
734
+ getToolRegistry?(): ToolRegistry;
706
735
  /**
707
736
  * The tool-open request seam, optional as a group.
708
737
  *
@@ -724,7 +753,7 @@ export interface ToolkitCoordinatorApi {
724
753
  * Whether a request for this tool would reach a toolbar. A surface asks before
725
754
  * offering the affordance.
726
755
  */
727
- canRequestTool?(toolId: string, level?: ToolOpenRequest["level"]): boolean;
756
+ canRequestTool?(toolId: string, level?: ToolOpenRequest["level"], scopeId?: string): boolean;
728
757
  /**
729
758
  * Subscribe to toolbar registration/removal, so a surface can re-evaluate the
730
759
  * actions it offers.
@@ -1,5 +1,6 @@
1
1
  import type { FormativeFeedbackReveal, FormativeMasteryRollup, FormativeSectionProjection, FormativeTryOutcome } from "@pie-players/pie-players-shared/formative";
2
2
  import type { MediaTimeSource, TimedMediaDegradation, TimedMediaSectionProjection, TimedMediaValidationError } from "@pie-players/pie-players-shared/timed-media";
3
+ import type { SessionCommitReason } from "@pie-players/pie-players-shared/pie";
3
4
  import type { SectionControllerSessionState } from "@pie-players/pie-players-shared/types";
4
5
  /**
5
6
  * Re-exported so every existing import site keeps its specifier. The shape is
@@ -80,16 +81,30 @@ export type SectionControllerItemSessionDataChangedEvent = SectionControllerEven
80
81
  canonicalItemId: string;
81
82
  session: unknown;
82
83
  intent?: string;
84
+ /** The reported completion: the element's own when `elementId` is set. */
83
85
  complete?: boolean;
84
86
  component?: string;
87
+ /** The id of the element that reported, when the change names one. */
88
+ elementId?: string;
89
+ /** The controller input's `sectionId`. */
90
+ sectionId?: string;
91
+ /** Set when the change is a commit at a teardown, navigation or page-hidden seam. */
92
+ sessionCommitReason?: SessionCommitReason;
85
93
  currentItemIndex: number;
86
94
  };
87
95
  export type SectionControllerItemSessionMetaChangedEvent = SectionControllerEventBase & {
88
96
  type: "item-session-meta-changed";
89
97
  itemId: string;
90
98
  canonicalItemId: string;
99
+ /** The reported completion: the element's own when `elementId` is set. */
91
100
  complete?: boolean;
92
101
  component?: string;
102
+ /** The id of the element that reported, when the change names one. */
103
+ elementId?: string;
104
+ /** The controller input's `sectionId`. */
105
+ sectionId?: string;
106
+ /** Set when the change is a commit at a teardown, navigation or page-hidden seam. */
107
+ sessionCommitReason?: SessionCommitReason;
93
108
  currentItemIndex: number;
94
109
  };
95
110
  export type SectionControllerItemSelectedEvent = SectionControllerEventBase & {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * A host-assigned section session, resolved against the session a live
3
+ * controller already holds.
4
+ *
5
+ * The rules are the ones `ItemController.setSession` applies to one item:
6
+ * - a value whose content equals the current session is a no-op, so echoing a
7
+ * snapshot back into the `session` property applies nothing;
8
+ * - an item session with neither a response value nor a response field leaves
9
+ * an item session that holds responses in place. The embedded item player
10
+ * applies the same rule when the section hands the session down, so applying
11
+ * it at section level keeps the controller and its item players from
12
+ * diverging.
13
+ *
14
+ * A controller that holds no session yet takes the value as assigned.
15
+ */
16
+ import type { SectionControllerSessionState } from "@pie-players/pie-players-shared/types";
17
+ /**
18
+ * The session to apply in replace mode, or `null` when applying `next` would
19
+ * change nothing.
20
+ */
21
+ export declare function resolveSectionSessionAssignment(current: SectionControllerSessionState | null | undefined, next: SectionControllerSessionState): SectionControllerSessionState | null;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * A host-assigned section session, resolved against the session a live
3
+ * controller already holds.
4
+ *
5
+ * The rules are the ones `ItemController.setSession` applies to one item:
6
+ * - a value whose content equals the current session is a no-op, so echoing a
7
+ * snapshot back into the `session` property applies nothing;
8
+ * - an item session with neither a response value nor a response field leaves
9
+ * an item session that holds responses in place. The embedded item player
10
+ * applies the same rule when the section hands the session down, so applying
11
+ * it at section level keeps the controller and its item players from
12
+ * diverging.
13
+ *
14
+ * A controller that holds no session yet takes the value as assigned.
15
+ */
16
+ import { hasResponseField, hasResponseValue, } from "@pie-players/pie-players-shared/pie";
17
+ import { structurallyEqual } from "../utils/structural-equality.js";
18
+ function isRecord(value) {
19
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
20
+ }
21
+ /** An entry is canonical (`{ itemIdentifier, session, ... }`) or the raw item session. */
22
+ function itemSessionOf(entry) {
23
+ return isRecord(entry) && isRecord(entry.session) ? entry.session : entry;
24
+ }
25
+ function sameItemSessions(next, current) {
26
+ const nextSessions = isRecord(next) ? next : {};
27
+ const currentSessions = isRecord(current) ? current : {};
28
+ const itemIds = new Set([
29
+ ...Object.keys(nextSessions),
30
+ ...Object.keys(currentSessions),
31
+ ]);
32
+ for (const itemId of itemIds) {
33
+ if (!structurallyEqual(itemSessionOf(nextSessions[itemId]), itemSessionOf(currentSessions[itemId]))) {
34
+ return false;
35
+ }
36
+ }
37
+ return true;
38
+ }
39
+ /**
40
+ * Whether applying `next` would leave `current` as it is. A field `next` leaves
41
+ * out is unchanged, and item sessions compare by their session payload, so a
42
+ * raw `{ id, data }` matches the canonical entry the controller stored for it.
43
+ */
44
+ function sameSessionContent(next, current) {
45
+ for (const [key, value] of Object.entries(next)) {
46
+ if (value === undefined)
47
+ continue;
48
+ if (key === "itemSessions") {
49
+ if (!sameItemSessions(value, current.itemSessions))
50
+ return false;
51
+ continue;
52
+ }
53
+ if (!structurallyEqual(value, current[key])) {
54
+ return false;
55
+ }
56
+ }
57
+ return true;
58
+ }
59
+ /**
60
+ * The session to apply in replace mode, or `null` when applying `next` would
61
+ * change nothing.
62
+ */
63
+ export function resolveSectionSessionAssignment(current, next) {
64
+ if (!current)
65
+ return next;
66
+ const itemSessions = isRecord(next.itemSessions)
67
+ ? { ...next.itemSessions }
68
+ : {};
69
+ const currentItemSessions = isRecord(current.itemSessions)
70
+ ? current.itemSessions
71
+ : {};
72
+ for (const [itemId, currentEntry] of Object.entries(currentItemSessions)) {
73
+ if (!hasResponseValue(itemSessionOf(currentEntry)))
74
+ continue;
75
+ const assigned = itemSessionOf(itemSessions[itemId]);
76
+ if (hasResponseValue(assigned) || hasResponseField(assigned))
77
+ continue;
78
+ itemSessions[itemId] = currentEntry;
79
+ }
80
+ const resolved = { ...next, itemSessions };
81
+ return sameSessionContent(resolved, current) ? null : resolved;
82
+ }
@@ -21,8 +21,6 @@
21
21
  * Part of PIE Assessment Toolkit.
22
22
  */
23
23
  import type { CatalogCardPayload, MediaFragmentRange, MediaSource } from "@pie-players/pie-players-shared/types";
24
- /** Catalog type token for spoken alternates. Matches QTI 3's `support` value. */
25
- export declare const SPOKEN_CATALOG_TYPE = "spoken";
26
24
  /** A validated recorded spoken alternate, flattened for playback. */
27
25
  export interface SpokenAudioMedia {
28
26
  /**
@@ -20,9 +20,7 @@
20
20
  *
21
21
  * Part of PIE Assessment Toolkit.
22
22
  */
23
- import { isUnsupportedMediaAssetVersion, normalizeMediaFragment, normalizeMediaSources, trimmedOrUndefined, } from "./catalog-media.js";
24
- /** Catalog type token for spoken alternates. Matches QTI 3's `support` value. */
25
- export const SPOKEN_CATALOG_TYPE = "spoken";
23
+ import { isUnsupportedMediaAssetVersion, normalizeMediaFragment, normalizeMediaSources, trimmedOrUndefined, } from "@pie-players/pie-players-shared/media";
26
24
  /**
27
25
  * Validate a `spoken` card's payload into something playable, or `null`.
28
26
  *