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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (228) hide show
  1. package/README.md +349 -313
  2. package/dist/attempt/TestSession.d.ts +1 -20
  3. package/dist/attempt/TestSession.js +8 -99
  4. package/dist/components/ItemScope.custom-element.d.ts +1 -0
  5. package/dist/components/ItemScope.custom-element.js +1 -0
  6. package/dist/components/ItemToolBar.custom-element.js +1 -1
  7. package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
  8. package/dist/components/SectionToolBar.custom-element.js +1 -1
  9. package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
  10. package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-97hx60z4.js} +1 -1
  11. package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
  12. package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
  13. package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
  14. package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
  15. package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
  16. package/dist/components/item-scope-element.d.ts +2 -0
  17. package/dist/components/item-scope-element.js +2 -0
  18. package/dist/context/assessment-toolkit-context.d.ts +27 -6
  19. package/dist/index.d.ts +22 -47
  20. package/dist/index.js +12 -36
  21. package/dist/policy/core/PolicySource.d.ts +1 -1
  22. package/dist/policy/core/ToolPolicyEngine.d.ts +77 -32
  23. package/dist/policy/core/ToolPolicyEngine.js +136 -46
  24. package/dist/policy/core/compose-decision.d.ts +28 -7
  25. package/dist/policy/core/compose-decision.js +75 -54
  26. package/dist/policy/core/decision-types.d.ts +65 -34
  27. package/dist/policy/core/feature-decision.d.ts +16 -9
  28. package/dist/policy/core/feature-decision.js +14 -8
  29. package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
  30. package/dist/policy/core/pnp-policy-inputs.js +28 -56
  31. package/dist/policy/core/provenance.d.ts +9 -4
  32. package/dist/policy/core/provenance.js +1 -1
  33. package/dist/policy/engine.d.ts +7 -23
  34. package/dist/policy/engine.js +5 -20
  35. package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
  36. package/dist/policy/sources/PnpPolicySource.js +117 -118
  37. package/dist/runtime/SectionControllerBinding.d.ts +279 -0
  38. package/dist/runtime/SectionControllerBinding.js +450 -0
  39. package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
  40. package/dist/runtime/SectionRuntimeEngine.js +30 -372
  41. package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
  42. package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
  43. package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
  44. package/dist/runtime/adapter/dom-event-bridge.js +17 -44
  45. package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
  46. package/dist/runtime/adapter/subscriber-fanout.js +1 -2
  47. package/dist/runtime/catalog-context.d.ts +19 -0
  48. package/dist/runtime/catalog-context.js +28 -0
  49. package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
  50. package/dist/runtime/composition-emit-scheduler.js +9 -15
  51. package/dist/runtime/content-language.d.ts +32 -0
  52. package/dist/runtime/content-language.js +46 -0
  53. package/dist/runtime/content-region.d.ts +17 -0
  54. package/dist/runtime/content-region.js +29 -0
  55. package/dist/runtime/content-registration.d.ts +13 -0
  56. package/dist/runtime/content-registration.js +25 -0
  57. package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
  58. package/dist/runtime/core/SectionEngineCore.js +2 -2
  59. package/dist/runtime/core/cohort.d.ts +1 -1
  60. package/dist/runtime/core/cohort.js +1 -1
  61. package/dist/runtime/core/engine-input.d.ts +18 -30
  62. package/dist/runtime/core/engine-input.js +11 -12
  63. package/dist/runtime/core/engine-output.d.ts +16 -40
  64. package/dist/runtime/core/engine-output.js +9 -21
  65. package/dist/runtime/core/engine-readiness.d.ts +5 -37
  66. package/dist/runtime/core/engine-readiness.js +8 -41
  67. package/dist/runtime/core/engine-resolver.d.ts +46 -74
  68. package/dist/runtime/core/engine-resolver.js +18 -43
  69. package/dist/runtime/core/engine-state.d.ts +28 -36
  70. package/dist/runtime/core/engine-state.js +18 -21
  71. package/dist/runtime/core/engine-transition.d.ts +14 -19
  72. package/dist/runtime/core/engine-transition.js +71 -51
  73. package/dist/runtime/engine.d.ts +16 -19
  74. package/dist/runtime/engine.js +12 -19
  75. package/dist/runtime/page-warnings.d.ts +13 -0
  76. package/dist/runtime/page-warnings.js +22 -0
  77. package/dist/runtime/policy-input-forwarding.d.ts +25 -0
  78. package/dist/runtime/policy-input-forwarding.js +25 -0
  79. package/dist/runtime/registration-events.d.ts +11 -0
  80. package/dist/runtime/runtime-event-claim.d.ts +8 -0
  81. package/dist/runtime/runtime-event-claim.js +15 -0
  82. package/dist/runtime/shell-event-bridge.d.ts +45 -0
  83. package/dist/runtime/shell-event-bridge.js +127 -0
  84. package/dist/runtime/shell-registration.d.ts +73 -0
  85. package/dist/runtime/shell-registration.js +95 -0
  86. package/dist/runtime/shell-scope.d.ts +68 -0
  87. package/dist/runtime/shell-scope.js +188 -0
  88. package/dist/runtime/tool-host-contract.d.ts +8 -10
  89. package/dist/runtime/tool-host-contract.js +11 -11
  90. package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
  91. package/dist/runtime/unclaimed-registration-watch.js +23 -0
  92. package/dist/services/ElementToolStateStore.d.ts +14 -10
  93. package/dist/services/ElementToolStateStore.js +37 -13
  94. package/dist/services/HighlightCoordinator.d.ts +21 -21
  95. package/dist/services/HighlightCoordinator.js +431 -325
  96. package/dist/services/RangeSerializer.d.ts +10 -1
  97. package/dist/services/RangeSerializer.js +87 -14
  98. package/dist/services/TTSService.d.ts +117 -70
  99. package/dist/services/TTSService.js +668 -578
  100. package/dist/services/ToolCoordinator.d.ts +43 -60
  101. package/dist/services/ToolCoordinator.js +164 -206
  102. package/dist/services/ToolRegistry.d.ts +74 -57
  103. package/dist/services/ToolRegistry.js +104 -114
  104. package/dist/services/ToolkitCoordinator.d.ts +183 -189
  105. package/dist/services/ToolkitCoordinator.js +477 -431
  106. package/dist/services/audio-handoff.d.ts +6 -7
  107. package/dist/services/audio-handoff.js +20 -14
  108. package/dist/services/catalog-owner.d.ts +0 -1
  109. package/dist/services/framework-error-bus.d.ts +3 -6
  110. package/dist/services/framework-error-bus.js +3 -6
  111. package/dist/services/framework-error.d.ts +22 -7
  112. package/dist/services/framework-error.js +9 -7
  113. package/dist/services/interfaces.d.ts +158 -129
  114. package/dist/services/section-session-assignment.d.ts +21 -0
  115. package/dist/services/section-session-assignment.js +82 -0
  116. package/dist/services/spoken-audio-cards.d.ts +0 -2
  117. package/dist/services/spoken-audio-cards.js +0 -2
  118. package/dist/services/tool-config-defaults.d.ts +3 -3
  119. package/dist/services/tool-config-defaults.js +3 -3
  120. package/dist/services/tool-config-validation.d.ts +4 -5
  121. package/dist/services/tool-config-validation.js +36 -61
  122. package/dist/services/tool-context.d.ts +0 -8
  123. package/dist/services/tool-context.js +0 -12
  124. package/dist/services/tool-icons.d.ts +4 -3
  125. package/dist/services/tool-icons.js +4 -3
  126. package/dist/services/tool-instance-id.d.ts +0 -1
  127. package/dist/services/tool-instance-id.js +0 -6
  128. package/dist/services/tool-providers/TTSToolProvider.d.ts +18 -19
  129. package/dist/services/tool-providers/TTSToolProvider.js +37 -34
  130. package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
  131. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +30 -26
  132. package/dist/services/tool-providers/ToolProviderRegistry.js +119 -93
  133. package/dist/services/tool-request.d.ts +27 -12
  134. package/dist/services/tool-request.js +24 -14
  135. package/dist/services/toolbar-decided-tools.d.ts +15 -0
  136. package/dist/services/toolbar-decided-tools.js +28 -0
  137. package/dist/services/tools-config-normalizer.d.ts +17 -22
  138. package/dist/services/tools-config-normalizer.js +2 -10
  139. package/dist/services/tts/browser-provider.d.ts +38 -2
  140. package/dist/services/tts/browser-provider.js +276 -140
  141. package/dist/services/tts/flat-tree.d.ts +71 -0
  142. package/dist/services/tts/flat-tree.js +269 -0
  143. package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
  144. package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
  145. package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
  146. package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
  147. package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
  148. package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
  149. package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +12 -4
  150. package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
  151. package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
  152. package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
  153. package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
  154. package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
  155. package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
  156. package/dist/services/tts/math-aware-text-processing.js +32 -18
  157. package/dist/services/tts/math-control-names.d.ts +21 -0
  158. package/dist/services/tts/math-control-names.js +142 -0
  159. package/dist/services/tts/math-speech.d.ts +11 -0
  160. package/dist/services/tts/math-speech.js +51 -18
  161. package/dist/services/tts/provider-options.d.ts +41 -0
  162. package/dist/services/tts/provider-options.js +1 -0
  163. package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
  164. package/dist/services/tts/ssml/spoken-text.js +29 -0
  165. package/dist/services/tts/start-failure.d.ts +11 -0
  166. package/dist/services/tts/start-failure.js +19 -0
  167. package/dist/services/tts/text-processing.d.ts +9 -0
  168. package/dist/services/tts/text-processing.js +46 -33
  169. package/dist/services/tts/text-segmentation.d.ts +7 -0
  170. package/dist/services/tts/text-segmentation.js +47 -0
  171. package/dist/services/tts-runtime-config.d.ts +25 -17
  172. package/dist/services/tts-runtime-config.js +16 -95
  173. package/dist/tools/content-capability-resolution.d.ts +7 -9
  174. package/dist/tools/content-capability-resolution.js +8 -45
  175. package/dist/tools/registration.d.ts +58 -0
  176. package/dist/tools/registration.js +54 -0
  177. package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
  178. package/dist/tools/tool-surface-host.d.ts +6 -10
  179. package/dist/tools/tool-surface-host.js +44 -42
  180. package/dist/tools/tool-tag-map.d.ts +5 -15
  181. package/dist/tools/tool-tag-map.js +6 -20
  182. package/dist/utils/structural-equality.d.ts +11 -0
  183. package/dist/utils/structural-equality.js +47 -0
  184. package/package.json +13 -29
  185. package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
  186. package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
  187. package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
  188. package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
  189. package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
  190. package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
  191. package/dist/context/runtime-context-consumer.d.ts +0 -13
  192. package/dist/context/runtime-context-consumer.js +0 -18
  193. package/dist/item-loader.d.ts +0 -44
  194. package/dist/item-loader.js +0 -61
  195. package/dist/policy/core/engine-context.d.ts +0 -29
  196. package/dist/policy/core/engine-context.js +0 -27
  197. package/dist/policy/core/policy-source-tag.d.ts +0 -31
  198. package/dist/policy/core/policy-source-tag.js +0 -28
  199. package/dist/policy/internal.d.ts +0 -25
  200. package/dist/policy/internal.js +0 -25
  201. package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
  202. package/dist/runtime/adapter/coordinator-bridge.js +0 -126
  203. package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
  204. package/dist/runtime/adapter/framework-error-bridge.js +0 -46
  205. package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
  206. package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
  207. package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
  208. package/dist/runtime/core/engine-stage-derivation.js +0 -29
  209. package/dist/runtime/engine-context.d.ts +0 -39
  210. package/dist/runtime/engine-context.js +0 -31
  211. package/dist/runtime/internal.d.ts +0 -43
  212. package/dist/runtime/internal.js +0 -45
  213. package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
  214. package/dist/runtime/section-runtime-engine-host-context.js +0 -57
  215. package/dist/runtime/stage-emit-gate.d.ts +0 -92
  216. package/dist/runtime/stage-emit-gate.js +0 -82
  217. package/dist/services/I18nService.d.ts +0 -47
  218. package/dist/services/I18nService.js +0 -84
  219. package/dist/services/ThemeProvider.d.ts +0 -95
  220. package/dist/services/ThemeProvider.js +0 -334
  221. package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
  222. package/dist/services/defaultPersonalNeedsProfile.js +0 -26
  223. package/dist/services/pnp-standard-features.d.ts +0 -238
  224. package/dist/services/pnp-standard-features.js +0 -338
  225. package/dist/tools/client.d.ts +0 -10
  226. package/dist/tools/client.js +0 -10
  227. package/dist/tools/internal.d.ts +0 -44
  228. package/dist/tools/internal.js +0 -39
