@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
@@ -0,0 +1,188 @@
1
+ import { ContextProvider, } from "@pie-players/pie-context";
2
+ import { assessmentToolkitRegionScopeContext, assessmentToolkitShellContext, } from "../context/assessment-toolkit-context.js";
3
+ import { catalogSourceSignature, } from "../services/catalog-owner.js";
4
+ import { PIE_INTERNAL_CONTENT_LOADED_EVENT } from "./registration-events.js";
5
+ import { createShellRegistrationDispatcher, shellEventTarget, } from "./shell-registration.js";
6
+ import { connectHostRuntimeContext, dispatchCrossBoundaryEvent, } from "./tool-host-contract.js";
7
+ /**
8
+ * The newest events kept until a runtime answers; an older one is dropped. A
9
+ * scope in plain mode sends only load and error events, a section shell its
10
+ * session events too.
11
+ */
12
+ const MAX_HELD_SHELL_EVENTS = 50;
13
+ function provide(host, context, value) {
14
+ const provider = new ContextProvider(host, { context, initialValue: value });
15
+ provider.connect();
16
+ return { provider, value };
17
+ }
18
+ function republish(provided, value, same) {
19
+ if (same(provided.value, value))
20
+ return;
21
+ provided.value = value;
22
+ provided.provider.setValue(value);
23
+ }
24
+ const sameItem = (a, b, kind) => {
25
+ if (a === b)
26
+ return true;
27
+ if (!a || !b)
28
+ return false;
29
+ if (a.id !== b.id)
30
+ return false;
31
+ const catalogs = catalogSourceSignature(a, kind);
32
+ return (catalogs !== null &&
33
+ catalogs === catalogSourceSignature(b, kind));
34
+ };
35
+ const sameShellContext = (a, b) => a.kind === b.kind &&
36
+ a.itemId === b.itemId &&
37
+ a.canonicalItemId === b.canonicalItemId &&
38
+ a.contentKind === b.contentKind &&
39
+ a.regionPolicy === b.regionPolicy &&
40
+ a.scopeElement === b.scopeElement &&
41
+ sameItem(a.item, b.item, a.kind);
42
+ const sameRegionScope = (a, b) => a.scopeElement === b.scopeElement &&
43
+ a.ttsHighlightTargetResolver === b.ttsHighlightTargetResolver;
44
+ const setAttributeIfChanged = (host, name, value) => {
45
+ if (host.getAttribute(name) !== value)
46
+ host.setAttribute(name, value);
47
+ };
48
+ /**
49
+ * A shell's publication of its content: the identity, as the shell context the
50
+ * tools inside it read, the region they act on, as the region scope, and the
51
+ * content itself, registered with the runtime, which files its accessibility
52
+ * catalogs under it. The section player's item and passage shells and
53
+ * `<pie-item-scope>` all publish through this, so a tool resolves its target
54
+ * and its catalogs the same way inside any of them.
55
+ *
56
+ * Content republished unchanged arrives as a new object, the composition being
57
+ * deep reactive state, so items compare by id and catalogs, the measure the
58
+ * registration applies.
59
+ *
60
+ * The registration waits for the runtime the shell sits in to answer through
61
+ * `assessmentToolkitHostRuntimeContext`, and carries that runtime's id, which
62
+ * the runtime claims it by. A shell mounted before its toolkit, which a host
63
+ * composing the elements itself can do, would otherwise register with no one
64
+ * listening, and nothing replays a registration. The document's context root
65
+ * replays the request instead, once the toolkit's provider connects. A
66
+ * different runtime answering later, a nearer toolkit taking over, moves the
67
+ * registration: the old runtime is told to unregister by its id, and the new
68
+ * one gets the registration and the content's last load, which only the old
69
+ * one heard.
70
+ */
71
+ export function createShellScope() {
72
+ const registration = createShellRegistrationDispatcher();
73
+ const contextVersion = Date.now();
74
+ let host = null;
75
+ let shell = null;
76
+ let region = null;
77
+ let stopFindingRuntime = null;
78
+ let runtime = null;
79
+ let identity = null;
80
+ let held = [];
81
+ let loaded = null;
82
+ const syncRegistration = () => {
83
+ if (runtime !== null)
84
+ registration.sync(identity, runtime);
85
+ };
86
+ function send(type, detail) {
87
+ if (type === PIE_INTERNAL_CONTENT_LOADED_EVENT)
88
+ loaded = detail;
89
+ if (host && runtime !== null) {
90
+ dispatchCrossBoundaryEvent(shellEventTarget(host, runtime), type, {
91
+ ...detail,
92
+ runtimeId: runtime.runtimeId,
93
+ });
94
+ return;
95
+ }
96
+ held.push({ type, detail });
97
+ if (held.length > MAX_HELD_SHELL_EVENTS)
98
+ held.shift();
99
+ }
100
+ // The subscription re-answers whenever the runtime republishes its context,
101
+ // so only a different id means anything here.
102
+ function onRuntime(value) {
103
+ if (value.runtimeId === runtime?.runtimeId)
104
+ return;
105
+ const moved = runtime !== null;
106
+ runtime = { runtimeId: value.runtimeId, eventTarget: value.eventTarget };
107
+ syncRegistration();
108
+ const pending = held;
109
+ held = [];
110
+ for (const event of pending)
111
+ send(event.type, event.detail);
112
+ if (moved && loaded)
113
+ send(PIE_INTERNAL_CONTENT_LOADED_EVENT, loaded);
114
+ }
115
+ function disconnect() {
116
+ for (const provided of [shell, region])
117
+ provided?.provider.disconnect();
118
+ stopFindingRuntime?.();
119
+ stopFindingRuntime = null;
120
+ runtime = null;
121
+ held = [];
122
+ loaded = null;
123
+ identity = null;
124
+ shell = null;
125
+ region = null;
126
+ host = null;
127
+ }
128
+ function publish(state) {
129
+ if (!state) {
130
+ registration.sync(null);
131
+ disconnect();
132
+ return;
133
+ }
134
+ if (state.host !== host)
135
+ disconnect();
136
+ host = state.host;
137
+ const canonicalItemId = state.canonicalItemId || state.itemId;
138
+ const regionPolicy = state.regionPolicy || "default";
139
+ const scopeElement = state.scopeElement || state.host;
140
+ const shellValue = {
141
+ kind: state.kind,
142
+ itemId: state.itemId,
143
+ canonicalItemId,
144
+ contentKind: state.contentKind,
145
+ regionPolicy,
146
+ scopeElement,
147
+ item: state.item ?? null,
148
+ contextVersion,
149
+ };
150
+ if (shell)
151
+ republish(shell, shellValue, sameShellContext);
152
+ else
153
+ shell = provide(host, assessmentToolkitShellContext, shellValue);
154
+ const regionValue = {
155
+ scopeElement,
156
+ ttsHighlightTargetResolver: state.ttsHighlightTargetResolver ?? null,
157
+ };
158
+ if (region)
159
+ republish(region, regionValue, sameRegionScope);
160
+ else
161
+ region = provide(host, assessmentToolkitRegionScopeContext, regionValue);
162
+ setAttributeIfChanged(host, "data-item-id", state.itemId);
163
+ setAttributeIfChanged(host, "data-canonical-item-id", canonicalItemId);
164
+ setAttributeIfChanged(host, "data-pie-shell-root", state.kind);
165
+ setAttributeIfChanged(host, "data-region-policy", regionPolicy);
166
+ const previousIdentity = identity;
167
+ identity = state.itemId
168
+ ? {
169
+ kind: state.kind,
170
+ host,
171
+ itemId: state.itemId,
172
+ canonicalItemId,
173
+ contentKind: state.contentKind,
174
+ item: state.item ?? null,
175
+ settings: state.settings ?? null,
176
+ }
177
+ : null;
178
+ if (previousIdentity &&
179
+ (previousIdentity.itemId !== identity?.itemId ||
180
+ previousIdentity.canonicalItemId !== identity?.canonicalItemId)) {
181
+ loaded = null;
182
+ }
183
+ // Answers at once when the runtime is already there.
184
+ stopFindingRuntime ??= connectHostRuntimeContext(host, onRuntime);
185
+ syncRegistration();
186
+ }
187
+ return { publish, retire: registration.retire, disconnect, send };
188
+ }
@@ -1,5 +1,6 @@
1
- import type { UnknownContext } from "@pie-players/pie-context";
2
- import { type RegionScopeContextListener, type RuntimeContextListener, type ShellContextListener } from "../context/runtime-context-consumer.js";
1
+ import { type AssessmentToolkitHostRuntimeContext, type AssessmentToolkitRegionScopeContext, type AssessmentToolkitRuntimeContext, type AssessmentToolkitShellContext } from "../context/assessment-toolkit-context.js";
2
+ import type { ZIndexLayer } from "../services/ToolCoordinator.js";
3
+ import type { ToolCoordinatorApi } from "../services/interfaces.js";
3
4
  type BaseEventInit = Pick<EventInit, "bubbles" | "composed" | "cancelable">;
