@pie-players/pie-assessment-toolkit 0.2.2 → 0.2.4

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 (100) hide show
  1. package/README.md +159 -1
  2. package/dist/index.d.ts +16 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +9 -2
  5. package/dist/index.js.map +1 -1
  6. package/dist/player/AssessmentPlayer.d.ts +9 -0
  7. package/dist/player/AssessmentPlayer.d.ts.map +1 -1
  8. package/dist/player/AssessmentPlayer.js +13 -4
  9. package/dist/player/AssessmentPlayer.js.map +1 -1
  10. package/dist/services/AnnotationToolbarConfig.d.ts +134 -0
  11. package/dist/services/AnnotationToolbarConfig.d.ts.map +1 -0
  12. package/dist/services/AnnotationToolbarConfig.js +100 -0
  13. package/dist/services/AnnotationToolbarConfig.js.map +1 -0
  14. package/dist/services/HighlightCoordinator.d.ts +23 -10
  15. package/dist/services/HighlightCoordinator.d.ts.map +1 -1
  16. package/dist/services/HighlightCoordinator.js +87 -40
  17. package/dist/services/HighlightCoordinator.js.map +1 -1
  18. package/dist/services/PNPToolResolver.d.ts +171 -15
  19. package/dist/services/PNPToolResolver.d.ts.map +1 -1
  20. package/dist/services/PNPToolResolver.js +324 -32
  21. package/dist/services/PNPToolResolver.js.map +1 -1
  22. package/dist/services/RangeSerializer.d.ts +119 -0
  23. package/dist/services/RangeSerializer.d.ts.map +1 -0
  24. package/dist/services/RangeSerializer.js +199 -0
  25. package/dist/services/RangeSerializer.js.map +1 -0
  26. package/dist/services/TTSService.d.ts +4 -1
  27. package/dist/services/TTSService.d.ts.map +1 -1
  28. package/dist/services/TTSService.js +58 -3
  29. package/dist/services/TTSService.js.map +1 -1
  30. package/dist/services/ToolRegistry.d.ts +225 -0
  31. package/dist/services/ToolRegistry.d.ts.map +1 -0
  32. package/dist/services/ToolRegistry.js +248 -0
  33. package/dist/services/ToolRegistry.js.map +1 -0
  34. package/dist/services/ToolkitCoordinator.d.ts +22 -0
  35. package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
  36. package/dist/services/ToolkitCoordinator.js +44 -0
  37. package/dist/services/ToolkitCoordinator.js.map +1 -1
  38. package/dist/services/createDefaultToolRegistry.d.ts +72 -0
  39. package/dist/services/createDefaultToolRegistry.d.ts.map +1 -0
  40. package/dist/services/createDefaultToolRegistry.js +147 -0
  41. package/dist/services/createDefaultToolRegistry.js.map +1 -0
  42. package/dist/services/interfaces.d.ts +8 -0
  43. package/dist/services/interfaces.d.ts.map +1 -1
  44. package/dist/services/interfaces.js.map +1 -1
  45. package/dist/services/pnp-provenance.d.ts +167 -0
  46. package/dist/services/pnp-provenance.d.ts.map +1 -0
  47. package/dist/services/pnp-provenance.js +230 -0
  48. package/dist/services/pnp-provenance.js.map +1 -0
  49. package/dist/services/pnp-standard-features.d.ts +240 -0
  50. package/dist/services/pnp-standard-features.d.ts.map +1 -0
  51. package/dist/services/pnp-standard-features.js +342 -0
  52. package/dist/services/pnp-standard-features.js.map +1 -0
  53. package/dist/services/tool-context.d.ts +150 -0
  54. package/dist/services/tool-context.d.ts.map +1 -0
  55. package/dist/services/tool-context.js +157 -0
  56. package/dist/services/tool-context.js.map +1 -0
  57. package/dist/services/tool-providers/ToolProviderRegistry.d.ts.map +1 -1
  58. package/dist/services/tool-providers/ToolProviderRegistry.js +7 -7
  59. package/dist/services/tool-providers/ToolProviderRegistry.js.map +1 -1
  60. package/dist/tools/registrations/accessibility-tools.d.ts +43 -0
  61. package/dist/tools/registrations/accessibility-tools.d.ts.map +1 -0
  62. package/dist/tools/registrations/accessibility-tools.js +230 -0
  63. package/dist/tools/registrations/accessibility-tools.js.map +1 -0
  64. package/dist/tools/registrations/calculator.d.ts +21 -0
  65. package/dist/tools/registrations/calculator.d.ts.map +1 -0
  66. package/dist/tools/registrations/calculator.js +98 -0
  67. package/dist/tools/registrations/calculator.js.map +1 -0
  68. package/dist/tools/registrations/interaction-tools.d.ts +28 -0
  69. package/dist/tools/registrations/interaction-tools.d.ts.map +1 -0
  70. package/dist/tools/registrations/interaction-tools.js +124 -0
  71. package/dist/tools/registrations/interaction-tools.js.map +1 -0
  72. package/dist/tools/registrations/measurement-tools.d.ts +25 -0
  73. package/dist/tools/registrations/measurement-tools.d.ts.map +1 -0
  74. package/dist/tools/registrations/measurement-tools.js +112 -0
  75. package/dist/tools/registrations/measurement-tools.js.map +1 -0
  76. package/dist/tools/registrations/subject-specific-tools.d.ts +28 -0
  77. package/dist/tools/registrations/subject-specific-tools.d.ts.map +1 -0
  78. package/dist/tools/registrations/subject-specific-tools.js +118 -0
  79. package/dist/tools/registrations/subject-specific-tools.js.map +1 -0
  80. package/dist/tools/registrations/tts.d.ts +20 -0
  81. package/dist/tools/registrations/tts.d.ts.map +1 -0
  82. package/dist/tools/registrations/tts.js +86 -0
  83. package/dist/tools/registrations/tts.js.map +1 -0
  84. package/dist/tools/tool-tag-map.d.ts +24 -0
  85. package/dist/tools/tool-tag-map.d.ts.map +1 -0
  86. package/dist/tools/tool-tag-map.js +38 -0
  87. package/dist/tools/tool-tag-map.js.map +1 -0
  88. package/package.json +15 -7
  89. package/src/README.md +25 -11
  90. package/src/components/PNPProfileTester.example.svelte +188 -0
  91. package/src/components/PNPProfileTester.svelte +397 -0
  92. package/src/components/PNPProvenanceViewer.svelte +260 -0
  93. package/src/components/QuestionToolBar.svelte +160 -83
  94. package/src/components/ToolButton.svelte +146 -0
  95. package/src/components/ToolButtonGroup.svelte +109 -0
  96. package/src/reference-layout/ReferenceLayout.svelte +5 -13
  97. package/dist/services/PNPMapper.d.ts +0 -71
  98. package/dist/services/PNPMapper.d.ts.map +0 -1
  99. package/dist/services/PNPMapper.js +0 -98
  100. package/dist/services/PNPMapper.js.map +0 -1