@@ -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,4 @@
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";
3
2
  import type { ZIndexLayer } from "../services/ToolCoordinator.js";
4
3
  import type { ToolCoordinatorApi } from "../services/interfaces.js";
5
4
  type BaseEventInit = Pick<EventInit, "bubbles" | "composed" | "cancelable">;
@@ -13,17 +12,20 @@ export declare function createCrossBoundaryEvent<T>(name: string, detail: T, ini
13
12
  export declare function dispatchCrossBoundaryEvent<T>(target: EventTarget, name: string, detail: T, init?: Partial<BaseEventInit>): boolean;
14
13
  /**
15
14
  * Shared runtime-context connection contract for tools and shells.
16
- * 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.
17
17
  */
18
- export declare function connectToolRuntimeContext(host: HTMLElement, onValue: RuntimeContextListener): () => void;
18
+ export declare function connectToolRuntimeContext(host: HTMLElement, onValue: (value: AssessmentToolkitRuntimeContext) => void): () => void;
19
19
  /**
20
20
  * Shared shell-context connection contract for tools needing item/passage scope.
21
21
  */
22
- export declare function connectToolShellContext(host: HTMLElement, onValue: ShellContextListener): () => void;
22
+ export declare function connectToolShellContext(host: HTMLElement, onValue: (value: AssessmentToolkitShellContext) => void): () => void;
23
23
  /**
24
24
  * Shared region-scope context contract for tools targeting content subregions.
25
25
  */
26
- export declare function connectToolRegionScopeContext(host: HTMLElement, onValue: RegionScopeContextListener): () => void;
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;
27
29
  /** A tool's registration with the coordinator in its runtime context. */
28
30
  export interface ToolCoordinatorRegistration {
29
31
  /**
@@ -42,8 +44,4 @@ export interface ToolCoordinatorRegistration {
42
44
  release(): void;
43
45
  }
44
46
  export declare function createToolCoordinatorRegistration(name: string, layer: ZIndexLayer): ToolCoordinatorRegistration;
45
- /**
46
- * Guard utility: narrows unknown context payloads when needed by callers.
47
- */
48
- export declare function isContextValueDefined<T extends UnknownContext>(value: unknown): value is T["__context__"];
49
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,22 +23,27 @@ 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);
43
+ }
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);
41
47
  }