4
5
  /**
5
6
  * Contract helper for all toolkit/tool events that must cross custom-element boundaries.
@@ -11,19 +12,36 @@ export declare function createCrossBoundaryEvent<T>(name: string, detail: T, ini
11
12
  export declare function dispatchCrossBoundaryEvent<T>(target: EventTarget, name: string, detail: T, init?: Partial<BaseEventInit>): boolean;
12
13
  /**
13
14
  * Shared runtime-context connection contract for tools and shells.
14
- * Uses retry + provider announcements so late providers are tolerated.
15
+ * A provider that connects after the tool answers it through the document's
16
+ * context root.
15
17
  */
16
- export declare function connectToolRuntimeContext(host: HTMLElement, onValue: RuntimeContextListener): () => void;
18
+ export declare function connectToolRuntimeContext(host: HTMLElement, onValue: (value: AssessmentToolkitRuntimeContext) => void): () => void;
17
19
  /**
18
20
  * Shared shell-context connection contract for tools needing item/passage scope.
19
21
  */
20
- export declare function connectToolShellContext(host: HTMLElement, onValue: ShellContextListener): () => void;
22
+ export declare function connectToolShellContext(host: HTMLElement, onValue: (value: AssessmentToolkitShellContext) => void): () => void;
21
23
  /**
22
24
  * Shared region-scope context contract for tools targeting content subregions.
23
25
  */
