@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
@@ -0,0 +1,616 @@
1
+ # Assessment Tools System
2
+
3
+ This directory contains the core infrastructure for the assessment tools system, including type definitions, store management, and coordination logic.
4
+
5
+ ## Architecture
6
+
7
+ ### Core Components
8
+
9
+ 1. **`types.ts`** - Type definitions for tools, tool state, and configuration
10
+ 2. **`toolCoordinator.ts`** - Central store for managing tool visibility, z-index, and coordination
11
+ 3. **`index.ts`** - Public API exports
12
+
13
+ ### Tool Lifecycle
14
+
15
+ ```typescript
16
+ // 1. Register tool (typically in onMount)
17
+ toolCoordinator.registerTool('my-tool', 'My Tool', element);
18
+
19
+ // 2. Show/hide tool
20
+ toolCoordinator.showTool('my-tool');
21
+ toolCoordinator.hideTool('my-tool');
22
+ toolCoordinator.toggleTool('my-tool');
23
+
24
+ // 3. Bring tool to front (when clicked)
25
+ toolCoordinator.bringToFront('my-tool');
26
+
27
+ // 4. Unregister (typically in onDestroy)
28
+ toolCoordinator.unregisterTool('my-tool');
29
+ ```
30
+
31
+ ## Tool Coordinator Store
32
+
33
+ The `toolCoordinator` manages:
34
+
35
+ - **Tool Registration**: Track all active tools
36
+ - **Visibility Management**: Show/hide tools
37
+ - **Z-Index Coordination**: Automatically manage layering
38
+ - **Active Tool Tracking**: Know which tool is currently in focus
39
+
40
+ ### API Reference
41
+
42
+ #### `registerTool(id, name, element?)`
43
+ Register a new tool with the coordinator.
44
+
45
+ **Parameters:**
46
+ - `id: ToolId` - Unique identifier
47
+ - `name: string` - Display name
48
+ - `element?: HTMLElement` - DOM element reference (optional)
49
+
50
+ #### `unregisterTool(id)`
51
+ Remove a tool from the coordinator.
52
+
53
+ #### `showTool(id)`
54
+ Show a tool and bring it to the front.
55
+
56
+ #### `hideTool(id)`
57
+ Hide a tool.
58
+
59
+ #### `toggleTool(id)`
60
+ Toggle tool visibility.
61
+
62
+ #### `bringToFront(id)`
63
+ Bring a visible tool to the front (highest z-index).
64
+
65
+ #### `updateToolElement(id, element)`
66
+ Update the DOM element reference for a tool.
67
+
68
+ #### `hideAllTools()`
69
+ Hide all registered tools.
70
+
71
+ #### `getToolState(id)`
72
+ Get the current state of a tool.
73
+
74
+ **Returns:** `ToolState | undefined`
75
+
76
+ #### `isToolVisible(id)`
77
+ Check if a tool is currently visible.
78
+
79
+ **Returns:** `boolean`
80
+
81
+ ### Derived Stores
82
+
83
+ - **`visibleTools`** - Array of all currently visible tools
84
+ - **`activeTool`** - The currently active (focused) tool
85
+
86
+ ## Creating a New Tool
87
+
88
+ Tools are packaged as Svelte components in `src/lib/tags/tool-{name}/`:
89
+
90
+ ### Directory Structure
91
+
92
+ ```
93
+ src/lib/tags/tool-{name}/
94
+ ├── package.json # NPM package configuration
95
+ ├── tool-{name}.svelte # Main tool component
96
+ ├── index.ts # Exports
97
+ └── README.md # Tool-specific documentation (optional)
98
+ ```
99
+
100
+ ### Example Tool Implementation
101
+
102
+ ```svelte
103
+ <script lang="ts">
104
+ import { onMount, onDestroy } from 'svelte';
105
+ import { toolCoordinator } from '$lib/assessment-toolkit/tools';
106
+ import type { Tool } from '$lib/assessment-toolkit/tools';
107
+
108
+ export let visible: boolean = false;
109
+ export let toolId: string = 'my-tool';
110
+
111
+ let containerEl: HTMLDivElement;
112
+
113
+ // Implement Tool interface
114
+ const tool: Tool = {
115
+ id: toolId,
116
+ name: 'My Tool',
117
+ show: () => { visible = true; },
118
+ hide: () => { visible = false; },
119
+ toggle: () => { visible = !visible; }
120
+ };
121
+
122
+ function handleClose() {
123
+ toolCoordinator.hideTool(toolId);
124
+ }
125
+
126
+ onMount(() => {
127
+ toolCoordinator.registerTool(toolId, 'My Tool', containerEl);
128
+ });
129
+
130
+ onDestroy(() => {
131
+ toolCoordinator.unregisterTool(toolId);
132
+ });
133
+
134
+ $: if (containerEl) {
135
+ toolCoordinator.updateToolElement(toolId, containerEl);
136
+ }
137
+ </script>
138
+
139
+ {#if visible}
140
+ <div
141
+ bind:this={containerEl}
142
+ class="my-tool"
143
+ on:mousedown={() => toolCoordinator.bringToFront(toolId)}
144
+ >
145
+ <div class="tool-header">
146
+ <span>My Tool</span>
147
+ <button on:click={handleClose}>×</button>
148
+ </div>
149
+
150
+ <!-- Tool content here -->
151
+ </div>
152
+ {/if}
153
+
154
+ <style>
155
+ .my-tool {
156
+ position: fixed;
157
+ /* Tool-specific styles */
158
+ }
159
+ </style>
160
+ ```
161
+
162
+ ### Package.json Template
163
+
164
+ ```json
165
+ {
166
+ "name": "@pie-framework/pie-tool-{name}",
167
+ "version": "1.0.0",
168
+ "type": "module",
169
+ "description": "{Tool Name} for PIE assessment player",
170
+ "keywords": [
171
+ "pie",
172
+ "assessment",
173
+ "tool"
174
+ ],
175
+ "svelte": "./tool-{name}.svelte",
176
+ "main": "./index.ts",
177
+ "exports": {
178
+ ".": {
179
+ "svelte": "./tool-{name}.svelte",
180
+ "import": "./index.ts"
181
+ }
182
+ },
183
+ "files": [
184
+ "tool-{name}.svelte",
185
+ "index.ts",
186
+ "README.md"
187
+ ],
188
+ "peerDependencies": {
189
+ "svelte": "^4.0.0"
190
+ },
191
+ "license": "MIT"
192
+ }
193
+ ```
194
+
195
+ ## Integration with Assessment Player
196
+
197
+ In the assessment player component:
198
+
199
+ ```svelte
200
+ <script lang="ts">
201
+ import { toolCoordinator } from '$lib/assessment-toolkit/tools';
202
+ import { ToolProtractor } from '$lib/tags/tool-protractor';
203
+ // Or using full package name: import ToolProtractor from '@pie-framework/pie-tool-protractor';
204
+
205
+ let showProtractor = false;
206
+
207
+ // Subscribe to tool state
208
+ $: {
209
+ const state = toolCoordinator.getToolState('protractor');
210
+ showProtractor = state?.isVisible ?? false;
211
+ }
212
+ </script>
213
+
214
+ <!-- Tool button -->
215
+ <button
216
+ on:click={() => toolCoordinator.toggleTool('protractor')}
217
+ class:active={showProtractor}
218
+ >
219
+ Protractor
220
+ </button>
221
+
222
+ <!-- Tool component -->
223
+ <ToolProtractor visible={showProtractor} toolId="protractor" />
224
+ ```
225
+
226
+ ## Tool Categories
227
+
228
+ ### Standalone Tools
229
+ Tools that don't interact with assessment content:
230
+ - Protractor
231
+ - Ruler
232
+ - Calculator
233
+ - Character Picker
234
+ - Graph Tool
235
+ - Periodic Table
236
+
237
+ ### Content-Interactive Tools
238
+ Tools that need to access/manipulate question content:
239
+ - Annotation Toolbar (highlights, underlines)
240
+ - Text Magnifier
241
+ - Color Overlay
242
+
243
+ ### Service-Dependent Tools
244
+ Tools that require external API integration:
245
+ - Dictionary
246
+ - Translation
247
+ - Text-to-Speech
248
+ - Picture Dictionary
249
+
250
+ ## Best Practices
251
+
252
+ 1. **Always register/unregister** tools in `onMount`/`onDestroy`
253
+ 2. **Use the coordinator** for all visibility changes (don't manipulate `visible` prop directly)
254
+ 3. **Bring to front** on user interaction (mousedown)
255
+ 4. **Update element reference** when container changes
256
+ 5. **Handle cleanup** properly to prevent memory leaks
257
+ 6. **Make tools draggable** for better UX
258
+ 7. **Add close buttons** to all tools
259
+ 8. **Use consistent styling** (header, body, controls)
260
+
261
+ ## Architectural Enhancement Services
262
+
263
+ The following services implement architectural enhancements based on analysis of production assessment platforms:
264
+
265
+ ### 1. Library Loader Service
266
+
267
+ Dynamically loads external JavaScript libraries with retry logic and fallback URLs.
268
+
269
+ ```typescript
270
+ import { libraryLoader, COMMON_LIBRARIES } from '$lib/assessment-toolkit/tools';
271
+
272
+ // Load Desmos calculator library
273
+ await libraryLoader.loadScript(COMMON_LIBRARIES.desmos);
274
+
275
+ // Check if loaded
276
+ if (libraryLoader.isLoaded('desmos')) {
277
+ // Use Desmos API
278
+ const calculator = Desmos.GraphingCalculator(element);
279
+ }
280
+
281
+ // Get loader statistics
282
+ const stats = libraryLoader.getStats();
283
+ console.log(`Loaded: ${stats.loaded.length}, Failed: ${stats.failed.length}`);
284
+ ```
285
+
286
+ **Features:**
287
+ - Retry logic with exponential backoff
288
+ - Multiple fallback URLs (CDN → backup CDN → local)
289
+ - Timeout handling
290
+ - SRI (Subresource Integrity) support
291
+ - Statistics tracking
292
+
293
+ **Common Libraries:**
294
+ - `desmos` - Desmos calculator API
295
+ - `mathjax` - Math rendering
296
+ - `katex` - Fast math typesetting
297
+ - `ti84`, `ti108`, `ti34mv` - TI calculator emulators (placeholders)
298
+
299
+ ### 2. Accommodation Resolver Service
300
+
301
+ Resolves final tool configuration by merging roster, student, and item configs.
302
+
303
+ ```typescript
304
+ import { accommodationResolver } from '$lib/assessment-toolkit/tools';
305
+ import type { AccommodationProfile, RosterToolConfiguration, ItemToolConfig } from '$lib/assessment-toolkit/tools';
306
+
307
+ // Define configurations
308
+ const student: AccommodationProfile = {
309
+ studentId: 'student-123',
310
+ accommodations: {
311
+ calculator: true,
312
+ highlighter: true,
313
+ },
314
+ };
315
+
316
+ const roster: RosterToolConfiguration = {
317
+ rosterId: 'roster-456',
318
+ toolAllowances: {
319
+ calculator: '1', // allowed
320
+ dictionary: '0', // blocked
321
+ },
322
+ };
323
+
324
+ const item: ItemToolConfig = {
325
+ itemId: 'item-789',
326
+ requiredTools: ['protractor'], // required for this item
327
+ restrictedTools: ['graphing-calculator'], // not allowed for this item
328
+ };
329
+
330
+ // Resolve final tools
331
+ const resolved = accommodationResolver.resolveToolsForItem(student, roster, item);
332
+ // Returns: [calculator, highlighter, protractor] (graphing-calculator blocked, dictionary blocked)
333
+
334
+ // Check specific tool
335
+ const result = accommodationResolver.isToolAllowed('calculator', student, roster, item);
336
+ console.log(result); // { allowed: true, reason: '...', source: 'student-accommodation' }
337
+
338
+ // Debug resolution
339
+ const trace = accommodationResolver.getResolutionTrace('calculator', student, roster, item);
340
+ ```
341
+
342
+ **Precedence (highest to lowest):**
343
+ 1. Roster block (`"0"` = blocked)
344
+ 2. Item restriction
345
+ 3. Item requirement
346
+ 4. Student accommodation
347
+ 5. Roster default (`"1"` = allowed)
348
+ 6. System default (not allowed)
349
+
350
+ ### 3. Variant Resolver Service
351
+
352
+ Resolves item configuration considering variants for A/B testing and scaffolding.
353
+
354
+ ```typescript
355
+ import { variantResolver } from '$lib/assessment-toolkit/tools';
356
+ import type { ItemToolConfig, VariantContext } from '$lib/assessment-toolkit/tools';
357
+
358
+ const itemConfig: ItemToolConfig = {
359
+ itemId: 'item-001',
360
+ requiredTools: ['calculator'],
361
+ variantConfig: {
362
+ variantId: 'scaffolding-2',
363
+ toolOverrides: {
364
+ calculator: {
365
+ parameters: {
366
+ showHints: true,
367
+ stepByStepMode: true,
368
+ },
369
+ },
370
+ },
371
+ adaptations: [
372
+ {
373
+ type: 'scaffolding',
374
+ level: 2,
375
+ affectedTools: ['calculator'],
376
+ },
377
+ ],
378
+ },
379
+ };
380
+
381
+ const context: VariantContext = {
382
+ studentId: 'student-123',
383
+ sessionId: 'session-456',
384
+ scaffoldingLevel: 2,
385
+ };
386
+
387
+ // Resolve variant
388
+ const resolved = variantResolver.resolveVariant(itemConfig, context);
389
+ console.log(resolved.appliedVariant); // 'scaffolding-2'
390
+ console.log(resolved.toolParameters.calculator.finalConfig); // Merged config with hints enabled
391
+ ```
392
+
393
+ **Use Cases:**
394
+ - A/B testing different tool configurations
395
+ - Scaffolding for struggling students
396
+ - Difficulty adaptations
397
+ - Language-specific tool variants
398
+
399
+ ### 4. Response Discovery Service
400
+
401
+ Finds and manages PIE response components for tool-to-response integration.
402
+
403
+ ```typescript
404
+ import { responseDiscovery } from '$lib/assessment-toolkit/tools';
405
+
406
+ // Setup (in player initialization)
407
+ responseDiscovery.setupFocusTracking(); // Auto-track active response
408
+ responseDiscovery.autoDiscoverResponses(); // Find all response elements
409
+
410
+ // In calculator tool
411
+ async function insertIntoResponse() {
412
+ const activeResponse = responseDiscovery.getActiveResponse();
413
+
414
+ if (!activeResponse) {
415
+ console.warn('No active response');
416
+ return;
417
+ }
418
+
419
+ const result = calculator.getValue();
420
+ const capabilities = activeResponse.getCapabilities();
421
+
422
+ if (capabilities.acceptsNumeric) {
423
+ await activeResponse.insertContent(result, {
424
+ mode: 'insert',
425
+ format: 'numeric',
426
+ focus: true,
427
+ source: {
428
+ toolId: 'calculator',
429
+ toolType: 'scientific',
430
+ timestamp: Date.now(),
431
+ },
432
+ });
433
+ }
434
+ }
435
+
436
+ // Listen for active response changes
437
+ responseDiscovery.onActiveResponseChanged((response) => {
438
+ if (response) {
439
+ console.log(`Active response: ${response.responseId}`);
440
+ }
441
+ });
442
+ ```
443
+
444
+ **Features:**
445
+ - Automatic response discovery from DOM
446
+ - Active response tracking (based on focus)
447
+ - Capability-based content insertion
448
+ - Format validation
449
+ - Event notifications
450
+
451
+ ### 5. Calculator Provider System
452
+
453
+ Multi-provider calculator architecture supporting Desmos, Math.js, and TI emulators.
454
+
455
+ ```typescript
456
+ import { desmosProvider, mathjsProvider, tiProvider } from '$lib/assessment-toolkit/tools';
457
+ import type { CalculatorType } from '$lib/assessment-toolkit/tools';
458
+
459
+ // Option 1: Math.js (Open Source - Apache 2.0)
460
+ // Perfect for testing without licensing requirements
461
+ await mathjsProvider.initialize();
462
+ const basicCalc = await mathjsProvider.createCalculator('basic', container);
463
+ const scientificCalc = await mathjsProvider.createCalculator('scientific', container);
464
+
465
+ // Option 2: Desmos (Requires License & API Key)
466
+ // Professional graphing calculator
467
+ // Obtain API key from https://www.desmos.com/api
468
+ await desmosProvider.initialize({
469
+ apiKey: 'your_desmos_api_key_here'
470
+ });
471
+ const graphingCalc = await desmosProvider.createCalculator('graphing', container, {
472
+ theme: 'light',
473
+ restrictedMode: false,
474
+ });
475
+
476
+ // Use calculator
477
+ graphingCalc.setValue('y = x^2');
478
+ const value = graphingCalc.getValue();
479
+
480
+ // Export state for persistence
481
+ const state = graphingCalc.exportState();
482
+ localStorage.setItem('calculator-state', JSON.stringify(state));
483
+
484
+ // Restore state
485
+ const savedState = JSON.parse(localStorage.getItem('calculator-state'));
486
+ graphingCalc.importState(savedState);
487
+
488
+ // Switch providers
489
+ const tiCalculator = await tiProvider.createCalculator('ti-84', container);
490
+
491
+ // Cleanup
492
+ graphingCalc.destroy();
493
+ ```
494
+
495
+ **Supported Calculator Types by Provider:**
496
+
497
+ | Provider | Basic | Scientific | Graphing | License | Status |
498
+ |----------|-------|------------|----------|---------|--------|
499
+ | **Math.js** | ✅ | ✅ | ❌ | Apache 2.0 (Free) | ✅ Production Ready |
500
+ | **Desmos** | ✅ | ✅ | ✅ | Proprietary | ✅ Production Ready |
501
+ | **TI** | ❌ | ❌ | ✅ (TI-84) | Proprietary | ⚠️ Stub Only |
502
+
503
+ **Math.js Provider Features:**
504
+ - ✅ **No licensing fees** - Apache 2.0 open source
505
+ - ✅ **Full calculator UI** - Button-based interface included
506
+ - ✅ **Scientific functions** - Trigonometry, logarithms, constants (π, e)
507
+ - ✅ **Angle modes** - Degrees and radians
508
+ - ✅ **History** - Calculation history tracking
509
+ - ✅ **Keyboard support** - Full keyboard navigation
510
+ - ✅ **State persistence** - Export/import calculator state
511
+ - ✅ **Perfect for testing** - Works out of the box without external dependencies
512
+
513
+ **When to Use Each Provider:**
514
+ - **Math.js**: Testing, basic/scientific calculators, cost-effective solution
515
+ - **Desmos**: Professional graphing, when graphing is required (requires API key for production)
516
+ - **TI**: Future - when TI emulator licensing is available
517
+
518
+ **Desmos API Key Configuration:**
519
+
520
+ Production usage of Desmos calculators requires an API key. There are three ways to provide it:
521
+
522
+ ```typescript
523
+ // Method 1: Initialize with API key (recommended)
524
+ await desmosProvider.initialize({
525
+ apiKey: 'your_desmos_api_key_here'
526
+ });
527
+
528
+ // Method 2: Per-calculator configuration
529
+ const calculator = await desmosProvider.createCalculator('graphing', container, {
530
+ desmos: {
531
+ apiKey: 'your_desmos_api_key_here'
532
+ }
533
+ });
534
+
535
+ // Method 3: Global configuration (set before initialization)
536
+ window.PIE_DESMOS_API_KEY = 'your_desmos_api_key_here';
537
+ await desmosProvider.initialize();
538
+ ```
539
+
540
+ To obtain a Desmos API key:
541
+ - Visit: <https://www.desmos.com/api>
542
+ - Contact: partnerships@desmos.com
543
+
544
+ **Note:** Development and testing work without an API key, but production deployments require a valid Desmos license.
545
+
546
+ **Provider Interface:**
547
+ All providers implement the same interface, allowing seamless switching between providers without code changes.
548
+
549
+ ## Service Integration Example
550
+
551
+ Complete example showing all services working together:
552
+
553
+ ```typescript
554
+ import {
555
+ libraryLoader,
556
+ accommodationResolver,
557
+ variantResolver,
558
+ responseDiscovery,
559
+ desmosProvider,
560
+ type AccommodationProfile,
561
+ type RosterToolConfiguration,
562
+ type ItemToolConfig,
563
+ } from '$lib/assessment-toolkit/tools';
564
+
565
+ // 1. Load required library
566
+ await libraryLoader.loadScript(COMMON_LIBRARIES.desmos);
567
+
568
+ // 2. Resolve tool configuration
569
+ const resolvedTools = accommodationResolver.resolveToolsForItem(
570
+ studentProfile,
571
+ rosterConfig,
572
+ itemConfig
573
+ );
574
+
575
+ // 3. Resolve item variant
576
+ const resolvedItem = variantResolver.resolveVariant(itemConfig, variantContext);
577
+
578
+ // 4. Initialize calculator if allowed
579
+ const calculatorAllowed = resolvedTools.some((t) => t.id === 'calculator');
580
+ if (calculatorAllowed) {
581
+ const calculator = await desmosProvider.createCalculator(
582
+ 'scientific',
583
+ calculatorContainer,
584
+ resolvedItem.toolParameters.calculator?.finalConfig
585
+ );
586
+
587
+ // 5. Setup response integration
588
+ responseDiscovery.setupFocusTracking();
589
+
590
+ // Insert calculator result into active response
591
+ calculator.insertIntoResponse = async () => {
592
+ const response = responseDiscovery.getActiveResponse();
593
+ if (response) {
594
+ await response.insertContent(calculator.getValue(), {
595
+ mode: 'insert',
596
+ format: 'numeric',
597
+ });
598
+ }
599
+ };
600
+ }
601
+ ```
602
+
603
+ ## Future Enhancements
604
+
605
+ - [ ] Tool configuration persistence (save position, settings)
606
+ - [ ] Tool presets per assessment
607
+ - [ ] Keyboard shortcuts for tool activation
608
+ - [ ] Tool usage analytics
609
+ - [ ] Multi-tool interactions
610
+ - [x] Tool state serialization/restoration (via calculator providers)
611
+ - [x] Library loading with fallbacks (via LibraryLoader)
612
+ - [x] Multi-provider calculator support (Desmos + TI)
613
+ - [x] Tool-to-response integration (via ResponseDiscovery)
614
+ - [x] Configuration merge resolution (via AccommodationResolver)
615
+ - [x] Item variant support (via VariantResolver)
616
+