@pie-players/pie-default-tool-loaders 0.3.75 → 1.0.0-beta.1
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 +109 -70
- package/dist/afa-profile.d.ts +48 -0
- package/dist/afa-profile.js +117 -0
- package/dist/calculator-providers/CortexToolProvider.d.ts +0 -2
- package/dist/calculator-providers/CortexToolProvider.js +0 -13
- package/dist/calculator-providers/DesmosToolProvider.d.ts +0 -2
- package/dist/calculator-providers/DesmosToolProvider.js +0 -13
- package/dist/calculator-providers/GeoGebraToolProvider.d.ts +0 -2
- package/dist/calculator-providers/GeoGebraToolProvider.js +0 -13
- package/dist/calculator-providers/LazyCalculatorToolProvider.d.ts +2 -3
- package/dist/calculator-providers/LazyCalculatorToolProvider.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -1
- package/dist/packaged-capability-composition.d.ts +5 -1
- package/dist/packaged-capability-composition.js +3 -7
- package/dist/registrations/accessibility-tools.js +5 -9
- package/dist/registrations/audio-transcript.d.ts +7 -6
- package/dist/registrations/audio-transcript.js +12 -7
- package/dist/registrations/calculator.js +8 -8
- package/dist/registrations/interaction-tools.d.ts +1 -1
- package/dist/registrations/interaction-tools.js +41 -15
- package/dist/registrations/overlay-toolbar-render.d.ts +8 -11
- package/dist/registrations/overlay-toolbar-render.js +8 -11
- package/dist/registrations/tts.d.ts +1 -2
- package/dist/registrations/tts.js +15 -27
- package/package.json +20 -20
package/README.md
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
# PIE Default Tool Loaders
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
The packaged PIE assessment tools: which capabilities exist in a deployment, how
|
|
4
|
+
they load, and which of them a program grants to everyone. This README is for
|
|
5
|
+
host integrators choosing a tool set and a calculator, and for contributors
|
|
6
|
+
adding a packaged capability.
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
(`pie-calculator-cortex`, `-desmos` and `-geogebra`) so toolkit core
|
|
8
|
-
dependency-light and cycle-safe
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
It owns five things, all answers to "which capabilities does this deployment have
|
|
13
|
-
and how does the program tier them":
|
|
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,
|
|
11
|
+
placement levels, activation kinds and precedence rules — and no concrete
|
|
12
|
+
capability id. A support id is the tool id a profile grants.
|
|
14
13
|
|
|
15
14
|
| Export | What it decides |
|
|
16
15
|
| --- | --- |
|
|
@@ -18,49 +17,56 @@ and how does the program tier them":
|
|
|
18
17
|
| `PACKAGED_TOOL_TAG_MAP` | Which custom element each one renders as |
|
|
19
18
|
| `SECTION_PLAYER_PREFERRED_TOOL_PLACEMENT` | Where they appear and in what order |
|
|
20
19
|
| `createUniversalPersonalNeedsProfile`, `createEmptyPersonalNeedsProfile` | Which of them the program grants to everyone, or to no one |
|
|
20
|
+
| `createPersonalNeedsProfileFromAfa` | Which of them a student's AfA PNP 3.0 record grants, prohibits and turns on at start, and with which settings |
|
|
21
21
|
| `DEFAULT_TOOL_MODULE_LOADERS` | When each one's bundle loads |
|
|
22
|
+
| `CONTENT_ALTERNATE_REGISTRATIONS` | Which of them carry an authored alternate that renders as a region; the print player mounts these |
|
|
22
23
|
|
|
23
|
-
The individual registrations are exported too, so a host can compose its own
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
These exports are projections of one internal **Packaged Capability
|
|
27
|
-
Composition**. Each packaged capability is authored once with its registration,
|
|
28
|
-
element delivery, module loader, preferred placement and explicit
|
|
29
|
-
universal-support policy. The package build rejects contradictory PIE-owned data
|
|
30
|
-
— for example, a region capability with a toolbar tag, or a content-dependent
|
|
31
|
-
capability marked universal — so release tests find a missing facet
|
|
32
|
-
instead of a learner finding a dead affordance. The browser does not repeat that
|
|
33
|
-
strict gate at import time: a PIE authoring defect must block publication, not an
|
|
34
|
-
otherwise usable assessment.
|
|
35
|
-
|
|
36
|
-
That invariant boundary does not make host input stricter: lazy module loading
|
|
37
|
-
is opt-in, registration/tag/factory overrides take precedence, an empty
|
|
38
|
-
`toolIds` array selects the full packaged set, and unknown selected ids are
|
|
39
|
-
ignored while known ids register.
|
|
24
|
+
The individual registrations are exported too, so a host can compose its own
|
|
25
|
+
set ([Custom sets](#custom-sets)).
|
|
40
26
|
|
|
41
27
|
## Usage
|
|
42
28
|
|
|
43
29
|
```ts
|
|
44
|
-
import {
|
|
45
|
-
createPackagedToolRegistry,
|
|
46
|
-
DEFAULT_TOOL_MODULE_LOADERS,
|
|
47
|
-
} from "@pie-players/pie-default-tool-loaders";
|
|
30
|
+
import { createPackagedToolRegistry } from "@pie-players/pie-default-tool-loaders";
|
|
48
31
|
|
|
49
32
|
// The packaged capability set, lazily loaded.
|
|
50
|
-
const registry = createPackagedToolRegistry(
|
|
51
|
-
|
|
52
|
-
|
|
33
|
+
const registry = createPackagedToolRegistry();
|
|
34
|
+
|
|
35
|
+
// The same registrations for a host that defines the tool elements itself.
|
|
36
|
+
const preloaded = createPackagedToolRegistry({ toolModuleLoaders: {} });
|
|
53
37
|
```
|
|
54
38
|
|
|
55
39
|
One loader set serves every host shape. `DEFAULT_TOOL_MODULE_LOADERS` holds a
|
|
56
|
-
loader per packaged capability
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
40
|
+
loader per packaged capability and is what `createPackagedToolRegistry`
|
|
41
|
+
installs unless `toolModuleLoaders` replaces it. A registry loads a module only
|
|
42
|
+
when a tool first renders, so an item player, a section player and an
|
|
43
|
+
assessment player share the set and load only the tools they place.
|
|
44
|
+
|
|
45
|
+
`createPackagedToolRegistry` is lenient with host input. Registration, tag-map
|
|
46
|
+
and component-factory overrides take precedence over the packaged data. An
|
|
47
|
+
empty `toolIds` array selects the full packaged set, and unknown ids in it are
|
|
48
|
+
ignored while the known ones register.
|
|
49
|
+
|
|
50
|
+
## Calculator providers
|
|
51
|
+
|
|
52
|
+
The calculator is one element, `<pie-tool-calculator>`, for every provider. The
|
|
53
|
+
toolkit picks the provider from `tools.providers.calculator` when the element
|
|
54
|
+
mounts, and an open calculator remounts on the new provider when a tool-config
|
|
55
|
+
update replaces it.
|
|
56
|
+
|
|
57
|
+
Desmos is the provider when none is configured, and it needs an API key: pass
|
|
58
|
+
it through `provider.runtime.authFetcher`, a `provider.init.proxyEndpoint` that
|
|
59
|
+
returns it, or `provider.init.apiKey` in development
|
|
60
|
+
([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.
|
|
64
70
|
|
|
65
71
|
```ts
|
|
66
72
|
const tools = {
|
|
@@ -76,10 +82,7 @@ const tools = {
|
|
|
76
82
|
};
|
|
77
83
|
```
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
`provider.id: "calculator-cortex"`. Its `settings` accept angle mode, precision,
|
|
81
|
-
history, evaluation limit, allowed functions, clipboard policy, and graph
|
|
82
|
-
viewport options documented by `@pie-players/pie-calculator-cortex`.
|
|
85
|
+
## Custom sets
|
|
83
86
|
|
|
84
87
|
A host composing its own set starts from the toolkit's empty `ToolRegistry`,
|
|
85
88
|
registers into it, and installs the tag map its registrations create elements
|
|
@@ -105,19 +108,21 @@ A later `setToolModuleLoaders` call adds or replaces entries. A replacement
|
|
|
105
108
|
registered after a tool's module has loaded is ignored with a warning, since the
|
|
106
109
|
elements it defines are already in place.
|
|
107
110
|
|
|
108
|
-
## Content-dependent capabilities
|
|
111
|
+
## Content-dependent capabilities
|
|
109
112
|
|
|
110
|
-
A content-dependent capability
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
A content-dependent capability declares `requiresAuthoredContent`: it renders
|
|
114
|
+
only where the content carries what it needs, and
|
|
115
|
+
`createUniversalPersonalNeedsProfile()` never grants one. It ships in
|
|
116
|
+
`PACKAGED_TOOL_REGISTRATIONS` only when it has a useful presentation path
|
|
117
|
+
without a grant and declares `resolvesWithoutGrant`. Audio transcript is the
|
|
118
|
+
shipped example: an authored `visibility: "always"` transcript reaches every
|
|
119
|
+
player, while its accommodation path stays policy-gated and `transcript` stays
|
|
120
|
+
out of the universal profile.
|
|
116
121
|
|
|
117
|
-
A content-dependent capability with no
|
|
118
|
-
|
|
119
|
-
`@pie-players/pie-tool-sign-language` and register it,
|
|
120
|
-
|
|
122
|
+
A content-dependent capability with no such path is an explicit deployment
|
|
123
|
+
choice. Signing is the shipped example: install
|
|
124
|
+
`@pie-players/pie-tool-sign-language` and register it, the same two lines a
|
|
125
|
+
host writes for a capability of its own.
|
|
121
126
|
|
|
122
127
|
```ts
|
|
123
128
|
import { signLanguageRegistration } from "@pie-players/pie-tool-sign-language";
|
|
@@ -129,25 +134,59 @@ registry.register(signLanguageRegistration);
|
|
|
129
134
|
|
|
130
135
|
`createUniversalPersonalNeedsProfile()` builds a fresh `PersonalNeedsProfile`
|
|
131
136
|
granting the tool ids of the capabilities the packaged set treats as universal
|
|
132
|
-
features.
|
|
137
|
+
features. Tool policy reads the profile from the assessment entity, so set it
|
|
138
|
+
there: on the player's `assessment` property, or through
|
|
139
|
+
`coordinator.updateAssessment(...)` on a coordinator you pass. A
|
|
140
|
+
`section.personalNeedsProfile` is ignored with a console warning.
|
|
133
141
|
|
|
134
142
|
```ts
|
|
135
143
|
import { createUniversalPersonalNeedsProfile } from "@pie-players/pie-default-tool-loaders";
|
|
136
144
|
|
|
137
|
-
|
|
138
|
-
...
|
|
145
|
+
sectionPlayer.assessment = {
|
|
146
|
+
...authoredAssessment,
|
|
139
147
|
personalNeedsProfile: createUniversalPersonalNeedsProfile(),
|
|
140
148
|
};
|
|
141
149
|
```
|
|
142
150
|
|
|
143
151
|
The toolkit ships no populated default, and `createEmptyPersonalNeedsProfile()`
|
|
144
|
-
builds a profile granting nothing. Which capabilities a
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
engages on
|
|
153
|
-
alone decides which tools appear.
|
|
152
|
+
builds a profile granting nothing. Which capabilities a deployment grants by
|
|
153
|
+
default is a property of the program: TTS is a universal feature in one program
|
|
154
|
+
and a documented accommodation in another. The universal profile is data to
|
|
155
|
+
adopt, extend or replace alongside the district and test-administration
|
|
156
|
+
configuration, and it excludes every capability that declares a content
|
|
157
|
+
dependency.
|
|
158
|
+
|
|
159
|
+
An assessment carrying no profile is left alone. `pnpEnforcement`
|
|
160
|
+
auto-detection engages only on host policy material, so with no profile,
|
|
161
|
+
placement alone decides which tools appear.
|
|
162
|
+
|
|
163
|
+
## AfA profiles
|
|
164
|
+
|
|
165
|
+
`createPersonalNeedsProfileFromAfa(record)` builds a fresh profile from a
|
|
166
|
+
student's AfA PNP 3.0 record, held as JSON keyed by the spec's terms. Features
|
|
167
|
+
become support ids in the packaged set's names, the activation sets fill
|
|
168
|
+
`activateAtInit` and `prohibitedSupports`, and the settings the packaged tools
|
|
169
|
+
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.
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { createPersonalNeedsProfileFromAfa } from "@pie-players/pie-default-tool-loaders";
|
|
175
|
+
|
|
176
|
+
sectionPlayer.assessment = {
|
|
177
|
+
...authoredAssessment,
|
|
178
|
+
personalNeedsProfile: createPersonalNeedsProfileFromAfa(afaRecord),
|
|
179
|
+
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Packaged capability composition
|
|
183
|
+
|
|
184
|
+
The exports are projections of one internal composition. Each packaged
|
|
185
|
+
capability is authored once with its registration, element delivery, module
|
|
186
|
+
loader, preferred placement and explicit universal-support flag.
|
|
187
|
+
|
|
188
|
+
The package build rejects contradictory PIE-owned data, such as a region
|
|
189
|
+
capability with a toolbar tag or a content-dependent capability marked
|
|
190
|
+
universal, so release tests catch a missing facet before it ships. The browser
|
|
191
|
+
skips that strict check at import time: a PIE authoring defect blocks
|
|
192
|
+
publication and never an otherwise usable assessment.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AfA PNP 3.0 adapter: an AfA record, as JSON keyed by the spec's
|
|
3
|
+
* kebab-case terms, becomes the toolkit's personal-needs profile.
|
|
4
|
+
*
|
|
5
|
+
* Each feature the record names is a support id, the term camelCased, except
|
|
6
|
+
* where a packaged tool carries the capability under an id of its own
|
|
7
|
+
* (`TOOL_REGISTRY.md`, Support ids). `spoken` resolves by its `reading-type`:
|
|
8
|
+
* `computer-read-aloud` is `textToSpeech`, and `screen-reader`, AfA's default,
|
|
9
|
+
* is `screenReader` (ADR 0004). `extensions` and the resource-matching
|
|
10
|
+
* preferences name no support and are skipped.
|
|
11
|
+
*
|
|
12
|
+
* A feature at the record's root or in `activate-as-option-set` is granted, one
|
|
13
|
+
* in `activate-at-initialization-set` is granted and on at start, and one in
|
|
14
|
+
* `prohibit-set` is prohibited, which withdraws it from both. The settings of
|
|
15
|
+
* a feature a packaged tool reads become that tool's `toolParameters`.
|
|
16
|
+
*/
|
|
17
|
+
import type { PersonalNeedsProfile } from "@pie-players/pie-players-shared/types";
|
|
18
|
+
/** `spoken`'s settings, in AfA's units. */
|
|
19
|
+
export interface AfaSpoken {
|
|
20
|
+
"reading-type"?: "screen-reader" | "computer-read-aloud";
|
|
21
|
+
/** Words per minute; AfA's default is 180. */
|
|
22
|
+
"speech-rate"?: number;
|
|
23
|
+
/** 0–1; AfA's default is 0.5. */
|
|
24
|
+
pitch?: number;
|
|
25
|
+
/** 0–1; AfA's default is 0.5. */
|
|
26
|
+
volume?: number;
|
|
27
|
+
[setting: string]: unknown;
|
|
28
|
+
}
|
|
29
|
+
/** A set's features: empty elements keyed by feature, or the feature names. */
|
|
30
|
+
export type AfaFeatureSet = Readonly<Record<string, unknown>> | readonly string[];
|
|
31
|
+
/** An AfA PNP 3.0 record in JSON. A feature is present unless absent, `null` or `false`. */
|
|
32
|
+
export interface AfaPnpRecord {
|
|
33
|
+
spoken?: AfaSpoken;
|
|
34
|
+
"calculator-on-screen"?: {
|
|
35
|
+
"calculator-type"?: string;
|
|
36
|
+
[setting: string]: unknown;
|
|
37
|
+
};
|
|
38
|
+
"sign-language"?: {
|
|
39
|
+
language?: string;
|
|
40
|
+
[setting: string]: unknown;
|
|
41
|
+
};
|
|
42
|
+
"activate-at-initialization-set"?: AfaFeatureSet;
|
|
43
|
+
"activate-as-option-set"?: AfaFeatureSet;
|
|
44
|
+
"prohibit-set"?: AfaFeatureSet;
|
|
45
|
+
[feature: string]: unknown;
|
|
46
|
+
}
|
|
47
|
+
/** A fresh profile carrying `record`'s grants, prohibitions and settings, ids sorted. */
|
|
48
|
+
export declare function createPersonalNeedsProfileFromAfa(record: AfaPnpRecord): PersonalNeedsProfile;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The AfA PNP 3.0 adapter: an AfA record, as JSON keyed by the spec's
|
|
3
|
+
* kebab-case terms, becomes the toolkit's personal-needs profile.
|
|
4
|
+
*
|
|
5
|
+
* Each feature the record names is a support id, the term camelCased, except
|
|
6
|
+
* where a packaged tool carries the capability under an id of its own
|
|
7
|
+
* (`TOOL_REGISTRY.md`, Support ids). `spoken` resolves by its `reading-type`:
|
|
8
|
+
* `computer-read-aloud` is `textToSpeech`, and `screen-reader`, AfA's default,
|
|
9
|
+
* is `screenReader` (ADR 0004). `extensions` and the resource-matching
|
|
10
|
+
* preferences name no support and are skipped.
|
|
11
|
+
*
|
|
12
|
+
* A feature at the record's root or in `activate-as-option-set` is granted, one
|
|
13
|
+
* in `activate-at-initialization-set` is granted and on at start, and one in
|
|
14
|
+
* `prohibit-set` is prohibited, which withdraws it from both. The settings of
|
|
15
|
+
* a feature a packaged tool reads become that tool's `toolParameters`.
|
|
16
|
+
*/
|
|
17
|
+
const RENAMED_FEATURES = {
|
|
18
|
+
"calculator-on-screen": "calculator",
|
|
19
|
+
"answer-masking": "answerEliminator",
|
|
20
|
+
"dictionary-on-screen": "dictionary",
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Keys that name no support: the sets, `extensions`, and the preferences that
|
|
24
|
+
* select among adapted resources by metadata PIE content does not carry. The
|
|
25
|
+
* interface language is the host's `locale`.
|
|
26
|
+
*/
|
|
27
|
+
const NON_SUPPORT_KEYS = new Set([
|
|
28
|
+
"activate-at-initialization-set",
|
|
29
|
+
"activate-as-option-set",
|
|
30
|
+
"prohibit-set",
|
|
31
|
+
"extensions",
|
|
32
|
+
"access-mode-required",
|
|
33
|
+
"adaptation-type-required",
|
|
34
|
+
"at-interoperable",
|
|
35
|
+
"educational-complexity-of-adaptation",
|
|
36
|
+
"hazard-avoidance",
|
|
37
|
+
"input-requirements",
|
|
38
|
+
"language-of-adaptation",
|
|
39
|
+
"language-of-interface",
|
|
40
|
+
"adaptation-detail-required",
|
|
41
|
+
"adaptation-media-required",
|
|
42
|
+
"educational-level-of-adaptation",
|
|
43
|
+
]);
|
|
44
|
+
/** AfA's `standard` calculator has no PIE type, so it keeps the tool's default. */
|
|
45
|
+
const CALCULATOR_TYPES = new Set(["basic", "scientific", "graphing"]);
|
|
46
|
+
/** AfA's default `speech-rate`, which PIE's normal speed 1 stands for. */
|
|
47
|
+
const AFA_NORMAL_SPEECH_RATE = 180;
|
|
48
|
+
const isPresent = (value) => value !== undefined && value !== null && value !== false;
|
|
49
|
+
const finiteNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
50
|
+
const camelCase = (term) => term.replace(/-([a-z0-9])/g, (_, next) => next.toUpperCase());
|
|
51
|
+
function featureNames(set) {
|
|
52
|
+
if (!set)
|
|
53
|
+
return [];
|
|
54
|
+
const names = Array.isArray(set)
|
|
55
|
+
? set.filter((name) => typeof name === "string")
|
|
56
|
+
: Object.entries(set)
|
|
57
|
+
.filter(([, value]) => isPresent(value))
|
|
58
|
+
.map(([name]) => name);
|
|
59
|
+
return names.filter((name) => !NON_SUPPORT_KEYS.has(name));
|
|
60
|
+
}
|
|
61
|
+
function spokenParameters(spoken) {
|
|
62
|
+
const speechRate = finiteNumber(spoken["speech-rate"]);
|
|
63
|
+
const pitch = finiteNumber(spoken.pitch);
|
|
64
|
+
const volume = finiteNumber(spoken.volume);
|
|
65
|
+
return {
|
|
66
|
+
...(speechRate !== undefined && speechRate > 0
|
|
67
|
+
? { rate: speechRate / AFA_NORMAL_SPEECH_RATE }
|
|
68
|
+
: {}),
|
|
69
|
+
...(pitch !== undefined ? { pitch: pitch * 2 } : {}),
|
|
70
|
+
...(volume !== undefined ? { volume } : {}),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/** A fresh profile carrying `record`'s grants, prohibitions and settings, ids sorted. */
|
|
74
|
+
export function createPersonalNeedsProfileFromAfa(record) {
|
|
75
|
+
const spoken = typeof record.spoken === "object" && record.spoken !== null
|
|
76
|
+
? record.spoken
|
|
77
|
+
: {};
|
|
78
|
+
const readAloud = spoken["reading-type"] === "computer-read-aloud";
|
|
79
|
+
const supportId = (feature) => feature === "spoken"
|
|
80
|
+
? readAloud
|
|
81
|
+
? "textToSpeech"
|
|
82
|
+
: "screenReader"
|
|
83
|
+
: (RENAMED_FEATURES[feature] ?? camelCase(feature));
|
|
84
|
+
// PIE cannot withhold a screen reader, so a prohibited `spoken` withholds
|
|
85
|
+
// the spoken presentation PIE makes, read-aloud.
|
|
86
|
+
const prohibited = new Set(featureNames(record["prohibit-set"]).map((feature) => feature === "spoken" ? "textToSpeech" : supportId(feature)));
|
|
87
|
+
const admitted = (ids) => [...new Set(ids)].filter((id) => !prohibited.has(id)).sort();
|
|
88
|
+
const activateAtInit = admitted(featureNames(record["activate-at-initialization-set"]).map(supportId));
|
|
89
|
+
const supports = admitted([
|
|
90
|
+
...featureNames(record).map(supportId),
|
|
91
|
+
...featureNames(record["activate-as-option-set"]).map(supportId),
|
|
92
|
+
...activateAtInit,
|
|
93
|
+
]);
|
|
94
|
+
const toolParameters = {};
|
|
95
|
+
if (readAloud) {
|
|
96
|
+
const speech = spokenParameters(spoken);
|
|
97
|
+
if (Object.keys(speech).length > 0) {
|
|
98
|
+
toolParameters.textToSpeech = speech;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const calculatorType = record["calculator-on-screen"]?.["calculator-type"];
|
|
102
|
+
if (calculatorType && CALCULATOR_TYPES.has(calculatorType)) {
|
|
103
|
+
toolParameters.calculator = {
|
|
104
|
+
type: calculatorType,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const signLang = record["sign-language"]?.language;
|
|
108
|
+
if (typeof signLang === "string" && signLang.length > 0) {
|
|
109
|
+
toolParameters.signLanguage = { signLang };
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
supports,
|
|
113
|
+
prohibitedSupports: [...prohibited].sort(),
|
|
114
|
+
...(activateAtInit.length > 0 ? { activateAtInit } : {}),
|
|
115
|
+
...(Object.keys(toolParameters).length > 0 ? { toolParameters } : {}),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
/** Fully bundled open-source calculator adapter for the tool-provider registry. */
|
|
2
2
|
import type { CalculatorToolProviderInitConfig, LazyCalculatorProviderDefinition } from "./LazyCalculatorToolProvider.js";
|
|
3
3
|
import { LazyCalculatorToolProvider } from "./LazyCalculatorToolProvider.js";
|
|
4
|
-
import type { ToolProviderCapabilities } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
5
4
|
export type CortexToolProviderConfig = CalculatorToolProviderInitConfig;
|
|
6
5
|
export declare class CortexToolProvider extends LazyCalculatorToolProvider<CortexToolProviderConfig> {
|
|
7
6
|
readonly providerName = "PIE Open-Source Calculator";
|
|
8
7
|
readonly version = "1";
|
|
9
8
|
readonly requiresAuth = false;
|
|
10
9
|
protected getDefinition(): LazyCalculatorProviderDefinition<CortexToolProviderConfig>;
|
|
11
|
-
getCapabilities(): ToolProviderCapabilities;
|
|
12
10
|
}
|
|
@@ -15,17 +15,4 @@ export class CortexToolProvider extends LazyCalculatorToolProvider {
|
|
|
15
15
|
initializationErrorMessage: "Failed to initialize the PIE open-source calculator provider. Confirm that this browser supports module workers and that the bundled worker assets are available.",
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
getCapabilities() {
|
|
19
|
-
return {
|
|
20
|
-
supportsOffline: true,
|
|
21
|
-
requiresAuth: false,
|
|
22
|
-
maxInstances: null,
|
|
23
|
-
features: {
|
|
24
|
-
basic: true,
|
|
25
|
-
scientific: true,
|
|
26
|
-
graphing: true,
|
|
27
|
-
fourFunction: true,
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
18
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/** Desmos calculator adapter for the generic tool-provider registry. */
|
|
2
2
|
import type { CalculatorProviderInit } from "@pie-players/pie-calculator";
|
|
3
3
|
import { LazyCalculatorToolProvider, type LazyCalculatorProviderDefinition } from "./LazyCalculatorToolProvider.js";
|
|
4
|
-
import type { ToolProviderCapabilities } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
5
4
|
/** Provider initialization is the provider-neutral calculator contract. */
|
|
6
5
|
export type DesmosToolProviderConfig = CalculatorProviderInit;
|
|
7
6
|
export declare class DesmosToolProvider extends LazyCalculatorToolProvider<DesmosToolProviderConfig> {
|
|
@@ -9,5 +8,4 @@ export declare class DesmosToolProvider extends LazyCalculatorToolProvider<Desmo
|
|
|
9
8
|
readonly version = "1.12";
|
|
10
9
|
readonly requiresAuth = true;
|
|
11
10
|
protected getDefinition(): LazyCalculatorProviderDefinition<DesmosToolProviderConfig>;
|
|
12
|
-
getCapabilities(): ToolProviderCapabilities;
|
|
13
11
|
}
|
|
@@ -15,17 +15,4 @@ export class DesmosToolProvider extends LazyCalculatorToolProvider {
|
|
|
15
15
|
initializationErrorMessage: "Failed to initialize Desmos calculator provider. Check the application key, preloaded API, runtime endpoint, or network access.",
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
getCapabilities() {
|
|
19
|
-
return {
|
|
20
|
-
supportsOffline: false,
|
|
21
|
-
requiresAuth: true,
|
|
22
|
-
maxInstances: null,
|
|
23
|
-
features: {
|
|
24
|
-
basic: true,
|
|
25
|
-
scientific: true,
|
|
26
|
-
graphing: true,
|
|
27
|
-
fourFunction: true,
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
18
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/** GeoGebra calculator adapter for the generic tool-provider registry. */
|
|
2
2
|
import type { CalculatorToolProviderInitConfig, LazyCalculatorProviderDefinition } from "./LazyCalculatorToolProvider.js";
|
|
3
3
|
import { LazyCalculatorToolProvider } from "./LazyCalculatorToolProvider.js";
|
|
4
|
-
import type { ToolProviderCapabilities } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
5
4
|
export interface GeoGebraToolProviderConfig extends CalculatorToolProviderInitConfig {
|
|
6
5
|
scriptUrl?: string;
|
|
7
6
|
appletTimeoutMs?: number;
|
|
@@ -11,5 +10,4 @@ export declare class GeoGebraToolProvider extends LazyCalculatorToolProvider<Geo
|
|
|
11
10
|
readonly version = "6";
|
|
12
11
|
readonly requiresAuth = false;
|
|
13
12
|
protected getDefinition(): LazyCalculatorProviderDefinition<GeoGebraToolProviderConfig>;
|
|
14
|
-
getCapabilities(): ToolProviderCapabilities;
|
|
15
13
|
}
|
|
@@ -15,17 +15,4 @@ export class GeoGebraToolProvider extends LazyCalculatorToolProvider {
|
|
|
15
15
|
initializationErrorMessage: "Failed to initialize GeoGebra calculator provider. Confirm that the deployment may load GeoGebra and that its script URL is reachable.",
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
getCapabilities() {
|
|
19
|
-
return {
|
|
20
|
-
supportsOffline: false,
|
|
21
|
-
requiresAuth: false,
|
|
22
|
-
maxInstances: null,
|
|
23
|
-
features: {
|
|
24
|
-
basic: true,
|
|
25
|
-
scientific: true,
|
|
26
|
-
graphing: true,
|
|
27
|
-
fourFunction: false,
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
18
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CalculatorProvider, CalculatorProviderInit } from "@pie-players/pie-calculator";
|
|
2
|
-
import type { ToolProviderApi
|
|
2
|
+
import type { ToolProviderApi } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
3
3
|
export type CalculatorToolProviderInitConfig = Pick<CalculatorProviderInit, "onTelemetry">;
|
|
4
4
|
type InitializableCalculatorProvider<TConfig> = CalculatorProvider & {
|
|
5
5
|
initialize(config: TConfig): Promise<void>;
|
|
@@ -12,7 +12,7 @@ export interface LazyCalculatorProviderDefinition<TConfig> {
|
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* Shared lazy-module and lifecycle implementation for calculator tool adapters.
|
|
15
|
-
* Concrete adapters own only their metadata
|
|
15
|
+
* Concrete adapters own only their metadata and provider import.
|
|
16
16
|
*/
|
|
17
17
|
export declare abstract class LazyCalculatorToolProvider<TConfig extends CalculatorToolProviderInitConfig> implements ToolProviderApi<CalculatorProvider> {
|
|
18
18
|
abstract readonly providerName: string;
|
|
@@ -20,7 +20,6 @@ export declare abstract class LazyCalculatorToolProvider<TConfig extends Calcula
|
|
|
20
20
|
abstract readonly version: string;
|
|
21
21
|
abstract readonly requiresAuth: boolean;
|
|
22
22
|
protected abstract getDefinition(): LazyCalculatorProviderDefinition<TConfig>;
|
|
23
|
-
abstract getCapabilities(): ToolProviderCapabilities;
|
|
24
23
|
private calculatorProvider;
|
|
25
24
|
private initializationPromise;
|
|
26
25
|
private lifecycleGeneration;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared lazy-module and lifecycle implementation for calculator tool adapters.
|
|
3
|
-
* Concrete adapters own only their metadata
|
|
3
|
+
* Concrete adapters own only their metadata and provider import.
|
|
4
4
|
*/
|
|
5
5
|
export class LazyCalculatorToolProvider {
|
|
6
6
|
category = "calculator";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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
4
|
export { CONTENT_ALTERNATE_REGISTRATIONS } from "./content-alternates.js";
|
|
4
5
|
export { annotationToolbarRegistration, lineReaderToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
|
|
5
|
-
export { AUDIO_TRANSCRIPT_FEATURE_ID, AUDIO_TRANSCRIPT_REGION_CLASS, AUDIO_TRANSCRIPT_REGION_LABEL, audioTranscriptRegistration, resolveAudioTranscript, type ResolvedAudioTranscript, } from "./registrations/audio-transcript.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";
|
|
6
7
|
export { calculatorToolRegistration } from "./registrations/calculator.js";
|
|
7
8
|
export { createDictionaryToolRegistration, createPictureDictionaryToolRegistration, dictionaryToolRegistration, type DictionaryVariantOptions, pictureDictionaryToolRegistration, spanishDictionaryToolRegistration, spanishPictureDictionaryToolRegistration, } from "./registrations/dictionary-tools.js";
|
|
8
9
|
export { answerEliminatorToolRegistration } from "./registrations/interaction-tools.js";
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
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";
|
|
2
|
+
// A profile from an AfA PNP 3.0 record, in the packaged set's support ids.
|
|
3
|
+
export { createPersonalNeedsProfileFromAfa, } from "./afa-profile.js";
|
|
2
4
|
// The authored-alternate subset, for a renderer that shows alternates and no
|
|
3
5
|
// toolbar — importable without the packaged composition's tool loaders.
|
|
4
6
|
export { CONTENT_ALTERNATE_REGISTRATIONS } from "./content-alternates.js";
|
|
5
7
|
// Individual registrations, for a host composing a subset rather than the whole
|
|
6
8
|
// packaged set.
|
|
7
9
|
export { annotationToolbarRegistration, lineReaderToolRegistration, themeToolRegistration, } from "./registrations/accessibility-tools.js";
|
|
8
|
-
export { AUDIO_TRANSCRIPT_FEATURE_ID, AUDIO_TRANSCRIPT_REGION_CLASS, AUDIO_TRANSCRIPT_REGION_LABEL, audioTranscriptRegistration, resolveAudioTranscript, } from "./registrations/audio-transcript.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";
|
|
9
11
|
export { calculatorToolRegistration } from "./registrations/calculator.js";
|
|
10
12
|
export { createDictionaryToolRegistration, createPictureDictionaryToolRegistration, dictionaryToolRegistration, pictureDictionaryToolRegistration, spanishDictionaryToolRegistration, spanishPictureDictionaryToolRegistration, } from "./registrations/dictionary-tools.js";
|
|
11
13
|
export { answerEliminatorToolRegistration } from "./registrations/interaction-tools.js";
|
|
@@ -16,7 +16,11 @@ export interface PackagedToolRegistryOptions {
|
|
|
16
16
|
toolTagMap?: Partial<ToolTagMap>;
|
|
17
17
|
/** Override the component factory per tool. */
|
|
18
18
|
toolComponentFactories?: Partial<ToolComponentFactoryMap>;
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Module loaders keyed by toolId, each loading its tool's element on first
|
|
21
|
+
* render. Defaults to `DEFAULT_TOOL_MODULE_LOADERS`; a map replaces it, so a
|
|
22
|
+
* host that defines its tool elements itself passes `{}`.
|
|
23
|
+
*/
|
|
20
24
|
toolModuleLoaders?: Partial<Record<string, ToolModuleLoader>>;
|
|
21
25
|
/** Restrict registration to specific packaged tool ids. */
|
|
22
26
|
toolIds?: string[];
|
|
@@ -248,13 +248,9 @@ class PackagedCapabilityComposition {
|
|
|
248
248
|
for (const registration of this.selectRegistrations(options.toolIds)) {
|
|
249
249
|
registry.register(options.overrides?.[registration.toolId] ?? registration);
|
|
250
250
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (options.toolModuleLoaders &&
|
|
255
|
-
Object.keys(options.toolModuleLoaders).length > 0) {
|
|
256
|
-
registry.setToolModuleLoaders({ ...options.toolModuleLoaders });
|
|
257
|
-
}
|
|
251
|
+
registry.setToolModuleLoaders({
|
|
252
|
+
...(options.toolModuleLoaders ?? this.moduleLoaders),
|
|
253
|
+
});
|
|
258
254
|
registry.setComponentOverrides({
|
|
259
255
|
toolTagMap: {
|
|
260
256
|
...this.toolTagMap,
|
|
@@ -23,7 +23,6 @@ export const lineReaderToolRegistration = {
|
|
|
23
23
|
nameKey: "tools.lineReader.name",
|
|
24
24
|
descriptionKey: "tools.lineReader.description",
|
|
25
25
|
icon: "bars-3",
|
|
26
|
-
// Line reader appears where there's text to read
|
|
27
26
|
supportedLevels: ["section", "passage", "rubric", "item"],
|
|
28
27
|
/**
|
|
29
28
|
* Pass 2: Line reader is relevant when readable text is present
|
|
@@ -56,7 +55,7 @@ export const themeToolRegistration = {
|
|
|
56
55
|
* Pass 2: Color scheme is always relevant when allowed
|
|
57
56
|
*/
|
|
58
57
|
isVisibleInContext(context) {
|
|
59
|
-
return true;
|
|
58
|
+
return true;
|
|
60
59
|
},
|
|
61
60
|
renderToolbar(context, toolbarContext) {
|
|
62
61
|
return renderOverlayToolbar(this, context, toolbarContext, {
|
|
@@ -86,15 +85,12 @@ export const annotationToolbarRegistration = {
|
|
|
86
85
|
activation: "selection-gateway",
|
|
87
86
|
singletonScope: "section",
|
|
88
87
|
/**
|
|
89
|
-
* Section-scoped singleton surface. The
|
|
90
|
-
* `
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* moves that wiring into this registration, and the renderer discovers it
|
|
94
|
-
* through `ToolRegistry.getToolsBySurface("section-overlay")`.
|
|
88
|
+
* Section-scoped singleton surface. The renderer discovers it through
|
|
89
|
+
* `ToolRegistry.getToolsBySurface("section-overlay")`, so the section player
|
|
90
|
+
* names no tool id and a host can contribute another section-scoped
|
|
91
|
+
* capability.
|
|
95
92
|
*/
|
|
96
93
|
surfaces: ["section-overlay"],
|
|
97
|
-
// Annotation appears where there's text content
|
|
98
94
|
supportedLevels: ["passage", "rubric", "item", "element"],
|
|
99
95
|
/**
|
|
100
96
|
* Pass 2: Annotation is relevant when readable text is present
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* Two kinds of content wear the same shape, and the card says which:
|
|
6
6
|
*
|
|
7
7
|
* - `visibility: "always"` is authored presentation. The item family was
|
|
8
|
-
* designed to be delivered with its transcript on screen —
|
|
9
|
-
*
|
|
10
|
-
* revokes it.
|
|
8
|
+
* designed to be delivered with its transcript on screen — sentence items in
|
|
9
|
+
* an early-literacy assessment, for instance — so no profile grants it and
|
|
10
|
+
* none revokes it.
|
|
11
11
|
* - anything else, `"onGrant"` included, is the accommodation. Policy decides
|
|
12
|
-
* against the `transcript` support id, and silence means no. For
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* against the `transcript` support id, and silence means no. For item
|
|
13
|
+
* families where a visible transcript turns the item into a reading task,
|
|
14
|
+
* that gate is the whole point.
|
|
15
15
|
*
|
|
16
16
|
* The toolkit renders the text, not the element. Before this, the transcript rode
|
|
17
17
|
* on `model.audioTranscript` and `mc-populated-blank` revealed its own copy when
|
|
@@ -47,6 +47,7 @@ export declare const AUDIO_TRANSCRIPT_REGION_CLASS = "pie-tool-audio-transcript"
|
|
|
47
47
|
export interface ResolvedAudioTranscript {
|
|
48
48
|
catalogId: string;
|
|
49
49
|
text: string;
|
|
50
|
+
/** BCP 47, as the region's `lang` takes it, whatever syntax the card used. */
|
|
50
51
|
language?: string;
|
|
51
52
|
/** True when the card declares itself authored presentation. */
|
|
52
53
|
always: boolean;
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
* Two kinds of content wear the same shape, and the card says which:
|
|
6
6
|
*
|
|
7
7
|
* - `visibility: "always"` is authored presentation. The item family was
|
|
8
|
-
* designed to be delivered with its transcript on screen —
|
|
9
|
-
*
|
|
10
|
-
* revokes it.
|
|
8
|
+
* designed to be delivered with its transcript on screen — sentence items in
|
|
9
|
+
* an early-literacy assessment, for instance — so no profile grants it and
|
|
10
|
+
* none revokes it.
|
|
11
11
|
* - anything else, `"onGrant"` included, is the accommodation. Policy decides
|
|
12
|
-
* against the `transcript` support id, and silence means no. For
|
|
13
|
-
*
|
|
14
|
-
*
|
|
12
|
+
* against the `transcript` support id, and silence means no. For item
|
|
13
|
+
* families where a visible transcript turns the item into a reading task,
|
|
14
|
+
* that gate is the whole point.
|
|
15
15
|
*
|
|
16
16
|
* The toolkit renders the text, not the element. Before this, the transcript rode
|
|
17
17
|
* on `model.audioTranscript` and `mc-populated-blank` revealed its own copy when
|
|
@@ -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 { toLanguageTag } from "@pie-players/pie-players-shared/i18n/language-tags";
|
|
38
39
|
/** QTI 3.0 / AfA support id gating an audio transcript. */
|
|
39
40
|
export const AUDIO_TRANSCRIPT_FEATURE_ID = "transcript";
|
|
40
41
|
/** Catalog type carrying the transcript text. */
|
|
@@ -55,6 +56,10 @@ const isTranscriptCard = (card) => !!card && card.catalog === AUDIO_TRANSCRIPT_C
|
|
|
55
56
|
const isAlwaysVisible = (card) => typeof card.visibility === "string" &&
|
|
56
57
|
card.visibility.trim().toLowerCase() === ALWAYS_VISIBLE;
|
|
57
58
|
const cardText = (card) => typeof card.content === "string" ? card.content.trim() : "";
|
|
59
|
+
// The Learnosity transform emits POSIX `es_ES`, which `lang` does not accept.
|
|
60
|
+
const cardLanguage = (card) => typeof card.language === "string"
|
|
61
|
+
? toLanguageTag(card.language) || undefined
|
|
62
|
+
: undefined;
|
|
58
63
|
/**
|
|
59
64
|
* The transcript an entity carries, or `null`.
|
|
60
65
|
*
|
|
@@ -83,7 +88,7 @@ export function resolveAudioTranscript(context) {
|
|
|
83
88
|
const resolved = {
|
|
84
89
|
catalogId,
|
|
85
90
|
text,
|
|
86
|
-
language:
|
|
91
|
+
language: cardLanguage(card),
|
|
87
92
|
always: isAlwaysVisible(card),
|
|
88
93
|
};
|
|
89
94
|
// An `always` card answers whether or not policy granted anything, so it
|
|
@@ -54,7 +54,10 @@ function normalizeCalculatorType(value) {
|
|
|
54
54
|
}
|
|
55
55
|
function getCalculatorRenderParams(toolbarContext) {
|
|
56
56
|
const params = toolbarContext.getToolRenderParams?.("calculator") ?? {};
|
|
57
|
-
|
|
57
|
+
// A host resolver or a request names the type; the policy parameters'
|
|
58
|
+
// `type` is the default beneath both.
|
|
59
|
+
const calculatorType = normalizeCalculatorType(params.calculatorType) ??
|
|
60
|
+
normalizeCalculatorType(toolbarContext.getToolParameters?.("calculator")?.type);
|
|
58
61
|
const availableTypesRaw = params.availableTypes;
|
|
59
62
|
const availableTypes = Array.isArray(availableTypesRaw)
|
|
60
63
|
? availableTypesRaw
|
|
@@ -135,7 +138,6 @@ export const calculatorToolRegistration = {
|
|
|
135
138
|
* (MathML, LaTeX, arithmetic markers).
|
|
136
139
|
*/
|
|
137
140
|
isVisibleInContext(context) {
|
|
138
|
-
// Show only when math is present in item content.
|
|
139
141
|
return hasMathContent(context);
|
|
140
142
|
},
|
|
141
143
|
renderToolbar(context, toolbarContext) {
|
|
@@ -156,8 +158,7 @@ export const calculatorToolRegistration = {
|
|
|
156
158
|
icon: typeof this.icon === "function" ? this.icon(context) : this.icon,
|
|
157
159
|
// Routes this button through <nds-icon-button> where the host enables NDS
|
|
158
160
|
// icons. Declared here because which capabilities render in the host's
|
|
159
|
-
// design system is a composition decision
|
|
160
|
-
// the `calculator` toolId, which put a capability name in the generic core.
|
|
161
|
+
// design system is a composition decision.
|
|
161
162
|
faIconName: "calculator",
|
|
162
163
|
disabled: false,
|
|
163
164
|
// The name stays put across open and closed. The toolbar mirrors
|
|
@@ -248,10 +249,9 @@ export const calculatorToolRegistration = {
|
|
|
248
249
|
const active = toolbarContext.isToolVisible(this.toolId);
|
|
249
250
|
button.active = active;
|
|
250
251
|
button.label = displayName;
|
|
251
|
-
// Static across the toggle
|
|
252
|
-
//
|
|
253
|
-
//
|
|
254
|
-
// `aria-pressed` already carries.
|
|
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
255
|
button.ariaLabel = displayName;
|
|
256
256
|
button.tooltip = displayName;
|
|
257
257
|
if (lastVisibleState !== active) {
|
|
@@ -9,6 +9,6 @@ import type { ToolRegistration } from "@pie-players/pie-assessment-toolkit/tools
|
|
|
9
9
|
* Answer Eliminator tool registration
|
|
10
10
|
*
|
|
11
11
|
* Allows students to strike through incorrect answer choices.
|
|
12
|
-
* Only appears on
|
|
12
|
+
* Only appears on items with an element that supports answer masking.
|
|
13
13
|
*/
|
|
14
14
|
export declare const answerEliminatorToolRegistration: ToolRegistration;
|
|
@@ -4,12 +4,40 @@
|
|
|
4
4
|
* Registers tools for interacting with question content:
|
|
5
5
|
* - Answer Eliminator (strike through answer choices)
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { elementSupports } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
8
8
|
import { createToolElement, } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
9
9
|
import { createScopedVisibilityBinding, syncButtonAndOverlayVisibility, } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
10
10
|
import { resolveOverlayElement } from "./overlay-element-cache.js";
|
|
11
|
-
/** `
|
|
11
|
+
/** The policy parameters' `strategy`, defaulting to strikethrough. */
|
|
12
12
|
const normalizeEliminationStrategy = (value) => value === "mask" ? "mask" : "strikethrough";
|
|
13
|
+
/** The AfA PNP 3.0 support term an element declares to take the eliminator. */
|
|
14
|
+
const ANSWER_MASKING = "answer-masking";
|
|
15
|
+
/**
|
|
16
|
+
* pie-item contract compatibility: legacy pie-elements, and pie-elements-ng
|
|
17
|
+
* releases from before elements declared their supports, render the markup the
|
|
18
|
+
* eliminator's built-in adapters read for these two. An element that declares
|
|
19
|
+
* its supports is decided by its declaration, these two included.
|
|
20
|
+
*/
|
|
21
|
+
const UNDECLARED_ANSWER_MASKING_ELEMENTS = new Set(["multiple-choice", "ebsr"]);
|
|
22
|
+
// Content names one element several ways: `multiple-choice`,
|
|
23
|
+
// `pie-multiple-choice`, `pie-element-multiple-choice`, a runtime `--version-*`
|
|
24
|
+
// tag, or any tag the item's `elements` map binds to `@pie-element/multiple-choice`.
|
|
25
|
+
function isUndeclaredAnswerMaskingElement(tag, packageSpec) {
|
|
26
|
+
const name = tag
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.replace(/--version-[0-9a-z-]+$/, "")
|
|
29
|
+
.replace(/^pie-(element-)?/, "");
|
|
30
|
+
if (UNDECLARED_ANSWER_MASKING_ELEMENTS.has(name))
|
|
31
|
+
return true;
|
|
32
|
+
const packageName = packageSpec?.match(/^@pie-element\/([^@]+)/)?.[1];
|
|
33
|
+
return !!packageName && UNDECLARED_ANSWER_MASKING_ELEMENTS.has(packageName);
|
|
34
|
+
}
|
|
35
|
+
/** Whether an element of the context supports answer masking. */
|
|
36
|
+
function supportsAnswerMasking(context) {
|
|
37
|
+
return elementSupports(context).some(({ supports, tag, packageSpec }) => supports
|
|
38
|
+
? supports.includes(ANSWER_MASKING)
|
|
39
|
+
: isUndeclaredAnswerMaskingElement(tag, packageSpec));
|
|
40
|
+
}
|
|
13
41
|
/** The state-store key of each PIE element in the item, by model id. */
|
|
14
42
|
function resolveElementStateKeys(context, toolbarContext) {
|
|
15
43
|
const keys = {};
|
|
@@ -29,7 +57,7 @@ function resolveElementStateKeys(context, toolbarContext) {
|
|
|
29
57
|
* Answer Eliminator tool registration
|
|
30
58
|
*
|
|
31
59
|
* Allows students to strike through incorrect answer choices.
|
|
32
|
-
* Only appears on
|
|
60
|
+
* Only appears on items with an element that supports answer masking.
|
|
33
61
|
*/
|
|
34
62
|
export const answerEliminatorToolRegistration = {
|
|
35
63
|
toolId: "answerEliminator",
|
|
@@ -38,32 +66,31 @@ export const answerEliminatorToolRegistration = {
|
|
|
38
66
|
nameKey: "tools.answerEliminator.name",
|
|
39
67
|
descriptionKey: "tools.answerEliminator.description",
|
|
40
68
|
icon: "strikethrough",
|
|
41
|
-
// Answer eliminator appears at item level only
|
|
42
69
|
supportedLevels: ["item"],
|
|
43
70
|
/**
|
|
44
|
-
* Pass 2:
|
|
71
|
+
* Pass 2: relevant where an element supports answer masking. An element
|
|
72
|
+
* declares it on its class, and the answer eliminator's README says what the
|
|
73
|
+
* element then supplies.
|
|
45
74
|
*/
|
|
46
75
|
isVisibleInContext(context) {
|
|
47
|
-
return
|
|
76
|
+
return supportsAnswerMasking(context);
|
|
48
77
|
},
|
|
49
78
|
/**
|
|
50
79
|
* Pass 3: the same question, asked as a capability. Elimination controls are
|
|
51
|
-
* rendered per choice, so an item
|
|
52
|
-
* nothing to act on
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* on those items for any learner whose profile grants answer masking.
|
|
80
|
+
* rendered per choice, so an item none of whose elements supports answer
|
|
81
|
+
* masking gives this tool nothing to act on. Answering the relevance gate
|
|
82
|
+
* alone left the button on such items for any learner whose profile grants
|
|
83
|
+
* answer masking.
|
|
56
84
|
*/
|
|
57
85
|
isApplicableToContent(context) {
|
|
58
|
-
return
|
|
86
|
+
return supportsAnswerMasking(context);
|
|
59
87
|
},
|
|
60
88
|
renderToolbar(context, toolbarContext) {
|
|
61
89
|
const visibility = createScopedVisibilityBinding(this.toolId, toolbarContext);
|
|
62
90
|
const componentOverrides = toolbarContext.componentOverrides ?? {};
|
|
63
91
|
const overlay = resolveOverlayElement(toolbarContext, visibility.fullToolId, () => createToolElement(this.toolId, context, toolbarContext, componentOverrides));
|
|
64
|
-
overlay.setAttribute("tool-id", visibility.fullToolId);
|
|
65
92
|
overlay.setAttribute("button-alignment", "inline");
|
|
66
|
-
overlay.setAttribute("strategy", normalizeEliminationStrategy(toolbarContext.
|
|
93
|
+
overlay.setAttribute("strategy", normalizeEliminationStrategy(toolbarContext.getToolParameters?.("answerEliminator")?.strategy));
|
|
67
94
|
const button = {
|
|
68
95
|
toolId: this.toolId,
|
|
69
96
|
label: this.name,
|
|
@@ -84,7 +111,6 @@ export const answerEliminatorToolRegistration = {
|
|
|
84
111
|
overlay,
|
|
85
112
|
isActive: visibility.isActive,
|
|
86
113
|
});
|
|
87
|
-
overlay.scopeElement = toolbarContext.getScopeElement?.() || null;
|
|
88
114
|
overlay.elementStateKeys = resolveElementStateKeys(context, toolbarContext);
|
|
89
115
|
},
|
|
90
116
|
subscribeActive: visibility.subscribeActive,
|
|
@@ -2,18 +2,15 @@
|
|
|
2
2
|
* The `renderToolbar` shape shared by every packaged capability that puts one
|
|
3
3
|
* overlay behind one toolbar button.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* sync — was identical, which is why one rule landing in three spellings was
|
|
10
|
-
* possible: the shell title was `resolveToolRegistrationName` in two files,
|
|
11
|
-
* `displayName` in a third and a raw `t(key)` in a fourth.
|
|
5
|
+
* What varies between capabilities is the two options below: whether the overlay
|
|
6
|
+
* paints its own chrome or takes a draggable window, and that window's size. The
|
|
7
|
+
* visibility binding, the button, the component-override lookup, the `tool-id`
|
|
8
|
+
* attribute and the sync are shared, so the shell title has one spelling.
|
|
12
9
|
*
|
|
13
|
-
* Catalog keys are derived from `toolId
|
|
14
|
-
* `tools.<toolId>.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* Catalog keys are derived from `toolId`: `tools.<toolId>.buttonA11y` and
|
|
11
|
+
* `tools.<toolId>.tooltip`. A capability needing a different prefix — the
|
|
12
|
+
* dictionary variants, which compose several capabilities off one element —
|
|
13
|
+
* keeps its own renderer.
|
|
17
14
|
*/
|
|
18
15
|
import { type ToolContext, type ToolRegistration, type ToolToolbarRenderResult, type ToolbarContext } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
19
16
|
/** A window's geometry. Absent for overlays that paint their own chrome. */
|
|
@@ -2,18 +2,15 @@
|
|
|
2
2
|
* The `renderToolbar` shape shared by every packaged capability that puts one
|
|
3
3
|
* overlay behind one toolbar button.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* sync — was identical, which is why one rule landing in three spellings was
|
|
10
|
-
* possible: the shell title was `resolveToolRegistrationName` in two files,
|
|
11
|
-
* `displayName` in a third and a raw `t(key)` in a fourth.
|
|
5
|
+
* What varies between capabilities is the two options below: whether the overlay
|
|
6
|
+
* paints its own chrome or takes a draggable window, and that window's size. The
|
|
7
|
+
* visibility binding, the button, the component-override lookup, the `tool-id`
|
|
8
|
+
* attribute and the sync are shared, so the shell title has one spelling.
|
|
12
9
|
*
|
|
13
|
-
* Catalog keys are derived from `toolId
|
|
14
|
-
* `tools.<toolId>.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
10
|
+
* Catalog keys are derived from `toolId`: `tools.<toolId>.buttonA11y` and
|
|
11
|
+
* `tools.<toolId>.tooltip`. A capability needing a different prefix — the
|
|
12
|
+
* dictionary variants, which compose several capabilities off one element —
|
|
13
|
+
* keeps its own renderer.
|
|
17
14
|
*/
|
|
18
15
|
import { applyOverlaySurface, createScopedVisibilityBinding, createToolElement, resolveToolRegistrationName, syncButtonAndOverlayVisibility, } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
19
16
|
import { resolveOverlayElement } from "./overlay-element-cache.js";
|
|
@@ -4,13 +4,12 @@
|
|
|
4
4
|
* Registers the TTS tool for reading content aloud.
|
|
5
5
|
*/
|
|
6
6
|
import type { ToolRegistration } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
7
|
-
export declare const TOOL_ACTIVE_CHANGE_EVENT = "pie-tool-active-change";
|
|
8
7
|
/**
|
|
9
8
|
* Text-to-Speech tool registration
|
|
10
9
|
*
|
|
11
10
|
* Supports:
|
|
12
11
|
* - Reading content aloud using browser TTS or external providers
|
|
13
12
|
* - Context-aware visibility (shows when readable text is available)
|
|
14
|
-
* -
|
|
13
|
+
* - Item and passage levels
|
|
15
14
|
*/
|
|
16
15
|
export declare const ttsToolRegistration: ToolRegistration;
|
|
@@ -3,22 +3,22 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Registers the TTS tool for reading content aloud.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
7
|
-
import { createScopedToolId, createToolElement, } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
8
|
-
import { buildRuntimeTTSConfig,
|
|
6
|
+
import { hasSpokenContent } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
7
|
+
import { createScopedToolId, createToolElement, TOOL_ACTIVE_CHANGE_EVENT, } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
8
|
+
import { buildRuntimeTTSConfig, normalizeTTSSpeedControlOptions, resolveTTSHostToolbarLayout, resolveTTSRuntimeSettings, } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
9
9
|
import { TTSToolProvider } from "@pie-players/pie-assessment-toolkit/tools/registration";
|
|
10
10
|
import { resolveOverlayElement } from "./overlay-element-cache.js";
|
|
11
11
|
// This package owns the server adapter's import, so the toolkit never names it
|
|
12
12
|
// and a bundler building the toolkit alone has nothing to resolve.
|
|
13
13
|
const loadServerTTSProvider = async () => (await import("@pie-players/tts-client-server")).ServerTTSProvider;
|
|
14
|
-
|
|
14
|
+
const numberOrUndefined = (value) => typeof value === "number" ? value : undefined;
|
|
15
15
|
/**
|
|
16
16
|
* Text-to-Speech tool registration
|
|
17
17
|
*
|
|
18
18
|
* Supports:
|
|
19
19
|
* - Reading content aloud using browser TTS or external providers
|
|
20
20
|
* - Context-aware visibility (shows when readable text is available)
|
|
21
|
-
* -
|
|
21
|
+
* - Item and passage levels
|
|
22
22
|
*/
|
|
23
23
|
export const ttsToolRegistration = {
|
|
24
24
|
toolId: "textToSpeech",
|
|
@@ -46,9 +46,6 @@ export const ttsToolRegistration = {
|
|
|
46
46
|
const normalizedConfig = {
|
|
47
47
|
...config,
|
|
48
48
|
};
|
|
49
|
-
if ("layoutMode" in normalizedConfig) {
|
|
50
|
-
normalizedConfig.layoutMode = normalizeTTSLayoutMode(normalizedConfig.layoutMode);
|
|
51
|
-
}
|
|
52
49
|
if ("speedOptions" in normalizedConfig) {
|
|
53
50
|
normalizedConfig.speedOptions = normalizeTTSSpeedControlOptions(normalizedConfig.speedOptions);
|
|
54
51
|
}
|
|
@@ -59,11 +56,10 @@ export const ttsToolRegistration = {
|
|
|
59
56
|
/**
|
|
60
57
|
* Pass 2: Determine if TTS is relevant in this context
|
|
61
58
|
*
|
|
62
|
-
* TTS is relevant when
|
|
63
|
-
* - Context contains readable text (at least 10 characters)
|
|
59
|
+
* TTS is relevant when the context holds readable text or a catalog idref.
|
|
64
60
|
*/
|
|
65
61
|
isVisibleInContext(context) {
|
|
66
|
-
return
|
|
62
|
+
return hasSpokenContent(context);
|
|
67
63
|
},
|
|
68
64
|
renderToolbar(context, toolbarContext) {
|
|
69
65
|
const resolveRuntimeSettings = () => resolveTTSRuntimeSettings(toolbarContext.toolkitCoordinator?.getToolConfig(this.toolId) ||
|
|
@@ -72,19 +68,7 @@ export const ttsToolRegistration = {
|
|
|
72
68
|
const runtimeSettings = resolveRuntimeSettings();
|
|
73
69
|
return normalizeTTSSpeedControlOptions(runtimeSettings.speedOptions);
|
|
74
70
|
};
|
|
75
|
-
const resolveLayoutMode = () => resolveTTSLayoutMode(resolveRuntimeSettings());
|
|
76
|
-
const resolveHostLayout = () => resolveTTSHostToolbarLayout(resolveRuntimeSettings());
|
|
77
71
|
const fullToolId = createScopedToolId(this.toolId, toolbarContext.scope.level, toolbarContext.scope.scopeId);
|
|
78
|
-
// Only a language the toolbar or its host named: unnamed, the control resolves
|
|
79
|
-
// markup `lang`, and the browser voice follows the browser's language.
|
|
80
|
-
const applyContentLanguage = (element) => {
|
|
81
|
-
if (toolbarContext.language) {
|
|
82
|
-
element.setAttribute("language", toolbarContext.language);
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
element.removeAttribute("language");
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
72
|
const resolveControlSize = () => {
|
|
89
73
|
const raw = toolbarContext.ui?.size;
|
|
90
74
|
return raw === "sm" || raw === "lg" ? raw : "md";
|
|
@@ -92,22 +76,26 @@ export const ttsToolRegistration = {
|
|
|
92
76
|
const element = resolveOverlayElement(toolbarContext, fullToolId, () => createToolElement(this.toolId, context, toolbarContext, toolbarContext.componentOverrides));
|
|
93
77
|
const applyAttributes = () => {
|
|
94
78
|
element.setAttribute("catalog-id", toolbarContext.catalogId || toolbarContext.itemId);
|
|
95
|
-
applyContentLanguage(element);
|
|
96
79
|
element.setAttribute("size", resolveControlSize());
|
|
97
|
-
element.setAttribute("layout-mode",
|
|
80
|
+
element.setAttribute("layout-mode", resolveRuntimeSettings().layoutMode);
|
|
98
81
|
element.speedOptions = resolveElementSpeedOptions();
|
|
99
82
|
element.showSingleSpeedOption =
|
|
100
83
|
resolveRuntimeSettings().showSingleSpeedOption === true;
|
|
84
|
+
// The element range-checks these; a non-number means the default.
|
|
85
|
+
const speech = toolbarContext.getToolParameters?.("textToSpeech");
|
|
86
|
+
element.baseRate = numberOrUndefined(speech?.rate);
|
|
87
|
+
element.pitch = numberOrUndefined(speech?.pitch);
|
|
88
|
+
element.volume = numberOrUndefined(speech?.volume);
|
|
101
89
|
};
|
|
102
90
|
applyAttributes();
|
|
103
|
-
const hostLayout =
|
|
91
|
+
const hostLayout = resolveTTSHostToolbarLayout(resolveRuntimeSettings());
|
|
104
92
|
return {
|
|
105
93
|
toolId: this.toolId,
|
|
106
94
|
button: null,
|
|
107
95
|
elements: [
|
|
108
96
|
{
|
|
109
97
|
element,
|
|
110
|
-
mount:
|
|
98
|
+
mount: "before-buttons",
|
|
111
99
|
layoutHints: {
|
|
112
100
|
controlsRow: {
|
|
113
101
|
reserveSpace: hostLayout.controlsRow.reserveSpace,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pie-players/pie-default-tool-loaders",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-beta.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composition layer for the packaged PIE assessment tools: registrations, tag map, placement preset, universal and empty profiles, and lazy module loaders",
|
|
6
6
|
"repository": {
|
|
@@ -33,25 +33,25 @@
|
|
|
33
33
|
"test": "bun test"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@pie-players/pie-assessment-toolkit": "0.
|
|
37
|
-
"@pie-players/pie-calculator": "0.
|
|
38
|
-
"@pie-players/pie-calculator-cortex": "0.
|
|
39
|
-
"@pie-players/pie-calculator-desmos": "0.
|
|
40
|
-
"@pie-players/pie-calculator-geogebra": "0.
|
|
41
|
-
"@pie-players/pie-players-shared": "0.
|
|
42
|
-
"@pie-players/pie-tool-annotation-toolbar": "0.
|
|
43
|
-
"@pie-players/pie-tool-answer-eliminator": "0.
|
|
44
|
-
"@pie-players/pie-tool-calculator-shared": "0.
|
|
45
|
-
"@pie-players/pie-tool-dictionary": "0.
|
|
46
|
-
"@pie-players/pie-tool-graph": "0.
|
|
47
|
-
"@pie-players/pie-tool-line-reader": "0.
|
|
48
|
-
"@pie-players/pie-tool-periodic-table": "0.
|
|
49
|
-
"@pie-players/pie-tool-picture-dictionary": "0.
|
|
50
|
-
"@pie-players/pie-tool-protractor": "0.
|
|
51
|
-
"@pie-players/pie-tool-ruler": "0.
|
|
52
|
-
"@pie-players/pie-tool-theme": "0.
|
|
53
|
-
"@pie-players/pie-tool-tts-inline": "0.
|
|
54
|
-
"@pie-players/tts-client-server": "0.
|
|
36
|
+
"@pie-players/pie-assessment-toolkit": "1.0.0-beta.1",
|
|
37
|
+
"@pie-players/pie-calculator": "1.0.0-beta.1",
|
|
38
|
+
"@pie-players/pie-calculator-cortex": "1.0.0-beta.1",
|
|
39
|
+
"@pie-players/pie-calculator-desmos": "1.0.0-beta.1",
|
|
40
|
+
"@pie-players/pie-calculator-geogebra": "1.0.0-beta.1",
|
|
41
|
+
"@pie-players/pie-players-shared": "1.0.0-beta.1",
|
|
42
|
+
"@pie-players/pie-tool-annotation-toolbar": "1.0.0-beta.1",
|
|
43
|
+
"@pie-players/pie-tool-answer-eliminator": "1.0.0-beta.1",
|
|
44
|
+
"@pie-players/pie-tool-calculator-shared": "1.0.0-beta.1",
|
|
45
|
+
"@pie-players/pie-tool-dictionary": "1.0.0-beta.1",
|
|
46
|
+
"@pie-players/pie-tool-graph": "1.0.0-beta.1",
|
|
47
|
+
"@pie-players/pie-tool-line-reader": "1.0.0-beta.1",
|
|
48
|
+
"@pie-players/pie-tool-periodic-table": "1.0.0-beta.1",
|
|
49
|
+
"@pie-players/pie-tool-picture-dictionary": "1.0.0-beta.1",
|
|
50
|
+
"@pie-players/pie-tool-protractor": "1.0.0-beta.1",
|
|
51
|
+
"@pie-players/pie-tool-ruler": "1.0.0-beta.1",
|
|
52
|
+
"@pie-players/pie-tool-theme": "1.0.0-beta.1",
|
|
53
|
+
"@pie-players/pie-tool-tts-inline": "1.0.0-beta.1",
|
|
54
|
+
"@pie-players/tts-client-server": "1.0.0-beta.1"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@biomejs/biome": "^2.5.15",
|