@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.
- package/README.md +520 -0
- package/dist/attempt/TestSession.d.ts +90 -0
- package/dist/attempt/TestSession.d.ts.map +1 -0
- package/dist/attempt/TestSession.js +174 -0
- package/dist/attempt/TestSession.js.map +1 -0
- package/dist/core/TypedEventBus.d.ts +48 -0
- package/dist/core/TypedEventBus.d.ts.map +1 -0
- package/dist/core/TypedEventBus.js +63 -0
- package/dist/core/TypedEventBus.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -0
- package/dist/item-loader.d.ts +45 -0
- package/dist/item-loader.d.ts.map +1 -0
- package/dist/item-loader.js +62 -0
- package/dist/item-loader.js.map +1 -0
- package/dist/player/AssessmentPlayer.d.ts +388 -0
- package/dist/player/AssessmentPlayer.d.ts.map +1 -0
- package/dist/player/AssessmentPlayer.js +965 -0
- package/dist/player/AssessmentPlayer.js.map +1 -0
- package/dist/player/index.d.ts +17 -0
- package/dist/player/index.d.ts.map +1 -0
- package/dist/player/index.js +15 -0
- package/dist/player/index.js.map +1 -0
- package/dist/player/navigation-types.d.ts +63 -0
- package/dist/player/navigation-types.d.ts.map +1 -0
- package/dist/player/navigation-types.js +7 -0
- package/dist/player/navigation-types.js.map +1 -0
- package/dist/player/qti-navigation.d.ts +29 -0
- package/dist/player/qti-navigation.d.ts.map +1 -0
- package/dist/player/qti-navigation.js +160 -0
- package/dist/player/qti-navigation.js.map +1 -0
- package/dist/reference-layout/index.d.ts +19 -0
- package/dist/reference-layout/index.d.ts.map +1 -0
- package/dist/reference-layout/index.js +20 -0
- package/dist/reference-layout/index.js.map +1 -0
- package/dist/services/AccessibilityCatalogResolver.d.ts +141 -0
- package/dist/services/AccessibilityCatalogResolver.d.ts.map +1 -0
- package/dist/services/AccessibilityCatalogResolver.js +249 -0
- package/dist/services/AccessibilityCatalogResolver.js.map +1 -0
- package/dist/services/AssessmentAuthoringService.d.ts +60 -0
- package/dist/services/AssessmentAuthoringService.d.ts.map +1 -0
- package/dist/services/AssessmentAuthoringService.js +183 -0
- package/dist/services/AssessmentAuthoringService.js.map +1 -0
- package/dist/services/ContextVariableStore.d.ts +114 -0
- package/dist/services/ContextVariableStore.d.ts.map +1 -0
- package/dist/services/ContextVariableStore.js +207 -0
- package/dist/services/ContextVariableStore.js.map +1 -0
- package/dist/services/ElementToolStateStore.d.ts +125 -0
- package/dist/services/ElementToolStateStore.d.ts.map +1 -0
- package/dist/services/ElementToolStateStore.js +200 -0
- package/dist/services/ElementToolStateStore.js.map +1 -0
- package/dist/services/HighlightCoordinator.d.ts +179 -0
- package/dist/services/HighlightCoordinator.d.ts.map +1 -0
- package/dist/services/HighlightCoordinator.js +446 -0
- package/dist/services/HighlightCoordinator.js.map +1 -0
- package/dist/services/I18nService.d.ts +121 -0
- package/dist/services/I18nService.d.ts.map +1 -0
- package/dist/services/I18nService.js +276 -0
- package/dist/services/I18nService.js.map +1 -0
- package/dist/services/PNPMapper.d.ts +71 -0
- package/dist/services/PNPMapper.d.ts.map +1 -0
- package/dist/services/PNPMapper.js +98 -0
- package/dist/services/PNPMapper.js.map +1 -0
- package/dist/services/PNPToolResolver.d.ts +127 -0
- package/dist/services/PNPToolResolver.d.ts.map +1 -0
- package/dist/services/PNPToolResolver.js +222 -0
- package/dist/services/PNPToolResolver.js.map +1 -0
- package/dist/services/SSMLExtractor.d.ts +61 -0
- package/dist/services/SSMLExtractor.d.ts.map +1 -0
- package/dist/services/SSMLExtractor.js +181 -0
- package/dist/services/SSMLExtractor.js.map +1 -0
- package/dist/services/TTSService.d.ts +162 -0
- package/dist/services/TTSService.d.ts.map +1 -0
- package/dist/services/TTSService.js +465 -0
- package/dist/services/TTSService.js.map +1 -0
- package/dist/services/ThemeProvider.d.ts +95 -0
- package/dist/services/ThemeProvider.d.ts.map +1 -0
- package/dist/services/ThemeProvider.js +299 -0
- package/dist/services/ThemeProvider.js.map +1 -0
- package/dist/services/ToolConfigResolver.d.ts +162 -0
- package/dist/services/ToolConfigResolver.d.ts.map +1 -0
- package/dist/services/ToolConfigResolver.js +230 -0
- package/dist/services/ToolConfigResolver.js.map +1 -0
- package/dist/services/ToolCoordinator.d.ts +137 -0
- package/dist/services/ToolCoordinator.d.ts.map +1 -0
- package/dist/services/ToolCoordinator.js +318 -0
- package/dist/services/ToolCoordinator.js.map +1 -0
- package/dist/services/ToolkitCoordinator.d.ts +188 -0
- package/dist/services/ToolkitCoordinator.d.ts.map +1 -0
- package/dist/services/ToolkitCoordinator.js +252 -0
- package/dist/services/ToolkitCoordinator.js.map +1 -0
- package/dist/services/interfaces.d.ts +391 -0
- package/dist/services/interfaces.d.ts.map +1 -0
- package/dist/services/interfaces.js +12 -0
- package/dist/services/interfaces.js.map +1 -0
- package/dist/services/tool-providers/DesmosToolProvider.d.ts +103 -0
- package/dist/services/tool-providers/DesmosToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/DesmosToolProvider.js +127 -0
- package/dist/services/tool-providers/DesmosToolProvider.js.map +1 -0
- package/dist/services/tool-providers/IToolProvider.d.ts +137 -0
- package/dist/services/tool-providers/IToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/IToolProvider.js +13 -0
- package/dist/services/tool-providers/IToolProvider.js.map +1 -0
- package/dist/services/tool-providers/TIToolProvider.d.ts +107 -0
- package/dist/services/tool-providers/TIToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/TIToolProvider.js +123 -0
- package/dist/services/tool-providers/TIToolProvider.js.map +1 -0
- package/dist/services/tool-providers/TTSToolProvider.d.ts +143 -0
- package/dist/services/tool-providers/TTSToolProvider.d.ts.map +1 -0
- package/dist/services/tool-providers/TTSToolProvider.js +168 -0
- package/dist/services/tool-providers/TTSToolProvider.js.map +1 -0
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts +165 -0
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts.map +1 -0
- package/dist/services/tool-providers/ToolProviderRegistry.js +240 -0
- package/dist/services/tool-providers/ToolProviderRegistry.js.map +1 -0
- package/dist/services/tool-providers/index.d.ts +18 -0
- package/dist/services/tool-providers/index.d.ts.map +1 -0
- package/dist/services/tool-providers/index.js +15 -0
- package/dist/services/tool-providers/index.js.map +1 -0
- package/dist/services/tts/browser-provider.d.ts +24 -0
- package/dist/services/tts/browser-provider.d.ts.map +1 -0
- package/dist/services/tts/browser-provider.js +177 -0
- package/dist/services/tts/browser-provider.js.map +1 -0
- package/dist/services/tts/provider-interface.d.ts +131 -0
- package/dist/services/tts/provider-interface.d.ts.map +1 -0
- package/dist/services/tts/provider-interface.js +10 -0
- package/dist/services/tts/provider-interface.js.map +1 -0
- package/dist/tools/calculators/desmos-provider.d.ts +54 -0
- package/dist/tools/calculators/desmos-provider.d.ts.map +1 -0
- package/dist/tools/calculators/desmos-provider.js +384 -0
- package/dist/tools/calculators/desmos-provider.js.map +1 -0
- package/dist/tools/calculators/mathjs-provider.d.ts +50 -0
- package/dist/tools/calculators/mathjs-provider.d.ts.map +1 -0
- package/dist/tools/calculators/mathjs-provider.js +660 -0
- package/dist/tools/calculators/mathjs-provider.js.map +1 -0
- package/dist/tools/calculators/ti-provider.d.ts +122 -0
- package/dist/tools/calculators/ti-provider.d.ts.map +1 -0
- package/dist/tools/calculators/ti-provider.js +587 -0
- package/dist/tools/calculators/ti-provider.js.map +1 -0
- package/dist/tools/client.d.ts +17 -0
- package/dist/tools/client.d.ts.map +1 -0
- package/dist/tools/client.js +26 -0
- package/dist/tools/client.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +10 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/library-loader.d.ts +63 -0
- package/dist/tools/library-loader.d.ts.map +1 -0
- package/dist/tools/library-loader.js +292 -0
- package/dist/tools/library-loader.js.map +1 -0
- package/dist/tools/response-discovery.d.ts +72 -0
- package/dist/tools/response-discovery.d.ts.map +1 -0
- package/dist/tools/response-discovery.js +183 -0
- package/dist/tools/response-discovery.js.map +1 -0
- package/dist/tools/tool-coordinator.d.ts +76 -0
- package/dist/tools/tool-coordinator.d.ts.map +1 -0
- package/dist/tools/tool-coordinator.js +197 -0
- package/dist/tools/tool-coordinator.js.map +1 -0
- package/dist/tools/types.d.ts +427 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +13 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/variant-resolver.d.ts +48 -0
- package/dist/tools/variant-resolver.d.ts.map +1 -0
- package/dist/tools/variant-resolver.js +214 -0
- package/dist/tools/variant-resolver.js.map +1 -0
- package/dist/types/events.d.ts +157 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +11 -0
- package/dist/types/events.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +71 -0
- package/src/README.md +626 -0
- package/src/components/QuestionToolBar.svelte +406 -0
- package/src/player/AssessmentLayout.svelte +68 -0
- package/src/player/README.md +154 -0
- package/src/reference-layout/README.md +135 -0
- package/src/reference-layout/ReferenceLayout.svelte +209 -0
- package/src/reference-layout/components/AssessmentContent.svelte +259 -0
- package/src/reference-layout/components/AssessmentFooter.svelte +89 -0
- package/src/reference-layout/components/AssessmentHeader.svelte +250 -0
- package/src/reference-layout/components/AssessmentNavigation.svelte +134 -0
- package/src/reference-layout/components/AssessmentToolsBar.svelte +176 -0
- package/src/reference-layout/components/ItemPanel.svelte +200 -0
- package/src/reference-layout/components/NotesPanel.svelte +179 -0
- package/src/reference-layout/components/PassagePanel.svelte +76 -0
- package/src/tools/README.md +616 -0
- 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
|
+
|