24
- export declare function connectToolRegionScopeContext(host: HTMLElement, onValue: RegionScopeContextListener): () => void;
25
- /**
26
- * Guard utility: narrows unknown context payloads when needed by callers.
27
- */
28
- export declare function isContextValueDefined<T extends UnknownContext>(value: unknown): value is T["__context__"];
26
+ export declare function connectToolRegionScopeContext(host: HTMLElement, onValue: (value: AssessmentToolkitRegionScopeContext) => void): () => void;
27
+ /** Connects a shell or toolkit element to the host runtime context above it. */
28
+ export declare function connectHostRuntimeContext(host: HTMLElement, onValue: (value: AssessmentToolkitHostRuntimeContext) => void): () => void;
29
+ /** A tool's registration with the coordinator in its runtime context. */
30
+ export interface ToolCoordinatorRegistration {
31
+ /**
32
+ * Registers `toolId` with `coordinator`, first unregistering it from the
33
+ * coordinator or id it was registered under. The coordinator arrives through
34
+ * a republished runtime context, so a new instance can replace the old one
35
+ * mid-session; a one-shot registration would leave z-index, `bringToFront`
36
+ * and visibility-restore bound to the dead one. Call it from an effect on
37
+ * both; it does nothing until both are set, or when neither changed.
38
+ */
39
+ sync(coordinator: ToolCoordinatorApi | null | undefined, toolId: string | null | undefined): void;
40
+ /**
41
+ * Unregisters from the coordinator the registration was made against, which
42
+ * is not necessarily the one currently in context.
43
+ */
44
+ release(): void;
45
+ }
46
+ export declare function createToolCoordinatorRegistration(name: string, layer: ZIndexLayer): ToolCoordinatorRegistration;
29
47
  export {};
