@pie-players/pie-default-tool-loaders 1.0.0-beta.0 → 1.0.0-beta.10

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 (30) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +116 -12
  3. package/dist/afa-profile.d.ts +65 -0
  4. package/dist/afa-profile.js +135 -0
  5. package/dist/calculator-providers/LazyCalculatorToolProvider.d.ts +32 -18
  6. package/dist/calculator-providers/LazyCalculatorToolProvider.js +24 -12
  7. package/dist/calculator-providers/packaged-calculator-providers.js +26 -0
  8. package/dist/index.d.ts +7 -5
  9. package/dist/index.js +10 -5
  10. package/dist/packaged-capability-composition.js +11 -1
  11. package/dist/registrations/accessibility-tools.d.ts +8 -0
  12. package/dist/registrations/accessibility-tools.js +38 -0
  13. package/dist/registrations/audio-transcript.d.ts +0 -7
  14. package/dist/registrations/audio-transcript.js +1 -7
  15. package/dist/registrations/calculator.d.ts +16 -5
  16. package/dist/registrations/calculator.js +171 -176
  17. package/dist/registrations/interaction-tools.d.ts +15 -2
  18. package/dist/registrations/interaction-tools.js +110 -13
  19. package/dist/registrations/tts.d.ts +23 -2
  20. package/dist/registrations/tts.js +121 -93
  21. package/package.json +27 -25
  22. package/dist/calculator-providers/CortexToolProvider.d.ts +0 -10
  23. package/dist/calculator-providers/CortexToolProvider.js +0 -18
  24. package/dist/calculator-providers/DesmosToolProvider.d.ts +0 -11
  25. package/dist/calculator-providers/DesmosToolProvider.js +0 -18
  26. package/dist/calculator-providers/GeoGebraToolProvider.d.ts +0 -13
  27. package/dist/calculator-providers/GeoGebraToolProvider.js +0 -18
  28. package/dist/registrations/overlay-element-cache.d.ts +0 -23
  29. package/dist/registrations/overlay-toolbar-render.d.ts +0 -33
  30. package/dist/registrations/selection-actions.d.ts +0 -24
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Registers tools for visual accessibility and reading support:
5
5
  * - Line Reader (reading guide)
6
+ * - Magnifier (movable magnifying lens)
6
7
  * - Color Scheme (theme/contrast)
7
8
  * - Annotation Toolbar (text highlighting)
8
9
  */
@@ -14,6 +15,13 @@ import type { ToolRegistration } from "@pie-players/pie-assessment-toolkit/tools
14
15
  * Useful for text-heavy passages and questions.
15
16
  */
16
17
  export declare const lineReaderToolRegistration: ToolRegistration;
18
+ /**
19
+ * Magnifier tool registration
20
+ *
21
+ * A movable lens that shows the content under it enlarged. The AfA
22
+ * `magnification` tool parameter `zoom` sets the lens's starting level.
23
+ */
24
+ export declare const magnificationToolRegistration: ToolRegistration;
17
25
  /**
18
26
  * Theme tool registration
19
27
  *
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Registers tools for visual accessibility and reading support:
5
5
  * - Line Reader (reading guide)
6
+ * - Magnifier (movable magnifying lens)
6
7
  * - Color Scheme (theme/contrast)
7
8
  * - Annotation Toolbar (text highlighting)
8
9
  */
@@ -36,6 +37,43 @@ export const lineReaderToolRegistration = {
36
37
  });
37
38
  },
38
39
  };
