@pie-players/pie-assessment-toolkit 0.3.63 → 0.3.65
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 +92 -1
- package/dist/components/ItemToolBar.custom-element.js +1 -1
- package/dist/components/PieAssessmentToolkit.custom-element.js +11 -11
- package/dist/components/SectionToolBar.custom-element.js +1 -1
- package/dist/components/chunks/ItemToolBar-cckwpz6c.js +51 -0
- package/dist/components/chunks/ItemToolBar-pryf0rtz.js +22 -0
- package/dist/index.d.ts +10 -6
- package/dist/index.js +12 -4
- package/dist/policy/core/ToolPolicyEngine.d.ts +21 -0
- package/dist/policy/core/ToolPolicyEngine.js +27 -0
- package/dist/policy/core/feature-decision.d.ts +57 -0
- package/dist/policy/core/feature-decision.js +40 -0
- package/dist/policy/engine.d.ts +1 -0
- package/dist/policy/sources/PnpPolicySource.d.ts +22 -0
- package/dist/policy/sources/PnpPolicySource.js +41 -11
- package/dist/runtime/catalog-registration.d.ts +56 -1
- package/dist/runtime/catalog-registration.js +64 -31
- package/dist/runtime/composition-emit-scheduler.d.ts +78 -0
- package/dist/runtime/composition-emit-scheduler.js +154 -0
- package/dist/runtime/core/engine-resolver.d.ts +1 -1
- package/dist/services/AccessibilityCatalogResolver.d.ts +100 -4
- package/dist/services/AccessibilityCatalogResolver.js +183 -58
- package/dist/services/SSMLExtractor.js +28 -18
- package/dist/services/TTSService.d.ts +25 -0
- package/dist/services/TTSService.js +241 -45
- package/dist/services/ToolRegistry.d.ts +218 -8
- package/dist/services/ToolRegistry.js +124 -8
- package/dist/services/ToolkitCoordinator.d.ts +25 -3
- package/dist/services/ToolkitCoordinator.js +47 -6
- package/dist/services/catalog-media.d.ts +25 -0
- package/dist/services/catalog-media.js +101 -0
- package/dist/services/createDefaultToolRegistry.d.ts +25 -58
- package/dist/services/createDefaultToolRegistry.js +24 -104
- package/dist/services/defaultPersonalNeedsProfile.d.ts +20 -2
- package/dist/services/defaultPersonalNeedsProfile.js +21 -19
- package/dist/services/interfaces.d.ts +29 -2
- package/dist/services/pnp-standard-features.d.ts +1 -1
- package/dist/services/spoken-audio-cards.d.ts +54 -0
- package/dist/services/spoken-audio-cards.js +66 -0
- package/dist/services/tool-config-defaults.d.ts +7 -23
- package/dist/services/tool-config-defaults.js +7 -46
- package/dist/services/tool-config-validation.d.ts +1 -1
- package/dist/services/tool-config-validation.js +44 -4
- package/dist/services/tts/browser-provider.js +2 -1
- package/dist/services/tts/math-aware-text-processing.js +3 -3
- package/dist/services/tts/text-processing.d.ts +51 -0
- package/dist/services/tts/text-processing.js +117 -1
- package/dist/services/tts-runtime-config.js +7 -2
- package/dist/tools/internal.d.ts +34 -0
- package/dist/tools/internal.js +33 -0
- package/dist/tools/tool-tag-map.d.ts +15 -3
- package/dist/tools/tool-tag-map.js +21 -18
- package/package.json +14 -10
- package/dist/components/chunks/ItemToolBar-843902tp.js +0 -51
- package/dist/components/chunks/ItemToolBar-84nv78dy.js +0 -22
- package/dist/tools/registrations/accessibility-tools.d.ts +0 -34
- package/dist/tools/registrations/accessibility-tools.js +0 -217
- package/dist/tools/registrations/calculator.d.ts +0 -20
- package/dist/tools/registrations/calculator.js +0 -228
- package/dist/tools/registrations/interaction-tools.d.ts +0 -27
- package/dist/tools/registrations/interaction-tools.js +0 -143
- package/dist/tools/registrations/measurement-tools.d.ts +0 -24
- package/dist/tools/registrations/measurement-tools.js +0 -130
- package/dist/tools/registrations/subject-specific-tools.d.ts +0 -27
- package/dist/tools/registrations/subject-specific-tools.js +0 -158
- package/dist/tools/registrations/tts.d.ts +0 -21
- package/dist/tools/registrations/tts.js +0 -184
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Accessibility Tools Registrations
|
|
3
|
-
*
|
|
4
|
-
* Registers tools for visual accessibility and reading support:
|
|
5
|
-
* - Line Reader (reading guide)
|
|
6
|
-
* - Color Scheme (theme/contrast)
|
|
7
|
-
* - Annotation Toolbar (text highlighting)
|
|
8
|
-
*
|
|
9
|
-
* Maps to QTI 3.0 standard access features from:
|
|
10
|
-
* - visual category: highContrastDisplay, colorContrast
|
|
11
|
-
* - reading category: readingMask, readingGuide, highlighting
|
|
12
|
-
*/
|
|
13
|
-
import { hasReadableText } from "../../services/tool-context.js";
|
|
14
|
-
import { createToolElement, } from "../tool-tag-map.js";
|
|
15
|
-
import { applyOverlaySurface, createScopedVisibilityBinding, syncButtonAndOverlayVisibility, } from "./toolbar-registration-helpers.js";
|
|
16
|
-
/**
|
|
17
|
-
* Line Reader tool registration
|
|
18
|
-
*
|
|
19
|
-
* Provides a reading guide overlay to help track reading position.
|
|
20
|
-
* Useful for text-heavy passages and questions.
|
|
21
|
-
*/
|
|
22
|
-
export const lineReaderToolRegistration = {
|
|
23
|
-
toolId: "lineReader",
|
|
24
|
-
name: "Line Reader",
|
|
25
|
-
description: "Reading guide overlay",
|
|
26
|
-
icon: "bars-3",
|
|
27
|
-
// Line reader appears where there's text to read
|
|
28
|
-
supportedLevels: ["section", "passage", "rubric", "item"],
|
|
29
|
-
// PNP support IDs
|
|
30
|
-
// Maps to QTI 3.0 standard features: readingMask, readingGuide, readingRuler
|
|
31
|
-
pnpSupportIds: [
|
|
32
|
-
"readingMask", // QTI 3.0 standard (reading.readingMask)
|
|
33
|
-
"readingGuide", // QTI 3.0 standard (reading.readingGuide)
|
|
34
|
-
"readingRuler", // QTI 3.0 standard (reading.readingRuler)
|
|
35
|
-
"lineReader", // Common variant
|
|
36
|
-
"trackingGuide", // Common variant
|
|
37
|
-
],
|
|
38
|
-
/**
|
|
39
|
-
* Pass 2: Line reader is relevant when readable text is present
|
|
40
|
-
*/
|
|
41
|
-
isVisibleInContext(context) {
|
|
42
|
-
return hasReadableText(context);
|
|
43
|
-
},
|
|
44
|
-
renderToolbar(context, toolbarContext) {
|
|
45
|
-
const visibility = createScopedVisibilityBinding(this.toolId, toolbarContext);
|
|
46
|
-
const button = {
|
|
47
|
-
toolId: this.toolId,
|
|
48
|
-
label: this.name,
|
|
49
|
-
icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
|
|
50
|
-
disabled: false,
|
|
51
|
-
ariaLabel: "Line reader - Reading guide",
|
|
52
|
-
tooltip: "Line Reader",
|
|
53
|
-
onClick: () => toolbarContext.toggleTool(this.toolId),
|
|
54
|
-
active: visibility.isActive(),
|
|
55
|
-
};
|
|
56
|
-
const componentOverrides = toolbarContext.componentOverrides ?? {};
|
|
57
|
-
const overlay = createToolElement(this.toolId, context, toolbarContext, componentOverrides);
|
|
58
|
-
overlay.setAttribute("tool-id", visibility.fullToolId);
|
|
59
|
-
applyOverlaySurface(overlay, "frameless");
|
|
60
|
-
return {
|
|
61
|
-
toolId: this.toolId,
|
|
62
|
-
button,
|
|
63
|
-
elements: [{ element: overlay, mount: "after-buttons" }],
|
|
64
|
-
sync: () => {
|
|
65
|
-
syncButtonAndOverlayVisibility({
|
|
66
|
-
button,
|
|
67
|
-
overlay,
|
|
68
|
-
isActive: visibility.isActive,
|
|
69
|
-
});
|
|
70
|
-
if (toolbarContext.toolkitCoordinator) {
|
|
71
|
-
overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
|
|
72
|
-
}
|
|
73
|
-
},
|
|
74
|
-
subscribeActive: visibility.subscribeActive,
|
|
75
|
-
};
|
|
76
|
-
},
|
|
77
|
-
};
|
|
78
|
-
/**
|
|
79
|
-
* Theme tool registration
|
|
80
|
-
*
|
|
81
|
-
* Provides accessible theme and contrast controls.
|
|
82
|
-
* Global tool that affects entire assessment.
|
|
83
|
-
*/
|
|
84
|
-
export const themeToolRegistration = {
|
|
85
|
-
toolId: "theme",
|
|
86
|
-
name: "Theme",
|
|
87
|
-
description: "Accessible themes and contrast",
|
|
88
|
-
icon: "swatch",
|
|
89
|
-
// Color scheme is assessment-wide
|
|
90
|
-
supportedLevels: ["assessment", "section"],
|
|
91
|
-
// PNP support IDs
|
|
92
|
-
// Maps to QTI 3.0 standard features: highContrastDisplay, colorContrast, invertColors
|
|
93
|
-
pnpSupportIds: [
|
|
94
|
-
"highContrastDisplay", // QTI 3.0 standard (visual.highContrastDisplay)
|
|
95
|
-
"colorContrast", // QTI 3.0 standard (visual.colorContrast)
|
|
96
|
-
"invertColors", // QTI 3.0 standard (visual.invertColors)
|
|
97
|
-
"theme", // Canonical id
|
|
98
|
-
"highContrast", // Common variant
|
|
99
|
-
"customColors", // Common variant
|
|
100
|
-
],
|
|
101
|
-
/**
|
|
102
|
-
* Pass 2: Color scheme is always relevant when allowed
|
|
103
|
-
*/
|
|
104
|
-
isVisibleInContext(context) {
|
|
105
|
-
return true; // Always show if allowed by orchestrator
|
|
106
|
-
},
|
|
107
|
-
renderToolbar(context, toolbarContext) {
|
|
108
|
-
const visibility = createScopedVisibilityBinding(this.toolId, toolbarContext);
|
|
109
|
-
const button = {
|
|
110
|
-
toolId: this.toolId,
|
|
111
|
-
label: this.name,
|
|
112
|
-
icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
|
|
113
|
-
disabled: false,
|
|
114
|
-
ariaLabel: "Theme - Change colors and contrast",
|
|
115
|
-
tooltip: "Theme",
|
|
116
|
-
onClick: () => toolbarContext.toggleTool(this.toolId),
|
|
117
|
-
active: visibility.isActive(),
|
|
118
|
-
};
|
|
119
|
-
const componentOverrides = toolbarContext.componentOverrides ?? {};
|
|
120
|
-
const overlay = createToolElement(this.toolId, context, toolbarContext, componentOverrides);
|
|
121
|
-
overlay.setAttribute("tool-id", visibility.fullToolId);
|
|
122
|
-
return {
|
|
123
|
-
toolId: this.toolId,
|
|
124
|
-
button,
|
|
125
|
-
elements: [
|
|
126
|
-
{
|
|
127
|
-
element: overlay,
|
|
128
|
-
mount: "after-buttons",
|
|
129
|
-
shell: {
|
|
130
|
-
title: this.name,
|
|
131
|
-
draggable: true,
|
|
132
|
-
resizable: false,
|
|
133
|
-
closeable: true,
|
|
134
|
-
initialWidth: 520,
|
|
135
|
-
initialHeight: 380,
|
|
136
|
-
minWidth: 420,
|
|
137
|
-
minHeight: 300,
|
|
138
|
-
},
|
|
139
|
-
},
|
|
140
|
-
],
|
|
141
|
-
sync: () => {
|
|
142
|
-
syncButtonAndOverlayVisibility({
|
|
143
|
-
button,
|
|
144
|
-
overlay,
|
|
145
|
-
isActive: visibility.isActive,
|
|
146
|
-
});
|
|
147
|
-
if (toolbarContext.toolkitCoordinator) {
|
|
148
|
-
overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
subscribeActive: visibility.subscribeActive,
|
|
152
|
-
};
|
|
153
|
-
},
|
|
154
|
-
};
|
|
155
|
-
/**
|
|
156
|
-
* Annotation Toolbar registration
|
|
157
|
-
*
|
|
158
|
-
* Text highlighting with CSS Custom Highlight API.
|
|
159
|
-
* Zero DOM mutation, optimal performance.
|
|
160
|
-
*/
|
|
161
|
-
export const annotationToolbarRegistration = {
|
|
162
|
-
toolId: "annotationToolbar",
|
|
163
|
-
name: "Highlighter",
|
|
164
|
-
description: "Highlight and annotate text",
|
|
165
|
-
icon: "highlighter",
|
|
166
|
-
activation: "selection-gateway",
|
|
167
|
-
singletonScope: "section",
|
|
168
|
-
// Annotation appears where there's text content
|
|
169
|
-
supportedLevels: ["passage", "rubric", "item", "element"],
|
|
170
|
-
// PNP support IDs
|
|
171
|
-
// Maps to QTI 3.0 standard features: highlighting, annotations
|
|
172
|
-
pnpSupportIds: [
|
|
173
|
-
"highlighting", // QTI 3.0 standard (cognitive.highlighting / reading.wordHighlighting)
|
|
174
|
-
"annotations", // QTI 3.0 standard (cognitive.annotations)
|
|
175
|
-
"highlighter", // Common variant
|
|
176
|
-
"textHighlight", // Common variant
|
|
177
|
-
"annotation", // Common variant
|
|
178
|
-
],
|
|
179
|
-
/**
|
|
180
|
-
* Pass 2: Annotation is relevant when readable text is present
|
|
181
|
-
*/
|
|
182
|
-
isVisibleInContext(context) {
|
|
183
|
-
return hasReadableText(context);
|
|
184
|
-
},
|
|
185
|
-
renderToolbar(context, toolbarContext) {
|
|
186
|
-
const visibility = createScopedVisibilityBinding(this.toolId, toolbarContext);
|
|
187
|
-
const button = {
|
|
188
|
-
toolId: this.toolId,
|
|
189
|
-
label: this.name,
|
|
190
|
-
icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
|
|
191
|
-
disabled: false,
|
|
192
|
-
ariaLabel: "Annotation toolbar - Highlight text",
|
|
193
|
-
tooltip: "Highlight",
|
|
194
|
-
onClick: () => toolbarContext.toggleTool(this.toolId),
|
|
195
|
-
active: visibility.isActive(),
|
|
196
|
-
};
|
|
197
|
-
const componentOverrides = toolbarContext.componentOverrides ?? {};
|
|
198
|
-
const overlay = createToolElement(this.toolId, context, toolbarContext, componentOverrides);
|
|
199
|
-
overlay.setAttribute("tool-id", visibility.fullToolId);
|
|
200
|
-
return {
|
|
201
|
-
toolId: this.toolId,
|
|
202
|
-
button,
|
|
203
|
-
elements: [{ element: overlay, mount: "after-buttons" }],
|
|
204
|
-
sync: () => {
|
|
205
|
-
syncButtonAndOverlayVisibility({
|
|
206
|
-
button,
|
|
207
|
-
overlay,
|
|
208
|
-
isActive: visibility.isActive,
|
|
209
|
-
});
|
|
210
|
-
if (toolbarContext.toolkitCoordinator) {
|
|
211
|
-
overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
|
|
212
|
-
}
|
|
213
|
-
},
|
|
214
|
-
subscribeActive: visibility.subscribeActive,
|
|
215
|
-
};
|
|
216
|
-
},
|
|
217
|
-
};
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calculator Tool Registration
|
|
3
|
-
*
|
|
4
|
-
* Registers the calculator tool with support for multiple calculator types
|
|
5
|
-
* (basic, scientific, graphing) via Desmos provider.
|
|
6
|
-
*
|
|
7
|
-
* Maps to QTI 3.0 standard access features:
|
|
8
|
-
* - calculator (cognitive support)
|
|
9
|
-
* - graphingCalculator (assessment tool)
|
|
10
|
-
*/
|
|
11
|
-
import type { ToolRegistration } from "../../services/ToolRegistry.js";
|
|
12
|
-
/**
|
|
13
|
-
* Calculator tool registration
|
|
14
|
-
*
|
|
15
|
-
* Supports:
|
|
16
|
-
* - Basic, scientific, and graphing calculators via Desmos
|
|
17
|
-
* - Context-aware visibility (shows only when math content is detected)
|
|
18
|
-
* - Item level only
|
|
19
|
-
*/
|
|
20
|
-
export declare const calculatorToolRegistration: ToolRegistration;
|
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Calculator Tool Registration
|
|
3
|
-
*
|
|
4
|
-
* Registers the calculator tool with support for multiple calculator types
|
|
5
|
-
* (basic, scientific, graphing) via Desmos provider.
|
|
6
|
-
*
|
|
7
|
-
* Maps to QTI 3.0 standard access features:
|
|
8
|
-
* - calculator (cognitive support)
|
|
9
|
-
* - graphingCalculator (assessment tool)
|
|
10
|
-
*/
|
|
11
|
-
import { hasMathContent } from "../../services/tool-context.js";
|
|
12
|
-
import { createScopedToolId } from "../../services/tool-instance-id.js";
|
|
13
|
-
import { DesmosToolProvider } from "../../services/tool-providers/index.js";
|
|
14
|
-
import { createToolElement } from "../tool-tag-map.js";
|
|
15
|
-
// The toolbar parent re-derives `renderedTools` whenever item state changes
|
|
16
|
-
// (e.g. the learner answers a question and `effectiveItem`/`renderContext`
|
|
17
|
-
// recompute). Calculator initialization is expensive (Desmos boot, container
|
|
18
|
-
// mount), so we cache the overlay element by coordinator + scoped tool id.
|
|
19
|
-
// Reusing the same element keeps `mountContent` a no-op and avoids tearing
|
|
20
|
-
// down and re-initializing the calculator on every re-render.
|
|
21
|
-
const overlayElementCache = new WeakMap();
|
|
22
|
-
function getCachedOverlay(coordinator, fullToolId) {
|
|
23
|
-
if (!coordinator)
|
|
24
|
-
return null;
|
|
25
|
-
const scoped = overlayElementCache.get(coordinator);
|
|
26
|
-
const element = scoped?.get(fullToolId);
|
|
27
|
-
if (!element)
|
|
28
|
-
return null;
|
|
29
|
-
// Svelte custom elements destroy their component when disconnected.
|
|
30
|
-
// A detached cached element is a dead instance — drop it and recreate.
|
|
31
|
-
if (!element.isConnected) {
|
|
32
|
-
scoped?.delete(fullToolId);
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
return element;
|
|
36
|
-
}
|
|
37
|
-
function setCachedOverlay(coordinator, fullToolId, element) {
|
|
38
|
-
if (!coordinator)
|
|
39
|
-
return;
|
|
40
|
-
let scoped = overlayElementCache.get(coordinator);
|
|
41
|
-
if (!scoped) {
|
|
42
|
-
scoped = new Map();
|
|
43
|
-
overlayElementCache.set(coordinator, scoped);
|
|
44
|
-
}
|
|
45
|
-
scoped.set(fullToolId, element);
|
|
46
|
-
}
|
|
47
|
-
function normalizeCalculatorType(value) {
|
|
48
|
-
return value === "basic" || value === "scientific" ? value : null;
|
|
49
|
-
}
|
|
50
|
-
function getCalculatorRenderParams(toolbarContext) {
|
|
51
|
-
const params = toolbarContext.getToolRenderParams?.("calculator") ?? {};
|
|
52
|
-
const calculatorType = normalizeCalculatorType(params.calculatorType);
|
|
53
|
-
const availableTypesRaw = params.availableTypes;
|
|
54
|
-
const availableTypes = Array.isArray(availableTypesRaw)
|
|
55
|
-
? availableTypesRaw
|
|
56
|
-
.map((value) => normalizeCalculatorType(value))
|
|
57
|
-
.filter((value) => value !== null)
|
|
58
|
-
: calculatorType
|
|
59
|
-
? [calculatorType]
|
|
60
|
-
: null;
|
|
61
|
-
return {
|
|
62
|
-
calculatorType,
|
|
63
|
-
availableTypes,
|
|
64
|
-
displayName: calculatorType === "scientific"
|
|
65
|
-
? "Scientific Calculator"
|
|
66
|
-
: calculatorType === "basic"
|
|
67
|
-
? "Basic Calculator"
|
|
68
|
-
: "Calculator",
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
function applyCalculatorParamsToElement(element, calculatorType, availableTypes) {
|
|
72
|
-
const calculatorElement = element;
|
|
73
|
-
if (calculatorType) {
|
|
74
|
-
calculatorElement.calculatorType = calculatorType;
|
|
75
|
-
element.setAttribute("calculator-type", calculatorType);
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
delete calculatorElement.calculatorType;
|
|
79
|
-
element.removeAttribute("calculator-type");
|
|
80
|
-
}
|
|
81
|
-
if (availableTypes && availableTypes.length > 0) {
|
|
82
|
-
calculatorElement.availableTypes = availableTypes;
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
delete calculatorElement.availableTypes;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Calculator tool registration
|
|
90
|
-
*
|
|
91
|
-
* Supports:
|
|
92
|
-
* - Basic, scientific, and graphing calculators via Desmos
|
|
93
|
-
* - Context-aware visibility (shows only when math content is detected)
|
|
94
|
-
* - Item level only
|
|
95
|
-
*/
|
|
96
|
-
export const calculatorToolRegistration = {
|
|
97
|
-
toolId: "calculator",
|
|
98
|
-
name: "Calculator",
|
|
99
|
-
description: "Multi-type calculator (basic, scientific, graphing)",
|
|
100
|
-
icon: "calculator",
|
|
101
|
-
provider: {
|
|
102
|
-
getProviderId: (config) => typeof config?.provider?.id === "string" && config.provider.id.length > 0
|
|
103
|
-
? config.provider.id
|
|
104
|
-
: "calculator-desmos",
|
|
105
|
-
createProvider: () => new DesmosToolProvider(),
|
|
106
|
-
getInitConfig: (config) => config?.provider?.init ?? {},
|
|
107
|
-
getAuthFetcher: (config) => {
|
|
108
|
-
const runtimeAuthFetcher = config?.provider?.runtime?.authFetcher;
|
|
109
|
-
if (typeof runtimeAuthFetcher === "function")
|
|
110
|
-
return runtimeAuthFetcher;
|
|
111
|
-
return async () => {
|
|
112
|
-
const response = await fetch("/api/tools/desmos/auth", {
|
|
113
|
-
method: "GET",
|
|
114
|
-
credentials: "same-origin",
|
|
115
|
-
});
|
|
116
|
-
if (!response.ok) {
|
|
117
|
-
throw new Error(`Failed to fetch Desmos auth config (${response.status})`);
|
|
118
|
-
}
|
|
119
|
-
return (await response.json());
|
|
120
|
-
};
|
|
121
|
-
},
|
|
122
|
-
lazy: true,
|
|
123
|
-
},
|
|
124
|
-
// Calculator is item-level in this player architecture.
|
|
125
|
-
supportedLevels: ["item"],
|
|
126
|
-
// PNP support IDs that enable this tool
|
|
127
|
-
// Maps to QTI 3.0 standard features: calculator, graphingCalculator
|
|
128
|
-
pnpSupportIds: [
|
|
129
|
-
"calculator", // QTI 3.0 standard (cognitive.calculator)
|
|
130
|
-
"graphingCalculator", // QTI 3.0 standard (assessment.graphingCalculator)
|
|
131
|
-
"basicCalculator", // Common variant
|
|
132
|
-
"scientificCalculator", // Common variant
|
|
133
|
-
],
|
|
134
|
-
/**
|
|
135
|
-
* Pass 2: Determine if calculator is relevant in this context
|
|
136
|
-
*
|
|
137
|
-
* Calculator is relevant when context contains mathematical content
|
|
138
|
-
* (MathML, LaTeX, arithmetic markers).
|
|
139
|
-
*/
|
|
140
|
-
isVisibleInContext(context) {
|
|
141
|
-
// Show only when math is present in item content.
|
|
142
|
-
return hasMathContent(context);
|
|
143
|
-
},
|
|
144
|
-
renderToolbar(context, toolbarContext) {
|
|
145
|
-
const { calculatorType, availableTypes, displayName } = getCalculatorRenderParams(toolbarContext);
|
|
146
|
-
const fullToolId = createScopedToolId(this.toolId, toolbarContext.scope.level, toolbarContext.scope.scopeId);
|
|
147
|
-
const componentOverrides = toolbarContext.componentOverrides;
|
|
148
|
-
const cachedOverlay = getCachedOverlay(toolbarContext.toolCoordinator, fullToolId);
|
|
149
|
-
const overlay = (cachedOverlay ??
|
|
150
|
-
createToolElement(this.toolId, context, toolbarContext, componentOverrides));
|
|
151
|
-
if (!cachedOverlay) {
|
|
152
|
-
setCachedOverlay(toolbarContext.toolCoordinator, fullToolId, overlay);
|
|
153
|
-
}
|
|
154
|
-
overlay.setAttribute("tool-id", fullToolId);
|
|
155
|
-
overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
|
|
156
|
-
applyCalculatorParamsToElement(overlay, calculatorType, availableTypes);
|
|
157
|
-
const openLabel = calculatorType === null
|
|
158
|
-
? "Open scientific calculator"
|
|
159
|
-
: `Open ${displayName.toLowerCase()}`;
|
|
160
|
-
const closeLabel = calculatorType === null
|
|
161
|
-
? "Close scientific calculator"
|
|
162
|
-
: `Close ${displayName.toLowerCase()}`;
|
|
163
|
-
const button = {
|
|
164
|
-
toolId: this.toolId,
|
|
165
|
-
label: displayName,
|
|
166
|
-
icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
|
|
167
|
-
disabled: false,
|
|
168
|
-
ariaLabel: openLabel,
|
|
169
|
-
tooltip: displayName,
|
|
170
|
-
onClick: () => toolbarContext.toggleTool(this.toolId),
|
|
171
|
-
active: toolbarContext.isToolVisible(fullToolId),
|
|
172
|
-
};
|
|
173
|
-
let lastVisibleState = button.active;
|
|
174
|
-
if (overlay.visible !== button.active) {
|
|
175
|
-
overlay.visible = button.active;
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
toolId: this.toolId,
|
|
179
|
-
elements: [
|
|
180
|
-
{
|
|
181
|
-
element: overlay,
|
|
182
|
-
mount: "after-buttons",
|
|
183
|
-
shell: {
|
|
184
|
-
title: this.name,
|
|
185
|
-
draggable: true,
|
|
186
|
-
resizable: true,
|
|
187
|
-
closeable: true,
|
|
188
|
-
initialWidth: 380,
|
|
189
|
-
initialHeight: 420,
|
|
190
|
-
minWidth: 380,
|
|
191
|
-
minHeight: 420,
|
|
192
|
-
initialAlign: "bottom-right",
|
|
193
|
-
initialMargin: 16,
|
|
194
|
-
content: {
|
|
195
|
-
overflowY: "auto",
|
|
196
|
-
preserveMinHeight: true,
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
],
|
|
201
|
-
button,
|
|
202
|
-
sync: () => {
|
|
203
|
-
const active = toolbarContext.isToolVisible(fullToolId);
|
|
204
|
-
button.active = active;
|
|
205
|
-
button.label = displayName;
|
|
206
|
-
button.ariaLabel = active ? closeLabel : openLabel;
|
|
207
|
-
button.tooltip = active
|
|
208
|
-
? `Close ${displayName.toLowerCase()}`
|
|
209
|
-
: displayName;
|
|
210
|
-
if (lastVisibleState !== active) {
|
|
211
|
-
overlay.visible = active;
|
|
212
|
-
lastVisibleState = active;
|
|
213
|
-
}
|
|
214
|
-
if (overlay.toolkitCoordinator !== toolbarContext.toolkitCoordinator) {
|
|
215
|
-
overlay.toolkitCoordinator = toolbarContext.toolkitCoordinator;
|
|
216
|
-
}
|
|
217
|
-
applyCalculatorParamsToElement(overlay, calculatorType, availableTypes);
|
|
218
|
-
},
|
|
219
|
-
subscribeActive: (callback) => {
|
|
220
|
-
if (!toolbarContext.subscribeVisibility)
|
|
221
|
-
return () => { };
|
|
222
|
-
return toolbarContext.subscribeVisibility(() => {
|
|
223
|
-
callback(toolbarContext.isToolVisible(fullToolId));
|
|
224
|
-
});
|
|
225
|
-
},
|
|
226
|
-
};
|
|
227
|
-
},
|
|
228
|
-
};
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interaction Tools Registrations
|
|
3
|
-
*
|
|
4
|
-
* Registers tools for interacting with question content:
|
|
5
|
-
* - Answer Eliminator (strike through answer choices)
|
|
6
|
-
* - Highlighter (highlight text passages)
|
|
7
|
-
*
|
|
8
|
-
* Maps to QTI 3.0 standard access features:
|
|
9
|
-
* - answerMasking (assessment tool)
|
|
10
|
-
* - strikethrough (visual transformation)
|
|
11
|
-
* - highlighting (cognitive/reading support)
|
|
12
|
-
*/
|
|
13
|
-
import type { ToolRegistration } from "../../services/ToolRegistry.js";
|
|
14
|
-
/**
|
|
15
|
-
* Answer Eliminator tool registration
|
|
16
|
-
*
|
|
17
|
-
* Allows students to strike through incorrect answer choices.
|
|
18
|
-
* Only appears on multiple-choice style questions.
|
|
19
|
-
*/
|
|
20
|
-
export declare const answerEliminatorToolRegistration: ToolRegistration;
|
|
21
|
-
/**
|
|
22
|
-
* Highlighter tool registration
|
|
23
|
-
*
|
|
24
|
-
* Allows students to highlight text in passages and questions.
|
|
25
|
-
* Appears on items with readable text content.
|
|
26
|
-
*/
|
|
27
|
-
export declare const highlighterToolRegistration: ToolRegistration;
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Interaction Tools Registrations
|
|
3
|
-
*
|
|
4
|
-
* Registers tools for interacting with question content:
|
|
5
|
-
* - Answer Eliminator (strike through answer choices)
|
|
6
|
-
* - Highlighter (highlight text passages)
|
|
7
|
-
*
|
|
8
|
-
* Maps to QTI 3.0 standard access features:
|
|
9
|
-
* - answerMasking (assessment tool)
|
|
10
|
-
* - strikethrough (visual transformation)
|
|
11
|
-
* - highlighting (cognitive/reading support)
|
|
12
|
-
*/
|
|
13
|
-
import { hasChoiceInteraction, hasReadableText, } from "../../services/tool-context.js";
|
|
14
|
-
import { createToolElement, } from "../tool-tag-map.js";
|
|
15
|
-
import { createScopedVisibilityBinding, syncButtonAndOverlayVisibility, } from "./toolbar-registration-helpers.js";
|
|
16
|
-
/**
|
|
17
|
-
* Answer Eliminator tool registration
|
|
18
|
-
*
|
|
19
|
-
* Allows students to strike through incorrect answer choices.
|
|
20
|
-
* Only appears on multiple-choice style questions.
|
|
21
|
-
*/
|
|
22
|
-
export const answerEliminatorToolRegistration = {
|
|
23
|
-
toolId: "answerEliminator",
|
|
24
|
-
name: "Answer Eliminator",
|
|
25
|
-
description: "Strike through answer choices",
|
|
26
|
-
icon: "strikethrough",
|
|
27
|
-
// Answer eliminator appears at item level only
|
|
28
|
-
supportedLevels: ["item"],
|
|
29
|
-
// PNP support IDs
|
|
30
|
-
// Maps to QTI 3.0 standard feature: answerMasking
|
|
31
|
-
pnpSupportIds: [
|
|
32
|
-
"answerMasking", // QTI 3.0 standard (assessment.answerMasking)
|
|
33
|
-
"answerEliminator", // QTI 3.0 standard (assessment.answerEliminator)
|
|
34
|
-
"strikethrough", // Common variant
|
|
35
|
-
"choiceMasking", // Common variant
|
|
36
|
-
],
|
|
37
|
-
/**
|
|
38
|
-
* Pass 2: Answer eliminator is relevant only for choice-based questions
|
|
39
|
-
*/
|
|
40
|
-
isVisibleInContext(context) {
|
|
41
|
-
return hasChoiceInteraction(context);
|
|
42
|
-
},
|
|
43
|
-
renderToolbar(context, toolbarContext) {
|
|
44
|
-
const visibility = createScopedVisibilityBinding(this.toolId, toolbarContext);
|
|
45
|
-
const componentOverrides = toolbarContext.componentOverrides ?? {};
|
|
46
|
-
const overlay = createToolElement(this.toolId, context, toolbarContext, componentOverrides);
|
|
47
|
-
overlay.setAttribute("tool-id", visibility.fullToolId);
|
|
48
|
-
overlay.setAttribute("strategy", "strikethrough");
|
|
49
|
-
overlay.setAttribute("button-alignment", "inline");
|
|
50
|
-
const button = {
|
|
51
|
-
toolId: this.toolId,
|
|
52
|
-
label: this.name,
|
|
53
|
-
icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="currentColor" aria-hidden="true"><path d="M19,3H16.3H7.7H5A2,2 0 0,0 3,5V7.7V16.4V19A2,2 0 0,0 5,21H7.7H16.4H19A2,2 0 0,0 21,19V16.3V7.7V5A2,2 0 0,0 19,3M15.6,17L12,13.4L8.4,17L7,15.6L10.6,12L7,8.4L8.4,7L12,10.6L15.6,7L17,8.4L13.4,12L17,15.6L15.6,17Z"/></svg>',
|
|
54
|
-
disabled: false,
|
|
55
|
-
ariaLabel: "Answer eliminator - Strike through choices",
|
|
56
|
-
tooltip: "Strike Through",
|
|
57
|
-
onClick: () => toolbarContext.toggleTool(this.toolId),
|
|
58
|
-
active: visibility.isActive(),
|
|
59
|
-
};
|
|
60
|
-
return {
|
|
61
|
-
toolId: this.toolId,
|
|
62
|
-
button,
|
|
63
|
-
elements: [{ element: overlay, mount: "after-buttons" }],
|
|
64
|
-
sync: () => {
|
|
65
|
-
syncButtonAndOverlayVisibility({
|
|
66
|
-
button,
|
|
67
|
-
overlay,
|
|
68
|
-
isActive: visibility.isActive,
|
|
69
|
-
});
|
|
70
|
-
if (toolbarContext.toolCoordinator) {
|
|
71
|
-
overlay.coordinator = toolbarContext.toolCoordinator;
|
|
72
|
-
}
|
|
73
|
-
overlay.scopeElement = toolbarContext.getScopeElement?.() || null;
|
|
74
|
-
if (toolbarContext.elementToolStateStore) {
|
|
75
|
-
overlay.elementToolStateStore = toolbarContext.elementToolStateStore;
|
|
76
|
-
}
|
|
77
|
-
const globalElementId = toolbarContext.getGlobalElementId?.();
|
|
78
|
-
if (globalElementId) {
|
|
79
|
-
overlay.globalElementId = globalElementId;
|
|
80
|
-
}
|
|
81
|
-
},
|
|
82
|
-
subscribeActive: visibility.subscribeActive,
|
|
83
|
-
};
|
|
84
|
-
},
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* Highlighter tool registration
|
|
88
|
-
*
|
|
89
|
-
* Allows students to highlight text in passages and questions.
|
|
90
|
-
* Appears on items with readable text content.
|
|
91
|
-
*/
|
|
92
|
-
export const highlighterToolRegistration = {
|
|
93
|
-
toolId: "highlighter",
|
|
94
|
-
name: "Highlighter",
|
|
95
|
-
description: "Highlight text",
|
|
96
|
-
icon: "highlighter",
|
|
97
|
-
activation: "toolbar-toggle",
|
|
98
|
-
// Highlighter appears at passage, rubric, item, and element levels
|
|
99
|
-
supportedLevels: ["passage", "rubric", "item", "element"],
|
|
100
|
-
// PNP support IDs
|
|
101
|
-
pnpSupportIds: ["highlighter", "textHighlight", "annotation"],
|
|
102
|
-
/**
|
|
103
|
-
* Pass 2: Highlighter is relevant when readable text is available
|
|
104
|
-
*/
|
|
105
|
-
isVisibleInContext(context) {
|
|
106
|
-
return hasReadableText(context);
|
|
107
|
-
},
|
|
108
|
-
renderToolbar(context, toolbarContext) {
|
|
109
|
-
const visibility = createScopedVisibilityBinding(this.toolId, toolbarContext);
|
|
110
|
-
const button = {
|
|
111
|
-
toolId: this.toolId,
|
|
112
|
-
label: this.name,
|
|
113
|
-
icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
|
|
114
|
-
disabled: false,
|
|
115
|
-
ariaLabel: "Highlighter - Highlight text",
|
|
116
|
-
tooltip: "Highlight",
|
|
117
|
-
onClick: () => toolbarContext.toggleTool(this.toolId),
|
|
118
|
-
active: visibility.isActive(),
|
|
119
|
-
};
|
|
120
|
-
const componentOverrides = toolbarContext.componentOverrides ?? {};
|
|
121
|
-
const overlay = createToolElement(this.toolId, context, toolbarContext, componentOverrides);
|
|
122
|
-
overlay.setAttribute("tool-id", visibility.fullToolId);
|
|
123
|
-
return {
|
|
124
|
-
toolId: this.toolId,
|
|
125
|
-
button,
|
|
126
|
-
elements: [{ element: overlay, mount: "after-buttons" }],
|
|
127
|
-
sync: () => {
|
|
128
|
-
syncButtonAndOverlayVisibility({
|
|
129
|
-
button,
|
|
130
|
-
overlay,
|
|
131
|
-
isActive: visibility.isActive,
|
|
132
|
-
onActiveChange: (active) => {
|
|
133
|
-
overlay.enabled = active;
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
if (toolbarContext.ttsService) {
|
|
137
|
-
overlay.ttsService = toolbarContext.ttsService;
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
subscribeActive: visibility.subscribeActive,
|
|
141
|
-
};
|
|
142
|
-
},
|
|
143
|
-
};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Measurement Tools Registrations
|
|
3
|
-
*
|
|
4
|
-
* Registers ruler and protractor tools for on-screen measurements.
|
|
5
|
-
*
|
|
6
|
-
* Maps to QTI 3.0 standard access features:
|
|
7
|
-
* - ruler (assessment tool)
|
|
8
|
-
* - protractor (assessment tool)
|
|
9
|
-
*/
|
|
10
|
-
import type { ToolRegistration } from "../../services/ToolRegistry.js";
|
|
11
|
-
/**
|
|
12
|
-
* Ruler tool registration
|
|
13
|
-
*
|
|
14
|
-
* Provides an on-screen ruler for measuring lengths.
|
|
15
|
-
* Typically appears on geometry or measurement problems.
|
|
16
|
-
*/
|
|
17
|
-
export declare const rulerToolRegistration: ToolRegistration;
|
|
18
|
-
/**
|
|
19
|
-
* Protractor tool registration
|
|
20
|
-
*
|
|
21
|
-
* Provides an on-screen protractor for measuring angles.
|
|
22
|
-
* Typically appears on geometry problems.
|
|
23
|
-
*/
|
|
24
|
-
export declare const protractorToolRegistration: ToolRegistration;
|