42
48
  export function createToolCoordinatorRegistration(name, layer) {
43
49
  let registered = null;
@@ -60,9 +66,3 @@ export function createToolCoordinatorRegistration(name, layer) {
60
66
  release,
61
67
  };
62
68
  }
63
- /**
64
- * Guard utility: narrows unknown context payloads when needed by callers.
65
- */
66
- export function isContextValueDefined(value) {
67
- return value !== null && value !== undefined;
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)) {
@@ -52,17 +52,16 @@ export interface Annotation {
52
52
  timestamp: number;
53
53
  }
54
54
  export declare class HighlightCoordinator implements HighlightCoordinatorApi {
55
- private ttsWordHighlight;
56
- private ttsSentenceHighlight;
55
+ private ttsWordRanges;
56
+ private ttsSentenceRanges;
57
57
  private ttsWordElementHighlights;
58
58
  private ttsSentenceElementHighlights;
59
59
  private annotations;
60
- private colorHighlights;
60
+ private ttsStyleSource;
61
61
  private nextAnnotationId;
62
62
  private supported;
63
63
  private rangeSerializer;
64
64
  private themeObserver;
65
- private explicitTTSColorOverride;
66
65
  constructor();
67
66
  private setupThemeObservation;
68
67
  private parseColor;
@@ -70,28 +69,32 @@ export declare class HighlightCoordinator implements HighlightCoordinatorApi {
70
69
  private contrastRatio;
71
70
  private blend;
72
71
  private resolveAdaptiveTTSStyle;
73
- private applyAdaptiveTTSStyle;
74
72
  /**
75
- * 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.
76
76
  */
77
+ private applyAdaptiveTTSStyle;
78
+ /** Register the shared highlights, unless another coordinator already did. */
77
79
  private initializeHighlights;
78
80
  /**
79
81
  * Register CSS styles for highlights
80
82
  */
81
83
  private registerStyles;
82
84
  /**
83
- * 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.
84
89
  *
85
- * @param textNode Text node containing the word
86
- * @param startOffset Start position in text node
87
- * @param endOffset End position in text node
90
+ * @param ranges The word's ranges: one per tree it spans
88
91
  */
89
- highlightTTSWord(textNode: Text, startOffset: number, endOffset: number): void;
92
+ highlightTTSWord(ranges: Range[]): void;
90
93
  /**
91
94
  * Highlight a single element as the active TTS word (temporary).
92
95
  *
93
- * Unlike {@link highlightTTSWord} (which paints a CSS range over a text node)
94
- * 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
95
98
  * for atomic targets that have no direct text node to range over — most
96
99
  * notably MathJax CHTML tokens (e.g. `<mjx-mi><mjx-c/></mjx-mi>`, whose glyph
97
100
  * lives in a font-driven pseudo-element a CSS range cannot paint) and
@@ -194,10 +197,8 @@ export declare class HighlightCoordinator implements HighlightCoordinatorApi {
194
197
  timestamp?: number;
195
198
  }>, root?: Element): number;
196
199
  /**
197
- * Highlight a text range (interface method)
198
- *
199
- * Generic method that adapts to the specific highlight type.
200
- * 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.
201
202
  */
202
203
  highlightRange(range: Range, type: HighlightType, color?: HighlightColor): void;
203
204
  /**
@@ -220,11 +221,10 @@ export declare class HighlightCoordinator implements HighlightCoordinatorApi {
220
221
  */
221
222
  updateTTSHighlightStyle(color: string, opacity: number): void;
222
223
  /**
223
- * Cleanup this coordinator's highlights and observer.
224
+ * Remove this coordinator's ranges and stop its observer.
224
225
  *
225
- * The stylesheet is process-global and shared by every live coordinator, so
226
- * removing it here would break a replacement or sibling coordinator. Keep the
227
- * 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.
228
228
  */
229
229
  destroy(): void;
230
230
  }