40
+ /** A `zoom` worth passing on: a finite magnification above 1. */
41
+ const normalizeMagnifierZoom = (value) => typeof value === "number" && Number.isFinite(value) && value > 1
42
+ ? value
43
+ : undefined;
44
+ /**
45
+ * Magnifier tool registration
46
+ *
47
+ * A movable lens that shows the content under it enlarged. The AfA
48
+ * `magnification` tool parameter `zoom` sets the lens's starting level.
49
+ */
50
+ export const magnificationToolRegistration = {
51
+ toolId: "magnification",
52
+ name: "Magnifier",
53
+ description: "Magnify the content under a movable lens",
54
+ nameKey: "tools.magnification.name",
55
+ descriptionKey: "tools.magnification.description",
56
+ icon: "magnifying-glass",
57
+ supportedLevels: ["section", "passage", "rubric", "item"],
58
+ /**
59
+ * Pass 2: anything on screen can be magnified, text or not
60
+ */
61
+ isVisibleInContext() {
62
+ return true;
63
+ },
64
+ renderToolbar(context, toolbarContext) {
65
+ const result = renderOverlayToolbar(this, context, toolbarContext, {
66
+ surface: "frameless",
67
+ });
68
+ const overlay = result.elements?.[0]?.element;
69
+ const zoom = normalizeMagnifierZoom(toolbarContext.getToolParameters?.("magnification")?.zoom);
70
+ if (zoom === undefined)
71
+ overlay?.removeAttribute("zoom");
72
+ else
73
+ overlay?.setAttribute("zoom", String(zoom));
74
+ return result;
75
+ },
76
+ };
39
77
  /**
40
78
  * Theme tool registration
41
79
  *
@@ -66,11 +66,4 @@ export interface ResolvedAudioTranscript {
66
66
  * transcript is in the language of the audio it transcribes, one per prompt.
67
67
  */
68
68
  export declare function resolveAudioTranscript(context: ToolContentDependencyContext): ResolvedAudioTranscript | null;
69
- /**
70
- * Host surface this capability fills: the full-width slot above a card's content.
71
- *
72
- * Item cards and passage cards open the same surface, so declaring it once
73
- * reaches both — a passage can carry an audio prompt as an item can.
74
- */
75
- export declare const CONTENT_LEAD_SURFACE = "content-lead";
76
69
  export declare const audioTranscriptRegistration: ToolRegistration;
@@ -35,6 +35,7 @@
35
35
  * absent from the universal preset — a content-dependent support never belongs in
36
36
  * a wholesale grant.
37
37
  */
38
+ import { CONTENT_LEAD_SURFACE } from "@pie-players/pie-assessment-toolkit/tools/registration";
38
39
  import { toLanguageTag } from "@pie-players/pie-players-shared/i18n/language-tags";
39
40
  /** QTI 3.0 / AfA support id gating an audio transcript. */
40
41
  export const AUDIO_TRANSCRIPT_FEATURE_ID = "transcript";
@@ -103,13 +104,6 @@ export function resolveAudioTranscript(context) {
103
104
  // and being asked anyway is the price of also serving the authored case.
104
105
  return context.granted ? firstOnGrant : null;
105
106
  }
106
- /**
107
- * Host surface this capability fills: the full-width slot above a card's content.
108
- *
109
- * Item cards and passage cards open the same surface, so declaring it once
110
- * reaches both — a passage can carry an audio prompt as an item can.
111
- */
112
- export const CONTENT_LEAD_SURFACE = "content-lead";
113
107
  /**
114
108
  * A labelled region holding the transcript text.
115
109
  *
@@ -6,15 +6,26 @@
6
6
  */
7
7
  import type { ToolRegistration } from "@pie-players/pie-assessment-toolkit/tools/registration";
8
8
  import type { ToolProviderConfig } from "@pie-players/pie-assessment-toolkit/tools/registration";