@@ -2,41 +2,142 @@
2
2
  * PNP Tool Resolver
3
3
  *
4
4
  * Resolves tool availability from QTI 3.0 Personal Needs Profile (PNP) and
5
- * assessment settings. Implements precedence hierarchy for tool configuration.
5
+ * assessment settings. Implements a precedence hierarchy for tool configuration
6
+ * based on common assessment platform governance patterns.
6
7
  *
7
- * Precedence (highest to lowest):
8
- * 1. District block (absolute veto)
9
- * 2. Test administration override
10
- * 3. Item restriction (per-item block)
11
- * 4. Item requirement (forces enable)
12
- * 5. District requirement
13
- * 6. PNP supports (student needs)
8
+ * PRECEDENCE HIERARCHY (highest to lowest):
9
+ * ==========================================
10
+ *
11
+ * Standards-Based (QTI 3.0):
12
+ * - PNP supports (#6): Student's documented accessibility needs (accessibilityInfo.accessFeature)
13
+ * - Item-level settings (#3, #4): Per-item accessibility requirements/restrictions
14
+ *
15
+ * Implementation-Specific (Common Practice):
16
+ * - District policy (#1, #5): Institutional governance and legal compliance
17
+ * - Test administration (#2): Session-level operational control
18
+ *
19
+ * The hierarchy itself is NOT defined by QTI 3.0 standards but follows common
20
+ * patterns in K-12 assessment platforms, aligning with US IEP/504 accommodation
21
+ * hierarchies where legal requirements and institutional policies take precedence.
22
+ *
23
+ * Precedence Order:
24
+ * 1. District block (absolute veto) - Legal/policy requirements
25
+ * 2. Test administration override - Proctor/administrator operational control
26
+ * 3. Item restriction (per-item block) - Content author can disable for specific items
27
+ * 4. Item requirement (forces enable) - Required by IEP/504 or content needs
28
+ * 5. District requirement - Institutional accessibility requirements
29
+ * 6. PNP supports (student needs) - QTI 3.0 standard student preferences
30
+ *
31
+ * Governance Context:
32
+ * - Institutional veto (district) trumps individual preferences (legal compliance)
33
+ * - Session control (test admin) enables operational flexibility
34
+ * - Content restrictions (item) prevent tools that invalidate assessment
35
+ * - Required accommodations (IEP/504) ensure legal compliance
36
+ * - Student preferences (PNP) are honored when not overridden
37
+ *
38
+ * References:
39
+ * - QTI 3.0: https://www.imsglobal.org/spec/qti/v3p0
40
+ * - IMS AfA 3.0: https://www.imsglobal.org/spec/afa/v3p0
41
+ * - Common assessment platform governance patterns (not standardized)
14
42
  *
15
43
  * Part of PIE Assessment Toolkit.
16
44
  */
17
- import { mapPNPSupportToToolId } from "./PNPMapper";
45
+ import { PNPProvenanceBuilder, } from "./pnp-provenance";
18
46
  /**
19
47
  * Resolves tool availability from QTI 3.0 assessment structure
20
48
  */
21
49
  export class PNPToolResolver {
50
+ toolRegistry;
51
+ enableProvenance;
52
+ /**
53
+ * Create a PNPToolResolver
54
+ *
55
+ * @param toolRegistry Tool registry for PNP support mapping
56
+ * @param enableProvenance Enable detailed provenance tracking (default: true)
57
+ */
58
+ constructor(toolRegistry, enableProvenance = true) {
59
+ this.toolRegistry = toolRegistry;
60
+ this.enableProvenance = enableProvenance;
61
+ }
22
62
  /**
23
63
  * Resolve all tools from QTI 3.0 assessment + optional item context
24
64
  *
25
65
  * @param assessment QTI 3.0 assessment with personalNeedsProfile
26
66
  * @param currentItemRef Current item for item-specific requirements (optional)
27
- * @returns Array of resolved tool configurations
67
+ * @returns Array of resolved tool configurations (for backward compatibility)
68
+ *
69
+ * @deprecated Use resolveToolsWithProvenance() to get provenance tracking
28
70
  *
29
71
  * @example
30
- * const resolver = new PNPToolResolver();
72
+ * const resolver = new PNPToolResolver(registry);
31
73
  * const tools = resolver.resolveTools(assessment, itemRef);
32
74
  * tools.forEach(tool => {
33
75
  * console.log(`${tool.id}: ${tool.enabled ? 'enabled' : 'disabled'}`);
34
76
  * });
35
77
  */
36
78
  resolveTools(assessment, currentItemRef) {
79
+ const result = this.resolveToolsWithProvenance(assessment, currentItemRef);
80
+ return result.tools;
81
+ }
82
+ /**
83
+ * Resolve all tools with full provenance tracking
84
+ *
85
+ * Returns both resolved tools and complete provenance trail showing:
86
+ * - All configuration sources consulted
87
+ * - Decision steps at each precedence level
88
+ * - Reasons for enabling/blocking each feature
89
+ * - Human-readable explanations
90
+ *
91
+ * @param assessment QTI 3.0 assessment with personalNeedsProfile
92
+ * @param currentItemRef Current item for item-specific requirements (optional)
93
+ * @returns Resolution result with tools and provenance
94
+ *
95
+ * @example
96
+ * const resolver = new PNPToolResolver(registry);
97
+ * const result = resolver.resolveToolsWithProvenance(assessment, itemRef);
98
+ *
99
+ * // Use resolved tools
100
+ * result.tools.forEach(tool => console.log(tool.id, tool.enabled));
101
+ *
102
+ * // Display provenance to user
103
+ * console.log(result.provenance.summary);
104
+ * result.provenance.features.forEach((trail, featureId) => {
105
+ * console.log(`${featureId}: ${trail.explanation}`);
106
+ * });
107
+ */
108
+ resolveToolsWithProvenance(assessment, currentItemRef) {
37
109
  const pnp = assessment.personalNeedsProfile;
38
110
  const settings = assessment.settings;
39
111
  const itemSettings = currentItemRef?.settings;
112
+ // Initialize provenance builder
113
+ const contextId = `assessment-${assessment.id || "unknown"}${currentItemRef ? `-item-${currentItemRef.identifier}` : ""}`;
114
+ const provenanceBuilder = this.enableProvenance
115
+ ? new PNPProvenanceBuilder(contextId)
116
+ : null;
117
+ // Record configuration sources
118
+ if (provenanceBuilder) {
119
+ if (settings?.districtPolicy || settings?.testAdministration) {
120
+ provenanceBuilder.addSource("assessment", {
121
+ id: assessment.id || "unknown",
122
+ name: assessment.name || assessment.id || "Assessment",
123
+ config: settings,
124
+ });
125
+ }
126
+ if (pnp) {
127
+ provenanceBuilder.addSource("student", {
128
+ id: "student", // Would come from session context
129
+ name: "Student PNP Profile",
130
+ config: pnp,
131
+ });
132
+ }
133
+ if (currentItemRef && itemSettings) {
134
+ provenanceBuilder.addSource("item", {
135
+ id: currentItemRef.identifier,
136
+ name: currentItemRef.identifier,
137
+ config: itemSettings,
138
+ });
139
+ }
140
+ }
40
141
  // Collect all PNP support IDs mentioned anywhere
41
142
  const allSupports = new Set();
42
143
  pnp?.supports?.forEach((s) => allSupports.add(s));
@@ -45,19 +146,25 @@ export class PNPToolResolver {
45
146
  itemSettings?.requiredTools?.forEach((s) => allSupports.add(s));
46
147
  itemSettings?.restrictedTools?.forEach((s) => allSupports.add(s));
47
148
  const resolved = [];
149
+ const context = {
150
+ pnp,
151
+ districtPolicy: settings?.districtPolicy,
152
+ testAdmin: settings?.testAdministration,
153
+ itemSettings,
154
+ toolConfigs: settings?.toolConfigs,
155
+ };
48
156
  for (const supportId of allSupports) {
49
- const config = this.resolveSupport(supportId, {
50
- pnp,
51
- districtPolicy: settings?.districtPolicy,
52
- testAdmin: settings?.testAdministration,
53
- itemSettings,
54
- toolConfigs: settings?.toolConfigs,
55
- });
157
+ const config = this.resolveSupport(supportId, context, provenanceBuilder);
56
158
  if (config) {
57
159
  resolved.push(config);
58
160
  }
59
161
  }
60
- return resolved;
162
+ return {
163
+ tools: resolved,
164
+ provenance: provenanceBuilder
165
+ ? provenanceBuilder.build()
166
+ : this.createEmptyProvenance(contextId),
167
+ };
61
168
  }
62
169
  /**
63
170
  * Resolve availability of a single PNP support
@@ -66,24 +173,61 @@ export class PNPToolResolver {
66
173
  *
67
174
  * @param supportId PNP support identifier
68
175
  * @param context Resolution context with all configuration sources
176
+ * @param provenanceBuilder Provenance tracking (optional)
69
177
  * @returns Resolved config or null if blocked/disabled
70
178
  */
71
- resolveSupport(supportId, context) {
179
+ resolveSupport(supportId, context, provenanceBuilder) {
72
180
  // Precedence (highest to lowest):
73
181
  // 1. District block (absolute veto)
74
182
  if (context.districtPolicy?.blockedTools?.includes(supportId)) {
183
+ provenanceBuilder?.addDecision({
184
+ precedence: 1,
185
+ rule: "district-block",
186
+ featureId: supportId,
187
+ action: "block",
188
+ sourceType: "assessment",
189
+ reason: `District policy blocks "${supportId}" for all assessments`,
190
+ value: context.districtPolicy.blockedTools,
191
+ });
75
192
  return null; // Blocked
76
193
  }
77
194
  // 2. Test administration override
78
195
  if (context.testAdmin?.toolOverrides?.[supportId] === false) {
196
+ provenanceBuilder?.addDecision({
197
+ precedence: 2,
198
+ rule: "test-admin-override",
199
+ featureId: supportId,
200
+ action: "block",
201
+ sourceType: "assessment",
202
+ reason: `Test administrator disabled "${supportId}" for this session`,
203
+ value: context.testAdmin.toolOverrides,
204
+ });
79
205
  return null; // Blocked
80
206
  }
81
207
  // 3. Item restriction
82
208
  if (context.itemSettings?.restrictedTools?.includes(supportId)) {
209
+ provenanceBuilder?.addDecision({
210
+ precedence: 3,
211
+ rule: "item-restriction",
212
+ featureId: supportId,
213
+ action: "block",
214
+ sourceType: "item",
215
+ reason: `Item restricts "${supportId}" (e.g., mental math question blocks calculator)`,
216
+ value: context.itemSettings.restrictedTools,
217
+ });
83
218
  return null; // Blocked for this item
84
219
  }
85
220
  // 4. Item requirement (forces enable)
86
221
  if (context.itemSettings?.requiredTools?.includes(supportId)) {
222
+ provenanceBuilder?.addDecision({
223
+ precedence: 4,
224
+ rule: "item-requirement",
225
+ featureId: supportId,
226
+ action: "enable",
227
+ sourceType: "item",
228
+ reason: `Item requires "${supportId}" for this question`,
229
+ value: context.itemSettings.requiredTools,
230
+ });
87
231
  return this.buildToolConfig(supportId, context, {
88
232
  required: true,
89
233
  source: "item",
@@ -91,6 +235,15 @@ export class PNPToolResolver {
91
235
  }
92
236
  // 5. District requirement
93
237
  if (context.districtPolicy?.requiredTools?.includes(supportId)) {
238
+ provenanceBuilder?.addDecision({
239
+ precedence: 5,
240
+ rule: "district-requirement",
241
+ featureId: supportId,
242
+ action: "enable",
243
+ sourceType: "assessment",
244
+ reason: `District policy requires "${supportId}" for all assessments`,
245
+ value: context.districtPolicy.requiredTools,
246
+ });
94
247
  return this.buildToolConfig(supportId, context, {
95
248
  required: true,
96
249
  source: "district",
@@ -100,16 +253,63 @@ export class PNPToolResolver {
100
253
  if (context.pnp?.supports?.includes(supportId)) {
101
254
  // Check if prohibited
102
255
  const isProhibited = context.pnp.prohibitedSupports?.includes(supportId);
103
- if (!isProhibited) {
104
- return this.buildToolConfig(supportId, context, {
105
- alwaysAvailable: true,
106
- source: "pnp",
256
+ if (isProhibited) {
257
+ provenanceBuilder?.addDecision({
258
+ precedence: 6,
259
+ rule: "pnp-support",
260
+ featureId: supportId,
261
+ action: "block",
262
+ sourceType: "student",
263
+ reason: `Student PNP profile prohibits "${supportId}"`,
264
+ value: context.pnp.prohibitedSupports,
107
265
  });
266
+ return null;
108
267
  }
268
+ provenanceBuilder?.addDecision({
269
+ precedence: 6,
270
+ rule: "pnp-support",
271
+ featureId: supportId,
272
+ action: "enable",
273
+ sourceType: "student",
274
+ reason: `Student PNP profile requests "${supportId}"`,
275
+ value: context.pnp.supports,
276
+ });
277
+ return this.buildToolConfig(supportId, context, {
278
+ alwaysAvailable: true,
279
+ source: "pnp",
280
+ });
109
281
  }
110
- // Not enabled
282
+ // Not enabled - log as skipped
283
+ provenanceBuilder?.addDecision({
284
+ precedence: 6,
285
+ rule: "pnp-support",
286
+ featureId: supportId,
287
+ action: "skip",
288
+ sourceType: "system",
289
+ reason: `Feature "${supportId}" not configured at any level`,
290
+ });
111
291
  return null;
112
292
  }
293
+ /**
294
+ * Create empty provenance when tracking is disabled
295
+ */
296
+ createEmptyProvenance(contextId) {
297
+ return {
298
+ contextId,
299
+ resolvedAt: new Date(),
300
+ sources: {},
301
+ features: new Map(),
302
+ decisionLog: [],
303
+ summary: {
304
+ totalFeatures: 0,
305
+ enabled: 0,
306
+ blocked: 0,
307
+ notConfigured: 0,
308
+ bySource: {},
309
+ byRule: {},
310
+ },
311
+ };
312
+ }
113
313
  /**
114
314
  * Build resolved tool configuration
115
315
  *
@@ -121,8 +321,11 @@ export class PNPToolResolver {
121
321
  * @returns Complete resolved tool configuration
122
322
  */
123
323
  buildToolConfig(supportId, context, flags) {
124
- // Map PNP support ID to PIE tool ID
125
- const pieToolId = mapPNPSupportToToolId(supportId) || supportId;
324
+ // Map PNP support ID to PIE tool ID via registry
325
+ const toolIds = this.toolRegistry.getToolsByPNPSupport(supportId);
326
+ // Use first matching tool (usually only one per PNP support)
327
+ // If no mapping exists, use the support ID directly as tool ID
328
+ const pieToolId = toolIds.size > 0 ? Array.from(toolIds)[0] : supportId;
126
329
  // Get tool-specific config from settings (item takes precedence)
127
330
  const toolConfig = context.itemSettings?.toolParameters?.[supportId] ||
128
331
  context.toolConfigs?.[supportId];
@@ -138,7 +341,7 @@ export class PNPToolResolver {
138
341
  /**
139
342
  * Check if a specific tool is enabled
140
343
  *
141
- * @param toolId PIE tool identifier (e.g., 'pie-tool-calculator')
344
+ * @param toolId Toolkit tool identifier (e.g., 'calculator')
142
345
  * @param assessment Assessment with PNP
143
346
  * @param itemRef Optional item context
144
347
  * @returns true if tool is enabled
@@ -150,7 +353,7 @@ export class PNPToolResolver {
150
353
  /**
151
354
  * Check if a tool is required (cannot be disabled)
152
355
  *
153
- * @param toolId PIE tool identifier
356
+ * @param toolId Toolkit tool identifier
154
357
  * @param assessment Assessment with PNP
155
358
  * @param itemRef Optional item context
156
359
  * @returns true if tool is required or always available
@@ -171,14 +374,17 @@ export class PNPToolResolver {
171
374
  *
172
375
  * @example
173
376
  * const autoActivate = resolver.getAutoActivateTools(assessment);
174
- * // Returns: ['pie-tool-text-to-speech', 'pie-tool-line-reader']
377
+ * // Returns: ['textToSpeech', 'lineReader']
175
378
  */
176
379
  getAutoActivateTools(assessment) {
177
380
  const pnp = assessment.personalNeedsProfile;
178
381
  if (!pnp?.activateAtInit)
179
382
  return [];
180
383
  return pnp.activateAtInit
181
- .map((supportId) => mapPNPSupportToToolId(supportId))
384
+ .map((supportId) => {
385
+ const toolIds = this.toolRegistry.getToolsByPNPSupport(supportId);
386
+ return toolIds.size > 0 ? Array.from(toolIds)[0] : null;
387
+ })
182
388
  .filter(Boolean);
183
389
  }
184
390
  /**
@@ -192,6 +398,92 @@ export class PNPToolResolver {
192
398
  const resolved = this.resolveTools(assessment, itemRef);
193
399
  return resolved.filter((t) => t.enabled).map((t) => t.id);
194
400
  }
401
+ /**
402
+ * Get allowed tool IDs (Pass 1 of two-pass visibility model)
403
+ *
404
+ * Returns tool IDs that pass orchestrator-level checks (PNP + policies + settings).
405
+ * This is the first gate - tools still need to pass Pass 2 (relevance check).
406
+ *
407
+ * This is the primary method to use with ToolRegistry.filterVisibleInContext()
408
+ *
409
+ * @param assessment Assessment with PNP
410
+ * @param itemRef Optional item context
411
+ * @returns Array of allowed tool IDs (Pass 1)
412
+ *
413
+ * @example
414
+ * // Two-pass visibility model
415
+ * const allowedToolIds = resolver.getAllowedToolIds(assessment, itemRef);
416
+ * const visibleTools = toolRegistry.filterVisibleInContext(allowedToolIds, context);
417
+ */
418
+ getAllowedToolIds(assessment, itemRef) {
419
+ return this.getEnabledTools(assessment, itemRef);
420
+ }
421
+ /**
422
+ * Resolve tools with a test/override PNP profile
423
+ *
424
+ * FOR TESTING/DEVELOPMENT ONLY
425
+ *
426
+ * Allows direct injection of a PNP profile for testing accessibility configurations
427
+ * without modifying the assessment entity. The override profile replaces the
428
+ * assessment's personalNeedsProfile entirely.
429
+ *
430
+ * Use cases:
431
+ * - Development/testing UI for trying different profiles
432
+ * - Automated testing with various accessibility configurations
433
+ * - Preview tools with profile simulation
434
+ * - Accessibility team testing and validation
435
+ *
436
+ * @param assessment Base assessment (settings, policies, etc.)
437
+ * @param overrideProfile PNP profile to inject (replaces assessment.personalNeedsProfile)
438
+ * @param itemRef Optional item context
439
+ * @returns Resolution result with tools and provenance
440
+ *
441
+ * @example
442
+ * // Test with low vision profile
443
+ * const testProfile = {
444
+ * supports: ['magnification', 'textToSpeech', 'highContrastDisplay']
445
+ * };
446
+ * const result = resolver.resolveWithOverride(assessment, testProfile);
447
+ *
448
+ * @example
449
+ * // Test with null profile (no accessibility features)
450
+ * const result = resolver.resolveWithOverride(assessment, null);
451
+ */
452
+ resolveWithOverride(assessment, overrideProfile, itemRef) {
453
+ // Create assessment clone with overridden PNP
454
+ const testAssessment = {
455
+ ...assessment,
456
+ personalNeedsProfile: overrideProfile || undefined,
457
+ };
458
+ return this.resolveToolsWithProvenance(testAssessment, itemRef);
459
+ }
460
+ /**
461
+ * Get allowed tool IDs with full provenance tracking
462
+ *
463
+ * Returns both tool IDs and complete resolution provenance for debugging,
464
+ * auditing, and displaying to users.
465
+ *
466
+ * @param assessment Assessment with PNP
467
+ * @param itemRef Optional item context
468
+ * @returns Object with tool IDs and provenance
469
+ *
470
+ * @example
471
+ * const result = resolver.getAllowedToolIdsWithProvenance(assessment, itemRef);
472
+ *
473
+ * // Use tool IDs
474
+ * const visibleTools = toolRegistry.filterVisibleInContext(result.toolIds, context);
475
+ *
476
+ * // Display provenance to user
477
+ * console.log('Resolution Summary:', result.provenance.summary);
478
+ * console.log('Explanation for calculator:', result.provenance.features.get('calculator')?.explanation);
479
+ */
480
+ getAllowedToolIdsWithProvenance(assessment, itemRef) {
481
+ const result = this.resolveToolsWithProvenance(assessment, itemRef);
482
+ return {
483
+ toolIds: result.tools.map((t) => t.id),
484
+ provenance: result.provenance,
485
+ };
486
+ }
195
487
  /**
196
488
  * Get all required tools
197
489
  *
@@ -208,7 +500,7 @@ export class PNPToolResolver {
208
500
  /**
209
501
  * Get tool configuration settings
210
502
  *
211
- * @param toolId PIE tool identifier
503
+ * @param toolId Toolkit tool identifier
212
504
  * @param assessment Assessment with PNP
213
505
  * @param itemRef Optional item context
214
506
  * @returns Tool-specific settings or null if not found
@@ -1 +1 @@
1
- {"version":3,"file":"PNPToolResolver.js","sourceRoot":"","sources":["../../src/services/PNPToolResolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PNPToolResolver.js","sourceRoot":"","sources":["../../src/services/PNPToolResolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * RangeSerializer - Serialize and deserialize DOM Range objects
3
+ *
4
+ * Provides utilities for storing and restoring text ranges across sessions.
5
+ * Used by both TTS (for timing data) and annotations (for persistence).
6
+ *
7
+ * Uses CSS selector paths and node indices for robust serialization that
8
+ * survives content changes when possible.
9
+ */
10
+ /**
11
+ * Serialized form of a Range suitable for storage.
12
+ */
13
+ export interface SerializedRange {
14
+ /** CSS selector path to start container */
15
+ startContainer: string;
16
+ /** Character offset in start container */
17
+ startOffset: number;
18
+ /** CSS selector path to end container */
19
+ endContainer: string;
20
+ /** Character offset in end container */
21
+ endOffset: number;
22
+ /** Original text for validation */
23
+ text: string;
24
+ /** Optional metadata */
25
+ metadata?: {
26
+ /** When this range was created */
27
+ timestamp?: number;
28
+ /** User-provided label */
29
+ label?: string;
30
+ /** Custom data */
31
+ custom?: Record<string, unknown>;
32
+ };
33
+ }
34
+ /**
35
+ * RangeSerializer - Serialize and deserialize Range objects.
36
+ */
37
+ export declare class RangeSerializer {
38
+ /**
39
+ * Serialize a Range to storable format.
40
+ *
41
+ * @param range - Range to serialize
42
+ * @param root - Root element (typically document.body or content container)
43
+ * @returns Serialized range data
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const serializer = new RangeSerializer();
48
+ * const range = window.getSelection()!.getRangeAt(0);
49
+ * const serialized = serializer.serialize(range, document.body);
50
+ * localStorage.setItem('savedRange', JSON.stringify(serialized));
51
+ * ```
52
+ */
53
+ serialize(range: Range, root: Element): SerializedRange;
54
+ /**
55
+ * Deserialize range from storage.
56
+ *
57
+ * @param data - Serialized range data
58
+ * @param root - Root element (same as used in serialize)
59
+ * @returns Range object, or null if content changed
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const serializer = new RangeSerializer();
64
+ * const data = JSON.parse(localStorage.getItem('savedRange')!);
65
+ * const range = serializer.deserialize(data, document.body);
66
+ * if (range) {
67
+ * // Range successfully restored
68
+ * } else {
69
+ * // Content changed, range invalid
70
+ * }
71
+ * ```
72
+ */
73
+ deserialize(data: SerializedRange, root: Element): Range | null;
74
+ /**
75
+ * Get a unique path to a node from root.
76
+ *
77
+ * Uses a hybrid approach:
78
+ * - For element nodes: CSS selector path
79
+ * - For text nodes: parent selector + text node index
80
+ *
81
+ * @param node - Node to get path for
82
+ * @param root - Root element
83
+ * @returns Path string
84
+ */
85
+ private getNodePath;
86
+ /**
87
+ * Get CSS selector path to an element.
88
+ *
89
+ * @param element - Element to get path for
90
+ * @param root - Root element
91
+ * @returns CSS selector path
92
+ */
93
+ private getElementPath;
94
+ /**
95
+ * Find a node by its path from root.
96
+ *
97
+ * @param path - Path string from getNodePath
98
+ * @param root - Root element
99
+ * @returns Node, or null if not found
100
+ */
101
+ private findNodeByPath;
102
+ /**
103
+ * Batch serialize multiple ranges.
104
+ *
105
+ * @param ranges - Ranges to serialize
106
+ * @param root - Root element
107
+ * @returns Array of serialized ranges
108
+ */
109
+ serializeMany(ranges: Range[], root: Element): SerializedRange[];
110
+ /**
111
+ * Batch deserialize multiple ranges.
112
+ *
113
+ * @param data - Array of serialized ranges
114
+ * @param root - Root element
115
+ * @returns Array of ranges (nulls for invalid ranges)
116
+ */
117
+ deserializeMany(data: SerializedRange[], root: Element): (Range | null)[];
118
+ }
119
+ //# sourceMappingURL=RangeSerializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RangeSerializer.d.ts","sourceRoot":"","sources":["../../src/services/RangeSerializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,2CAA2C;IAC3C,cAAc,EAAE,MAAM,CAAC;IAEvB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IAEpB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IAErB,wCAAwC;IACxC,SAAS,EAAE,MAAM,CAAC;IAElB,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IAEb,wBAAwB;IACxB,QAAQ,CAAC,EAAE;QACV,kCAAkC;QAClC,SAAS,CAAC,EAAE,MAAM,CAAC;QAEnB,0BAA0B;QAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;QAEf,kBAAkB;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,CAAC;CACF;AAED;;GAEG;AACH,qBAAa,eAAe;IAC3B;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,GAAG,eAAe;IAUvD;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,GAAG,KAAK,GAAG,IAAI;IA2B/D;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IA8BnB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAmCtB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IA+BtB;;;;;;OAMG;IACH,aAAa,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,eAAe,EAAE;IAIhE;;;;;;OAMG;IACH,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE;CAGzE"}