@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.
Files changed (67) hide show
  1. package/README.md +92 -1
  2. package/dist/components/ItemToolBar.custom-element.js +1 -1
  3. package/dist/components/PieAssessmentToolkit.custom-element.js +11 -11
  4. package/dist/components/SectionToolBar.custom-element.js +1 -1
  5. package/dist/components/chunks/ItemToolBar-cckwpz6c.js +51 -0
  6. package/dist/components/chunks/ItemToolBar-pryf0rtz.js +22 -0
  7. package/dist/index.d.ts +10 -6
  8. package/dist/index.js +12 -4
  9. package/dist/policy/core/ToolPolicyEngine.d.ts +21 -0
  10. package/dist/policy/core/ToolPolicyEngine.js +27 -0
  11. package/dist/policy/core/feature-decision.d.ts +57 -0
  12. package/dist/policy/core/feature-decision.js +40 -0
  13. package/dist/policy/engine.d.ts +1 -0
  14. package/dist/policy/sources/PnpPolicySource.d.ts +22 -0
  15. package/dist/policy/sources/PnpPolicySource.js +41 -11
  16. package/dist/runtime/catalog-registration.d.ts +56 -1
  17. package/dist/runtime/catalog-registration.js +64 -31
  18. package/dist/runtime/composition-emit-scheduler.d.ts +78 -0
  19. package/dist/runtime/composition-emit-scheduler.js +154 -0
  20. package/dist/runtime/core/engine-resolver.d.ts +1 -1
  21. package/dist/services/AccessibilityCatalogResolver.d.ts +100 -4
  22. package/dist/services/AccessibilityCatalogResolver.js +183 -58
  23. package/dist/services/SSMLExtractor.js +28 -18
  24. package/dist/services/TTSService.d.ts +25 -0
  25. package/dist/services/TTSService.js +241 -45
  26. package/dist/services/ToolRegistry.d.ts +218 -8
  27. package/dist/services/ToolRegistry.js +124 -8
  28. package/dist/services/ToolkitCoordinator.d.ts +25 -3
  29. package/dist/services/ToolkitCoordinator.js +47 -6
  30. package/dist/services/catalog-media.d.ts +25 -0
  31. package/dist/services/catalog-media.js +101 -0
  32. package/dist/services/createDefaultToolRegistry.d.ts +25 -58
  33. package/dist/services/createDefaultToolRegistry.js +24 -104
  34. package/dist/services/defaultPersonalNeedsProfile.d.ts +20 -2
  35. package/dist/services/defaultPersonalNeedsProfile.js +21 -19
  36. package/dist/services/interfaces.d.ts +29 -2
  37. package/dist/services/pnp-standard-features.d.ts +1 -1
  38. package/dist/services/spoken-audio-cards.d.ts +54 -0
  39. package/dist/services/spoken-audio-cards.js +66 -0
  40. package/dist/services/tool-config-defaults.d.ts +7 -23
  41. package/dist/services/tool-config-defaults.js +7 -46
  42. package/dist/services/tool-config-validation.d.ts +1 -1
  43. package/dist/services/tool-config-validation.js +44 -4
  44. package/dist/services/tts/browser-provider.js +2 -1
  45. package/dist/services/tts/math-aware-text-processing.js +3 -3
  46. package/dist/services/tts/text-processing.d.ts +51 -0
  47. package/dist/services/tts/text-processing.js +117 -1
  48. package/dist/services/tts-runtime-config.js +7 -2
  49. package/dist/tools/internal.d.ts +34 -0
  50. package/dist/tools/internal.js +33 -0
  51. package/dist/tools/tool-tag-map.d.ts +15 -3
  52. package/dist/tools/tool-tag-map.js +21 -18
  53. package/package.json +14 -10
  54. package/dist/components/chunks/ItemToolBar-843902tp.js +0 -51
  55. package/dist/components/chunks/ItemToolBar-84nv78dy.js +0 -22
  56. package/dist/tools/registrations/accessibility-tools.d.ts +0 -34
  57. package/dist/tools/registrations/accessibility-tools.js +0 -217
  58. package/dist/tools/registrations/calculator.d.ts +0 -20
  59. package/dist/tools/registrations/calculator.js +0 -228
  60. package/dist/tools/registrations/interaction-tools.d.ts +0 -27
  61. package/dist/tools/registrations/interaction-tools.js +0 -143
  62. package/dist/tools/registrations/measurement-tools.d.ts +0 -24
  63. package/dist/tools/registrations/measurement-tools.js +0 -130
  64. package/dist/tools/registrations/subject-specific-tools.d.ts +0 -27
  65. package/dist/tools/registrations/subject-specific-tools.js +0 -158
  66. package/dist/tools/registrations/tts.d.ts +0 -21
  67. 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;