@@ -1,4 +1,5 @@
1
- import { connectAssessmentToolkitRegionScopeContext, connectAssessmentToolkitRuntimeContext, connectAssessmentToolkitShellContext, } from "../context/runtime-context-consumer.js";
1
+ import { connectContextWithRetry } from "@pie-players/pie-context";
2
+ import { assessmentToolkitHostRuntimeContext, assessmentToolkitRegionScopeContext, assessmentToolkitRuntimeContext, assessmentToolkitShellContext, } from "../context/assessment-toolkit-context.js";
2
3
  const CROSS_BOUNDARY_EVENT_INIT = {
3
4
  bubbles: true,
4
5
  composed: true,
@@ -22,26 +23,46 @@ export function dispatchCrossBoundaryEvent(target, name, detail, init = {}) {
22
23
  }
23
24
  /**
24
25
  * Shared runtime-context connection contract for tools and shells.
25
- * Uses retry + provider announcements so late providers are tolerated.
26
+ * A provider that connects after the tool answers it through the document's
27
+ * context root.
26
28
  */
27
29
  export function connectToolRuntimeContext(host, onValue) {
28
- return connectAssessmentToolkitRuntimeContext(host, onValue);
30
+ return connectContextWithRetry(host, assessmentToolkitRuntimeContext, onValue);
29
31
  }
30
32
  /**
31
33
  * Shared shell-context connection contract for tools needing item/passage scope.
32
34
  */
33
35
  export function connectToolShellContext(host, onValue) {
34
- return connectAssessmentToolkitShellContext(host, onValue);
36
+ return connectContextWithRetry(host, assessmentToolkitShellContext, onValue);
35
37
  }
36
38
  /**
37
39
  * Shared region-scope context contract for tools targeting content subregions.
38
40
  */
39
41
  export function connectToolRegionScopeContext(host, onValue) {
40
- return connectAssessmentToolkitRegionScopeContext(host, onValue);
42
+ return connectContextWithRetry(host, assessmentToolkitRegionScopeContext, onValue);
41
43
  }
42
- /**
43
- * Guard utility: narrows unknown context payloads when needed by callers.
44
- */
45
- export function isContextValueDefined(value) {
46
- return value !== null && value !== undefined;
44
+ /** Connects a shell or toolkit element to the host runtime context above it. */
45
+ export function connectHostRuntimeContext(host, onValue) {
46
+ return connectContextWithRetry(host, assessmentToolkitHostRuntimeContext, onValue);
47
+ }
48
+ export function createToolCoordinatorRegistration(name, layer) {
49
+ let registered = null;
50
+ function release() {
51
+ registered?.coordinator.unregisterTool(registered.toolId);
52
+ registered = null;
53
+ }
54
+ return {
55
+ sync(coordinator, toolId) {
56
+ if (!coordinator || !toolId)
57
+ return;
58
+ if (registered?.coordinator === coordinator &&
59
+ registered.toolId === toolId) {
60
+ return;
61
+ }
62
+ release();
63
+ coordinator.registerTool(toolId, name, undefined, layer);
64
+ registered = { coordinator, toolId };
65
+ },
66
+ release,
67
+ };
47
68
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A toolkit stops every registration it claims, so one that reaches the
3
+ * document went unclaimed: its item's catalogs are not filed and it does not
4
+ * count toward its section's loading. Installed from a toolkit's host at
5
+ * connect, once per document across every copy of the toolkit.
6
+ */
7
+ export declare function watchForUnclaimedRegistrations(doc: Document | null | undefined): void;
@@ -0,0 +1,23 @@
1
+ import { warnOncePerDocument } from "./page-warnings.js";
2
+ import { PIE_REGISTER_EVENT, } from "./registration-events.js";
3
+ /**
4
+ * A toolkit stops every registration it claims, so one that reaches the
5
+ * document went unclaimed: its item's catalogs are not filed and it does not
6
+ * count toward its section's loading. Installed from a toolkit's host at
7
+ * connect, once per document across every copy of the toolkit.
8
+ */
9
+ export function watchForUnclaimedRegistrations(doc) {
10
+ if (!doc)
11
+ return;
12
+ const slot = Symbol.for("pie.assessmentToolkit.unclaimedRegistrationWatch");
13
+ const slots = doc;
14
+ if (slots[slot])
15
+ return;
16
+ slots[slot] = true;
17
+ doc.addEventListener(PIE_REGISTER_EVENT, (event) => {
18
+ const detail = event
19
+ .detail;
20
+ const itemId = typeof detail?.itemId === "string" ? detail.itemId : "";
21
+ warnOncePerDocument(doc, "unclaimedRegistration", `[pie-assessment-toolkit] Item "${itemId}" registered, but no toolkit claimed the registration, so its accessibility catalogs are not filed and it does not count toward its section's loading. Render its shell or scope inside the <pie-assessment-toolkit> that answered it. Reported once per page.`);
22
+ });
23
+ }
@@ -25,7 +25,9 @@ export declare class ElementToolStateStore {
25
25
  private onStateChange;
26
26
  /**
27
27
  * Generates a globally unique element ID by combining assessment, section, item, and element IDs.
28
- * Format: `${assessmentId}:${sectionId}:${itemId}:${elementId}`
28
+ * Format: `${assessmentId}:${sectionId}:${itemId}:${elementId}`, each part with
29
+ * `%` written as `%25` and `:` as `%3A`, so an id containing `:` round-trips
30
+ * and cannot collide with another split of the same characters.
29
31
  *
30
32
  * @example
31
33
  * getGlobalElementId('demo-assessment', 'section-1', 'q1', 'mc1')
@@ -48,7 +50,7 @@ export declare class ElementToolStateStore {
48
50
  * @param toolId Tool identifier (e.g., 'answerEliminator', 'flagging')
49
51
  * @param state Tool-specific state object
50
52
  */
51
- setState(globalElementId: string, toolId: string, state: any): void;
53
+ setState(globalElementId: string, toolId: string, state: unknown): void;
52
54
  /**
53
55
  * Gets tool state for a specific element.
54
56
  *
@@ -56,29 +58,30 @@ export declare class ElementToolStateStore {
56
58
  * @param toolId Tool identifier
57
59
  * @returns Tool state or undefined if not found
58
60
  */
59
- getState(globalElementId: string, toolId: string): any | undefined;
61
+ getState(globalElementId: string, toolId: string): unknown;
60
62
  /**
61
63
  * Gets all tool states for a specific element.
62
64
  *
63
65
  * @param globalElementId Composite key identifying the element
64
66
  * @returns Object with tool states keyed by toolId
65
67
  */
66
- getElementState(globalElementId: string): Record<string, any>;
68
+ getElementState(globalElementId: string): Record<string, unknown>;
67
69
  /**
68
70
  * Gets all tool states for all elements.
69
71
  * Used for persistence and debugging.
70
72
  *
71
73
  * @returns Nested object: { globalElementId: { toolId: state } }
72
74
  */
73
- getAllState(): Record<string, Record<string, any>>;
75
+ getAllState(): Record<string, Record<string, unknown>>;
74
76
  /**
75
77
  * Subscribes to state changes.
76
- * Callback is invoked whenever any element's tool state changes.
78
+ * Callback is invoked whenever any element's tool state changes, including
79
+ * a `loadState` restore, with a copy of the state that it may keep.
77
80
  *
78
81
  * @param callback Function to call on state changes
79
82
  * @returns Unsubscribe function
80
83
  */
81
- subscribe(callback: (state: Map<string, Map<string, any>>) => void): () => void;
84
+ subscribe(callback: (state: Map<string, Map<string, unknown>>) => void): () => void;
82
85
  private _notifyListeners;
83
86
  /**
84
87
  * Sets a callback to be invoked on state changes for persistence integration.
@@ -86,15 +89,16 @@ export declare class ElementToolStateStore {
86
89
  *
87
90
  * @param callback Function to call with serialized state on changes
88
91
  */
89
- setOnStateChange(callback: (state: Record<string, Record<string, any>>) => void): void;
92
+ setOnStateChange(callback: (state: Record<string, Record<string, unknown>>) => void): void;
90
93
  private _notifyStateChange;
91
94
  /**
92
95
  * Loads tool state from serialized format.
93
- * Used to restore state from localStorage or server.
96
+ * Used to restore state from localStorage or server. Subscribers are
97
+ * notified; the persistence callback is not, so a restore never writes back.
94
98
  *
95
99
  * @param state Serialized state object
96
100
  */
97
- loadState(state: Record<string, Record<string, any>>): void;
101
+ loadState(state: Record<string, Record<string, unknown>>): void;
98
102
  /**
99
103
  * Clears all tool state for a specific element.
100
104
  *
@@ -13,20 +13,30 @@
13
13
  * - Optional persistence callback integration
14
14
  * - Generic solution for any tool
15
15
  */
16
+ function escapeIdPart(part) {
17
+ return part.replace(/%/g, "%25").replace(/:/g, "%3A");
18
+ }
19
+ function unescapeIdPart(part) {
20
+ return part.replace(/%(25|3A)/g, (_, code) => (code === "25" ? "%" : ":"));
21
+ }
16
22
  export class ElementToolStateStore {
17
23
  elementStates = new Map();
18
24
  listeners = new Set();
19
25
  onStateChange = null;
20
26
  /**
21
27
  * Generates a globally unique element ID by combining assessment, section, item, and element IDs.
22
- * Format: `${assessmentId}:${sectionId}:${itemId}:${elementId}`
28
+ * Format: `${assessmentId}:${sectionId}:${itemId}:${elementId}`, each part with
29
+ * `%` written as `%25` and `:` as `%3A`, so an id containing `:` round-trips
30
+ * and cannot collide with another split of the same characters.
23
31
  *
24
32
  * @example
25
33
  * getGlobalElementId('demo-assessment', 'section-1', 'q1', 'mc1')
26
34
  * // Returns: "demo-assessment:section-1:q1:mc1"
27
35
  */
28
36
  getGlobalElementId(assessmentId, sectionId, itemId, elementId) {
29
- return `${assessmentId}:${sectionId}:${itemId}:${elementId}`;
37
+ return [assessmentId, sectionId, itemId, elementId]
38
+ .map(escapeIdPart)
39
+ .join(":");
30
40
  }
31
41
  /**
32
42
  * Parses a global element ID into its component parts.
@@ -39,12 +49,8 @@ export class ElementToolStateStore {
39
49
  const parts = globalElementId.split(":");
40
50
  if (parts.length !== 4)
41
51
  return null;
42
- return {
43
- assessmentId: parts[0],
44
- sectionId: parts[1],
45
- itemId: parts[2],
46
- elementId: parts[3],
47
- };
52
+ const [assessmentId, sectionId, itemId, elementId] = parts.map(unescapeIdPart);
53
+ return { assessmentId, sectionId, itemId, elementId };
48
54
  }
49
55
  /**
50
56
  * Sets tool state for a specific element.
@@ -101,7 +107,8 @@ export class ElementToolStateStore {
101
107
  }
102
108
  /**
103
109
  * Subscribes to state changes.
104
- * Callback is invoked whenever any element's tool state changes.
110
+ * Callback is invoked whenever any element's tool state changes, including
111
+ * a `loadState` restore, with a copy of the state that it may keep.
105
112
  *
106
113
  * @param callback Function to call on state changes
107
114
  * @returns Unsubscribe function
@@ -111,8 +118,19 @@ export class ElementToolStateStore {
111
118
  return () => this.listeners.delete(callback);
112
119
  }
113
120
  _notifyListeners() {
121
+ if (this.listeners.size === 0)
122
+ return;
123
+ const snapshot = new Map([...this.elementStates].map(([key, toolStates]) => [
124
+ key,
125
+ new Map(toolStates),
126
+ ]));
114
127
  for (const listener of this.listeners) {
115
- listener(this.elementStates);
128
+ try {
129
+ listener(snapshot);
130
+ }
131
+ catch (error) {
132
+ console.warn("[ElementToolStateStore] listener failed:", error);
133
+ }
116
134
  }
117
135
  }
118
136
  /**
@@ -125,13 +143,19 @@ export class ElementToolStateStore {
125
143
  this.onStateChange = callback;
126
144
  }
127
145
  _notifyStateChange() {
128
- if (this.onStateChange) {
146
+ if (!this.onStateChange)
147
+ return;
148
+ try {
129
149
  this.onStateChange(this.getAllState());
130
150
  }
151
+ catch (error) {
152
+ console.warn("[ElementToolStateStore] state-change callback failed:", error);
153
+ }
131
154
  }
132
155
  /**
133
156
  * Loads tool state from serialized format.
134
- * Used to restore state from localStorage or server.
157
+ * Used to restore state from localStorage or server. Subscribers are
158
+ * notified; the persistence callback is not, so a restore never writes back.
135
159
  *
136
160
  * @param state Serialized state object
137
161
  */
@@ -174,7 +198,7 @@ export class ElementToolStateStore {
174
198
  * @param sectionId Section identifier
175
199
  */
176
200
  clearSection(assessmentId, sectionId) {
177
- const prefix = `${assessmentId}:${sectionId}:`;
201
+ const prefix = `${escapeIdPart(assessmentId)}:${escapeIdPart(sectionId)}:`;
178
202
  const keysToDelete = [];
179
203
  for (const key of this.elementStates.keys()) {
180
204
  if (key.startsWith(prefix)) {
@@ -51,53 +51,50 @@ export interface Annotation {
51
51
  color: HighlightColor;
52
52
  timestamp: number;
53
53
  }
54
- /**
55
- * Configuration for HighlightCoordinator
56
- * Currently empty but allows future extension without breaking changes
57
- */
58
- export type HighlightCoordinatorConfig = Record<string, never>;
59
54
  export declare class HighlightCoordinator implements HighlightCoordinatorApi {
60
- private config;
61
- private ttsWordHighlight;
62
- private ttsSentenceHighlight;
55
+ private ttsWordRanges;
56
+ private ttsSentenceRanges;
63
57
  private ttsWordElementHighlights;
64
58
  private ttsSentenceElementHighlights;
65
59
  private annotations;
66
- private colorHighlights;
60
+ private ttsStyleSource;
67
61
  private nextAnnotationId;
68
62
  private supported;
69
63
  private rangeSerializer;
70
64
  private themeObserver;
71
- private explicitTTSColorOverride;
72
- constructor(config?: HighlightCoordinatorConfig);
65
+ constructor();
73
66
  private setupThemeObservation;
74
67
  private parseColor;
75
68
  private relativeLuminance;
76
69
  private contrastRatio;
77
70
  private blend;
78
71
  private resolveAdaptiveTTSStyle;
79
- private applyAdaptiveTTSStyle;
80
72
  /**
81
- * Initialize CSS Custom Highlights
73
+ * Adapt the TTS colors to `sourceEl`, the element being highlighted. Without
74
+ * one, to the element highlighted last on the page while it stays connected,
75
+ * else to the document.
82
76
  */
77
+ private applyAdaptiveTTSStyle;
78
+ /** Register the shared highlights, unless another coordinator already did. */
83
79
  private initializeHighlights;
84
80
  /**
85
81
  * Register CSS styles for highlights
86
82
  */
87
83
  private registerStyles;
88
84
  /**
89
- * Highlight a word for TTS (temporary)
85
+ * Highlight the word being read (temporary), replacing the previous one.
86
+ * Every range paints, so a word split across inline elements highlights
87
+ * whole. A range that covers elements also marks the replaced elements in it
88
+ * (svg, img, canvas), which a CSS range cannot paint.
90
89
  *
91
- * @param textNode Text node containing the word
92
- * @param startOffset Start position in text node
93
- * @param endOffset End position in text node
90
+ * @param ranges The word's ranges: one per tree it spans
94
91
  */
95
- highlightTTSWord(textNode: Text, startOffset: number, endOffset: number): void;
92
+ highlightTTSWord(ranges: Range[]): void;
96
93
  /**
97
94
  * Highlight a single element as the active TTS word (temporary).
98
95
  *
99
- * Unlike {@link highlightTTSWord} (which paints a CSS range over a text node)
100
- * this marks the element itself via `data-pie-tts-word-element`. It is used
96
+ * Unlike {@link highlightTTSWord} (which paints CSS ranges over text) this
97
+ * marks the element itself via `data-pie-tts-word-element`. It is used
101
98
  * for atomic targets that have no direct text node to range over — most
102
99
  * notably MathJax CHTML tokens (e.g. `<mjx-mi><mjx-c/></mjx-mi>`, whose glyph
103
100
  * lives in a font-driven pseudo-element a CSS range cannot paint) and
@@ -200,10 +197,8 @@ export declare class HighlightCoordinator implements HighlightCoordinatorApi {
200
197
  timestamp?: number;
201
198
  }>, root?: Element): number;
202
199
  /**
203
- * Highlight a text range (interface method)
204
- *
205
- * Generic method that adapts to the specific highlight type.
206
- * For more control, use the specific methods like highlightTTSWord().
200
+ * Highlight a range as the given type: the TTS types paint as
201
+ * {@link highlightTTSWord} and {@link highlightTTSSentence} do.
207
202
  */
208
203
  highlightRange(range: Range, type: HighlightType, color?: HighlightColor): void;
209
204
  /**
@@ -226,11 +221,10 @@ export declare class HighlightCoordinator implements HighlightCoordinatorApi {
226
221
  */
227
222
  updateTTSHighlightStyle(color: string, opacity: number): void;
228
223
  /**
229
- * Cleanup this coordinator's highlights and observer.
224
+ * Remove this coordinator's ranges and stop its observer.
230
225
  *
231
- * The stylesheet is process-global and shared by every live coordinator, so
232
- * removing it here would break a replacement or sibling coordinator. Keep the
233
- * single bounded style element installed for the lifetime of the document.
226
+ * The stylesheet and the registered highlights are page-wide and shared by
227
+ * every live coordinator, so they stay installed for the document's lifetime.
234
228
  */
235
229
  destroy(): void;
236
230
  }