9
- export type CalculatorProviderId = "calculator-desmos" | "calculator-geogebra" | "calculator-cortex";
10
- export declare const DEFAULT_CALCULATOR_PROVIDER_ID: CalculatorProviderId;
11
- export declare function resolveCalculatorProviderId(config: ToolProviderConfig | undefined): CalculatorProviderId;
9
+ import { type CalculatorProviderDefinition } from "../calculator-providers/LazyCalculatorToolProvider.js";
10
+ export type { CalculatorProviderDefinition };
11
+ export interface CalculatorRegistrationOptions {
12
+ /**
13
+ * Backends by `provider.id`, merged over the packaged Desmos and GeoGebra
14
+ * definitions. An entry under a packaged id replaces that backend.
15
+ */
16
+ providers?: Readonly<Record<string, CalculatorProviderDefinition>>;
17
+ }
18
+ export declare function resolveCalculatorProviderId(config: ToolProviderConfig | undefined, providers?: Readonly<Record<string, CalculatorProviderDefinition>>): string;
12
19
  /**
13
- * Calculator tool registration
20
+ * Builds the calculator registration over the packaged backends plus any the
21
+ * host adds, so a host bundles a backend only by naming it.
14
22
  *
15
23
  * Supports:
16
- * - Basic, scientific, and graphing calculators through Desmos, GeoGebra, or Cortex
24
+ * - Basic, scientific, and graphing calculators through the backend that
25
+ * `provider.id` selects, Desmos by default
17
26
  * - Context-aware visibility (shows only when math content is detected)
18
27
  * - Item level only
19
28
  */
29
+ export declare function createCalculatorToolRegistration(options?: CalculatorRegistrationOptions): ToolRegistration;
30
+ /** The calculator over the packaged Desmos and GeoGebra backends. */
20
31
  export declare const calculatorToolRegistration: ToolRegistration;
@@ -7,32 +7,21 @@
7
7
  import { hasMathContent } from "@pie-players/pie-assessment-toolkit/tools/registration";
8
8
  import { createScopedToolId } from "@pie-players/pie-assessment-toolkit/tools/registration";
9
9
  import { createToolElement } from "@pie-players/pie-assessment-toolkit/tools/registration";
10
- import { CortexToolProvider } from "../calculator-providers/CortexToolProvider.js";
11
- import { DesmosToolProvider } from "../calculator-providers/DesmosToolProvider.js";
12
- import { GeoGebraToolProvider } from "../calculator-providers/GeoGebraToolProvider.js";
10
+ import { LazyCalculatorToolProvider, } from "../calculator-providers/LazyCalculatorToolProvider.js";
11
+ import { DEFAULT_CALCULATOR_PROVIDER_ID, PACKAGED_CALCULATOR_PROVIDERS, } from "../calculator-providers/packaged-calculator-providers.js";
13
12
  import { resolveOverlayElement } from "./overlay-element-cache.js";
