@pie-players/pie-default-tool-loaders 1.0.0-beta.1 → 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 +98 -14
  3. package/dist/afa-profile.d.ts +17 -0
  4. package/dist/afa-profile.js +18 -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 -6
  9. package/dist/index.js +8 -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 +14 -1
  18. package/dist/registrations/interaction-tools.js +91 -23
  19. package/dist/registrations/tts.d.ts +23 -2
  20. package/dist/registrations/tts.js +120 -98
  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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PIE Framework contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -5,9 +5,9 @@ they load, and which of them a program grants to everyone. This README is for
5
5
  host integrators choosing a tool set and a calculator, and for contributors
6
6
  adding a packaged capability.
7
7
 
8
- The package owns the concrete `pie-tool-*` dependencies and the calculator
9
- engines (`pie-calculator-cortex`, `-desmos` and `-geogebra`), so toolkit core
10
- stays dependency-light and cycle-safe. Core knows the concepts — support ids,
8
+ The package owns the concrete `pie-tool-*` dependencies and the Desmos and
9
+ GeoGebra calculator adapters (`pie-calculator-desmos` and `-geogebra`), so
10
+ toolkit core stays dependency-light and cycle-safe. Core knows the concepts — support ids,
11
11
  placement levels, activation kinds and precedence rules — and no concrete
12
12
  capability id. A support id is the tool id a profile grants.
13
13
 
@@ -15,6 +15,8 @@ capability id. A support id is the tool id a profile grants.
15
15
  | --- | --- |
16
16
  | `PACKAGED_TOOL_REGISTRATIONS`, `createPackagedToolRegistry` | Which capabilities exist, and their toolbar and surface contracts |
17
17
  | `PACKAGED_TOOL_TAG_MAP` | Which custom element each one renders as |
18
+ | `createCalculatorToolRegistration` | Which calculator backends `provider.id` selects among |
19
+ | `createTTSToolRegistration` | Which host text-to-speech providers `provider.id` selects among |
18
20
  | `SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT` | Where they appear and in what order |
19
21
  | `createUniversalPersonalNeedsProfile`, `createEmptyPersonalNeedsProfile` | Which of them the program grants to everyone, or to no one |
20
22
  | `createPersonalNeedsProfileFromAfa` | Which of them a student's AfA PNP 3.0 record grants, prohibits and turns on at start, and with which settings |
@@ -58,15 +60,8 @@ Desmos is the provider when none is configured, and it needs an API key: pass
58
60
  it through `provider.runtime.authFetcher`, a `provider.init.proxyEndpoint` that
59
61
  returns it, or `provider.init.apiKey` in development
