@pie-players/pie-assessment-toolkit 0.2.0

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 (194) hide show
  1. package/README.md +520 -0
  2. package/dist/attempt/TestSession.d.ts +90 -0
  3. package/dist/attempt/TestSession.d.ts.map +1 -0
  4. package/dist/attempt/TestSession.js +174 -0
  5. package/dist/attempt/TestSession.js.map +1 -0
  6. package/dist/core/TypedEventBus.d.ts +48 -0
  7. package/dist/core/TypedEventBus.d.ts.map +1 -0
  8. package/dist/core/TypedEventBus.js +63 -0
  9. package/dist/core/TypedEventBus.js.map +1 -0
  10. package/dist/index.d.ts +37 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +44 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/item-loader.d.ts +45 -0
  15. package/dist/item-loader.d.ts.map +1 -0
  16. package/dist/item-loader.js +62 -0
  17. package/dist/item-loader.js.map +1 -0
  18. package/dist/player/AssessmentPlayer.d.ts +388 -0
  19. package/dist/player/AssessmentPlayer.d.ts.map +1 -0
  20. package/dist/player/AssessmentPlayer.js +965 -0
  21. package/dist/player/AssessmentPlayer.js.map +1 -0
  22. package/dist/player/index.d.ts +17 -0
  23. package/dist/player/index.d.ts.map +1 -0
  24. package/dist/player/index.js +15 -0
  25. package/dist/player/index.js.map +1 -0
  26. package/dist/player/navigation-types.d.ts +63 -0
  27. package/dist/player/navigation-types.d.ts.map +1 -0
  28. package/dist/player/navigation-types.js +7 -0
  29. package/dist/player/navigation-types.js.map +1 -0
  30. package/dist/player/qti-navigation.d.ts +29 -0
  31. package/dist/player/qti-navigation.d.ts.map +1 -0
  32. package/dist/player/qti-navigation.js +160 -0
  33. package/dist/player/qti-navigation.js.map +1 -0
  34. package/dist/reference-layout/index.d.ts +19 -0
  35. package/dist/reference-layout/index.d.ts.map +1 -0
  36. package/dist/reference-layout/index.js +20 -0
  37. package/dist/reference-layout/index.js.map +1 -0
  38. package/dist/services/AccessibilityCatalogResolver.d.ts +141 -0
  39. package/dist/services/AccessibilityCatalogResolver.d.ts.map +1 -0
  40. package/dist/services/AccessibilityCatalogResolver.js +249 -0
  41. package/dist/services/AccessibilityCatalogResolver.js.map +1 -0
  42. package/dist/services/AssessmentAuthoringService.d.ts +60 -0
  43. package/dist/services/AssessmentAuthoringService.d.ts.map +1 -0
  44. package/dist/services/AssessmentAuthoringService.js +183 -0
  45. package/dist/services/AssessmentAuthoringService.js.map +1 -0
  46. package/dist/services/ContextVariableStore.d.ts +114 -0
  47. package/dist/services/ContextVariableStore.d.ts.map +1 -0
  48. package/dist/services/ContextVariableStore.js +207 -0
  49. package/dist/services/ContextVariableStore.js.map +1 -0
  50. package/dist/services/ElementToolStateStore.d.ts +125 -0
  51. package/dist/services/ElementToolStateStore.d.ts.map +1 -0
  52. package/dist/services/ElementToolStateStore.js +200 -0
  53. package/dist/services/ElementToolStateStore.js.map +1 -0
  54. package/dist/services/HighlightCoordinator.d.ts +179 -0
  55. package/dist/services/HighlightCoordinator.d.ts.map +1 -0
  56. package/dist/services/HighlightCoordinator.js +446 -0
  57. package/dist/services/HighlightCoordinator.js.map +1 -0
  58. package/dist/services/I18nService.d.ts +121 -0
  59. package/dist/services/I18nService.d.ts.map +1 -0
  60. package/dist/services/I18nService.js +276 -0
  61. package/dist/services/I18nService.js.map +1 -0
  62. package/dist/services/PNPMapper.d.ts +71 -0
  63. package/dist/services/PNPMapper.d.ts.map +1 -0
  64. package/dist/services/PNPMapper.js +98 -0
  65. package/dist/services/PNPMapper.js.map +1 -0
  66. package/dist/services/PNPToolResolver.d.ts +127 -0
  67. package/dist/services/PNPToolResolver.d.ts.map +1 -0
  68. package/dist/services/PNPToolResolver.js +222 -0
  69. package/dist/services/PNPToolResolver.js.map +1 -0
  70. package/dist/services/SSMLExtractor.d.ts +61 -0
  71. package/dist/services/SSMLExtractor.d.ts.map +1 -0
  72. package/dist/services/SSMLExtractor.js +181 -0
  73. package/dist/services/SSMLExtractor.js.map +1 -0
  74. package/dist/services/TTSService.d.ts +162 -0
  75. package/dist/services/TTSService.d.ts.map +1 -0
  76. package/dist/services/TTSService.js +465 -0
  77. package/dist/services/TTSService.js.map +1 -0
  78. package/dist/services/ThemeProvider.d.ts +95 -0
  79. package/dist/services/ThemeProvider.d.ts.map +1 -0
  80. package/dist/services/ThemeProvider.js +299 -0
  81. package/dist/services/ThemeProvider.js.map +1 -0
  82. package/dist/services/ToolConfigResolver.d.ts +162 -0
  83. package/dist/services/ToolConfigResolver.d.ts.map +1 -0
  84. package/dist/services/ToolConfigResolver.js +230 -0
  85. package/dist/services/ToolConfigResolver.js.map +1 -0
  86. package/dist/services/ToolCoordinator.d.ts +137 -0
  87. package/dist/services/ToolCoordinator.d.ts.map +1 -0
  88. package/dist/services/ToolCoordinator.js +318 -0
  89. package/dist/services/ToolCoordinator.js.map +1 -0
  90. package/dist/services/ToolkitCoordinator.d.ts +188 -0
  91. package/dist/services/ToolkitCoordinator.d.ts.map +1 -0
  92. package/dist/services/ToolkitCoordinator.js +252 -0
  93. package/dist/services/ToolkitCoordinator.js.map +1 -0
  94. package/dist/services/interfaces.d.ts +391 -0
  95. package/dist/services/interfaces.d.ts.map +1 -0
  96. package/dist/services/interfaces.js +12 -0
  97. package/dist/services/interfaces.js.map +1 -0
  98. package/dist/services/tool-providers/DesmosToolProvider.d.ts +103 -0
  99. package/dist/services/tool-providers/DesmosToolProvider.d.ts.map +1 -0
  100. package/dist/services/tool-providers/DesmosToolProvider.js +127 -0
  101. package/dist/services/tool-providers/DesmosToolProvider.js.map +1 -0
  102. package/dist/services/tool-providers/IToolProvider.d.ts +137 -0
  103. package/dist/services/tool-providers/IToolProvider.d.ts.map +1 -0
  104. package/dist/services/tool-providers/IToolProvider.js +13 -0
  105. package/dist/services/tool-providers/IToolProvider.js.map +1 -0
  106. package/dist/services/tool-providers/TIToolProvider.d.ts +107 -0
  107. package/dist/services/tool-providers/TIToolProvider.d.ts.map +1 -0
  108. package/dist/services/tool-providers/TIToolProvider.js +123 -0
  109. package/dist/services/tool-providers/TIToolProvider.js.map +1 -0
  110. package/dist/services/tool-providers/TTSToolProvider.d.ts +143 -0
  111. package/dist/services/tool-providers/TTSToolProvider.d.ts.map +1 -0
  112. package/dist/services/tool-providers/TTSToolProvider.js +168 -0
  113. package/dist/services/tool-providers/TTSToolProvider.js.map +1 -0
  114. package/dist/services/tool-providers/ToolProviderRegistry.d.ts +165 -0
  115. package/dist/services/tool-providers/ToolProviderRegistry.d.ts.map +1 -0
  116. package/dist/services/tool-providers/ToolProviderRegistry.js +240 -0
  117. package/dist/services/tool-providers/ToolProviderRegistry.js.map +1 -0
  118. package/dist/services/tool-providers/index.d.ts +18 -0
  119. package/dist/services/tool-providers/index.d.ts.map +1 -0
  120. package/dist/services/tool-providers/index.js +15 -0
  121. package/dist/services/tool-providers/index.js.map +1 -0
  122. package/dist/services/tts/browser-provider.d.ts +24 -0
  123. package/dist/services/tts/browser-provider.d.ts.map +1 -0
  124. package/dist/services/tts/browser-provider.js +177 -0
  125. package/dist/services/tts/browser-provider.js.map +1 -0
  126. package/dist/services/tts/provider-interface.d.ts +131 -0
  127. package/dist/services/tts/provider-interface.d.ts.map +1 -0
  128. package/dist/services/tts/provider-interface.js +10 -0
  129. package/dist/services/tts/provider-interface.js.map +1 -0
  130. package/dist/tools/calculators/desmos-provider.d.ts +54 -0
  131. package/dist/tools/calculators/desmos-provider.d.ts.map +1 -0
  132. package/dist/tools/calculators/desmos-provider.js +384 -0
  133. package/dist/tools/calculators/desmos-provider.js.map +1 -0
  134. package/dist/tools/calculators/mathjs-provider.d.ts +50 -0
  135. package/dist/tools/calculators/mathjs-provider.d.ts.map +1 -0
  136. package/dist/tools/calculators/mathjs-provider.js +660 -0
  137. package/dist/tools/calculators/mathjs-provider.js.map +1 -0
  138. package/dist/tools/calculators/ti-provider.d.ts +122 -0
  139. package/dist/tools/calculators/ti-provider.d.ts.map +1 -0
  140. package/dist/tools/calculators/ti-provider.js +587 -0
  141. package/dist/tools/calculators/ti-provider.js.map +1 -0
  142. package/dist/tools/client.d.ts +17 -0
  143. package/dist/tools/client.d.ts.map +1 -0
  144. package/dist/tools/client.js +26 -0
  145. package/dist/tools/client.js.map +1 -0
  146. package/dist/tools/index.d.ts +8 -0
  147. package/dist/tools/index.d.ts.map +1 -0
  148. package/dist/tools/index.js +10 -0
  149. package/dist/tools/index.js.map +1 -0
  150. package/dist/tools/library-loader.d.ts +63 -0
  151. package/dist/tools/library-loader.d.ts.map +1 -0
  152. package/dist/tools/library-loader.js +292 -0
  153. package/dist/tools/library-loader.js.map +1 -0
  154. package/dist/tools/response-discovery.d.ts +72 -0
  155. package/dist/tools/response-discovery.d.ts.map +1 -0
  156. package/dist/tools/response-discovery.js +183 -0
  157. package/dist/tools/response-discovery.js.map +1 -0
  158. package/dist/tools/tool-coordinator.d.ts +76 -0
  159. package/dist/tools/tool-coordinator.d.ts.map +1 -0
  160. package/dist/tools/tool-coordinator.js +197 -0
  161. package/dist/tools/tool-coordinator.js.map +1 -0
  162. package/dist/tools/types.d.ts +427 -0
  163. package/dist/tools/types.d.ts.map +1 -0
  164. package/dist/tools/types.js +13 -0
  165. package/dist/tools/types.js.map +1 -0
  166. package/dist/tools/variant-resolver.d.ts +48 -0
  167. package/dist/tools/variant-resolver.d.ts.map +1 -0
  168. package/dist/tools/variant-resolver.js +214 -0
  169. package/dist/tools/variant-resolver.js.map +1 -0
  170. package/dist/types/events.d.ts +157 -0
  171. package/dist/types/events.d.ts.map +1 -0
  172. package/dist/types/events.js +11 -0
  173. package/dist/types/events.js.map +1 -0
  174. package/dist/utils/logger.d.ts +7 -0
  175. package/dist/utils/logger.d.ts.map +1 -0
  176. package/dist/utils/logger.js +11 -0
  177. package/dist/utils/logger.js.map +1 -0
  178. package/package.json +71 -0
  179. package/src/README.md +626 -0
  180. package/src/components/QuestionToolBar.svelte +406 -0
  181. package/src/player/AssessmentLayout.svelte +68 -0
  182. package/src/player/README.md +154 -0
  183. package/src/reference-layout/README.md +135 -0
  184. package/src/reference-layout/ReferenceLayout.svelte +209 -0
  185. package/src/reference-layout/components/AssessmentContent.svelte +259 -0
  186. package/src/reference-layout/components/AssessmentFooter.svelte +89 -0
  187. package/src/reference-layout/components/AssessmentHeader.svelte +250 -0
  188. package/src/reference-layout/components/AssessmentNavigation.svelte +134 -0
  189. package/src/reference-layout/components/AssessmentToolsBar.svelte +176 -0
  190. package/src/reference-layout/components/ItemPanel.svelte +200 -0
  191. package/src/reference-layout/components/NotesPanel.svelte +179 -0
  192. package/src/reference-layout/components/PassagePanel.svelte +76 -0
  193. package/src/tools/README.md +616 -0
  194. package/src/tools/calculators/README.md +395 -0