14
- export const DEFAULT_CALCULATOR_PROVIDER_ID = "calculator-desmos";
15
- export function resolveCalculatorProviderId(config) {
13
+ export function resolveCalculatorProviderId(config, providers = PACKAGED_CALCULATOR_PROVIDERS) {
16
14
  const configured = config?.provider?.id;
17
15
  if (configured === undefined || configured === "") {
18
16
  return DEFAULT_CALCULATOR_PROVIDER_ID;
19
17
  }
20
- if (configured === "calculator-desmos" ||
21
- configured === "calculator-geogebra" ||
22
- configured === "calculator-cortex") {
18
+ if (typeof configured === "string" && Object.hasOwn(providers, configured)) {
23
19
  return configured;
24
20
  }
25
- throw new Error(`Unsupported calculator provider "${String(configured)}". Expected "calculator-desmos", "calculator-geogebra", or "calculator-cortex".`);
26
- }
27
- function createCalculatorToolProvider(config) {
28
- switch (resolveCalculatorProviderId(config)) {
29
- case "calculator-cortex":
30
- return new CortexToolProvider();
31
- case "calculator-geogebra":
32
- return new GeoGebraToolProvider();
33
- default:
34
- return new DesmosToolProvider();
35
- }
21
+ const expected = Object.keys(providers)
22
+ .map((id) => `"${id}"`)
23
+ .join(", ");
24
+ throw new Error(`Unsupported calculator provider "${String(configured)}". Expected one of ${expected}.`);
36
25
  }
37
26
  function getCalculatorInstanceConfig(config) {
38
27
  const theme = config?.theme;
@@ -104,169 +93,175 @@ function applyCalculatorParamsToElement(element, calculatorType, availableTypes,
104
93
  }
105
94
  }
106
95
  /**
107
- * Calculator tool registration
96
+ * Builds the calculator registration over the packaged backends plus any the
97
+ * host adds, so a host bundles a backend only by naming it.
108
98
  *
109
99
  * Supports:
110
- * - Basic, scientific, and graphing calculators through Desmos, GeoGebra, or Cortex
100
+ * - Basic, scientific, and graphing calculators through the backend that
101
+ * `provider.id` selects, Desmos by default
111
102
  * - Context-aware visibility (shows only when math content is detected)
112
103
  * - Item level only
113
104
  */
114
- export const calculatorToolRegistration = {
115
- toolId: "calculator",
116
- name: "Calculator",
117
- description: "Multi-type calculator (basic, scientific, graphing)",
118
- nameKey: "tools.calculator.name",
119
- descriptionKey: "tools.calculator.description",
120
- icon: "calculator",
121
- provider: {
122
- createProvider: createCalculatorToolProvider,
123
- getInitConfig: (config) => config?.provider?.init ?? {},
124
- getAuthFetcher: (config) => {
125
- const runtimeAuthFetcher = config?.provider?.runtime?.authFetcher;
126
- return typeof runtimeAuthFetcher === "function"
127
- ? runtimeAuthFetcher
128
- : undefined;
105
+ export function createCalculatorToolRegistration(options = {}) {
106
+ const providers = { ...PACKAGED_CALCULATOR_PROVIDERS, ...options.providers };
107
+ return {
108
+ toolId: "calculator",
109
+ name: "Calculator",
110
+ description: "Multi-type calculator (basic, scientific, graphing)",
111
+ nameKey: "tools.calculator.name",
112
+ descriptionKey: "tools.calculator.description",
113
+ icon: "calculator",
114
+ provider: {
115
+ createProvider: (config) => new LazyCalculatorToolProvider(providers[resolveCalculatorProviderId(config, providers)]),
116
+ getInitConfig: (config) => config?.provider?.init ?? {},
117
+ getAuthFetcher: (config) => {
118
+ const runtimeAuthFetcher = config?.provider?.runtime?.authFetcher;
119
+ return typeof runtimeAuthFetcher === "function"
120
+ ? runtimeAuthFetcher
121
+ : undefined;
122
+ },
123
+ lazy: true,
124
+ },
125
+ // Calculator is item-level in this player architecture.
126
+ supportedLevels: ["item"],
127
+ /**
128
+ * Pass 2: Determine if calculator is relevant in this context
129
+ *
130
+ * Calculator is relevant when context contains mathematical content
131
+ * (MathML, LaTeX, arithmetic markers).
132
+ */
133
+ isVisibleInContext(context) {
134
+ return hasMathContent(context);
129
135
  },
130
- lazy: true,
131
- },
132
- // Calculator is item-level in this player architecture.
133
- supportedLevels: ["item"],
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
- return hasMathContent(context);
142
- },
143
- renderToolbar(context, toolbarContext) {
144
- const { calculatorType, availableTypes, displayName } = getCalculatorRenderParams(toolbarContext);
145
- const calculatorToolConfig = toolbarContext.toolkitCoordinator?.getToolConfig(this.toolId) ||
146
- undefined;
147
- const calculatorConfig = getCalculatorInstanceConfig(calculatorToolConfig);
148
- const fullToolId = createScopedToolId(this.toolId, toolbarContext.scope.level, toolbarContext.scope.scopeId);
149
- const componentOverrides = toolbarContext.componentOverrides;
150
- // Reused across renders: besides keeping state, this spares a Desmos boot
151
- // and container mount on every re-render.
152
- const overlay = resolveOverlayElement(toolbarContext, fullToolId, () => createToolElement(this.toolId, context, toolbarContext, componentOverrides));
153
- overlay.setAttribute("tool-id", fullToolId);
154
- applyCalculatorParamsToElement(overlay, calculatorType, availableTypes, calculatorConfig);
155
- const button = {
156
- toolId: this.toolId,
157
- label: displayName,
158
- icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
159
- // Routes this button through <nds-icon-button> where the host enables NDS
160
- // icons. Declared here because which capabilities render in the host's
161
- // design system is a composition decision.
162
- faIconName: "calculator",
163
- disabled: false,
164
- // The name stays put across open and closed. The toolbar mirrors
165
- // `active` onto the button as `aria-pressed`, so the state is already
166
- // exposed; a name that also swapped to "Close …" would contradict it.
167
- ariaLabel: displayName,
168
- tooltip: displayName,
169
- onClick: () => toolbarContext.toggleTool(this.toolId),
170
- active: toolbarContext.isToolVisible(this.toolId),
171
- };
172
- let lastVisibleState = button.active;
173
- if (overlay.visible !== button.active) {
174
- overlay.visible = button.active;
175
- }
176
- return {
177
- toolId: this.toolId,
178
- elements: [
179
- {
180
- element: overlay,
181
- mount: "after-buttons",
182
- shell: {
183
- // The variant name the button already carries — "Basic Calculator",
184
- // not the registration's generic one.
185
- title: displayName,
186
- draggable: true,
187
- resizable: true,
188
- closeable: true,
189
- /*
190
- * Sized per type from what each layout measures, rather than one
191
- * size for all three. A shell subtracts ~74px of header from the
192
- * height it is given, and the numbers below are the content each
193
- * type needs plus room for its history tape:
194
- *
195
- * - basic: strip, display, edit row and a four-row keypad measure
196
- * 398px. 560 left ~90px of blank above the entry line, which is
197
- * the gap that made the panel look mis-sized.
198
- * - scientific: the same plus a keypad layer tab row.
199
- * - graphing: the expression rail beside the plot, whose column
200
- * wants 486px on its own — viewport controls, a 14rem board floor
201
- * and the readout that *is* the graph for assistive technology,
202
- * since the board itself is `aria-hidden`. At 620 that column was
203
- * 12px short and the readout's last line was cut off.
204
- *
205
- * The minimums stay shared across types and providers. This
206
- * registration serves Desmos, GeoGebra and Cortex alike, so a
207
- * graphing-only floor would move the resize limit under two vendors
208
- * whose layouts were never measured for it. Cortex below 42rem
209
- * stacks the rail above the plot and needs 701px there, which no
210
- * spacing tier closes — it scrolls its own content instead of
211
- * clipping it, which is the contract every size below the opening
212
- * one already relies on.
213
- */
214
- initialWidth: calculatorType === "graphing" ? 720 : 380,
215
- initialHeight: calculatorType === "graphing"
216
- ? 660
217
- : calculatorType === "basic"
218
- ? 500
219
- : 560,
220
- minWidth: 380,
221
- minHeight: 480,
222
- /*
223
- * `bottom-right` put a 560-620px-tall shell over the items
224
- * column it shares the viewport with — tall enough, at
225
- * ordinary viewport heights, to sit on top of a sibling
226
- * item's own toolbar row and block its button from mouse
227
- * and touch input. `bottom-left` keeps the same footprint
228
- * over the passage column instead, which carries no
229
- * per-item controls to collide with.
230
- */
231
- initialAlign: "bottom-left",
232
- initialMargin: 16,
233
- // Header controls in the host's design system, and the layout that
234
- // goes with them.
235
- ndsHeaderControls: true,
236
- // A learner reads the question while using the calculator, so Tab and
237
- // Shift+Tab cross between the page and the shell instead of cycling
238
- // inside it.
239
- pageTabOrder: true,
240
- content: {
241
- overflowY: "auto",
242
- preserveMinHeight: true,
136
+ renderToolbar(context, toolbarContext) {
137
+ const { calculatorType, availableTypes, displayName } = getCalculatorRenderParams(toolbarContext);
138
+ const calculatorToolConfig = toolbarContext.toolkitCoordinator?.getToolConfig(this.toolId) ||
139
+ undefined;
140
+ const calculatorConfig = getCalculatorInstanceConfig(calculatorToolConfig);
141
+ const fullToolId = createScopedToolId(this.toolId, toolbarContext.scope.level, toolbarContext.scope.scopeId);
142
+ const componentOverrides = toolbarContext.componentOverrides;
143
+ // Reused across renders: besides keeping state, this spares a Desmos boot
144
+ // and container mount on every re-render.
145
+ const overlay = resolveOverlayElement(toolbarContext, fullToolId, () => createToolElement(this.toolId, context, toolbarContext, componentOverrides));
146
+ overlay.setAttribute("tool-id", fullToolId);
147
+ applyCalculatorParamsToElement(overlay, calculatorType, availableTypes, calculatorConfig);
148
+ const button = {
149
+ toolId: this.toolId,
150
+ label: displayName,
151
+ icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
152
+ // Routes this button through <pie-nds-icon-button> where the host enables NDS
153
+ // icons. Declared here because which capabilities render in the host's
154
+ // design system is a composition decision.
155
+ faIconName: "calculator",
156
+ disabled: false,
157
+ // The name stays put across open and closed. The toolbar mirrors
158
+ // `active` onto the button as `aria-pressed`, so the state is already
159
+ // exposed; a name that also swapped to "Close …" would contradict it.
160
+ ariaLabel: displayName,
161
+ tooltip: displayName,
162
+ onClick: () => toolbarContext.toggleTool(this.toolId),
163
+ active: toolbarContext.isToolVisible(this.toolId),
164
+ };
165
+ let lastVisibleState = button.active;
166
+ if (overlay.visible !== button.active) {
167
+ overlay.visible = button.active;
168
+ }
169
+ return {
170
+ toolId: this.toolId,
171
+ elements: [
172
+ {
173
+ element: overlay,
174
+ mount: "after-buttons",
175
+ shell: {
176
+ // The variant name the button already carries — "Basic Calculator",
177
+ // not the registration's generic one.
178
+ title: displayName,
179
+ draggable: true,
180
+ resizable: true,
181
+ closeable: true,
182
+ /*
183
+ * Sized per type from what each layout measures, rather than one
184
+ * size for all three. A shell subtracts ~74px of header from the
185
+ * height it is given, and the numbers below are the content each
186
+ * type needs plus room for its history tape:
187
+ *
188
+ * - basic: strip, display, edit row and a four-row keypad measure
189
+ * 398px. 560 left ~90px of blank above the entry line, which is
190
+ * the gap that made the panel look mis-sized.
191
+ * - scientific: the same plus a keypad layer tab row.
192
+ * - graphing: the expression rail beside the plot, whose column
193
+ * wants 486px on its own — viewport controls, a 14rem board floor
194
+ * and the readout that *is* the graph for assistive technology,
195
+ * since the board itself is `aria-hidden`. At 620 that column was
196
+ * 12px short and the readout's last line was cut off.
197
+ *
198
+ * The minimums stay shared across types and backends. One
199
+ * registration serves every backend, so a graphing-only floor
200
+ * would move the resize limit under vendors whose layouts were
201
+ * never measured for it. Cortex below 42rem stacks the rail above
202
+ * the plot and needs 701px there, which no spacing tier closes — it
203
+ * scrolls its own content instead of clipping it, which is the
204
+ * contract every size below the opening one already relies on.
205
+ */
206
+ initialWidth: calculatorType === "graphing" ? 720 : 380,
207
+ initialHeight: calculatorType === "graphing"
208
+ ? 660
209
+ : calculatorType === "basic"
210
+ ? 500
211
+ : 560,
212
+ minWidth: 380,
213
+ minHeight: 480,
214
+ /*
215
+ * `bottom-right` put a 560-620px-tall shell over the items
216
+ * column it shares the viewport with — tall enough, at
217
+ * ordinary viewport heights, to sit on top of a sibling
218
+ * item's own toolbar row and block its button from mouse
219
+ * and touch input. `bottom-left` keeps the same footprint
220
+ * over the passage column instead, which carries no
221
+ * per-item controls to collide with.
222
+ */
223
+ initialAlign: "bottom-left",
224
+ initialMargin: 16,
225
+ // Header controls in the host's design system, and the layout that
226
+ // goes with them.
227
+ ndsHeaderControls: true,
228
+ // A learner reads the question while using the calculator, so Tab and
229
+ // Shift+Tab cross between the page and the shell instead of cycling
230
+ // inside it.
231
+ pageTabOrder: true,
232
+ content: {
233
+ overflowY: "auto",
234
+ preserveMinHeight: true,
235
+ },
243
236
  },
244
237
  },
238
+ ],
239
+ button,
240
+ sync: () => {
241
+ const active = toolbarContext.isToolVisible(this.toolId);
242
+ button.active = active;
243
+ button.label = displayName;
244
+ // Static across the toggle: `aria-pressed` carries the state, and a
245
+ // "Close …" template built from a localized noun mixes languages
246
+ // ("Close rekenmachine" under nl-NL).
247
+ button.ariaLabel = displayName;
248
+ button.tooltip = displayName;
249
+ if (lastVisibleState !== active) {
250
+ overlay.visible = active;
251
+ lastVisibleState = active;
252
+ }
253
+ applyCalculatorParamsToElement(overlay, calculatorType, availableTypes, calculatorConfig);
245
254
  },
246
- ],
247
- button,
248
- sync: () => {
249
- const active = toolbarContext.isToolVisible(this.toolId);
250
- button.active = active;
251
- button.label = displayName;
252
- // Static across the toggle: `aria-pressed` carries the state, and a
253
- // "Close …" template built from a localized noun mixes languages
254
- // ("Close rekenmachine" under nl-NL).
255
- button.ariaLabel = displayName;
256
- button.tooltip = displayName;
257
- if (lastVisibleState !== active) {
258
- overlay.visible = active;
259
- lastVisibleState = active;
260
- }
261
- applyCalculatorParamsToElement(overlay, calculatorType, availableTypes, calculatorConfig);
262
- },
263
- subscribeActive: (callback) => {
264
- if (!toolbarContext.subscribeVisibility)
265
- return () => { };
266
- return toolbarContext.subscribeVisibility(() => {
267
- callback(toolbarContext.isToolVisible(this.toolId));
268
- });
269
- },
270
- };
271
- },
272
- };
255
+ subscribeActive: (callback) => {
256
+ if (!toolbarContext.subscribeVisibility)
257
+ return () => { };
258
+ return toolbarContext.subscribeVisibility(() => {
259
+ callback(toolbarContext.isToolVisible(this.toolId));
260
+ });
261
+ },
262
+ };
263
+ },
264
+ };
265
+ }
266
+ /** The calculator over the packaged Desmos and GeoGebra backends. */
267
+ export const calculatorToolRegistration = createCalculatorToolRegistration();
@@ -4,11 +4,24 @@
4
4
  * Registers tools for interacting with question content:
5
5
  * - Answer Eliminator (strike through answer choices)
6
6
  */
7
- import type { ToolRegistration } from "@pie-players/pie-assessment-toolkit/tools/registration";
7
+ import type { ToolProviderConfig, ToolRegistration } from "@pie-players/pie-assessment-toolkit/tools/registration";
8
+ /**
9
+ * The answer eliminator's `tools.providers.answerEliminator` entry.
10
+ */
11
+ export interface AnswerEliminatorToolConfig extends ToolProviderConfig {
12
+ /**
13
+ * The elements the eliminator acts on, by the name `<name>` of their
14
+ * `@pie-element/<name>` package, such as `["multiple-choice"]`. An element
15
+ * must also support answer masking. Omitted, every element that supports it
16
+ * takes the eliminator; empty, none does. A host lists the elements whose
17
+ * support it has vetted.
18
+ */
19
+ elements?: readonly string[];
20
+ }
8
21
  /**
9
22
  * Answer Eliminator tool registration
10
23
  *
11
24
  * Allows students to strike through incorrect answer choices.
12
- * Only appears on multiple-choice style questions.
25
+ * Only appears on items with an element that supports answer masking.
13
26
  */
14
27
  export declare const answerEliminatorToolRegistration: ToolRegistration;