@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.
- package/README.md +349 -313
- package/dist/attempt/TestSession.d.ts +1 -20
- package/dist/attempt/TestSession.js +8 -99
- package/dist/components/ItemScope.custom-element.d.ts +1 -0
- package/dist/components/ItemScope.custom-element.js +1 -0
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.js +14 -14
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-7tt7v2xp.js +4 -0
- package/dist/components/chunks/{ItemToolBar-gmrryv2v.js → ItemToolBar-97hx60z4.js} +1 -1
- package/dist/components/chunks/ItemToolBar-bxwyhr5p.js +2 -0
- package/dist/components/chunks/ItemToolBar-jx38w99s.js +2 -0
- package/dist/components/chunks/ItemToolBar-rd2bb4p6.js +40 -0
- package/dist/components/chunks/ItemToolBar-vdkyn9f3.js +2 -0
- package/dist/components/chunks/sre-engine-jd8cc08x.js +1 -0
- package/dist/components/item-scope-element.d.ts +2 -0
- package/dist/components/item-scope-element.js +2 -0
- package/dist/context/assessment-toolkit-context.d.ts +27 -6
- package/dist/index.d.ts +22 -47
- package/dist/index.js +12 -36
- package/dist/policy/core/PolicySource.d.ts +1 -1
- package/dist/policy/core/ToolPolicyEngine.d.ts +77 -32
- package/dist/policy/core/ToolPolicyEngine.js +136 -46
- package/dist/policy/core/compose-decision.d.ts +28 -7
- package/dist/policy/core/compose-decision.js +75 -54
- package/dist/policy/core/decision-types.d.ts +65 -34
- package/dist/policy/core/feature-decision.d.ts +16 -9
- package/dist/policy/core/feature-decision.js +14 -8
- package/dist/policy/core/pnp-policy-inputs.d.ts +24 -33
- package/dist/policy/core/pnp-policy-inputs.js +28 -56
- package/dist/policy/core/provenance.d.ts +9 -4
- package/dist/policy/core/provenance.js +1 -1
- package/dist/policy/engine.d.ts +7 -23
- package/dist/policy/engine.js +5 -20
- package/dist/policy/sources/PnpPolicySource.d.ts +44 -61
- package/dist/policy/sources/PnpPolicySource.js +117 -118
- package/dist/runtime/SectionControllerBinding.d.ts +279 -0
- package/dist/runtime/SectionControllerBinding.js +450 -0
- package/dist/runtime/SectionRuntimeEngine.d.ts +26 -289
- package/dist/runtime/SectionRuntimeEngine.js +30 -372
- package/dist/runtime/adapter/SectionEngineAdapter.d.ts +19 -80
- package/dist/runtime/adapter/SectionEngineAdapter.js +19 -92
- package/dist/runtime/adapter/dom-event-bridge.d.ts +15 -30
- package/dist/runtime/adapter/dom-event-bridge.js +17 -44
- package/dist/runtime/adapter/subscriber-fanout.d.ts +1 -2
- package/dist/runtime/adapter/subscriber-fanout.js +1 -2
- package/dist/runtime/catalog-context.d.ts +19 -0
- package/dist/runtime/catalog-context.js +28 -0
- package/dist/runtime/composition-emit-scheduler.d.ts +9 -15
- package/dist/runtime/composition-emit-scheduler.js +9 -15
- package/dist/runtime/content-language.d.ts +32 -0
- package/dist/runtime/content-language.js +46 -0
- package/dist/runtime/content-region.d.ts +17 -0
- package/dist/runtime/content-region.js +29 -0
- package/dist/runtime/content-registration.d.ts +13 -0
- package/dist/runtime/content-registration.js +25 -0
- package/dist/runtime/core/SectionEngineCore.d.ts +2 -2
- package/dist/runtime/core/SectionEngineCore.js +2 -2
- package/dist/runtime/core/cohort.d.ts +1 -1
- package/dist/runtime/core/cohort.js +1 -1
- package/dist/runtime/core/engine-input.d.ts +18 -30
- package/dist/runtime/core/engine-input.js +11 -12
- package/dist/runtime/core/engine-output.d.ts +16 -40
- package/dist/runtime/core/engine-output.js +9 -21
- package/dist/runtime/core/engine-readiness.d.ts +5 -37
- package/dist/runtime/core/engine-readiness.js +8 -41
- package/dist/runtime/core/engine-resolver.d.ts +46 -74
- package/dist/runtime/core/engine-resolver.js +18 -43
- package/dist/runtime/core/engine-state.d.ts +28 -36
- package/dist/runtime/core/engine-state.js +18 -21
- package/dist/runtime/core/engine-transition.d.ts +14 -19
- package/dist/runtime/core/engine-transition.js +71 -51
- package/dist/runtime/engine.d.ts +16 -19
- package/dist/runtime/engine.js +12 -19
- package/dist/runtime/page-warnings.d.ts +13 -0
- package/dist/runtime/page-warnings.js +22 -0
- package/dist/runtime/policy-input-forwarding.d.ts +25 -0
- package/dist/runtime/policy-input-forwarding.js +25 -0
- package/dist/runtime/registration-events.d.ts +11 -0
- package/dist/runtime/runtime-event-claim.d.ts +8 -0
- package/dist/runtime/runtime-event-claim.js +15 -0
- package/dist/runtime/shell-event-bridge.d.ts +45 -0
- package/dist/runtime/shell-event-bridge.js +127 -0
- package/dist/runtime/shell-registration.d.ts +73 -0
- package/dist/runtime/shell-registration.js +95 -0
- package/dist/runtime/shell-scope.d.ts +68 -0
- package/dist/runtime/shell-scope.js +188 -0
- package/dist/runtime/tool-host-contract.d.ts +8 -10
- package/dist/runtime/tool-host-contract.js +11 -11
- package/dist/runtime/unclaimed-registration-watch.d.ts +7 -0
- package/dist/runtime/unclaimed-registration-watch.js +23 -0
- package/dist/services/ElementToolStateStore.d.ts +14 -10
- package/dist/services/ElementToolStateStore.js +37 -13
- package/dist/services/HighlightCoordinator.d.ts +21 -21
- package/dist/services/HighlightCoordinator.js +431 -325
- package/dist/services/RangeSerializer.d.ts +10 -1
- package/dist/services/RangeSerializer.js +87 -14
- package/dist/services/TTSService.d.ts +117 -70
- package/dist/services/TTSService.js +668 -578
- package/dist/services/ToolCoordinator.d.ts +43 -60
- package/dist/services/ToolCoordinator.js +164 -206
- package/dist/services/ToolRegistry.d.ts +74 -57
- package/dist/services/ToolRegistry.js +104 -114
- package/dist/services/ToolkitCoordinator.d.ts +183 -189
- package/dist/services/ToolkitCoordinator.js +477 -431
- package/dist/services/audio-handoff.d.ts +6 -7
- package/dist/services/audio-handoff.js +20 -14
- package/dist/services/catalog-owner.d.ts +0 -1
- package/dist/services/framework-error-bus.d.ts +3 -6
- package/dist/services/framework-error-bus.js +3 -6
- package/dist/services/framework-error.d.ts +22 -7
- package/dist/services/framework-error.js +9 -7
- package/dist/services/interfaces.d.ts +158 -129
- package/dist/services/section-session-assignment.d.ts +21 -0
- package/dist/services/section-session-assignment.js +82 -0
- package/dist/services/spoken-audio-cards.d.ts +0 -2
- package/dist/services/spoken-audio-cards.js +0 -2
- package/dist/services/tool-config-defaults.d.ts +3 -3
- package/dist/services/tool-config-defaults.js +3 -3
- package/dist/services/tool-config-validation.d.ts +4 -5
- package/dist/services/tool-config-validation.js +36 -61
- package/dist/services/tool-context.d.ts +0 -8
- package/dist/services/tool-context.js +0 -12
- package/dist/services/tool-icons.d.ts +4 -3
- package/dist/services/tool-icons.js +4 -3
- package/dist/services/tool-instance-id.d.ts +0 -1
- package/dist/services/tool-instance-id.js +0 -6
- package/dist/services/tool-providers/TTSToolProvider.d.ts +18 -19
- package/dist/services/tool-providers/TTSToolProvider.js +37 -34
- package/dist/services/tool-providers/ToolProviderApi.d.ts +10 -4
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +30 -26
- package/dist/services/tool-providers/ToolProviderRegistry.js +119 -93
- package/dist/services/tool-request.d.ts +27 -12
- package/dist/services/tool-request.js +24 -14
- package/dist/services/toolbar-decided-tools.d.ts +15 -0
- package/dist/services/toolbar-decided-tools.js +28 -0
- package/dist/services/tools-config-normalizer.d.ts +17 -22
- package/dist/services/tools-config-normalizer.js +2 -10
- package/dist/services/tts/browser-provider.d.ts +38 -2
- package/dist/services/tts/browser-provider.js +276 -140
- package/dist/services/tts/flat-tree.d.ts +71 -0
- package/dist/services/tts/flat-tree.js +269 -0
- package/dist/services/tts/generated-speech/assemble-plan.d.ts +3 -1
- package/dist/services/tts/generated-speech/assemble-plan.js +3 -1
- package/dist/services/tts/generated-speech/dom/build-dom-plan.d.ts +2 -0
- package/dist/services/tts/generated-speech/dom/build-dom-plan.js +1 -1
- package/dist/services/tts/generated-speech/math-speech-cache.d.ts +3 -1
- package/dist/services/tts/generated-speech/math-speech-cache.js +5 -1
- package/dist/services/tts/highlight-pipeline/create-highlight-plan.js +12 -4
- package/dist/services/tts/highlight-pipeline/prose-boundary-resolver.js +6 -4
- package/dist/services/tts/highlight-pipeline/readable-region.js +3 -2
- package/dist/services/tts/highlight-pipeline/types.d.ts +5 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.d.ts +12 -0
- package/dist/services/tts/highlight-pipeline/visible-map-range.js +73 -7
- package/dist/services/tts/math-aware-text-processing.d.ts +5 -1
- package/dist/services/tts/math-aware-text-processing.js +32 -18
- package/dist/services/tts/math-control-names.d.ts +21 -0
- package/dist/services/tts/math-control-names.js +142 -0
- package/dist/services/tts/math-speech.d.ts +11 -0
- package/dist/services/tts/math-speech.js +51 -18
- package/dist/services/tts/provider-options.d.ts +41 -0
- package/dist/services/tts/provider-options.js +1 -0
- package/dist/services/tts/ssml/spoken-text.d.ts +2 -26
- package/dist/services/tts/ssml/spoken-text.js +29 -0
- package/dist/services/tts/start-failure.d.ts +11 -0
- package/dist/services/tts/start-failure.js +19 -0
- package/dist/services/tts/text-processing.d.ts +9 -0
- package/dist/services/tts/text-processing.js +46 -33
- package/dist/services/tts/text-segmentation.d.ts +7 -0
- package/dist/services/tts/text-segmentation.js +47 -0
- package/dist/services/tts-runtime-config.d.ts +25 -17
- package/dist/services/tts-runtime-config.js +16 -95
- package/dist/tools/content-capability-resolution.d.ts +7 -9
- package/dist/tools/content-capability-resolution.js +8 -45
- package/dist/tools/registration.d.ts +58 -0
- package/dist/tools/registration.js +54 -0
- package/dist/tools/registrations/toolbar-registration-helpers.js +2 -2
- package/dist/tools/tool-surface-host.d.ts +6 -10
- package/dist/tools/tool-surface-host.js +44 -42
- package/dist/tools/tool-tag-map.d.ts +5 -15
- package/dist/tools/tool-tag-map.js +6 -20
- package/dist/utils/structural-equality.d.ts +11 -0
- package/dist/utils/structural-equality.js +47 -0
- package/package.json +13 -29
- package/dist/attempt/adapters/activity-to-test-attempt-session.d.ts +0 -49
- package/dist/attempt/adapters/activity-to-test-attempt-session.js +0 -138
- package/dist/components/chunks/ItemToolBar-b9dz5jvd.js +0 -40
- package/dist/components/chunks/ItemToolBar-h4xap0t0.js +0 -4
- package/dist/components/chunks/ItemToolBar-wr2myaah.js +0 -2
- package/dist/components/chunks/sre-engine-wf9s43gg.js +0 -1
- package/dist/context/runtime-context-consumer.d.ts +0 -13
- package/dist/context/runtime-context-consumer.js +0 -18
- package/dist/item-loader.d.ts +0 -44
- package/dist/item-loader.js +0 -61
- package/dist/policy/core/engine-context.d.ts +0 -29
- package/dist/policy/core/engine-context.js +0 -27
- package/dist/policy/core/policy-source-tag.d.ts +0 -31
- package/dist/policy/core/policy-source-tag.js +0 -28
- package/dist/policy/internal.d.ts +0 -25
- package/dist/policy/internal.js +0 -25
- package/dist/runtime/adapter/coordinator-bridge.d.ts +0 -92
- package/dist/runtime/adapter/coordinator-bridge.js +0 -126
- package/dist/runtime/adapter/framework-error-bridge.d.ts +0 -36
- package/dist/runtime/adapter/framework-error-bridge.js +0 -46
- package/dist/runtime/adapter/instrumentation-bridge.d.ts +0 -31
- package/dist/runtime/adapter/instrumentation-bridge.js +0 -38
- package/dist/runtime/core/engine-stage-derivation.d.ts +0 -18
- package/dist/runtime/core/engine-stage-derivation.js +0 -29
- package/dist/runtime/engine-context.d.ts +0 -39
- package/dist/runtime/engine-context.js +0 -31
- package/dist/runtime/internal.d.ts +0 -43
- package/dist/runtime/internal.js +0 -45
- package/dist/runtime/section-runtime-engine-host-context.d.ts +0 -74
- package/dist/runtime/section-runtime-engine-host-context.js +0 -57
- package/dist/runtime/stage-emit-gate.d.ts +0 -92
- package/dist/runtime/stage-emit-gate.js +0 -82
- package/dist/services/I18nService.d.ts +0 -47
- package/dist/services/I18nService.js +0 -84
- package/dist/services/ThemeProvider.d.ts +0 -95
- package/dist/services/ThemeProvider.js +0 -334
- package/dist/services/defaultPersonalNeedsProfile.d.ts +0 -21
- package/dist/services/defaultPersonalNeedsProfile.js +0 -26
- package/dist/services/pnp-standard-features.d.ts +0 -238
- package/dist/services/pnp-standard-features.js +0 -338
- package/dist/tools/client.d.ts +0 -10
- package/dist/tools/client.js +0 -10
- package/dist/tools/internal.d.ts +0 -44
- package/dist/tools/internal.js +0 -39
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The examples build their tool registry with `createPackagedToolRegistry()` from
|
|
|
15
15
|
in its own dependencies. The section player depends on it too, but strict
|
|
16
16
|
installers such as pnpm expose only the packages a host declares.
|
|
17
17
|
|
|
18
|
-
Server-backed TTS (`backend: "
|
|
18
|
+
Server-backed TTS (`backend: "server"`) loads
|
|
19
19
|
`@pie-players/tts-client-server`, a dependency of
|
|
20
20
|
`@pie-players/pie-default-tool-loaders` whose TTS registration imports it on
|
|
21
21
|
first use. If it fails to load, TTS initialization reports a `provider-init`
|
|
@@ -52,10 +52,15 @@ player.toolCoordinator = toolCoordinator;
|
|
|
52
52
|
|
|
53
53
|
**After** (coordinator orchestrates):
|
|
54
54
|
```typescript
|
|
55
|
-
import {
|
|
55
|
+
import {
|
|
56
|
+
createPackagedToolRegistry,
|
|
57
|
+
DEFAULT_TOOL_MODULE_LOADERS,
|
|
58
|
+
} from '@pie-players/pie-default-tool-loaders';
|
|
56
59
|
|
|
57
60
|
// Create one coordinator with configuration
|
|
58
|
-
const toolRegistry = createPackagedToolRegistry(
|
|
61
|
+
const toolRegistry = createPackagedToolRegistry({
|
|
62
|
+
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
63
|
+
});
|
|
59
64
|
const toolkitCoordinator = new ToolkitCoordinator({
|
|
60
65
|
assessmentId: 'my-assessment',
|
|
61
66
|
toolRegistry,
|
|
@@ -113,9 +118,9 @@ provider path is the item-player loader config:
|
|
|
113
118
|
|
|
114
119
|
### Toolkit-Owned Canonical Event Stream
|
|
115
120
|
|
|
116
|
-
- `pie-toolkit-stage-change`
|
|
117
121
|
- `pie-toolkit-runtime-owned`
|
|
118
122
|
- `pie-toolkit-runtime-inherited`
|
|
123
|
+
- `pie-toolkit-runtime-ready`
|
|
119
124
|
- `pie-toolkit-ready`
|
|
120
125
|
- `pie-toolkit-section-ready`
|
|
121
126
|
- `pie-toolkit-framework-error`
|
|
@@ -139,20 +144,20 @@ See the [ToolkitCoordinator section in the architecture overview](../../docs/arc
|
|
|
139
144
|
1. **Centralized Coordination**: ToolkitCoordinator orchestrates all services
|
|
140
145
|
2. **Composable Services**: Import only what you need (or use coordinator for convenience)
|
|
141
146
|
3. **No Framework Lock-in**: Works with any JavaScript framework
|
|
142
|
-
4. **Product Control**: Products control navigation, persistence,
|
|
147
|
+
4. **Product Control**: Products control navigation, layout and backend. Session persistence defaults to `localStorage` at the section and assessment layers, stored per attempt id and inactive without one, and a product replaces either through its persistence hook (`createSectionSessionPersistence` on the coordinator, `createAssessmentSessionPersistence` on the assessment player)
|
|
143
148
|
5. **Standard Contracts**: Well-defined event types for component communication
|
|
144
149
|
6. **Element-Level Granularity**: Tool state tracked per PIE element, not per item
|
|
145
150
|
7. **State Separation**: Tool state (ephemeral) separate from PIE session data (persistent)
|
|
146
151
|
|
|
147
|
-
## Configuration
|
|
152
|
+
## Configuration: attributes and a constructed coordinator
|
|
148
153
|
|
|
149
|
-
This package and `@pie-players/pie-section-player`
|
|
150
|
-
|
|
151
|
-
|
|
154
|
+
This package and `@pie-players/pie-section-player` give each input one entry
|
|
155
|
+
point (see [One tier per input](#one-tier-per-input)). A host that needs more
|
|
156
|
+
than the element inputs builds the coordinator itself.
|
|
152
157
|
|
|
153
|
-
###
|
|
158
|
+
### Element inputs and a constructed coordinator
|
|
154
159
|
|
|
155
|
-
- **
|
|
160
|
+
- **Element attributes / properties.** Use these for the
|
|
156
161
|
common cases that are static for the lifetime of the player or that hosts
|
|
157
162
|
want to set declaratively in HTML / templating frameworks. Example:
|
|
158
163
|
|
|
@@ -164,16 +169,21 @@ tier; the choice is about ergonomics, not capability.
|
|
|
164
169
|
></pie-assessment-toolkit>
|
|
165
170
|
```
|
|
166
171
|
|
|
167
|
-
- **
|
|
172
|
+
- **A constructed `ToolkitCoordinator` (or a
|
|
168
173
|
`runtime` object on consumer CEs).** Use this for advanced cases: composed
|
|
169
|
-
configuration, dynamic overrides, runtime mutation, fields without
|
|
170
|
-
|
|
174
|
+
configuration, dynamic overrides, runtime mutation, fields without an
|
|
175
|
+
attribute, or anything that benefits from being a single typed
|
|
171
176
|
object passed by reference. Example:
|
|
172
177
|
|
|
173
178
|
```ts
|
|
174
|
-
import {
|
|
179
|
+
import {
|
|
180
|
+
createPackagedToolRegistry,
|
|
181
|
+
DEFAULT_TOOL_MODULE_LOADERS,
|
|
182
|
+
} from "@pie-players/pie-default-tool-loaders";
|
|
175
183
|
|
|
176
|
-
const toolRegistry = createPackagedToolRegistry(
|
|
184
|
+
const toolRegistry = createPackagedToolRegistry({
|
|
185
|
+
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
186
|
+
});
|
|
177
187
|
const coordinator = new ToolkitCoordinator({
|
|
178
188
|
assessmentId: "my-assessment",
|
|
179
189
|
toolRegistry,
|
|
@@ -192,38 +202,32 @@ Top-level attributes use kebab-case (`assessment-id`,
|
|
|
192
202
|
`tool-config-strictness`). Section-player runtime configuration is grouped
|
|
193
203
|
under the `runtime` object instead of duplicated as top-level props.
|
|
194
204
|
|
|
195
|
-
###
|
|
205
|
+
### One tier per input
|
|
196
206
|
|
|
197
|
-
|
|
198
|
-
|
|
207
|
+
Each input has one entry point. On the section-player layout elements,
|
|
208
|
+
`runtime` carries `assessmentId`, the player, tool, accessibility,
|
|
209
|
+
coordinator, env and isolation fields, runtime factories and the `on*`
|
|
210
|
+
callbacks; attributes carry section identity, layout controls, `nds-icons`,
|
|
211
|
+
`locale` and `tool-config-strictness`. Unset inputs take their documented
|
|
212
|
+
defaults.
|
|
199
213
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
3. Use documented defaults when neither is provided.
|
|
214
|
+
`<pie-assessment-toolkit>` takes the same fields as its own properties,
|
|
215
|
+
with `assessment-id`, `nds-icons`, `locale` and `tool-config-strictness` as
|
|
216
|
+
attributes, and reads `pnpEnforcement` from `tools.pnpEnforcement`.
|
|
204
217
|
|
|
205
|
-
### Canonical
|
|
218
|
+
### Canonical attribute set
|
|
206
219
|
|
|
207
|
-
|
|
208
|
-
`pie-assessment-toolkit`, `pie-section-player-base`, and the
|
|
209
|
-
`pie-section-player-*` layout elements (locked in M5). Every tier-1
|
|
210
|
-
surface obeys the strict mirror rule:
|
|
211
|
-
|
|
212
|
-
```
|
|
213
|
-
kebab-attribute ↔ camelCaseProp ↔ runtime.<sameCamelCaseKey>
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
Common members include:
|
|
217
|
-
|
|
218
|
-
- Identity: `assessment-id`, `section-id`, `attempt-id`
|
|
220
|
+
- Identity: `assessment-id` on the toolkit, `section-id`, `attempt-id`
|
|
219
221
|
- Runtime config on section-player CEs: `runtime`
|
|
220
222
|
- Toolkit-only object properties: `tools`, `tool-registry`, `coordinator`,
|
|
221
223
|
`accessibility`
|
|
224
|
+
- Interface: `nds-icons`, `locale`
|
|
222
225
|
- Diagnostics: `tool-config-strictness`, `debug`. Framework-error
|
|
223
|
-
delivery is via the
|
|
226
|
+
delivery is via the `onFrameworkError` callback (a toolkit property,
|
|
227
|
+
`runtime.onFrameworkError` on a section player) and the
|
|
224
228
|
`framework-error` DOM event dispatched on the layout CE host.
|
|
225
229
|
|
|
226
|
-
|
|
230
|
+
Inputs outside the runtime config:
|
|
227
231
|
|
|
228
232
|
- Identity (`section-id`, `attempt-id`, `section`): per-attempt host
|
|
229
233
|
state, not configuration.
|
|
@@ -235,17 +239,15 @@ Documented exceptions to the mirror rule:
|
|
|
235
239
|
`runtime.tools.placement.{item,passage}` directly.
|
|
236
240
|
- Runtime-only keys on the section-player layout CEs
|
|
237
241
|
(`createSectionController`, `isolation`): accepted only via
|
|
238
|
-
`runtime.<key>`.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
to override coordinator inheritance should pass an explicit
|
|
244
|
-
`coordinator={...}` instead.
|
|
242
|
+
`runtime.<key>`. Section-player layouts forward both to the wrapped
|
|
243
|
+
toolkit as properties. `<pie-assessment-toolkit>` keeps
|
|
244
|
+
`createSectionController` JS-only and takes `isolation` as an
|
|
245
|
+
attribute or a property. A `coordinator` passed to it wins over an
|
|
246
|
+
outer toolkit's.
|
|
245
247
|
|
|
246
|
-
### When to add
|
|
248
|
+
### When to add an attribute
|
|
247
249
|
|
|
248
|
-
Add
|
|
250
|
+
Add an attribute only if all of the following hold:
|
|
249
251
|
|
|
250
252
|
- It is a common case that hosts set without composing a `ToolkitCoordinator`
|
|
251
253
|
/ `runtime` object.
|
|
@@ -263,10 +265,15 @@ Otherwise expose it through the configuration object only.
|
|
|
263
265
|
|
|
264
266
|
```typescript
|
|
265
267
|
import { ToolkitCoordinator } from '@pie-players/pie-assessment-toolkit';
|
|
266
|
-
import {
|
|
268
|
+
import {
|
|
269
|
+
createPackagedToolRegistry,
|
|
270
|
+
DEFAULT_TOOL_MODULE_LOADERS,
|
|
271
|
+
} from '@pie-players/pie-default-tool-loaders';
|
|
267
272
|
|
|
268
273
|
// Create coordinator with configuration
|
|
269
|
-
const toolRegistry = createPackagedToolRegistry(
|
|
274
|
+
const toolRegistry = createPackagedToolRegistry({
|
|
275
|
+
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
276
|
+
});
|
|
270
277
|
const coordinator = new ToolkitCoordinator({
|
|
271
278
|
assessmentId: 'demo-assessment',
|
|
272
279
|
toolRegistry,
|
|
@@ -324,6 +331,7 @@ Behavior pins (PIE-512 Phase D):
|
|
|
324
331
|
|
|
325
332
|
- Subscribe **after** the first `getOrCreateSectionController(...)` resolves; calling subscribe before any cohort exists throws.
|
|
326
333
|
- On every cohort transition (navigation, fresh `getOrCreateSectionController` for a new section), the listener is automatically migrated to the new controller and receives a snapshot replay (`content-loaded` × N then `section-loading-complete`) in the same order a fresh subscriber would have seen.
|
|
334
|
+
- A section's subscriptions receive `content-loaded` only for that section's renderables, live or replayed; the previous section's loads stay with it. A renderable in both sections, such as a passage that stays mounted across the switch, counts as loaded in each.
|
|
327
335
|
- Subscribing the **same listener function** twice replaces the first subscription (filter args from the second call win).
|
|
328
336
|
- A listener that throws is caught and `console.warn`-logged; the throw does not interrupt fan-out to other listeners.
|
|
329
337
|
|
|
@@ -357,7 +365,7 @@ What this means in practice for typed integrations:
|
|
|
357
365
|
|
|
358
366
|
- **TypeScript breaking change.** `SectionEventSubscriptionArgs`, `SectionItemEventSubscriptionArgs`, and `SectionScopedEventSubscriptionArgs` no longer declare `sectionId?` / `attemptId?` properties. Any host that imports these arg types directly and passes those keys will fail to compile after upgrade. **Action required.**
|
|
359
367
|
- **Runtime is tolerant.** The runtime silently ignores extra unknown properties, so an untyped or lightly-typed call site that still passes `sectionId` / `attemptId` continues to work without source changes. The args have **no effect** at runtime — the subscription always follows the active cohort.
|
|
360
|
-
- **New precondition.** `subscribe*`
|
|
368
|
+
- **New precondition.** `subscribe*` throws until the first `getOrCreateSectionController(...)` call; a listener added while a section is starting binds when that section becomes active. Subscribe **after** the first `getOrCreateSectionController(...)` resolves. Subscribing on `toolkit-ready` alone is no longer sufficient — though in practice the section player emits `toolkit-ready` *after* its first `getOrCreateSectionController(...)` resolves, so a `toolkit-ready` anchor is safe in section-player hosts.
|
|
361
369
|
- **Cohort migration is automatic.** If your wrapper previously re-subscribed on every navigation to keep listeners alive across sections, that wiring is no longer needed (and should be removed). A single subscribe call after the first controller-resolve is now enough — the listener migrates automatically and is replayed the new cohort's snapshot on every transition.
|
|
362
370
|
- **Watch for double-replay if you re-subscribe on every `toolkit-ready`.** Hosts that detached and re-subscribed on every `toolkit-ready` event (the correct pre-Phase D pattern, since each subscription was pinned to a `sectionId`) will now observe **two snapshot replays per navigation**: one delivered automatically when Phase D migrates the existing listener to the new active cohort, and a second when the manual re-subscribe attaches a fresh listener that replays again. Listener handlers that are not strictly idempotent will fire twice — analytics `pageAction`s, non-Set counters, side-effecting hydration. The fix is a one-line guard (`if (this.controllerUnsubscribe) return;`) so the subscribe runs only on the first `toolkit-ready`.
|
|
363
371
|
- **For intentionally-pinned subscriptions to inactive sections** (e.g. a host UI that wants to keep watching section A while the user views section B), the helper API does not support that pattern by design. Use `coordinator.getSectionController({ sectionId, attemptId })` and subscribe directly on the controller handle (`controller.subscribe?.(...)`) — that binding is pinned to one controller instance and does not migrate.
|
|
@@ -425,27 +433,41 @@ const catalogResolver = new AccessibilityCatalogResolver([], 'en-US');
|
|
|
425
433
|
await ttsService.initialize(new BrowserTTSProvider());
|
|
426
434
|
ttsService.setCatalogResolver(catalogResolver);
|
|
427
435
|
|
|
428
|
-
//
|
|
429
|
-
player.ttsService = ttsService;
|
|
430
|
-
player.toolCoordinator = toolCoordinator;
|
|
431
|
-
// ...
|
|
436
|
+
// Use them from host-built UI
|
|
432
437
|
```
|
|
433
438
|
|
|
439
|
+
No player element takes services one by one. The section and assessment
|
|
440
|
+
players reach services only through a coordinator, `runtime.coordinator` on a
|
|
441
|
+
section player and `coordinator` on an assessment player; manually created
|
|
442
|
+
services serve host code that drives them directly.
|
|
443
|
+
|
|
434
444
|
### Without a Section Player
|
|
435
445
|
|
|
436
446
|
`<pie-assessment-toolkit>` needs no section. Bind none and it provides tools,
|
|
437
447
|
policy and services to the item toolbars and item players inside it, which is
|
|
438
|
-
how the toolkit accompanies a plain item player
|
|
448
|
+
how the toolkit accompanies a plain item player. `<pie-item-scope>` holds the
|
|
449
|
+
item for its tools, as it does in a section player's card. The tree this
|
|
450
|
+
builds:
|
|
439
451
|
|
|
440
452
|
```html
|
|
441
|
-
<pie-assessment-toolkit
|
|
442
|
-
<pie-item-
|
|
443
|
-
|
|
453
|
+
<pie-assessment-toolkit>
|
|
454
|
+
<pie-item-scope item-id="q1">
|
|
455
|
+
<pie-item-toolbar></pie-item-toolbar>
|
|
456
|
+
<div data-region="content">
|
|
457
|
+
<pie-item-player></pie-item-player>
|
|
458
|
+
</div>
|
|
459
|
+
</pie-item-scope>
|
|
444
460
|
</pie-assessment-toolkit>
|
|
445
461
|
```
|
|
446
462
|
|
|
463
|
+
The host sets the toolkit's properties before the tree enters the document. A
|
|
464
|
+
scope registers as soon as it mounts, and that first registration starts the
|
|
465
|
+
coordinator from the toolkit's inputs at that moment; see the binding rules
|
|
466
|
+
below.
|
|
467
|
+
|
|
447
468
|
```typescript
|
|
448
469
|
import '@pie-players/pie-assessment-toolkit/components/pie-assessment-toolkit-element';
|
|
470
|
+
import '@pie-players/pie-assessment-toolkit/components/item-scope-element';
|
|
449
471
|
import '@pie-players/pie-assessment-toolkit/components/item-toolbar-element';
|
|
450
472
|
import '@pie-players/pie-item-player';
|
|
451
473
|
import {
|
|
@@ -458,42 +480,80 @@ const toolRegistry = createPackagedToolRegistry({
|
|
|
458
480
|
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
459
481
|
});
|
|
460
482
|
|
|
461
|
-
toolkit
|
|
483
|
+
const toolkit = document.createElement('pie-assessment-toolkit');
|
|
484
|
+
toolkit.tools = {
|
|
485
|
+
pnpEnforcement: 'on',
|
|
486
|
+
placement: { item: ['textToSpeech', 'calculator'] },
|
|
487
|
+
};
|
|
462
488
|
toolkit.toolRegistry = toolRegistry;
|
|
463
489
|
toolkit.toolContextResolvers = toolContextResolvers;
|
|
464
490
|
toolkit.assessment = { id: 'a1', personalNeedsProfile: { supports: ['calculator'] } };
|
|
465
491
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
492
|
+
const scope = document.createElement('pie-item-scope');
|
|
493
|
+
scope.setAttribute('item-id', 'q1');
|
|
494
|
+
scope.item = item;
|
|
495
|
+
scope.innerHTML = `
|
|
496
|
+
<pie-item-toolbar></pie-item-toolbar>
|
|
497
|
+
<div data-region="content"><pie-item-player></pie-item-player></div>
|
|
498
|
+
`;
|
|
499
|
+
|
|
500
|
+
toolkit.append(scope);
|
|
501
|
+
container.append(toolkit);
|
|
469
502
|
```
|
|
470
503
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
504
|
+
The toolbars and tools inside the scope take the item and its id from it, and
|
|
505
|
+
act on the scope, or on the element its `scopeElement` property names;
|
|
506
|
+
`textToSpeech` reads the scope's `[data-region="content"]` first. A toolbar's
|
|
507
|
+
own `item`, `item-id` and `scopeElement` still override the scope, and its
|
|
508
|
+
`toolRegistry` the toolkit's. The scope
|
|
509
|
+
registers the item with the toolkit once it finds one above it, so it may mount
|
|
510
|
+
first, and the toolkit files the item's accessibility catalogs, which read-aloud
|
|
511
|
+
speaks in place of the markup they name. Item player events pass through the
|
|
512
|
+
scope unchanged, and the session stays the item player's. A registry built
|
|
513
|
+
without `toolModuleLoaders` renders toolbar buttons whose tool elements never
|
|
514
|
+
load.
|
|
475
515
|
|
|
476
516
|
A profile change is a new `assessment` value; the toolbars re-derive on the
|
|
477
|
-
policy change it emits.
|
|
478
|
-
`
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
`
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
517
|
+
policy change it emits. The toolkit announces `runtime-ready`, with
|
|
518
|
+
`{ runtimeId, coordinator, ownership }`, once per coordinator, with or without a
|
|
519
|
+
section. Without one, the coordinator starts at the first item that registers,
|
|
520
|
+
and a host reads its readiness from `coordinator.waitUntilReady()` or
|
|
521
|
+
`isReady()`; `toolkit-ready` and `section-ready` wait for a bound section. The
|
|
522
|
+
toolkit emits no stage events: `pie-stage-change` is the section player's. A
|
|
523
|
+
host that holds
|
|
524
|
+
the coordinator from `runtime-ready`, or passes its own as `coordinator`,
|
|
525
|
+
changes the profile with `coordinator.updateAssessment(...)`; the toolkit
|
|
526
|
+
applies its `assessment` property only to a coordinator it owns. The
|
|
527
|
+
`calculator-pnp` section demo composes the owned-coordinator form.
|
|
485
528
|
|
|
486
529
|
The toolkit builds its own coordinator at mount from `tools`, `enabled-tools`,
|
|
487
530
|
`assessment-id`, `accessibility`, `lazy-init`, `tool-config-strictness` and
|
|
488
|
-
`toolRegistry`.
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
531
|
+
`toolRegistry`. Its first content binds it: the section, or without one the
|
|
532
|
+
first item scope that registers. Content that arrives after one of those inputs
|
|
533
|
+
changed binds a coordinator rebuilt from the current values, and the toolbars
|
|
534
|
+
move to it. After that, a change to them is reported once in the console and
|
|
535
|
+
does not reach the coordinator, except a `toolRegistry` given to a toolkit that
|
|
536
|
+
had none, which the coordinator adopts in place; `tools.pnpEnforcement`,
|
|
537
|
+
`assessment` and `toolContextResolvers` apply to it at any
|
|
538
|
+
time. That
|
|
493
539
|
coordinator reports feature policy asked with no assessment bound only while
|
|
494
|
-
`
|
|
540
|
+
`tools.pnpEnforcement` is `on`: a toolkit given no `assessment` and no enforcement
|
|
495
541
|
has asked for no accommodation.
|
|
496
542
|
|
|
543
|
+
A toolkit nested in another inherits the outer one's coordinator when the outer
|
|
544
|
+
one has a coordinator by the time the inner one connects, which holds for both
|
|
545
|
+
mounted together. Otherwise the inner one builds its own and keeps it, and
|
|
546
|
+
reports an outer coordinator arriving later once in the console. `isolation`
|
|
547
|
+
`"force"` keeps a nested toolkit on its own coordinator by design.
|
|
548
|
+
|
|
549
|
+
Text-to-speech starts at the toolkit's first content, once the section composes
|
|
550
|
+
or the first item scope registers, and `coordinator.waitUntilReady()` waits for it. With `lazy-init` it starts at the first read-aloud instead, unless policy
|
|
551
|
+
grants it. A tool provider or text-to-speech that fails to start is a
|
|
552
|
+
recoverable framework error: the tool reports itself unavailable and the
|
|
553
|
+
assessment goes on. When policy grants the tool, through an item or district
|
|
554
|
+
requirement or a profile support, the failure is fatal, including one that a
|
|
555
|
+
later policy change grants.
|
|
556
|
+
|
|
497
557
|
## Tool Configuration Model
|
|
498
558
|
|
|
499
559
|
The toolkit uses one canonical `tools` model with three concerns:
|
|
@@ -625,9 +685,14 @@ Use **floating tools** when:
|
|
|
625
685
|
Complete example showing both types:
|
|
626
686
|
|
|
627
687
|
```typescript
|
|
628
|
-
import {
|
|
688
|
+
import {
|
|
689
|
+
createPackagedToolRegistry,
|
|
690
|
+
DEFAULT_TOOL_MODULE_LOADERS,
|
|
691
|
+
} from '@pie-players/pie-default-tool-loaders';
|
|
629
692
|
|
|
630
|
-
const toolRegistry = createPackagedToolRegistry(
|
|
693
|
+
const toolRegistry = createPackagedToolRegistry({
|
|
694
|
+
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
695
|
+
});
|
|
631
696
|
const coordinator = new ToolkitCoordinator({
|
|
632
697
|
assessmentId: 'math-exam',
|
|
633
698
|
toolRegistry,
|
|
@@ -661,14 +726,22 @@ const coordinator = new ToolkitCoordinator({
|
|
|
661
726
|
});
|
|
662
727
|
```
|
|
663
728
|
|
|
664
|
-
**
|
|
729
|
+
**A common placement:**
|
|
665
730
|
|
|
666
|
-
|
|
731
|
+
A coordinator shows a tool only where `tools.placement` lists it. Its default
|
|
732
|
+
placement is empty, so a coordinator configured without one shows no tools, and
|
|
733
|
+
a profile grant does not place a tool either. This placement covers the
|
|
734
|
+
commonly used tools:
|
|
667
735
|
|
|
668
736
|
```typescript
|
|
669
|
-
import {
|
|
737
|
+
import {
|
|
738
|
+
createPackagedToolRegistry,
|
|
739
|
+
DEFAULT_TOOL_MODULE_LOADERS,
|
|
740
|
+
} from '@pie-players/pie-default-tool-loaders';
|
|
670
741
|
|
|
671
|
-
const toolRegistry = createPackagedToolRegistry(
|
|
742
|
+
const toolRegistry = createPackagedToolRegistry({
|
|
743
|
+
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
744
|
+
});
|
|
672
745
|
const coordinator = new ToolkitCoordinator({
|
|
673
746
|
assessmentId: 'my-assessment',
|
|
674
747
|
toolRegistry,
|
|
@@ -736,7 +809,8 @@ tools: {
|
|
|
736
809
|
providers: {
|
|
737
810
|
textToSpeech: {
|
|
738
811
|
enabled: true,
|
|
739
|
-
backend: '
|
|
812
|
+
backend: 'server',
|
|
813
|
+
serverProvider: 'polly'
|
|
740
814
|
}
|
|
741
815
|
}
|
|
742
816
|
}
|
|
@@ -752,7 +826,7 @@ You can still set `apiEndpoint` explicitly when your host route is not `/api/tts
|
|
|
752
826
|
|
|
753
827
|
### Inline TTS Speed Options
|
|
754
828
|
|
|
755
|
-
Inline TTS speed buttons are configurable via `speedOptions`
|
|
829
|
+
Inline TTS speed buttons are configurable via `speedOptions` on the provider config.
|
|
756
830
|
|
|
757
831
|
```typescript
|
|
758
832
|
tools: {
|
|
@@ -760,9 +834,7 @@ tools: {
|
|
|
760
834
|
textToSpeech: {
|
|
761
835
|
enabled: true,
|
|
762
836
|
backend: "browser",
|
|
763
|
-
|
|
764
|
-
speedOptions: [2, 1.25, 1.5] // host options keep this order; Normal is added if omitted
|
|
765
|
-
}
|
|
837
|
+
speedOptions: [2, 1.25, 1.5] // host options keep this order; Normal is added if omitted
|
|
766
838
|
}
|
|
767
839
|
}
|
|
768
840
|
}
|
|
@@ -779,13 +851,11 @@ tools: {
|
|
|
779
851
|
enabled: true,
|
|
780
852
|
backend: "server",
|
|
781
853
|
serverProvider: "custom",
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
]
|
|
788
|
-
}
|
|
854
|
+
speedOptions: [
|
|
855
|
+
{ rate: 0.8, label: "Slow", ariaLabel: "Slow speed" },
|
|
856
|
+
{ rate: 1, label: "Normal", ariaLabel: "Normal speed", default: true },
|
|
857
|
+
{ rate: 1.5, label: "Fast", ariaLabel: "Fast speed" }
|
|
858
|
+
]
|
|
789
859
|
}
|
|
790
860
|
}
|
|
791
861
|
}
|
|
@@ -810,25 +880,15 @@ tools: {
|
|
|
810
880
|
- Speed choices are exposed as a `Playback speed` radio group: one option is
|
|
811
881
|
always selected, and choosing the already-selected speed is a no-op.
|
|
812
882
|
- If only one speed option remains, the speed group is hidden by default. Set
|
|
813
|
-
`showSingleSpeedOption: true`
|
|
814
|
-
|
|
815
|
-
### Runtime Fallback: Server TTS -> Browser TTS
|
|
816
|
-
|
|
817
|
-
When server-backed playback fails at runtime (for example `503`, network outage,
|
|
818
|
-
or synthesized asset fetch failure), `TTSService` now performs a one-time
|
|
819
|
-
runtime fallback for that session:
|
|
820
|
-
|
|
821
|
-
1. Switches provider from server-backed implementation to browser speech synthesis.
|
|
822
|
-
2. Rebinds highlight callbacks to the browser provider.
|
|
823
|
-
3. Retries the same `speak()` request once.
|
|
883
|
+
`showSingleSpeedOption: true` on the provider config to surface that one-option state.
|
|
824
884
|
|
|
825
|
-
|
|
826
|
-
incidents without requiring host-side reconfiguration.
|
|
885
|
+
### Browser Fallback
|
|
827
886
|
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
887
|
+
The coordinator owns fallback to browser speech. When a server-backed provider
|
|
888
|
+
fails to initialize, the coordinator re-initializes TTS on the browser backend
|
|
889
|
+
and reports `pie-tool-init-fallback`. `TTSService.initialize` itself rejects
|
|
890
|
+
when its provider fails to start, and a playback failure after initialization
|
|
891
|
+
rejects that read without switching provider.
|
|
832
892
|
|
|
833
893
|
`provider.runtime.authFetcher` is optional. Add it only when your host environment
|
|
834
894
|
requires runtime auth material for TTS requests:
|
|
@@ -838,7 +898,8 @@ tools: {
|
|
|
838
898
|
providers: {
|
|
839
899
|
textToSpeech: {
|
|
840
900
|
enabled: true,
|
|
841
|
-
backend: '
|
|
901
|
+
backend: 'server',
|
|
902
|
+
serverProvider: 'polly',
|
|
842
903
|
apiEndpoint: '/api/tts',
|
|
843
904
|
provider: {
|
|
844
905
|
runtime: {
|
|
@@ -858,10 +919,10 @@ provider config: a returned `authToken` is sent as `Authorization: Bearer <token
|
|
|
858
919
|
and returned `headers` with every synthesis request, and under
|
|
859
920
|
`includeAuthOnAssetFetch` the `Authorization` header also reaches the custom
|
|
860
921
|
transport's speech-mark and audio fetches. A failed fetch falls back to browser
|
|
861
|
-
speech and reports `pie-tool-init-
|
|
922
|
+
speech and reports `pie-tool-init-fallback`.
|
|
862
923
|
|
|
863
924
|
For a server that authenticates by cookie on another origin, set `credentials:
|
|
864
|
-
"include"`
|
|
925
|
+
"include"` on the `textToSpeech` provider config, beside
|
|
865
926
|
`includeAuthOnAssetFetch`. The cookie reaches speech-mark and audio fetches only
|
|
866
927
|
for origins in `assetOrigins`; unset, every TTS fetch keeps the browser default.
|
|
867
928
|
|
|
@@ -900,27 +961,34 @@ SchoolCity is used as a host-configured integration example for custom transport
|
|
|
900
961
|
Toolkit defaults still remain browser/standard providers unless the host explicitly
|
|
901
962
|
configures custom server-backed TTS.
|
|
902
963
|
|
|
903
|
-
## Test Attempt Session
|
|
964
|
+
## Test Attempt Session
|
|
904
965
|
|
|
905
|
-
The toolkit exposes a canonical `TestAttemptSession` runtime
|
|
966
|
+
The toolkit exposes a canonical `TestAttemptSession` runtime. The host maps its
|
|
967
|
+
backend's attempt payload into it and back out.
|
|
906
968
|
|
|
907
969
|
```typescript
|
|
908
970
|
import {
|
|
909
|
-
|
|
971
|
+
createNewTestAttemptSession,
|
|
910
972
|
toItemSessionsRecord,
|
|
911
|
-
|
|
973
|
+
upsertItemSessionFromPieSessionChange,
|
|
912
974
|
} from "@pie-players/pie-assessment-toolkit";
|
|
913
975
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
976
|
+
let testAttemptSession = createNewTestAttemptSession({
|
|
977
|
+
testAttemptSessionIdentifier: attempt.id,
|
|
978
|
+
assessmentId: assessment.id,
|
|
979
|
+
seed: attempt.id,
|
|
980
|
+
itemIdentifiers: assessment.itemIdentifiers,
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
// Record a PIE session change against its item
|
|
984
|
+
testAttemptSession = upsertItemSessionFromPieSessionChange(testAttemptSession, {
|
|
985
|
+
itemIdentifier: "q1",
|
|
986
|
+
pieSessionId: change.session.id,
|
|
987
|
+
session: change.session,
|
|
917
988
|
});
|
|
918
989
|
|
|
919
990
|
// Use in section-player handoff (same item session shape as item players expect)
|
|
920
991
|
const itemSessions = toItemSessionsRecord(testAttemptSession);
|
|
921
|
-
|
|
922
|
-
// Host-owned backend persistence payload
|
|
923
|
-
const patch = buildActivitySessionPatchFromTestAttemptSession(testAttemptSession);
|
|
924
992
|
```
|
|
925
993
|
|
|
926
994
|
### Integration Boundary
|
|
@@ -944,21 +1012,17 @@ The persistence strategy works with the same `SectionControllerSessionState` sha
|
|
|
944
1012
|
|
|
945
1013
|
- **ToolkitCoordinator**: ⭐ NEW - Centralized service orchestration
|
|
946
1014
|
- **ElementToolStateStore**: ⭐ NEW - Element-level ephemeral tool state management
|
|
947
|
-
- **ToolRegistry**: ⭐ NEW - Registry-based tool management with
|
|
948
|
-
- **ToolPolicyEngine**:
|
|
1015
|
+
- **ToolRegistry**: ⭐ NEW - Registry-based tool management with AfA PNP 3.0 profile support
|
|
1016
|
+
- **ToolPolicyEngine**: AfA PNP 3.0 Personal Needs Profile and host policy decisions via registry-backed policy sources
|
|
949
1017
|
- **ToolCoordinator**: Manages z-index layering and visibility for floating tools
|
|
950
1018
|
- **HighlightCoordinator**: Separate highlight layers for TTS (temporary) and annotations (persistent)
|
|
951
1019
|
- **TTSService**: Text-to-speech with QTI 3.0 catalog support
|
|
952
1020
|
- **AccessibilityCatalogResolver**: QTI 3.0 accessibility catalog management
|
|
953
1021
|
- **SSMLExtractor**: Automatic extraction of embedded `<speak>` tags
|
|
954
|
-
- **ThemeProvider**: Consistent accessibility theming
|
|
955
1022
|
|
|
956
|
-
### ✅
|
|
1023
|
+
### ✅ PNP Support Ids
|
|
957
1024
|
|
|
958
|
-
- **
|
|
959
|
-
- **8 Feature Categories**: Visual, auditory, motor, cognitive, reading, navigation, linguistic, assessment
|
|
960
|
-
- **Example Configurations**: Illustrative PNP profile examples (low vision, dyslexia, ADHD, etc.)
|
|
961
|
-
- **Tool Mappings**: Every packaged tool registration maps to standard QTI 3.0 features
|
|
1025
|
+
- **Support ids are tool ids**: A profile, district policy or item setting names a tool by its `toolId`, and an id no registered tool carries raises `tool-policy.unknownSupportId`. [Support ids](docs/TOOL_REGISTRY.md#support-ids) lists the AfA PNP 3.0 terms the packaged ids serve.
|
|
962
1026
|
|
|
963
1027
|
### ✅ Section Player Integration
|
|
964
1028
|
|
|
@@ -990,7 +1054,8 @@ export interface ToolkitCoordinatorConfig {
|
|
|
990
1054
|
providers?: {
|
|
991
1055
|
textToSpeech?: {
|
|
992
1056
|
enabled?: boolean;
|
|
993
|
-
backend?: 'browser' | '
|
|
1057
|
+
backend?: 'browser' | 'server';
|
|
1058
|
+
serverProvider?: 'polly' | 'google' | 'custom';
|
|
994
1059
|
defaultVoice?: string;
|
|
995
1060
|
rate?: number;
|
|
996
1061
|
};
|
|
@@ -1019,14 +1084,9 @@ export interface ToolkitCoordinatorConfig {
|
|
|
1019
1084
|
### Methods
|
|
1020
1085
|
|
|
1021
1086
|
```typescript
|
|
1022
|
-
// Get all services as a bundle
|
|
1023
|
-
const services = coordinator.getServiceBundle();
|
|
1024
|
-
// Returns: { ttsService, toolCoordinator, highlightCoordinator, elementToolStateStore, catalogResolver }
|
|
1025
|
-
|
|
1026
1087
|
// Tool configuration
|
|
1027
|
-
coordinator.isToolEnabled('textToSpeech'); // Check if tool is enabled
|
|
1028
1088
|
coordinator.getToolConfig('textToSpeech'); // Get tool-specific config
|
|
1029
|
-
coordinator.updateToolConfig('textToSpeech', { rate: 1.5 }); // Update tool config
|
|
1089
|
+
coordinator.updateToolConfig('textToSpeech', { rate: 1.5 }); // Update tool config; replaces the tool's provider, which starts again on next use
|
|
1030
1090
|
|
|
1031
1091
|
// Final teardown by the owner that constructed the coordinator
|
|
1032
1092
|
await coordinator.dispose();
|
|
@@ -1138,6 +1198,8 @@ unsubscribe();
|
|
|
1138
1198
|
### TTSService
|
|
1139
1199
|
|
|
1140
1200
|
```typescript
|
|
1201
|
+
import { BrowserTTSProvider, TTSService } from '@pie-players/pie-assessment-toolkit';
|
|
1202
|
+
|
|
1141
1203
|
const ttsService = new TTSService();
|
|
1142
1204
|
|
|
1143
1205
|
// Initialize with provider
|
|
@@ -1146,8 +1208,8 @@ await ttsService.initialize(new BrowserTTSProvider());
|
|
|
1146
1208
|
// Set catalog resolver for SSML support
|
|
1147
1209
|
ttsService.setCatalogResolver(catalogResolver);
|
|
1148
1210
|
|
|
1149
|
-
// Playback
|
|
1150
|
-
await ttsService.speak(
|
|
1211
|
+
// Playback: reads an element or a range, or the named card in its place
|
|
1212
|
+
await ttsService.speak(promptElement, {
|
|
1151
1213
|
catalogId: 'prompt-001',
|
|
1152
1214
|
language: 'en-US'
|
|
1153
1215
|
});
|
|
@@ -1166,36 +1228,46 @@ await ttsService.updateSettings({
|
|
|
1166
1228
|
|
|
1167
1229
|
### ToolCoordinator
|
|
1168
1230
|
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
// Register tools
|
|
1173
|
-
toolCoordinator.registerTool('calculator', 'Calculator', element);
|
|
1231
|
+
The toolkit owns the coordinator. A tool element registers with the one in its
|
|
1232
|
+
runtime context and hands it the element once it renders:
|
|
1174
1233
|
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1234
|
+
```typescript
|
|
1235
|
+
import {
|
|
1236
|
+
createToolCoordinatorRegistration,
|
|
1237
|
+
ZIndexLayer,
|
|
1238
|
+
} from '@pie-players/pie-assessment-toolkit/tools/registration';
|
|
1179
1239
|
|
|
1180
|
-
|
|
1181
|
-
toolCoordinator.bringToFront(element);
|
|
1240
|
+
const registration = createToolCoordinatorRegistration('Calculator', ZIndexLayer.TOOL);
|
|
1182
1241
|
|
|
1183
|
-
//
|
|
1184
|
-
|
|
1242
|
+
// Re-registers when a republished context brings a new coordinator.
|
|
1243
|
+
$effect(() => registration.sync(coordinator, toolId));
|
|
1244
|
+
$effect(() => {
|
|
1245
|
+
if (coordinator && containerEl && toolId) {
|
|
1246
|
+
coordinator.updateToolElement(toolId, containerEl);
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
onDestroy(() => registration.release());
|
|
1185
1250
|
```
|
|
1186
1251
|
|
|
1252
|
+
Visibility and stacking then go through the coordinator by scoped tool id:
|
|
1253
|
+
`showTool`, `hideTool`, `toggleTool`, `isToolVisible`, `bringToFront(element)`,
|
|
1254
|
+
and `getVisibleTools({ baseId })` / `hideAllTools({ baseId })` for one tool
|
|
1255
|
+
across scopes.
|
|
1256
|
+
|
|
1187
1257
|
### HighlightCoordinator
|
|
1188
1258
|
|
|
1189
1259
|
```typescript
|
|
1260
|
+
import { HighlightColor, HighlightCoordinator } from '@pie-players/pie-assessment-toolkit';
|
|
1261
|
+
|
|
1190
1262
|
const highlightCoordinator = new HighlightCoordinator();
|
|
1191
1263
|
|
|
1192
1264
|
// TTS highlights (temporary)
|
|
1193
|
-
highlightCoordinator.highlightTTSWord(
|
|
1265
|
+
highlightCoordinator.highlightTTSWord([wordRange]); // one range per tree the word spans
|
|
1194
1266
|
highlightCoordinator.highlightTTSSentence([range1, range2]);
|
|
1195
1267
|
highlightCoordinator.clearTTS();
|
|
1196
1268
|
|
|
1197
1269
|
// Annotation highlights (persistent)
|
|
1198
|
-
const id = highlightCoordinator.addAnnotation(range,
|
|
1270
|
+
const id = highlightCoordinator.addAnnotation(range, HighlightColor.YELLOW);
|
|
1199
1271
|
highlightCoordinator.removeAnnotation(id);
|
|
1200
1272
|
```
|
|
1201
1273
|
|
|
@@ -1220,6 +1292,8 @@ maintaining, across every scheme it ships.
|
|
|
1220
1292
|
### AccessibilityCatalogResolver
|
|
1221
1293
|
|
|
1222
1294
|
```typescript
|
|
1295
|
+
import { AccessibilityCatalogResolver } from '@pie-players/pie-assessment-toolkit';
|
|
1296
|
+
|
|
1223
1297
|
const resolver = new AccessibilityCatalogResolver(
|
|
1224
1298
|
assessment.accessibilityCatalogs,
|
|
1225
1299
|
'en-US'
|
|
@@ -1283,6 +1357,8 @@ such as TTS may still call `getAlternative(...)` with a context built by
|
|
|
1283
1357
|
### SSMLExtractor
|
|
1284
1358
|
|
|
1285
1359
|
```typescript
|
|
1360
|
+
import { SSMLExtractor } from '@pie-players/pie-assessment-toolkit';
|
|
1361
|
+
|
|
1286
1362
|
const extractor = new SSMLExtractor();
|
|
1287
1363
|
|
|
1288
1364
|
// Extract from item config
|
|
@@ -1313,20 +1389,22 @@ needs them — signing's card validators and its resolution rules live in
|
|
|
1313
1389
|
|
|
1314
1390
|
### Feature policy without a placement
|
|
1315
1391
|
|
|
1316
|
-
`ToolPolicyEngine.decideFeature(featureId)` (and
|
|
1317
|
-
`ToolkitCoordinator.decideFeaturePolicy(featureId)`) resolve one feature id
|
|
1392
|
+
`ToolPolicyEngine.decideFeature(featureId, scope?)` (and
|
|
1393
|
+
`ToolkitCoordinator.decideFeaturePolicy(featureId, scope?)`) resolve one feature id
|
|
1318
1394
|
through `PnpPolicySource`'s six-level precedence, independent of any toolbar
|
|
1319
1395
|
placement. Use it for capabilities that are not toolbar surfaces — signing is the
|
|
1320
1396
|
first — where a placement-scoped `decide(...)` would answer the wrong question:
|
|
1321
1397
|
absent because it was never placed, rather than absent because policy said no.
|
|
1398
|
+
An item scope applies that item's registered settings, the item restriction and
|
|
1399
|
+
requirement rungs; a decision without one applies no item's.
|
|
1322
1400
|
|
|
1323
|
-
|
|
1324
|
-
grants nothing. Which capabilities a deployment grants by default is a property
|
|
1401
|
+
This package ships no profile. Which capabilities a deployment grants by default is a property
|
|
1325
1402
|
of the program rather than of a capability — TTS is a universal feature in one
|
|
1326
1403
|
program and a documented accommodation in another — so it belongs in policy
|
|
1327
1404
|
configuration alongside the district and test-administration levels. Hosts that
|
|
1328
1405
|
want today's universal set take `createUniversalPersonalNeedsProfile()` from
|
|
1329
|
-
`@pie-players/pie-default-tool-loaders`, which ships it as data
|
|
1406
|
+
`@pie-players/pie-default-tool-loaders`, which ships it as data beside
|
|
1407
|
+
`createEmptyPersonalNeedsProfile()`.
|
|
1330
1408
|
|
|
1331
1409
|
Nothing derives a profile from the registry any more. Doing so read registry
|
|
1332
1410
|
membership as eligibility tier — registration means "policy-addressable", not
|
|
@@ -1352,10 +1430,15 @@ The section player provides automatic ToolkitCoordinator integration:
|
|
|
1352
1430
|
|
|
1353
1431
|
<script type="module">
|
|
1354
1432
|
import { ToolkitCoordinator } from '@pie-players/pie-assessment-toolkit';
|
|
1355
|
-
import {
|
|
1433
|
+
import {
|
|
1434
|
+
createPackagedToolRegistry,
|
|
1435
|
+
DEFAULT_TOOL_MODULE_LOADERS,
|
|
1436
|
+
} from '@pie-players/pie-default-tool-loaders';
|
|
1356
1437
|
|
|
1357
1438
|
// Create coordinator
|
|
1358
|
-
const toolRegistry = createPackagedToolRegistry(
|
|
1439
|
+
const toolRegistry = createPackagedToolRegistry({
|
|
1440
|
+
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
1441
|
+
});
|
|
1359
1442
|
const coordinator = new ToolkitCoordinator({
|
|
1360
1443
|
assessmentId: 'my-assessment',
|
|
1361
1444
|
toolRegistry,
|
|
@@ -1378,21 +1461,27 @@ The section player provides automatic ToolkitCoordinator integration:
|
|
|
1378
1461
|
// - Extracts services from coordinator
|
|
1379
1462
|
// - Scopes its runtime engine to `section-id`
|
|
1380
1463
|
// - Provides runtime context to child components
|
|
1381
|
-
// -
|
|
1464
|
+
// - Registers item catalogs, including SSML catalogs a host extracted
|
|
1465
|
+
// into `config.extractedCatalogs` with `SSMLExtractor`
|
|
1382
1466
|
// - Handles catalog lifecycle
|
|
1383
1467
|
</script>
|
|
1384
1468
|
```
|
|
1385
1469
|
|
|
1386
1470
|
### Runtime Context Contract
|
|
1387
1471
|
|
|
1388
|
-
|
|
1389
|
-
|
|
1472
|
+
Section-player and toolkit components share one runtime context. An element
|
|
1473
|
+
inside the toolkit's tree connects to it:
|
|
1390
1474
|
|
|
1391
1475
|
```typescript
|
|
1392
1476
|
import {
|
|
1393
|
-
|
|
1477
|
+
connectToolRuntimeContext,
|
|
1394
1478
|
type AssessmentToolkitRuntimeContext
|
|
1395
1479
|
} from "@pie-players/pie-assessment-toolkit";
|
|
1480
|
+
|
|
1481
|
+
let runtime: AssessmentToolkitRuntimeContext | undefined;
|
|
1482
|
+
const disconnect = connectToolRuntimeContext(hostElement, (value) => {
|
|
1483
|
+
runtime = value;
|
|
1484
|
+
});
|
|
1396
1485
|
```
|
|
1397
1486
|
|
|
1398
1487
|
`AssessmentToolkitRuntimeContext` carries ambient orchestration dependencies
|
|
@@ -1422,7 +1511,7 @@ player.section = mySection;
|
|
|
1422
1511
|
|
|
1423
1512
|
// Internally creates:
|
|
1424
1513
|
// new ToolkitCoordinator({
|
|
1425
|
-
// assessmentId: 'section-demo-direct', // or
|
|
1514
|
+
// assessmentId: 'section-demo-direct', // or runtime.assessmentId
|
|
1426
1515
|
// toolRegistry, // the player's toolRegistry, else the packaged registry
|
|
1427
1516
|
// tools: player.runtime?.tools // no tools are placed when this is unset
|
|
1428
1517
|
// })
|
|
@@ -1444,9 +1533,14 @@ import {
|
|
|
1444
1533
|
createToolsConfig,
|
|
1445
1534
|
ToolkitCoordinator
|
|
1446
1535
|
} from "@pie-players/pie-assessment-toolkit";
|
|
1447
|
-
import {
|
|
1536
|
+
import {
|
|
1537
|
+
createPackagedToolRegistry,
|
|
1538
|
+
DEFAULT_TOOL_MODULE_LOADERS,
|
|
1539
|
+
} from "@pie-players/pie-default-tool-loaders";
|
|
1448
1540
|
|
|
1449
|
-
const toolRegistry = createPackagedToolRegistry(
|
|
1541
|
+
const toolRegistry = createPackagedToolRegistry({
|
|
1542
|
+
toolModuleLoaders: DEFAULT_TOOL_MODULE_LOADERS,
|
|
1543
|
+
});
|
|
1450
1544
|
const { config, diagnostics } = createToolsConfig({
|
|
1451
1545
|
source: "host.bootstrap",
|
|
1452
1546
|
strictness: "error",
|
|
@@ -1474,7 +1568,7 @@ const coordinator = new ToolkitCoordinator({
|
|
|
1474
1568
|
Notes:
|
|
1475
1569
|
- `providers.textToSpeech` is the canonical TTS provider key.
|
|
1476
1570
|
- `providers.tts` is rejected by the validation contract.
|
|
1477
|
-
-
|
|
1571
|
+
- A tool registration can declare `sanitizeConfig` and `validateConfig` hooks for its `providers.<toolId>` entry.
|
|
1478
1572
|
- Hosts can react to framework errors via the `framework-error` DOM event,
|
|
1479
1573
|
the `onFrameworkError(model)` callback prop, or by subscribing directly
|
|
1480
1574
|
to the package-internal bus via
|
|
@@ -1488,133 +1582,63 @@ Notes:
|
|
|
1488
1582
|
|
|
1489
1583
|
## Section Runtime Engine (advanced)
|
|
1490
1584
|
|
|
1491
|
-
The
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
`<pie-assessment-toolkit>`
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
## Writing a capability package
|
|
1523
|
-
|
|
1524
|
-
`@pie-players/pie-assessment-toolkit/tools/internal` is what a capability
|
|
1525
|
-
package imports: the `ToolRegistration` contract, the `ToolProviderApi` its
|
|
1526
|
-
provider descriptor creates, the surface and content dependency types,
|
|
1527
|
-
`resolveToolTag` and the toolbar registration helpers. Same
|
|
1528
|
-
stability contract as the other `*/internal` entry points — symbols may change
|
|
1529
|
-
between minor versions with a changeset note.
|
|
1530
|
-
|
|
1531
|
-
Import it rather than the package root: the root pulls in `ToolkitCoordinator`,
|
|
1532
|
-
`TTSService` and the components, none of which a registration needs, and a
|
|
1533
|
-
capability bundle that inlines them ends up with a second `ToolRegistry` class
|
|
1534
|
-
that fails every `instanceof` across the host boundary. Mark the toolkit external
|
|
1535
|
-
in the package's build with a pattern that covers subpaths, not a bare specifier.
|
|
1536
|
-
|
|
1537
|
-
`@pie-players/pie-tool-sign-language` is the worked example end to end: a
|
|
1538
|
-
registration, a content resolver, its own custom element, and no edit to any
|
|
1539
|
-
generic package. `packages/default-tool-loaders/README.md` covers how a
|
|
1540
|
-
deployment then composes it in, and `docs/TOOL_REGISTRY.md` the registration and
|
|
1541
|
-
host-surface contracts.
|
|
1542
|
-
|
|
1543
|
-
### Lifecycle emit coordination
|
|
1544
|
-
|
|
1545
|
-
When `<pie-assessment-toolkit>` is nested inside a section-player layout,
|
|
1546
|
-
the layout kernel publishes a lifecycle handle via
|
|
1547
|
-
`sectionRuntimeEngineHostContext`. The toolkit detects that host
|
|
1548
|
-
lifecycle owner and **suppresses its own external lifecycle DOM emits
|
|
1549
|
-
and `onStageChange` callback** in favor of the layout CE host. From the
|
|
1550
|
-
outside, one cohort yields one `pie-stage-change` /
|
|
1551
|
-
`pie-loading-complete` chain on the layout CE host regardless of wrapper
|
|
1552
|
-
depth. Controller-side
|
|
1553
|
-
registration, content loading, session propagation, and persistence
|
|
1554
|
-
remain toolkit-local through its own `SectionRuntimeEngine` instance.
|
|
1555
|
-
A standalone `<pie-assessment-toolkit>` (no host context) emits from
|
|
1556
|
-
its own engine.
|
|
1557
|
-
|
|
1558
|
-
**Detection.** If a custom layout shell emits two `pie-stage-change`
|
|
1559
|
-
events per stage transition (or two `pie-loading-complete` per cohort)
|
|
1560
|
-
on the same layout CE — typically with two distinct `detail.runtimeId`
|
|
1561
|
-
values — the shell has not published its engine via
|
|
1562
|
-
`sectionRuntimeEngineHostContext`, so the wrapped
|
|
1563
|
-
`<pie-assessment-toolkit>` falls back to its standalone lifecycle emit
|
|
1564
|
-
path. Wire the bridge as shown below.
|
|
1585
|
+
The section runtime engine is the section player's stage chain: a pure FSM that
|
|
1586
|
+
turns the cohort a layout shows, the controller resolving and the readiness
|
|
1587
|
+
signals the layout derives into `pie-stage-change` and `pie-loading-complete`.
|
|
1588
|
+
The section player's layout kernel owns one per layout element, and it is the
|
|
1589
|
+
only stage emitter. `<pie-assessment-toolkit>` emits no stages: its
|
|
1590
|
+
`SectionControllerBinding` resolves the section's controller, which the toolkit
|
|
1591
|
+
announces with `toolkit-ready`, and the toolkit publishes framework errors.
|
|
1592
|
+
|
|
1593
|
+
### Entry point
|
|
1594
|
+
|
|
1595
|
+
`@pie-players/pie-assessment-toolkit/runtime/engine` is the stable entry for a
|
|
1596
|
+
host that drives a section's stage chain. It carries `SectionRuntimeEngine` and
|
|
1597
|
+
the vocabulary of its surface: the `SectionEngineInput`s `dispatchInput` takes,
|
|
1598
|
+
the `SectionEngineOutput`s `subscribe` delivers, the `SectionEngineState` and
|
|
1599
|
+
`SectionEnginePhase` `getState` returns, the cohort helpers (`makeCohort`,
|
|
1600
|
+
`cohortsEqual`), the runtime config types with
|
|
1601
|
+
`resolveSectionEngineRuntimeState`, and the readiness signals. The engine core, its adapter and its DOM bridge have no
|
|
1602
|
+
entry: a host reaches them through the facade.
|
|
1603
|
+
|
|
1604
|
+
### Stage chain
|
|
1605
|
+
|
|
1606
|
+
A cohort moves through `composed`, `engine-ready` and `interactive`, and ends at
|
|
1607
|
+
`disposed` on a cohort change or unmount. `engine-ready` follows the
|
|
1608
|
+
`section-controller-resolved` input, `interactive` the readiness signals that
|
|
1609
|
+
satisfy the readiness mode. A readiness update that reports `runtimeError`
|
|
1610
|
+
before `interactive` ends the chain: the first stage the cohort did not reach is
|
|
1611
|
+
`failed` and the rest up to `interactive` are `skipped`. The section player sets
|
|
1612
|
+
`runtimeError` from a non-recoverable `framework-error`, so a host waiting on
|
|
1613
|
+
`engine-ready` or `interactive` always hears an answer. `pie-loading-complete`
|
|
1614
|
+
fires once per cohort, when every item has loaded.
|
|
1565
1615
|
|
|
1566
1616
|
### Common-host wiring example
|
|
1567
1617
|
|
|
1568
|
-
Most hosts never construct the engine
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1618
|
+
Most hosts never construct the engine: the section-player layout elements do,
|
|
1619
|
+
and a host-built section layout takes its engine from
|
|
1620
|
+
`pie-section-player-kernel-host` (see the section player's
|
|
1621
|
+
[Custom layout authoring](../section-player/README.md#custom-layout-authoring)).
|
|
1622
|
+
Use the facade for a section renderer outside the section player. The shape
|
|
1623
|
+
mirrors the section-player kernel:
|
|
1572
1624
|
|
|
1573
1625
|
```ts
|
|
1574
|
-
import { ContextProvider } from "@pie-players/pie-context";
|
|
1575
1626
|
import {
|
|
1576
1627
|
SectionRuntimeEngine,
|
|
1577
|
-
sectionRuntimeEngineHostContext,
|
|
1578
|
-
} from "@pie-players/pie-assessment-toolkit/runtime/engine";
|
|
1579
|
-
import {
|
|
1580
|
-
FrameworkErrorBus,
|
|
1581
1628
|
makeCohort,
|
|
1582
|
-
} from "@pie-players/pie-assessment-toolkit/runtime/
|
|
1629
|
+
} from "@pie-players/pie-assessment-toolkit/runtime/engine";
|
|
1583
1630
|
|
|
1584
|
-
const bus = new FrameworkErrorBus();
|
|
1585
1631
|
const engine = new SectionRuntimeEngine();
|
|
1586
1632
|
|
|
1587
|
-
// 1. Attach to the layout
|
|
1588
|
-
|
|
1589
|
-
engine.attachHost({
|
|
1590
|
-
host: layoutHostElement,
|
|
1591
|
-
sourceCe: "my-custom-layout",
|
|
1592
|
-
frameworkErrorBus: bus,
|
|
1593
|
-
coordinator: toolkitCoordinator,
|
|
1594
|
-
});
|
|
1633
|
+
// 1. Attach to the layout host. `sourceCe` is stamped onto every event.
|
|
1634
|
+
engine.attachHost({ host: layoutHostElement, sourceCe: "my-custom-layout" });
|
|
1595
1635
|
|
|
1596
|
-
// 2.
|
|
1597
|
-
|
|
1598
|
-
//
|
|
1599
|
-
// plumbing locally.
|
|
1600
|
-
const engineProvider = new ContextProvider(layoutHostElement, {
|
|
1601
|
-
context: sectionRuntimeEngineHostContext,
|
|
1602
|
-
initialValue: {
|
|
1603
|
-
engine: {
|
|
1604
|
-
getRuntimeId: () => engine.getRuntimeId(),
|
|
1605
|
-
},
|
|
1606
|
-
},
|
|
1636
|
+
// 2. (Optional) Subscribe to the output batches the DOM bridge dispatches.
|
|
1637
|
+
engine.subscribe((outputs) => {
|
|
1638
|
+
// `stage-change` and `loading-complete` outputs
|
|
1607
1639
|
});
|
|
1608
|
-
engineProvider.connect();
|
|
1609
1640
|
|
|
1610
|
-
// 3.
|
|
1611
|
-
// of outputs the DOM-event bridge fans out to the host element.
|
|
1612
|
-
engine.subscribe((output) => {
|
|
1613
|
-
// tap stage transitions, readiness updates, framework errors,
|
|
1614
|
-
// instrumentation events
|
|
1615
|
-
});
|
|
1616
|
-
|
|
1617
|
-
// 4. Drive the engine. Use real `SectionEngineInput` shapes:
|
|
1641
|
+
// 3. Drive the engine with `SectionEngineInput`s.
|
|
1618
1642
|
const cohort = makeCohort({ sectionId, attemptId });
|
|
1619
1643
|
engine.dispatchInput({
|
|
1620
1644
|
kind: "initialize",
|
|
@@ -1624,7 +1648,10 @@ engine.dispatchInput({
|
|
|
1624
1648
|
itemCount,
|
|
1625
1649
|
});
|
|
1626
1650
|
|
|
1627
|
-
//
|
|
1651
|
+
// When the toolkit's `section-ready` delivers the cohort's section controller:
|
|
1652
|
+
engine.dispatchInput({ kind: "section-controller-resolved" });
|
|
1653
|
+
|
|
1654
|
+
// On readiness signal updates:
|
|
1628
1655
|
engine.dispatchInput({
|
|
1629
1656
|
kind: "update-readiness-signals",
|
|
1630
1657
|
signals: {
|
|
@@ -1633,34 +1660,44 @@ engine.dispatchInput({
|
|
|
1633
1660
|
allLoadingComplete,
|
|
1634
1661
|
runtimeError,
|
|
1635
1662
|
},
|
|
1636
|
-
loadedCount,
|
|
1637
1663
|
itemCount,
|
|
1638
1664
|
mode: "progressive",
|
|
1639
1665
|
});
|
|
1640
1666
|
|
|
1641
|
-
// On unmount
|
|
1642
|
-
engineProvider.disconnect();
|
|
1667
|
+
// On unmount; emits `disposed` for the active cohort.
|
|
1643
1668
|
engine.dispose();
|
|
1644
1669
|
```
|
|
1645
1670
|
|
|
1646
|
-
The
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
section-player
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1671
|
+
The adapter dispatches `pie-stage-change` and `pie-loading-complete` on `host`,
|
|
1672
|
+
bubbling and composed, as the toolkit dispatches its own events. Framework
|
|
1673
|
+
errors are not an engine output. The toolkit that owns the coordinator publishes
|
|
1674
|
+
each one once: one bubbling, composed `framework-error` event, which reaches the
|
|
1675
|
+
layout host and `document`, and one `onFrameworkError` call. A coordinator the
|
|
1676
|
+
host passes in reports through the same surfaces.
|
|
1677
|
+
`packages/section-player/tests/section-player-event-delivery.spec.ts` pins these
|
|
1678
|
+
counts.
|
|
1679
|
+
|
|
1680
|
+
## Writing a capability package
|
|
1681
|
+
|
|
1682
|
+
`@pie-players/pie-assessment-toolkit/tools/registration` is the stable entry a
|
|
1683
|
+
capability package imports: the `ToolRegistration` contract, the
|
|
1684
|
+
`ToolProviderApi` its provider descriptor creates, the surface and content
|
|
1685
|
+
dependency types, the context predicates, `createToolElement`, `resolveToolTag`
|
|
1686
|
+
and the toolbar registration helpers. A renderer that puts registrations on
|
|
1687
|
+
screen uses the same entry for `createToolSurfaceHost` and
|
|
1688
|
+
`resolveContentCapabilities`.
|
|
1689
|
+
|
|
1690
|
+
Import it rather than the package root: the root pulls in `ToolkitCoordinator`,
|
|
1691
|
+
`TTSService` and the components, none of which a registration needs, and a
|
|
1692
|
+
capability bundle that inlines them ends up with a second `ToolRegistry` class
|
|
1693
|
+
that fails every `instanceof` across the host boundary. Mark the toolkit external
|
|
1694
|
+
in the package's build with a pattern that covers subpaths, not a bare specifier.
|
|
1695
|
+
|
|
1696
|
+
`@pie-players/pie-tool-sign-language` is the worked example end to end: a
|
|
1697
|
+
registration, a content resolver, its own custom element, and no edit to any
|
|
1698
|
+
generic package. `packages/default-tool-loaders/README.md` covers how a
|
|
1699
|
+
deployment then composes it in, and `docs/TOOL_REGISTRY.md` the registration and
|
|
1700
|
+
host-surface contracts.
|
|
1664
1701
|
|
|
1665
1702
|
## State Separation: Tool State vs Session Data
|
|
1666
1703
|
|
|
@@ -1725,8 +1762,7 @@ Full TypeScript definitions included:
|
|
|
1725
1762
|
import type {
|
|
1726
1763
|
ToolkitCoordinatorApi,
|
|
1727
1764
|
ElementToolStateStoreApi,
|
|
1728
|
-
ToolkitCoordinatorConfig
|
|
1729
|
-
ToolkitServiceBundle
|
|
1765
|
+
ToolkitCoordinatorConfig
|
|
1730
1766
|
} from '@pie-players/pie-assessment-toolkit';
|
|
1731
1767
|
```
|
|
1732
1768
|
|
|
@@ -1760,7 +1796,7 @@ from tool configuration. Two sanitization layers apply:
|
|
|
1760
1796
|
|
|
1761
1797
|
## Related Documentation
|
|
1762
1798
|
|
|
1763
|
-
- **[Tool Registry Architecture](docs/TOOL_REGISTRY.md)** - ⭐ NEW - Registry-based tool management and
|
|
1799
|
+
- **[Tool Registry Architecture](docs/TOOL_REGISTRY.md)** - ⭐ NEW - Registry-based tool management and AfA PNP 3.0 profile support
|
|
1764
1800
|
- **[PNP Configuration Guide](docs/PNP_CONFIGURATION.md)** - ⭐ NEW - How to configure student profiles, district policies, and governance rules
|
|
1765
1801
|
- [ToolkitCoordinator Architecture](../../docs/architecture/architecture.md#toolkitcoordinator-centralized-service-management) - Design decisions and patterns
|
|
1766
1802
|
- [Section Player README](../section-player/README.md) - Section player integration
|