package/README.md ADDED
@@ -0,0 +1,520 @@
1
+ # PIE Assessment Toolkit
2
+
3
+ **Independent, composable services** for coordinating tools, accommodations, and item players in assessment applications.
4
+
5
+ This is not an opinionated framework or monolithic "player" - it's a toolkit that solves specific problems through centralized service management.
6
+
7
+ ## What's New: ToolkitCoordinator
8
+
9
+ ✨ **Centralized Service Management**: The new `ToolkitCoordinator` provides a single entry point for all toolkit services, simplifying initialization and configuration.
10
+
11
+ **Before** (scattered services):
12
+ ```typescript
13
+ // Create 5+ services independently
14
+ const ttsService = new TTSService();
15
+ const toolCoordinator = new ToolCoordinator();
16
+ const highlightCoordinator = new HighlightCoordinator();
17
+ const catalogResolver = new AccessibilityCatalogResolver([...]);
18
+ // Missing: ElementToolStateStore
19
+
20
+ await ttsService.initialize(new BrowserTTSProvider());
21
+ ttsService.setCatalogResolver(catalogResolver);
22
+
23
+ // Pass all services separately
24
+ player.ttsService = ttsService;
25
+ player.toolCoordinator = toolCoordinator;
26
+ // ...
27
+ ```
28
+
29
+ **After** (coordinator orchestrates):
30
+ ```typescript
31
+ // Create one coordinator with configuration
32
+ const toolkitCoordinator = new ToolkitCoordinator({
33
+ assessmentId: 'my-assessment',
34
+ tools: {
35
+ tts: { enabled: true },
36
+ answerEliminator: { enabled: true }
37
+ }
38
+ });
39
+
40
+ // Pass single coordinator to player
41
+ player.toolkitCoordinator = toolkitCoordinator;
42
+ ```
43
+
44
+ ## What Does It Solve?
45
+
46
+ - **Centralized service management**: One coordinator owns all toolkit services
47
+ - **Tool coordination**: z-index management, visibility state, element-level state
48
+ - **Accommodation support**: IEP/504 tool configuration logic
49
+ - **TTS + annotation coordination**: Prevent conflicts between highlights
50
+ - **Event communication**: Standard contracts between components
51
+ - **Accessibility theming**: Consistent high-contrast, font sizing
52
+ - **State separation**: Ephemeral tool state separate from persistent session data
53
+
54
+ ## Architecture Overview
55
+
56
+ See [ToolkitCoordinator Architecture](../../docs/architecture/TOOLKIT_COORDINATOR.md) for complete design documentation.
57
+
58
+ ### Core Principles
59
+
60
+ 1. **Centralized Coordination**: ToolkitCoordinator orchestrates all services
61
+ 2. **Composable Services**: Import only what you need (or use coordinator for convenience)
62
+ 3. **No Framework Lock-in**: Works with any JavaScript framework
63
+ 4. **Product Control**: Products control navigation, persistence, layout, backend
64
+ 5. **Standard Contracts**: Well-defined event types for component communication
65
+ 6. **Element-Level Granularity**: Tool state tracked per PIE element, not per item
66
+ 7. **State Separation**: Tool state (ephemeral) separate from PIE session data (persistent)
67
+
68
+ ## Quick Start
69
+
70
+ ### Option 1: Use ToolkitCoordinator (Recommended)
71
+
72
+ ```typescript
73
+ import { ToolkitCoordinator } from '@pie-players/pie-assessment-toolkit';
74
+
75
+ // Create coordinator with configuration
76
+ const coordinator = new ToolkitCoordinator({
77
+ assessmentId: 'demo-assessment',
78
+ tools: {
79
+ tts: { enabled: true, defaultVoice: 'en-US' },
80
+ answerEliminator: { enabled: true }
81
+ },
82
+ accessibility: {
83
+ catalogs: assessment.accessibilityCatalogs || [],
84
+ language: 'en-US'
85
+ }
86
+ });
87
+
88
+ // Pass to section player
89
+ const player = document.getElementById('player');
90
+ player.toolkitCoordinator = coordinator;
91
+
92
+ // Access services directly if needed
93
+ const ttsService = coordinator.ttsService;
94
+ const toolState = coordinator.elementToolStateStore.getAllState();
95
+ ```
96
+
97
+ ### Option 2: Create Services Manually (Advanced)
98
+
99
+ ```typescript
100
+ import {
101
+ TTSService,
102
+ BrowserTTSProvider,
103
+ ToolCoordinator,
104
+ HighlightCoordinator,
105
+ AccessibilityCatalogResolver,
106
+ ElementToolStateStore
107
+ } from '@pie-players/pie-assessment-toolkit';
108
+
109
+ // Initialize each service independently
110
+ const ttsService = new TTSService();
111
+ const toolCoordinator = new ToolCoordinator();
112
+ const highlightCoordinator = new HighlightCoordinator();
113
+ const elementToolStateStore = new ElementToolStateStore();
114
+ const catalogResolver = new AccessibilityCatalogResolver([], 'en-US');
115
+
116
+ await ttsService.initialize(new BrowserTTSProvider());
117
+ ttsService.setCatalogResolver(catalogResolver);
118
+
119
+ // Pass services individually
120
+ player.ttsService = ttsService;
121
+ player.toolCoordinator = toolCoordinator;
122
+ // ...
123
+ ```
124
+
125
+ ## Implementation Status
126
+
127
+ ### ✅ Core Infrastructure
128
+
129
+ - **TypedEventBus**: Type-safe event bus built on native EventTarget
130
+ - **Event Types**: Complete event definitions (player, tools, navigation, state, interaction)
131
+
132
+ ### ✅ Toolkit Services
133
+
134
+ - **ToolkitCoordinator**: ⭐ NEW - Centralized service orchestration
135
+ - **ElementToolStateStore**: ⭐ NEW - Element-level ephemeral tool state management
136
+ - **ToolCoordinator**: Manages z-index layering and visibility for floating tools
137
+ - **HighlightCoordinator**: Separate highlight layers for TTS (temporary) and annotations (persistent)
138
+ - **TTSService**: Text-to-speech with QTI 3.0 catalog support
139
+ - **AccessibilityCatalogResolver**: QTI 3.0 accessibility catalog management
140
+ - **SSMLExtractor**: Automatic extraction of embedded `<speak>` tags
141
+ - **ThemeProvider**: Consistent accessibility theming
142
+ - **PNPToolResolver**: QTI 3.0 Personal Needs Profile tool resolution
143
+
144
+ ### ✅ Section Player Integration
145
+
146
+ The toolkit integrates seamlessly with the **PIE Section Player**:
147
+
148
+ - **Primary Interface**: Section player is the main integration point
149
+ - **Default Coordinator**: Creates ToolkitCoordinator automatically if not provided
150
+ - **Automatic SSML Extraction**: Extracts embedded `<speak>` tags from passages and items
151
+ - **Catalog Lifecycle**: Manages item-level catalogs automatically
152
+ - **Service Coordination**: All toolkit services work together automatically
153
+
154
+ ## ToolkitCoordinator API
155
+
156
+ ### Configuration
157
+
158
+ ```typescript
159
+ export interface ToolkitCoordinatorConfig {
160
+ assessmentId: string; // Required: unique assessment identifier
161
+ tools?: {
162
+ tts?: {
163
+ enabled?: boolean;
164
+ defaultVoice?: string;
165
+ rate?: number;
166
+ provider?: 'browser' | 'server';
167
+ };
168
+ answerEliminator?: {
169
+ enabled?: boolean;
170
+ strategy?: 'strikethrough' | 'hide';
171
+ };
172
+ highlighter?: { enabled?: boolean };
173
+ // ... other tools
174
+ };
175
+ accessibility?: {
176
+ catalogs?: any[];
177
+ language?: string;
178
+ };
179
+ }
180
+ ```
181
+
182
+ ### Methods
183
+
184
+ ```typescript
185
+ // Get all services as a bundle
186
+ const services = coordinator.getServiceBundle();
187
+ // Returns: { ttsService, toolCoordinator, highlightCoordinator, elementToolStateStore, catalogResolver }
188
+
189
+ // Tool configuration
190
+ coordinator.isToolEnabled('tts'); // Check if tool is enabled
191
+ coordinator.getToolConfig('tts'); // Get tool-specific config
192
+ coordinator.updateToolConfig('tts', { rate: 1.5 }); // Update tool config
193
+ ```
194
+
195
+ ### Direct Service Access
196
+
197
+ All services are public properties for direct access:
198
+
199
+ ```typescript
200
+ coordinator.ttsService // TTSService instance
201
+ coordinator.toolCoordinator // ToolCoordinator instance
202
+ coordinator.highlightCoordinator // HighlightCoordinator instance
203
+ coordinator.elementToolStateStore // ElementToolStateStore instance
204
+ coordinator.catalogResolver // AccessibilityCatalogResolver instance
205
+ ```
206
+
207
+ ## ElementToolStateStore API
208
+
209
+ The `ElementToolStateStore` manages ephemeral tool state at the element level using globally unique composite keys.
210
+
211
+ ### Key Concepts
212
+
213
+ - **Global Element ID**: Composite key format: `${assessmentId}:${sectionId}:${itemId}:${elementId}`
214
+ - **Element-Level Granularity**: State tracked per PIE element (not per item)
215
+ - **Ephemeral State**: Tool state is client-only, separate from PIE session data
216
+ - **Cross-Section Persistence**: State persists when navigating between sections
217
+
218
+ ### ID Utilities
219
+
220
+ ```typescript
221
+ // Generate global element ID
222
+ const globalElementId = store.getGlobalElementId(
223
+ 'demo-assessment',
224
+ 'section-1',
225
+ 'question-1',
226
+ 'mc1'
227
+ );
228
+ // Returns: "demo-assessment:section-1:question-1:mc1"
229
+
230
+ // Parse global element ID
231
+ const components = store.parseGlobalElementId(globalElementId);
232
+ // Returns: { assessmentId, sectionId, itemId, elementId }
233
+ ```
234
+
235
+ ### CRUD Operations
236
+
237
+ ```typescript
238
+ // Set state for a tool on an element
239
+ store.setState(globalElementId, 'answerEliminator', {
240
+ eliminatedChoices: ['choice-a', 'choice-c']
241
+ });
242
+
243
+ // Get state for a specific tool
244
+ const state = store.getState(globalElementId, 'answerEliminator');
245
+
246
+ // Get all tool states for an element
247
+ const elementState = store.getElementState(globalElementId);
248
+
249
+ // Get all states across all elements
250
+ const allState = store.getAllState();
251
+ ```
252
+
253
+ ### Cleanup Operations
254
+
255
+ ```typescript
256
+ // Clear state for a specific element
257
+ store.clearElement(globalElementId);
258
+
259
+ // Clear state for a specific tool across all elements
260
+ store.clearTool('answerEliminator');
261
+
262
+ // Clear all elements in a specific section
263
+ store.clearSection('demo-assessment', 'section-1');
264
+
265
+ // Clear all state
266
+ store.clearAll();
267
+ ```
268
+
269
+ ### Persistence Integration
270
+
271
+ ```typescript
272
+ // Set callback for persistence (e.g., localStorage)
273
+ store.setOnStateChange((state) => {
274
+ localStorage.setItem('tool-state', JSON.stringify(state));
275
+ });
276
+
277
+ // Load state from persistence
278
+ const saved = localStorage.getItem('tool-state');
279
+ if (saved) {
280
+ store.loadState(JSON.parse(saved));
281
+ }
282
+ ```
283
+
284
+ ### Reactivity
285
+
286
+ ```typescript
287
+ // Subscribe to state changes
288
+ const unsubscribe = store.subscribe((state) => {
289
+ console.log('State changed:', state);
290
+ });
291
+
292
+ // Unsubscribe when done
293
+ unsubscribe();
294
+ ```
295
+
296
+ ## Service APIs
297
+
298
+ ### TTSService
299
+
300
+ ```typescript
301
+ const ttsService = new TTSService();
302
+
303
+ // Initialize with provider
304
+ await ttsService.initialize(new BrowserTTSProvider());
305
+
306
+ // Set catalog resolver for SSML support
307
+ ttsService.setCatalogResolver(catalogResolver);
308
+
309
+ // Playback
310
+ await ttsService.speak('Read this text', {
311
+ catalogId: 'prompt-001',
312
+ language: 'en-US'
313
+ });
314
+
315
+ // Controls
316
+ ttsService.pause();
317
+ ttsService.resume();
318
+ ttsService.stop();
319
+
320
+ // Settings
321
+ await ttsService.updateSettings({
322
+ rate: 1.5,
323
+ voice: 'Matthew'
324
+ });
325
+ ```
326
+
327
+ ### ToolCoordinator
328
+
329
+ ```typescript
330
+ const toolCoordinator = new ToolCoordinator();
331
+
332
+ // Register tools
333
+ toolCoordinator.registerTool('calculator', 'Calculator', element);
334
+
335
+ // Manage visibility
336
+ toolCoordinator.showTool('calculator');
337
+ toolCoordinator.hideTool('calculator');
338
+ toolCoordinator.toggleTool('calculator');
339
+
340
+ // Z-index management
341
+ toolCoordinator.bringToFront(element);
342
+
343
+ // Check state
344
+ const isVisible = toolCoordinator.isToolVisible('calculator');
345
+ ```
346
+
347
+ ### HighlightCoordinator
348
+
349
+ ```typescript
350
+ const highlightCoordinator = new HighlightCoordinator();
351
+
352
+ // TTS highlights (temporary)
353
+ highlightCoordinator.highlightTTSWord(textNode, start, end);
354
+ highlightCoordinator.highlightTTSSentence([range1, range2]);
355
+ highlightCoordinator.clearTTS();
356
+
357
+ // Annotation highlights (persistent)
358
+ const id = highlightCoordinator.addAnnotation(range, 'yellow');
359
+ highlightCoordinator.removeAnnotation(id);
360
+ ```
361
+
362
+ ### AccessibilityCatalogResolver
363
+
364
+ ```typescript
365
+ const resolver = new AccessibilityCatalogResolver(
366
+ assessment.accessibilityCatalogs,
367
+ 'en-US'
368
+ );
369
+
370
+ // Add item-level catalogs
371
+ resolver.addItemCatalogs(item.accessibilityCatalogs);
372
+
373
+ // Get alternative representation
374
+ const alternative = resolver.getAlternative('prompt-001', {
375
+ type: 'spoken',
376
+ language: 'en-US'
377
+ });
378
+
379
+ // Clear item catalogs when navigating away
380
+ resolver.clearItemCatalogs();
381
+ ```
382
+
383
+ ### SSMLExtractor
384
+
385
+ ```typescript
386
+ const extractor = new SSMLExtractor();
387
+
388
+ // Extract from item config
389
+ const result = extractor.extractFromItemConfig(item.config);
390
+
391
+ // Update item with cleaned config
392
+ item.config = result.cleanedConfig;
393
+ item.config.extractedCatalogs = result.catalogs;
394
+
395
+ // Register with catalog resolver
396
+ catalogResolver.addItemCatalogs(result.catalogs);
397
+ ```
398
+
399
+ ## Integration with Section Player
400
+
401
+ The section player provides automatic ToolkitCoordinator integration:
402
+
403
+ ```html
404
+ <pie-section-player id="player"></pie-section-player>
405
+
406
+ <script type="module">
407
+ import { ToolkitCoordinator } from '@pie-players/pie-assessment-toolkit';
408
+
409
+ // Create coordinator
410
+ const coordinator = new ToolkitCoordinator({
411
+ assessmentId: 'my-assessment',
412
+ tools: { tts: { enabled: true } }
413
+ });
414
+
415
+ // Pass to player
416
+ const player = document.getElementById('player');
417
+ player.toolkitCoordinator = coordinator;
418
+ player.section = mySection;
419
+
420
+ // Player automatically:
421
+ // - Extracts services from coordinator
422
+ // - Generates section ID
423
+ // - Passes services to all child components
424
+ // - Manages SSML extraction
425
+ // - Handles catalog lifecycle
426
+ </script>
427
+ ```
428
+
429
+ ### Standalone Sections (No Coordinator Provided)
430
+
431
+ If no coordinator is provided, the section player creates a default one:
432
+
433
+ ```javascript
434
+ // No coordinator provided - section player creates default
435
+ player.section = mySection;
436
+
437
+ // Internally creates:
438
+ // new ToolkitCoordinator({
439
+ // assessmentId: 'anon_...', // auto-generated
440
+ // tools: { tts: { enabled: true }, answerEliminator: { enabled: true } }
441
+ // })
442
+ ```
443
+
444
+ ## State Separation: Tool State vs Session Data
445
+
446
+ The toolkit enforces a clear separation between ephemeral tool state and persistent session data:
447
+
448
+ ### Tool State (Ephemeral - ElementToolStateStore)
449
+
450
+ **Client-only**, never sent to server for scoring:
451
+
452
+ ```typescript
453
+ {
454
+ "demo-assessment:section-1:question-1:mc1": {
455
+ "answerEliminator": {
456
+ "eliminatedChoices": ["choice-b", "choice-d"]
457
+ },
458
+ "highlighter": {
459
+ "annotations": [...]
460
+ }
461
+ }
462
+ }
463
+ ```
464
+
465
+ **Use for:**
466
+ - Answer eliminations
467
+ - Highlighting/annotations
468
+ - Tool preferences
469
+ - UI state
470
+
471
+ ### PIE Session Data (Persistent)
472
+
473
+ **Sent to server** for scoring:
474
+
475
+ ```typescript
476
+ {
477
+ "question-1": {
478
+ "id": "session-123",
479
+ "data": [
480
+ { "id": "mc1", "element": "multiple-choice", "value": ["choice-a"] }
481
+ ]
482
+ }
483
+ }
484
+ ```
485
+
486
+ **Use for:**
487
+ - Student responses
488
+ - Scoring data
489
+ - Assessment outcomes
490
+
491
+ ## Examples
492
+
493
+ See the [section-demos](../../apps/section-demos/) for complete examples:
494
+
495
+ - **Three Questions Demo**: Element-level answer eliminator with state persistence
496
+ - **TTS Integration Demo**: Toolkit coordinator with TTS service
497
+ - **Paired Passages Demo**: Multi-section assessment with cross-section state
498
+
499
+ ## TypeScript Support
500
+
501
+ Full TypeScript definitions included:
502
+
503
+ ```typescript
504
+ import type {
505
+ IToolkitCoordinator,
506
+ IElementToolStateStore,
507
+ ToolkitCoordinatorConfig,
508
+ ToolkitServiceBundle
509
+ } from '@pie-players/pie-assessment-toolkit';
510
+ ```
511
+
512
+ ## Related Documentation
513
+
514
+ - [ToolkitCoordinator Architecture](../../docs/architecture/TOOLKIT_COORDINATOR.md) - Design decisions and patterns
515
+ - [Section Player README](../section-player/README.md) - Section player integration
516
+ - [Architecture Overview](../../docs/ARCHITECTURE.md) - Complete system architecture
517
+
518
+ ## License
519
+
520
+ MIT
@@ -0,0 +1,90 @@
1
+ export interface StorageLike {
2
+ getItem(key: string): string | null;
3
+ setItem(key: string, value: string): void;
4
+ removeItem(key: string): void;
5
+ }
6
+ export interface TestSessionNavigationState {
7
+ currentItemIndex: number;
8
+ visitedItemIdentifiers: string[];
9
+ currentSectionIdentifier?: string;
10
+ }
11
+ export interface TestSessionRealization {
12
+ /**
13
+ * Deterministic seed for shuffling/selection.
14
+ * (Future: use for QTI ordering.shuffle / selection rules.)
15
+ */
16
+ seed: string;
17
+ /**
18
+ * Realized item order for this attempt (QTI item identifiers).
19
+ */
20
+ itemIdentifiers: string[];
21
+ }
22
+ export interface ItemSession {
23
+ /**
24
+ * QTI assessmentItemRef identifier (same as questionRef.identifier).
25
+ */
26
+ itemIdentifier: string;
27
+ /**
28
+ * PIE session id for the underlying item attempt (if/when created).
29
+ */
30
+ pieSessionId?: string;
31
+ attemptCount: number;
32
+ isCompleted: boolean;
33
+ startedAt?: string;
34
+ updatedAt?: string;
35
+ completedAt?: string;
36
+ }
37
+ export interface TestSession {
38
+ version: 1;
39
+ /**
40
+ * QTI-like identifier for the delivery attempt (administration).
41
+ */
42
+ testSessionIdentifier: string;
43
+ assessmentId: string;
44
+ startedAt: string;
45
+ updatedAt: string;
46
+ completedAt?: string;
47
+ navigationState: TestSessionNavigationState;
48
+ realization: TestSessionRealization;
49
+ /**
50
+ * Keyed by QTI item identifier (questionRef.identifier).
51
+ */
52
+ itemSessions: Record<string, ItemSession>;
53
+ /**
54
+ * QTI 3.0 context variables (global assessment-level variables).
55
+ * Managed by ContextVariableStore.
56
+ */
57
+ contextVariables?: Record<string, any>;
58
+ }
59
+ export declare function createMemoryStorage(initial?: Record<string, string>): StorageLike;
60
+ export declare function getBrowserLocalStorage(): StorageLike | null;
61
+ export declare function getOrCreateAnonymousDeviceId(storage: StorageLike): string;
62
+ export declare function createTestSessionIdentifier(args: {
63
+ assessmentId: string;
64
+ assignmentId?: string | null;
65
+ userId?: string | null;
66
+ storage: StorageLike;
67
+ }): {
68
+ testSessionIdentifier: string;
69
+ seed: string;
70
+ };
71
+ export declare function getTestSessionStorageKey(testSessionIdentifier: string): string;
72
+ export declare function loadTestSession(storage: StorageLike, testSessionIdentifier: string): TestSession | null;
73
+ export declare function saveTestSession(storage: StorageLike, session: TestSession): void;
74
+ export declare function createNewTestSession(args: {
75
+ testSessionIdentifier: string;
76
+ assessmentId: string;
77
+ seed: string;
78
+ itemIdentifiers: string[];
79
+ }): TestSession;
80
+ export declare function upsertVisitedItem(session: TestSession, itemIdentifier: string): TestSession;
81
+ export declare function setCurrentPosition(session: TestSession, args: {
82
+ currentItemIndex: number;
83
+ currentSectionIdentifier?: string;
84
+ }): TestSession;
85
+ export declare function upsertItemSessionFromPieSessionChange(session: TestSession, args: {
86
+ itemIdentifier: string;
87
+ pieSessionId: string;
88
+ isCompleted?: boolean;
89
+ }): TestSession;
90
+ //# sourceMappingURL=TestSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TestSession.d.ts","sourceRoot":"","sources":["../../src/attempt/TestSession.ts"],"names":[],"mappings":"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"}