@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.
- package/README.md +159 -1
- package/dist/index.d.ts +16 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/player/AssessmentPlayer.d.ts +9 -0
- package/dist/player/AssessmentPlayer.d.ts.map +1 -1
- package/dist/player/AssessmentPlayer.js +13 -4
- package/dist/player/AssessmentPlayer.js.map +1 -1
- package/dist/services/AnnotationToolbarConfig.d.ts +134 -0
- package/dist/services/AnnotationToolbarConfig.d.ts.map +1 -0
- package/dist/services/AnnotationToolbarConfig.js +100 -0
- package/dist/services/AnnotationToolbarConfig.js.map +1 -0
- package/dist/services/HighlightCoordinator.d.ts +23 -10
- package/dist/services/HighlightCoordinator.d.ts.map +1 -1
- package/dist/services/HighlightCoordinator.js +87 -40
- package/dist/services/HighlightCoordinator.js.map +1 -1
- package/dist/services/PNPToolResolver.d.ts +171 -15
- package/dist/services/PNPToolResolver.d.ts.map +1 -1
- package/dist/services/PNPToolResolver.js +324 -32
- package/dist/services/PNPToolResolver.js.map +1 -1
- package/dist/services/RangeSerializer.d.ts +119 -0
- package/dist/services/RangeSerializer.d.ts.map +1 -0
- package/dist/services/RangeSerializer.js +199 -0
- package/dist/services/RangeSerializer.js.map +1 -0
- package/dist/services/TTSService.d.ts +4 -1
- package/dist/services/TTSService.d.ts.map +1 -1
- package/dist/services/TTSService.js +58 -3
- package/dist/services/TTSService.js.map +1 -1
- package/dist/services/ToolRegistry.d.ts +225 -0
- package/dist/services/ToolRegistry.d.ts.map +1 -0
- package/dist/services/ToolRegistry.js +248 -0
- package/dist/services/ToolRegistry.js.map +1 -0
- package/dist/services/ToolkitCoordinator.d.ts +22 -0
- package/dist/services/ToolkitCoordinator.d.ts.map +1 -1
- package/dist/services/ToolkitCoordinator.js +44 -0
- package/dist/services/ToolkitCoordinator.js.map +1 -1
- package/dist/services/createDefaultToolRegistry.d.ts +72 -0
- package/dist/services/createDefaultToolRegistry.d.ts.map +1 -0
- package/dist/services/createDefaultToolRegistry.js +147 -0
- package/dist/services/createDefaultToolRegistry.js.map +1 -0
- package/dist/services/interfaces.d.ts +8 -0
- package/dist/services/interfaces.d.ts.map +1 -1
- package/dist/services/interfaces.js.map +1 -1
- package/dist/services/pnp-provenance.d.ts +167 -0
- package/dist/services/pnp-provenance.d.ts.map +1 -0
- package/dist/services/pnp-provenance.js +230 -0
- package/dist/services/pnp-provenance.js.map +1 -0
- package/dist/services/pnp-standard-features.d.ts +240 -0
- package/dist/services/pnp-standard-features.d.ts.map +1 -0
- package/dist/services/pnp-standard-features.js +342 -0
- package/dist/services/pnp-standard-features.js.map +1 -0
- package/dist/services/tool-context.d.ts +150 -0
- package/dist/services/tool-context.d.ts.map +1 -0
- package/dist/services/tool-context.js +157 -0
- package/dist/services/tool-context.js.map +1 -0
- package/dist/services/tool-providers/ToolProviderRegistry.d.ts.map +1 -1
- package/dist/services/tool-providers/ToolProviderRegistry.js +7 -7
- package/dist/services/tool-providers/ToolProviderRegistry.js.map +1 -1
- package/dist/tools/registrations/accessibility-tools.d.ts +43 -0
- package/dist/tools/registrations/accessibility-tools.d.ts.map +1 -0
- package/dist/tools/registrations/accessibility-tools.js +230 -0
- package/dist/tools/registrations/accessibility-tools.js.map +1 -0
- package/dist/tools/registrations/calculator.d.ts +21 -0
- package/dist/tools/registrations/calculator.d.ts.map +1 -0
- package/dist/tools/registrations/calculator.js +98 -0
- package/dist/tools/registrations/calculator.js.map +1 -0
- package/dist/tools/registrations/interaction-tools.d.ts +28 -0
- package/dist/tools/registrations/interaction-tools.d.ts.map +1 -0
- package/dist/tools/registrations/interaction-tools.js +124 -0
- package/dist/tools/registrations/interaction-tools.js.map +1 -0
- package/dist/tools/registrations/measurement-tools.d.ts +25 -0
- package/dist/tools/registrations/measurement-tools.d.ts.map +1 -0
- package/dist/tools/registrations/measurement-tools.js +112 -0
- package/dist/tools/registrations/measurement-tools.js.map +1 -0
- package/dist/tools/registrations/subject-specific-tools.d.ts +28 -0
- package/dist/tools/registrations/subject-specific-tools.d.ts.map +1 -0
- package/dist/tools/registrations/subject-specific-tools.js +118 -0
- package/dist/tools/registrations/subject-specific-tools.js.map +1 -0
- package/dist/tools/registrations/tts.d.ts +20 -0
- package/dist/tools/registrations/tts.d.ts.map +1 -0
- package/dist/tools/registrations/tts.js +86 -0
- package/dist/tools/registrations/tts.js.map +1 -0
- package/dist/tools/tool-tag-map.d.ts +24 -0
- package/dist/tools/tool-tag-map.d.ts.map +1 -0
- package/dist/tools/tool-tag-map.js +38 -0
- package/dist/tools/tool-tag-map.js.map +1 -0
- package/package.json +15 -7
- package/src/README.md +25 -11
- package/src/components/PNPProfileTester.example.svelte +188 -0
- package/src/components/PNPProfileTester.svelte +397 -0
- package/src/components/PNPProvenanceViewer.svelte +260 -0
- package/src/components/QuestionToolBar.svelte +160 -83
- package/src/components/ToolButton.svelte +146 -0
- package/src/components/ToolButtonGroup.svelte +109 -0
- package/src/reference-layout/ReferenceLayout.svelte +5 -13
- package/dist/services/PNPMapper.d.ts +0 -71
- package/dist/services/PNPMapper.d.ts.map +0 -1
- package/dist/services/PNPMapper.js +0 -98
- 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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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 {
|
|
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
|
|
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 (
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
|
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
|
|
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
|
|
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: ['
|
|
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) =>
|
|
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
|
|
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":"AAAA
|
|
1
|
+
{"version":3,"file":"PNPToolResolver.js","sourceRoot":"","sources":["../../src/services/PNPToolResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAUH,OAAO,EACN,oBAAoB,GAEpB,MAAM,kBAAkB,CAAC;AAoC1B;;GAEG;AACH,MAAM,OAAO,eAAe;IACnB,YAAY,CAAe;IAC3B,gBAAgB,CAAU;IAElC;;;;;OAKG;IACH,YAAY,YAA0B,EAAE,gBAAgB,GAAG,IAAI;QAC9D,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;QACjC,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IAC1C,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CACX,UAA4B,EAC5B,cAAkC;QAElC,MAAM,MAAM,GAAG,IAAI,CAAC,0BAA0B,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;QAC3E,OAAO,MAAM,CAAC,KAAK,CAAC;IACrB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,0BAA0B,CACzB,UAA4B,EAC5B,cAAkC;QAElC,MAAM,GAAG,GAAG,UAAU,CAAC,oBAAoB,CAAC;QAC5C,MAAM,QAAQ,GAAG,UAAU,CAAC,QAA8B,CAAC;QAC3D,MAAM,YAAY,GAAG,cAAc,EAAE,QAAwB,CAAC;QAE9D,gCAAgC;QAChC,MAAM,SAAS,GAAG,cAAc,UAAU,CAAC,EAAE,IAAI,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,cAAc,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;QAC1H,MAAM,iBAAiB,GAAG,IAAI,CAAC,gBAAgB;YAC9C,CAAC,CAAC,IAAI,oBAAoB,CAAC,SAAS,CAAC;YACrC,CAAC,CAAC,IAAI,CAAC;QAER,+BAA+B;QAC/B,IAAI,iBAAiB,EAAE,CAAC;YACvB,IAAI,QAAQ,EAAE,cAAc,IAAI,QAAQ,EAAE,kBAAkB,EAAE,CAAC;gBAC9D,iBAAiB,CAAC,SAAS,CAAC,YAAY,EAAE;oBACzC,EAAE,EAAE,UAAU,CAAC,EAAE,IAAI,SAAS;oBAC9B,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,EAAE,IAAI,YAAY;oBACtD,MAAM,EAAE,QAAQ;iBAChB,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,GAAG,EAAE,CAAC;gBACT,iBAAiB,CAAC,SAAS,CAAC,SAAS,EAAE;oBACtC,EAAE,EAAE,SAAS,EAAE,kCAAkC;oBACjD,IAAI,EAAE,qBAAqB;oBAC3B,MAAM,EAAE,GAAG;iBACX,CAAC,CAAC;YACJ,CAAC;YAED,IAAI,cAAc,IAAI,YAAY,EAAE,CAAC;gBACpC,iBAAiB,CAAC,SAAS,CAAC,MAAM,EAAE;oBACnC,EAAE,EAAE,cAAc,CAAC,UAAU;oBAC7B,IAAI,EAAE,cAAc,CAAC,UAAU;oBAC/B,MAAM,EAAE,YAAY;iBACpB,CAAC,CAAC;YACJ,CAAC;QACF,CAAC;QAED,iDAAiD;QACjD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;QACtC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAClD,QAAQ,EAAE,cAAc,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3E,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5E,YAAY,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,YAAY,EAAE,eAAe,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAElE,MAAM,QAAQ,GAAyB,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAsB;YAClC,GAAG;YACH,cAAc,EAAE,QAAQ,EAAE,cAAc;YACxC,SAAS,EAAE,QAAQ,EAAE,kBAAkB;YACvC,YAAY;YACZ,WAAW,EAAE,QAAQ,EAAE,WAAW;SAClC,CAAC;QAEF,KAAK,MAAM,SAAS,IAAI,WAAW,EAAE,CAAC;YACrC,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,EAAE,iBAAiB,CAAC,CAAC;YAE1E,IAAI,MAAM,EAAE,CAAC;gBACZ,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACvB,CAAC;QACF,CAAC;QAED,OAAO;YACN,KAAK,EAAE,QAAQ;YACf,UAAU,EAAE,iBAAiB;gBAC5B,CAAC,CAAC,iBAAiB,CAAC,KAAK,EAAE;gBAC3B,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,SAAS,CAAC;SACxC,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACK,cAAc,CACrB,SAAiB,EACjB,OAA0B,EAC1B,iBAA8C;QAE9C,kCAAkC;QAElC,oCAAoC;QACpC,IAAI,OAAO,CAAC,cAAc,EAAE,YAAY,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/D,iBAAiB,EAAE,WAAW,CAAC;gBAC9B,UAAU,EAAE,CAAC;gBACb,IAAI,EAAE,gBAAgB;gBACtB,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,OAAO;gBACf,UAAU,EAAE,YAAY;gBACxB,MAAM,EAAE,2BAA2B,SAAS,uBAAuB;gBACnE,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,YAAY;aAC1C,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,CAAC,UAAU;QACxB,CAAC;QAED,kCAAkC;QAClC,IAAI,OAAO,CAAC,SAAS,EAAE,aAAa,EAAE,CAAC,SAAS,CAAC,KAAK,KAAK,EAAE,CAAC;YAC7D,iBAAiB,EAAE,WAAW,CAAC;gBAC9B,UAAU,EAAE,CAAC;gBACb,IAAI,EAAE,qBAAqB;gBAC3B,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,OAAO;gBACf,UAAU,EAAE,YAAY;gBACxB,MAAM,EAAE,gCAAgC,SAAS,oBAAoB;gBACrE,KAAK,EAAE,OAAO,CAAC,SAAS,CAAC,aAAa;aACtC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,CAAC,UAAU;QACxB,CAAC;QAED,sBAAsB;QACtB,IAAI,OAAO,CAAC,YAAY,EAAE,eAAe,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChE,iBAAiB,EAAE,WAAW,CAAC;gBAC9B,UAAU,EAAE,CAAC;gBACb,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,OAAO;gBACf,UAAU,EAAE,MAAM;gBAClB,MAAM,EAAE,mBAAmB,SAAS,kDAAkD;gBACtF,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,eAAe;aAC3C,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,CAAC,wBAAwB;QACtC,CAAC;QAED,sCAAsC;QACtC,IAAI,OAAO,CAAC,YAAY,EAAE,aAAa,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAC9D,iBAAiB,EAAE,WAAW,CAAC;gBAC9B,UAAU,EAAE,CAAC;gBACb,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,QAAQ;gBAChB,UAAU,EAAE,MAAM;gBAClB,MAAM,EAAE,kBAAkB,SAAS,qBAAqB;gBACxD,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,aAAa;aACzC,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE;gBAC/C,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,MAAM;aACd,CAAC,CAAC;QACJ,CAAC;QAED,0BAA0B;QAC1B,IAAI,OAAO,CAAC,cAAc,EAAE,aAAa,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChE,iBAAiB,EAAE,WAAW,CAAC;gBAC9B,UAAU,EAAE,CAAC;gBACb,IAAI,EAAE,sBAAsB;gBAC5B,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,QAAQ;gBAChB,UAAU,EAAE,YAAY;gBACxB,MAAM,EAAE,6BAA6B,SAAS,uBAAuB;gBACrE,KAAK,EAAE,OAAO,CAAC,cAAc,CAAC,aAAa;aAC3C,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE;gBAC/C,QAAQ,EAAE,IAAI;gBACd,MAAM,EAAE,UAAU;aAClB,CAAC,CAAC;QACJ,CAAC;QAED,kCAAkC;QAClC,IAAI,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChD,sBAAsB;YACtB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAC;YAEzE,IAAI,YAAY,EAAE,CAAC;gBAClB,iBAAiB,EAAE,WAAW,CAAC;oBAC9B,UAAU,EAAE,CAAC;oBACb,IAAI,EAAE,aAAa;oBACnB,SAAS,EAAE,SAAS;oBACpB,MAAM,EAAE,OAAO;oBACf,UAAU,EAAE,SAAS;oBACrB,MAAM,EAAE,kCAAkC,SAAS,GAAG;oBACtD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;iBACrC,CAAC,CAAC;gBACH,OAAO,IAAI,CAAC;YACb,CAAC;YAED,iBAAiB,EAAE,WAAW,CAAC;gBAC9B,UAAU,EAAE,CAAC;gBACb,IAAI,EAAE,aAAa;gBACnB,SAAS,EAAE,SAAS;gBACpB,MAAM,EAAE,QAAQ;gBAChB,UAAU,EAAE,SAAS;gBACrB,MAAM,EAAE,iCAAiC,SAAS,GAAG;gBACrD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ;aAC3B,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC,eAAe,CAAC,SAAS,EAAE,OAAO,EAAE;gBAC/C,eAAe,EAAE,IAAI;gBACrB,MAAM,EAAE,KAAK;aACb,CAAC,CAAC;QACJ,CAAC;QAED,+BAA+B;QAC/B,iBAAiB,EAAE,WAAW,CAAC;YAC9B,UAAU,EAAE,CAAC;YACb,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,SAAS;YACpB,MAAM,EAAE,MAAM;YACd,UAAU,EAAE,QAAQ;YACpB,MAAM,EAAE,YAAY,SAAS,+BAA+B;SAC5D,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC;IACb,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,SAAiB;QAC9C,OAAO;YACN,SAAS;YACT,UAAU,EAAE,IAAI,IAAI,EAAE;YACtB,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,IAAI,GAAG,EAAE;YACnB,WAAW,EAAE,EAAE;YACf,OAAO,EAAE;gBACR,aAAa,EAAE,CAAC;gBAChB,OAAO,EAAE,CAAC;gBACV,OAAO,EAAE,CAAC;gBACV,aAAa,EAAE,CAAC;gBAChB,QAAQ,EAAE,EAAE;gBACZ,MAAM,EAAE,EAAE;aACV;SACD,CAAC;IACH,CAAC;IAED;;;;;;;;;OASG;IACK,eAAe,CACtB,SAAiB,EACjB,OAA0B,EAC1B,KAIC;QAED,iDAAiD;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;QAElE,6DAA6D;QAC7D,+DAA+D;QAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAExE,iEAAiE;QACjE,MAAM,UAAU,GACf,OAAO,CAAC,YAAY,EAAE,cAAc,EAAE,CAAC,SAAS,CAAC;YACjD,OAAO,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,CAAC;QAElC,OAAO;YACN,EAAE,EAAE,SAAS;YACb,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK;YACjC,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,KAAK;YAC/C,QAAQ,EAAE,UAAU;YACpB,MAAM,EAAE,KAAK,CAAC,MAAa;SAC3B,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,aAAa,CACZ,MAAc,EACd,UAA4B,EAC5B,OAA2B;QAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;IAC3D,CAAC;IAED;;;;;;;OAOG;IACH,cAAc,CACb,MAAc,EACd,UAA4B,EAC5B,OAA2B;QAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;QACnD,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,EAAE,eAAe,IAAI,KAAK,CAAC;IACzD,CAAC;IAED;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,UAA4B;QAChD,MAAM,GAAG,GAAG,UAAU,CAAC,oBAAoB,CAAC;QAC5C,IAAI,CAAC,GAAG,EAAE,cAAc;YAAE,OAAO,EAAE,CAAC;QAEpC,OAAO,GAAG,CAAC,cAAc;aACvB,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE;YAClB,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC;YAClE,OAAO,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACzD,CAAC,CAAC;aACD,MAAM,CAAC,OAAO,CAAa,CAAC;IAC/B,CAAC;IAED;;;;;;OAMG;IACH,eAAe,CACd,UAA4B,EAC5B,OAA2B;QAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,iBAAiB,CAChB,UAA4B,EAC5B,OAA2B;QAE3B,OAAO,IAAI,CAAC,eAAe,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,mBAAmB,CAClB,UAA4B,EAC5B,eAA4C,EAC5C,OAA2B;QAE3B,8CAA8C;QAC9C,MAAM,cAAc,GAAqB;YACxC,GAAG,UAAU;YACb,oBAAoB,EAAE,eAAe,IAAI,SAAS;SAClD,CAAC;QAEF,OAAO,IAAI,CAAC,0BAA0B,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IACjE,CAAC;IAED;;;;;;;;;;;;;;;;;;;OAmBG;IACH,+BAA+B,CAC9B,UAA4B,EAC5B,OAA2B;QAE3B,MAAM,MAAM,GAAG,IAAI,CAAC,0BAA0B,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACpE,OAAO;YACN,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,UAAU,EAAE,MAAM,CAAC,UAAU;SAC7B,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,gBAAgB,CACf,UAA4B,EAC5B,OAA2B;QAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,QAAQ;aACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,eAAe,CAAC;aAC9C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACpB,CAAC;IAED;;;;;;;OAOG;IACH,eAAe,CACd,MAAc,EACd,UAA4B,EAC5B,OAA2B;QAE3B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;QACnD,OAAO,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;IAC/B,CAAC;CACD"}
|
|
@@ -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"}
|