60
62
  ([Calculator with host auth](../../docs/tools-and-accomodations/tool_provider_system.md#calculator-with-host-auth)).
61
- Without one the calculator fails to load. For a keyless, offline-capable
62
- calculator with no runtime CDN, use `provider.id: "calculator-cortex"`. Its
63
- `settings` take the `CortexCalculatorSettings` type that
64
- `@pie-players/pie-calculator-cortex` exports: `angleMode`,
65
- `calculationPrecision`, `displayPrecision`, `historyLimit`,
66
- `evaluationTimeLimitMs`, `allowedFunctions`, `allowClipboard`, `messages`,
67
- `direction` and `graph`. Clipboard blocking follows `restrictedMode`;
68
- `allowClipboard` alone blocks nothing. The [Cortex README](../calculator-cortex/README.md)
69
- covers localization, message overrides and the keypad.
63
+ Without one the calculator fails to load. `provider.id: "calculator-geogebra"`
64
+ selects GeoGebra, which needs no key.
70
65
 
71
66
  ```ts
72
67
  const tools = {
@@ -82,6 +77,95 @@ const tools = {
82
77
  };
83
78
  ```
84
79
 
80
+ ### Adding a backend
81
+
82
+ The packaged registration selects among Desmos and GeoGebra only, because a
83
+ bundler that inlines dynamic imports inlines every backend the registration
84
+ names, selected or not. `createCalculatorToolRegistration({ providers })` builds
85
+ the registration with more backends, keyed by `provider.id`; an id the packaged
86
+ set already holds is replaced. The keyless, offline-capable Cortex calculator,
87
+ which needs no runtime CDN, is composed in this way from
88
+ `@pie-players/pie-calculator-cortex`, which the host installs:
89
+
90
+ ```ts
91
+ import {
92
+ createCalculatorToolRegistration,
93
+ createPackagedToolRegistry,
94
+ } from "@pie-players/pie-default-tool-loaders";
95
+
96
+ const registry = createPackagedToolRegistry({
97
+ overrides: {
98
+ calculator: createCalculatorToolRegistration({
99
+ providers: {
100
+ "calculator-cortex": {
101
+ providerName: "PIE Open-Source Calculator",
102
+ version: "1",
103
+ requiresAuth: false,
104
+ backend: "cortex",
105
+ load: async () =>
106
+ (await import("@pie-players/pie-calculator-cortex"))
107
+ .CortexCalculatorProvider,
108
+ initializationErrorMessage:
109
+ "Failed to initialize the Cortex calculator provider.",
110
+ },
111
+ },
112
+ }),
113
+ },
114
+ });
115
+ ```
116
+
117
+ `provider.id: "calculator-cortex"` then selects it. Its `settings` take the
118
+ `CortexCalculatorSettings` type that `@pie-players/pie-calculator-cortex`
119
+ exports: `angleMode`, `calculationPrecision`, `displayPrecision`,
120
+ `historyLimit`, `evaluationTimeLimitMs`, `allowedFunctions`, `allowClipboard`,
121
+ `messages`, `direction` and `graph`. Clipboard blocking follows
122
+ `restrictedMode`; `allowClipboard` alone blocks nothing. The
123
+ [Cortex README](../calculator-cortex/README.md) covers localization, message
124
+ overrides and the keypad.
125
+
126
+ ## Text-to-speech providers
127
+
128
+ The packaged text-to-speech registration speaks through the browser or a TTS
129
+ server, as `backend` selects. `createTTSToolRegistration({ providers })` adds a
130
+ host's own `ITTSProvider`, keyed by `provider.id`; the class is imported only
131
+ when its id is selected.
132
+
133
+ ```ts
134
+ import {
135
+ createPackagedToolRegistry,
136
+ createTTSToolRegistration,
137
+ } from "@pie-players/pie-default-tool-loaders";
138
+
139
+ const registry = createPackagedToolRegistry({
140
+ overrides: {
141
+ textToSpeech: createTTSToolRegistration({
142
+ providers: {
143
+ "host-tts": {
144
+ load: async () => (await import("./host-tts.js")).HostTTSProvider,
145
+ },
146
+ },
147
+ }),
148
+ },
149
+ });
150
+
151
+ // tools.providers.textToSpeech
152
+ const textToSpeech = {
153
+ enabled: true,
154
+ language: "en-US",
155
+ provider: { id: "host-tts" },
156
+ };
157
+ ```
158
+
159
+ The provider's `initialize()` receives the config the toolkit builds for every
160
+ backend: `voice`, `rate`, `pitch`, `language`, `providerOptions` (the host's
161
+ `language` as `locale`) and `mathTokenHighlighting`. `apiEndpoint` and the
162
+ backend and transport keys are not required. With `requiresAuth: true` on the
163
+ descriptor, the toolkit first awaits `provider.runtime.authFetcher` and merges what it returns, such as
164
+ `authToken` or `headers`, into that config. A provider that fails to initialize
165
+ falls back to browser speech, as a server backend does. A `provider.id` that
166
+ names no registered provider, or none at all, keeps the browser or server
167
+ backend.
168
+
85
169
  ## Custom sets
86
170
 
87
171
  A host composing its own set starts from the toolkit's empty `ToolRegistry`,
@@ -167,8 +251,8 @@ student's AfA PNP 3.0 record, held as JSON keyed by the spec's terms. Features
167
251
  become support ids in the packaged set's names, the activation sets fill
168
252
  `activateAtInit` and `prohibitedSupports`, and the settings the packaged tools
169
253
  read become `toolParameters`: read-aloud speed, pitch and volume, the calculator
170
- type and the sign language. The toolkit's PNP configuration guide gives each AfA
171
- feature's disposition.
254
+ type, the sign language and the magnifier's starting zoom. The toolkit's PNP
255
+ configuration guide gives each AfA feature's disposition.
172
256
 
173
257
  ```ts
174
258
  import { createPersonalNeedsProfileFromAfa } from "@pie-players/pie-default-tool-loaders";
@@ -28,6 +28,10 @@ export interface AfaSpoken {
28
28
  }
29
29
  /** A set's features: empty elements keyed by feature, or the feature names. */
30
30
  export type AfaFeatureSet = Readonly<Record<string, unknown>> | readonly string[];
31
+ /** AfA's `ZoomAmount`: a magnification multiplier. */
32
+ export interface AfaZoomAmount {
33
+ "zoom-amount"?: number;
34
+ }
31
35
  /** An AfA PNP 3.0 record in JSON. A feature is present unless absent, `null` or `false`. */
32
36
  export interface AfaPnpRecord {
33
37
  spoken?: AfaSpoken;
@@ -39,6 +43,19 @@ export interface AfaPnpRecord {
39
43
  language?: string;
40
44
  [setting: string]: unknown;
41
45
  };
46
+ "item-translation"?: {
47
+ language?: string;
48
+ [setting: string]: unknown;
49
+ };
50
+ /** One selection: the amount for all content, or for text and non-text apart. */
51
+ magnification?: {
52
+ "all-content"?: AfaZoomAmount;
53
+ selectMagnification?: {
54
+ text?: AfaZoomAmount;
55
+ "non-text"?: AfaZoomAmount;
56
+ };
57
+ [setting: string]: unknown;
58
+ };
42
59
  "activate-at-initialization-set"?: AfaFeatureSet;
43
60
  "activate-as-option-set"?: AfaFeatureSet;
44
61
  "prohibit-set"?: AfaFeatureSet;
@@ -70,6 +70,16 @@ function spokenParameters(spoken) {
70
70
  ...(volume !== undefined ? { volume } : {}),
71
71
  };
72
72
  }
73
+ /**
74
+ * The lens's starting zoom. The lens magnifies text and non-text alike, so a
75
+ * record that sets them apart gives the text amount, else the non-text one.
76
+ */
77
+ function magnifierZoom(magnification) {
78
+ const select = magnification?.selectMagnification;
79
+ return (finiteNumber(magnification?.["all-content"]?.["zoom-amount"]) ??
80
+ finiteNumber(select?.text?.["zoom-amount"]) ??
81
+ finiteNumber(select?.["non-text"]?.["zoom-amount"]));
82
+ }
73
83
  /** A fresh profile carrying `record`'s grants, prohibitions and settings, ids sorted. */
74
84
  export function createPersonalNeedsProfileFromAfa(record) {
75
85
  const spoken = typeof record.spoken === "object" && record.spoken !== null
@@ -108,6 +118,14 @@ export function createPersonalNeedsProfileFromAfa(record) {
108
118
  if (typeof signLang === "string" && signLang.length > 0) {
109
119
  toolParameters.signLanguage = { signLang };
110
120
  }
121
+ const translationLang = record["item-translation"]?.language;
122
+ if (typeof translationLang === "string" && translationLang.length > 0) {
123
+ toolParameters.itemTranslation = { language: translationLang };
124
+ }
125
+ const zoom = magnifierZoom(record.magnification);
126
+ if (zoom !== undefined && zoom > 1) {
127
+ toolParameters.magnification = { zoom };
128
+ }
111
129
  return {
112
130
  supports,
113
131
  prohibitedSupports: [...prohibited].sort(),
@@ -1,33 +1,47 @@
1
1
  import type { CalculatorProvider, CalculatorProviderInit } from "@pie-players/pie-calculator";
2
2
  import type { ToolProviderApi } from "@pie-players/pie-assessment-toolkit/tools/registration";
3
- export type CalculatorToolProviderInitConfig = Pick<CalculatorProviderInit, "onTelemetry">;
4
- type InitializableCalculatorProvider<TConfig> = CalculatorProvider & {
5
- initialize(config: TConfig): Promise<void>;
6
- };
7
- export interface LazyCalculatorProviderDefinition<TConfig> {
8
- backend: string;
9
- moduleImportOperation: string;
10
- loadProvider: () => Promise<new () => InitializableCalculatorProvider<TConfig>>;
11
- initializationErrorMessage: string;
3
+ /**
4
+ * One calculator backend, selected by the toolkit's `provider.id`. `load` runs
5
+ * on first use, but a bundler that inlines dynamic imports inlines what it
6
+ * names whether or not the host selects the backend. The packaged set therefore
7
+ * holds only the thin Desmos and GeoGebra adapters, and a host adds any other
8
+ * backend through `createCalculatorToolRegistration`.
9
+ */
10
+ export interface CalculatorProviderDefinition {
11
+ /** Names the provider in toolkit logs, telemetry and error messages. */
12
+ readonly providerName: string;
13
+ readonly version: string;
14
+ /**
15
+ * Whether the registry merges the result of `provider.runtime.authFetcher`
16
+ * into the configuration `initialize()` receives.
17
+ */
18
+ readonly requiresAuth: boolean;
19
+ /** The `backend` of library-load telemetry. */
20
+ readonly backend: string;
21
+ /** Loads the adapter's provider class, usually through a dynamic import. */
22
+ readonly load: () => Promise<new () => CalculatorProvider>;
23
+ /** Thrown, with the failure as its cause, when loading or initializing fails. */
24
+ readonly initializationErrorMessage: string;
12
25
  }
13
26
  /**
14
- * Shared lazy-module and lifecycle implementation for calculator tool adapters.
15
- * Concrete adapters own only their metadata and provider import.
27
+ * Lazy-module and lifecycle implementation shared by every calculator backend:
28
+ * concurrent initialization is deduplicated, and `destroy()` cancels one in
29
+ * flight.
16
30
  */
17
- export declare abstract class LazyCalculatorToolProvider<TConfig extends CalculatorToolProviderInitConfig> implements ToolProviderApi<CalculatorProvider> {
18
- abstract readonly providerName: string;
31
+ export declare class LazyCalculatorToolProvider implements ToolProviderApi<CalculatorProvider> {
32
+ readonly providerName: string;
19
33
  readonly category: "calculator";
20
- abstract readonly version: string;
21
- abstract readonly requiresAuth: boolean;
22
- protected abstract getDefinition(): LazyCalculatorProviderDefinition<TConfig>;
34
+ readonly version: string;
35
+ readonly requiresAuth: boolean;
36
+ private readonly definition;
23
37
  private calculatorProvider;
24
38
  private initializationPromise;
25
39
  private lifecycleGeneration;
40
+ constructor(definition: CalculatorProviderDefinition);
26
41
  private emitTelemetry;
27
- initialize(config?: TConfig): Promise<void>;
42
+ initialize(config?: CalculatorProviderInit): Promise<void>;
28
43
  private initializeProvider;
29
44
  createInstance(): Promise<CalculatorProvider>;
30
45
  isReady(): boolean;
31
46
  destroy(): void;
32
47
  }
33
- export {};
@@ -1,12 +1,23 @@
1
1
  /**
2
- * Shared lazy-module and lifecycle implementation for calculator tool adapters.
3
- * Concrete adapters own only their metadata and provider import.
2
+ * Lazy-module and lifecycle implementation shared by every calculator backend:
3
+ * concurrent initialization is deduplicated, and `destroy()` cancels one in
4
+ * flight.
4
5
  */
5
6
  export class LazyCalculatorToolProvider {
7
+ providerName;
6
8
  category = "calculator";
9
+ version;
10
+ requiresAuth;
11
+ definition;
7
12
  calculatorProvider = null;
8
13
  initializationPromise = null;
9
14
  lifecycleGeneration = 0;
15
+ constructor(definition) {
16
+ this.definition = definition;
17
+ this.providerName = definition.providerName;
18
+ this.version = definition.version;
19
+ this.requiresAuth = definition.requiresAuth;
20
+ }
10
21
  async emitTelemetry(config, eventName, payload) {
11
22
  try {
12
23
  await config.onTelemetry?.(eventName, payload);
@@ -33,33 +44,34 @@ export class LazyCalculatorToolProvider {
33
44
  }
34
45
  }
35
46
  async initializeProvider(config, generation) {
36
- const definition = this.getDefinition();
47
+ const { backend, initializationErrorMessage } = this.definition;
48
+ const operation = `${backend}-provider-module-import`;
37
49
  const moduleLoadStartedAt = Date.now();
38
50
  await this.emitTelemetry(config, "pie-tool-library-load-start", {
39
51
  toolId: "calculator",
40
- operation: definition.moduleImportOperation,
41
- backend: definition.backend,
52
+ operation,
53
+ backend,
42
54
  });
43
55
  let ProviderConstructor;
44
56
  try {
45
- ProviderConstructor = await definition.loadProvider();
57
+ ProviderConstructor = await this.definition.load();
46
58
  await this.emitTelemetry(config, "pie-tool-library-load-success", {
47
59
  toolId: "calculator",
48
- operation: definition.moduleImportOperation,
49
- backend: definition.backend,
60
+ operation,
61
+ backend,
50
62
  duration: Date.now() - moduleLoadStartedAt,
51
63
  });
52
64
  }
53
65
  catch (error) {
54
66
  await this.emitTelemetry(config, "pie-tool-library-load-error", {
55
67
  toolId: "calculator",
56
- operation: definition.moduleImportOperation,
57
- backend: definition.backend,
68
+ operation,
69
+ backend,
58
70
  duration: Date.now() - moduleLoadStartedAt,
59
71
  errorType: "ToolLibraryLoadError",
60
72
  message: error instanceof Error ? error.message : String(error),
61
73
  });
62
- throw new Error(definition.initializationErrorMessage, { cause: error });
74
+ throw new Error(initializationErrorMessage, { cause: error });
63
75
  }
64
76
  if (generation !== this.lifecycleGeneration) {
65
77
  throw new Error(`${this.providerName} initialization was cancelled`);
@@ -74,7 +86,7 @@ export class LazyCalculatorToolProvider {
74
86
  }
75
87
  catch (error) {
76
88
  candidate.destroy();
77
- throw new Error(definition.initializationErrorMessage, { cause: error });
89
+ throw new Error(initializationErrorMessage, { cause: error });
78
90
  }
79
91
  }
80
92
  async createInstance() {
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The calculator backends of the packaged set, by `provider.id`. Every import
3
+ * named here lands in a host bundle that inlines dynamic imports, so only thin
4
+ * adapters over a vendor-hosted engine belong in it.
5
+ */
6
+ export const DEFAULT_CALCULATOR_PROVIDER_ID = "calculator-desmos";
7
+ export const PACKAGED_CALCULATOR_PROVIDERS = {
8
+ "calculator-desmos": {
9
+ providerName: "Desmos Calculator",
10
+ version: "1.12",
11
+ requiresAuth: true,
12
+ backend: "desmos",
13
+ load: async () => (await import("@pie-players/pie-calculator-desmos"))
14
+ .DesmosCalculatorProvider,
15
+ initializationErrorMessage: "Failed to initialize Desmos calculator provider. Check the application key, preloaded API, runtime endpoint, or network access.",
16
+ },
17
+ "calculator-geogebra": {
18
+ providerName: "GeoGebra Calculator",
19
+ version: "6",
20
+ requiresAuth: false,
21
+ backend: "geogebra",
22
+ load: async () => (await import("@pie-players/pie-calculator-geogebra"))
23
+ .GeoGebraCalculatorProvider,
24
+ initializationErrorMessage: "Failed to initialize GeoGebra calculator provider. Confirm that the deployment may load GeoGebra and that its script URL is reachable.",
25
+ },
26
+ };
package/dist/index.d.ts CHANGED
@@ -1,12 +1,13 @@
1
1
  export type { PackagedToolRegistryOptions } from "./packaged-capability-composition.js";
2
2
  export { createEmptyPersonalNeedsProfile, createPackagedToolRegistry, createUniversalPersonalNeedsProfile, DEFAULT_TOOL_MODULE_LOADERS, PACKAGED_TOOL_REGISTRATIONS, PACKAGED_TOOL_TAG_MAP, SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT, } from "./packaged-capability-composition.js";
3
- export { type AfaFeatureSet, type AfaPnpRecord, type AfaSpoken, createPersonalNeedsProfileFromAfa, } from "./afa-profile.js";
3
+ export { type AfaFeatureSet, type AfaPnpRecord, type AfaSpoken, type AfaZoomAmount, createPersonalNeedsProfileFromAfa, } from "./afa-profile.js";
4
4
  export { CONTENT_ALTERNATE_REGISTRATIONS } from "./content-alternates.js";
5
- export { annotationToolbarRegistration, lineReaderToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
6
- export { AUDIO_TRANSCRIPT_FEATURE_ID, AUDIO_TRANSCRIPT_REGION_CLASS, AUDIO_TRANSCRIPT_REGION_LABEL, audioTranscriptRegistration, CONTENT_LEAD_SURFACE, resolveAudioTranscript, type ResolvedAudioTranscript, } from "./registrations/audio-transcript.js";
7
- export { calculatorToolRegistration } from "./registrations/calculator.js";
5
+ export { annotationToolbarRegistration, lineReaderToolRegistration, magnificationToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
6
+ export { AUDIO_TRANSCRIPT_FEATURE_ID, AUDIO_TRANSCRIPT_REGION_CLASS, AUDIO_TRANSCRIPT_REGION_LABEL, audioTranscriptRegistration, resolveAudioTranscript, type ResolvedAudioTranscript, } from "./registrations/audio-transcript.js";
7
+ export { CONTENT_LEAD_SURFACE } from "@pie-players/pie-assessment-toolkit/tools/registration";
8
+ export { type CalculatorProviderDefinition, type CalculatorRegistrationOptions, calculatorToolRegistration, createCalculatorToolRegistration, } from "./registrations/calculator.js";
8
9
  export { createDictionaryToolRegistration, createPictureDictionaryToolRegistration, dictionaryToolRegistration, type DictionaryVariantOptions, pictureDictionaryToolRegistration, spanishDictionaryToolRegistration, spanishPictureDictionaryToolRegistration, } from "./registrations/dictionary-tools.js";
9
- export { answerEliminatorToolRegistration } from "./registrations/interaction-tools.js";
10
+ export { type AnswerEliminatorToolConfig, answerEliminatorToolRegistration, } from "./registrations/interaction-tools.js";
10
11
  export { protractorToolRegistration, rulerToolRegistration, } from "./registrations/measurement-tools.js";
11
12
  export { graphToolRegistration, periodicTableToolRegistration, } from "./registrations/subject-specific-tools.js";
12
- export { ttsToolRegistration } from "./registrations/tts.js";
13
+ export { createTTSToolRegistration, type TTSHostProviderDescriptor, type TTSRegistrationOptions, ttsToolRegistration, } from "./registrations/tts.js";
package/dist/index.js CHANGED
@@ -6,11 +6,14 @@ export { createPersonalNeedsProfileFromAfa, } from "./afa-profile.js";
6
6
  export { CONTENT_ALTERNATE_REGISTRATIONS } from "./content-alternates.js";
7
7
  // Individual registrations, for a host composing a subset rather than the whole
8
8
  // packaged set.
9
- export { annotationToolbarRegistration, lineReaderToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
10
- export { AUDIO_TRANSCRIPT_FEATURE_ID, AUDIO_TRANSCRIPT_REGION_CLASS, AUDIO_TRANSCRIPT_REGION_LABEL, audioTranscriptRegistration, CONTENT_LEAD_SURFACE, resolveAudioTranscript, } from "./registrations/audio-transcript.js";
11
- export { calculatorToolRegistration } from "./registrations/calculator.js";
9
+ export { annotationToolbarRegistration, lineReaderToolRegistration, magnificationToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
10
+ export { AUDIO_TRANSCRIPT_FEATURE_ID, AUDIO_TRANSCRIPT_REGION_CLASS, AUDIO_TRANSCRIPT_REGION_LABEL, audioTranscriptRegistration, resolveAudioTranscript, } from "./registrations/audio-transcript.js";
11
+ // The canonical surface ids live in the toolkit; re-exported so the packaged
12
+ // set's surface stays unchanged.
13
+ export { CONTENT_LEAD_SURFACE } from "@pie-players/pie-assessment-toolkit/tools/registration";
14
+ export { calculatorToolRegistration, createCalculatorToolRegistration, } from "./registrations/calculator.js";
12
15
  export { createDictionaryToolRegistration, createPictureDictionaryToolRegistration, dictionaryToolRegistration, pictureDictionaryToolRegistration, spanishDictionaryToolRegistration, spanishPictureDictionaryToolRegistration, } from "./registrations/dictionary-tools.js";
13
- export { answerEliminatorToolRegistration } from "./registrations/interaction-tools.js";
16
+ export { answerEliminatorToolRegistration, } from "./registrations/interaction-tools.js";
14
17
  export { protractorToolRegistration, rulerToolRegistration, } from "./registrations/measurement-tools.js";
15
18
  export { graphToolRegistration, periodicTableToolRegistration, } from "./registrations/subject-specific-tools.js";
16
- export { ttsToolRegistration } from "./registrations/tts.js";
19
+ export { createTTSToolRegistration, ttsToolRegistration, } from "./registrations/tts.js";
@@ -8,7 +8,7 @@
8
8
  * cannot leave a second hand-maintained catalogue silently behind.
9
9
  */
10
10
  import { ToolRegistry, } from "@pie-players/pie-assessment-toolkit/tools/registration";
11
- import { annotationToolbarRegistration, lineReaderToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
11
+ import { annotationToolbarRegistration, lineReaderToolRegistration, magnificationToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
12
12
  import { audioTranscriptRegistration } from "./registrations/audio-transcript.js";
13
13
  import { calculatorToolRegistration } from "./registrations/calculator.js";
14
14
  import { dictionaryToolRegistration, pictureDictionaryToolRegistration, spanishDictionaryToolRegistration, spanishPictureDictionaryToolRegistration, } from "./registrations/dictionary-tools.js";
@@ -28,6 +28,7 @@ const loadProtractorModule = () => loadSideEffectModule(() => import("@pie-playe
28
28
  const loadAnswerEliminatorModule = () => loadSideEffectModule(() => import("@pie-players/pie-tool-answer-eliminator"));
29
29
  const loadAnnotationToolbarModule = () => loadSideEffectModule(() => import("@pie-players/pie-tool-annotation-toolbar"));
30
30
  const loadLineReaderModule = () => loadSideEffectModule(() => import("@pie-players/pie-tool-line-reader"));
31
+ const loadMagnifierModule = () => loadSideEffectModule(() => import("@pie-players/pie-tool-magnifier"));
31
32
  const loadThemeModule = () => loadSideEffectModule(() => import("@pie-players/pie-tool-theme"));
32
33
  const loadGraphModule = () => loadSideEffectModule(() => import("@pie-players/pie-tool-graph"));
33
34
  const loadPeriodicTableModule = () => loadSideEffectModule(() => import("@pie-players/pie-tool-periodic-table"));
@@ -76,6 +77,15 @@ const PACKAGED_CAPABILITY_DEFINITIONS = [
76
77
  preferredPlacementOrder: { section: 40 },
77
78
  universal: true,
78
79
  },
80
+ {
81
+ registration: magnificationToolRegistration,
82
+ tagName: "pie-tool-magnifier",
83
+ loadModule: loadMagnifierModule,
84
+ preferredPlacementOrder: { section: 45 },
85
+ // Magnification is a granted low-vision accommodation in AfA PNP, so it
86
+ // is never universal.
87
+ universal: false,
88
+ },
79
89
  {
80
90
  registration: themeToolRegistration,
81
91
  tagName: "pie-tool-theme",
@@ -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;