@khanacademy/perseus-core 23.6.0 → 23.7.0
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/dist/data-schema.d.ts +773 -18
- package/dist/es/index.item-splitting.js +1 -1
- package/dist/es/index.item-splitting.js.map +1 -1
- package/dist/es/index.js +6 -4
- package/dist/es/index.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.item-splitting.js +1 -1
- package/dist/index.item-splitting.js.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/parse-perseus-json/perseus-parsers/interactive-graph-user-input.d.ts +16 -0
- package/dist/parse-perseus-json/perseus-parsers/interactive-graph-widget.d.ts +48 -0
- package/dist/utils/generators/interactive-graph-widget-generator.d.ts +2 -1
- package/dist/utils/remove-orphaned-widgets.d.ts +13 -0
- package/dist/validation.types.d.ts +358 -2
- package/package.json +4 -4
|
@@ -57,4 +57,20 @@ export declare const parseInteractiveGraphUserInput: import("../parser-types").P
|
|
|
57
57
|
type: "sinusoid";
|
|
58
58
|
coords: [number, number][] | null | undefined;
|
|
59
59
|
startCoords: [number, number][] | undefined;
|
|
60
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
61
|
+
type: "exponential";
|
|
62
|
+
coords: [number, number][] | null | undefined;
|
|
63
|
+
asymptote: number | null | undefined;
|
|
64
|
+
startCoords: import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
65
|
+
coords: [[number, number], [number, number]];
|
|
66
|
+
asymptote: number;
|
|
67
|
+
}> | undefined;
|
|
68
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
69
|
+
type: "absolute-value";
|
|
70
|
+
coords: [[number, number], [number, number]] | null | undefined;
|
|
71
|
+
startCoords: [[number, number], [number, number]] | undefined;
|
|
72
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
73
|
+
type: "tangent";
|
|
74
|
+
coords: [number, number][] | null | undefined;
|
|
75
|
+
startCoords: [number, number][] | undefined;
|
|
60
76
|
}>>;
|
|
@@ -57,6 +57,22 @@ export declare const parsePerseusGraphType: import("../parser-types").Parser<imp
|
|
|
57
57
|
type: "sinusoid";
|
|
58
58
|
coords: [number, number][] | null | undefined;
|
|
59
59
|
startCoords: [number, number][] | undefined;
|
|
60
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
61
|
+
type: "exponential";
|
|
62
|
+
coords: [number, number][] | null | undefined;
|
|
63
|
+
asymptote: number | null | undefined;
|
|
64
|
+
startCoords: import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
65
|
+
coords: [[number, number], [number, number]];
|
|
66
|
+
asymptote: number;
|
|
67
|
+
}> | undefined;
|
|
68
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
69
|
+
type: "absolute-value";
|
|
70
|
+
coords: [[number, number], [number, number]] | null | undefined;
|
|
71
|
+
startCoords: [[number, number], [number, number]] | undefined;
|
|
72
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
73
|
+
type: "tangent";
|
|
74
|
+
coords: [number, number][] | null | undefined;
|
|
75
|
+
startCoords: [number, number][] | undefined;
|
|
60
76
|
}>>;
|
|
61
77
|
export declare const parseLockedFunctionDomain: import("../parser-types").Parser<[number, number]>;
|
|
62
78
|
export declare const parseInteractiveGraphWidget: import("../parser-types").Parser<import("../..").WidgetOptions<"interactive-graph", import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
@@ -146,6 +162,22 @@ export declare const parseInteractiveGraphWidget: import("../parser-types").Pars
|
|
|
146
162
|
type: "sinusoid";
|
|
147
163
|
coords: [number, number][] | null | undefined;
|
|
148
164
|
startCoords: [number, number][] | undefined;
|
|
165
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
166
|
+
type: "exponential";
|
|
167
|
+
coords: [number, number][] | null | undefined;
|
|
168
|
+
asymptote: number | null | undefined;
|
|
169
|
+
startCoords: import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
170
|
+
coords: [[number, number], [number, number]];
|
|
171
|
+
asymptote: number;
|
|
172
|
+
}> | undefined;
|
|
173
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
174
|
+
type: "absolute-value";
|
|
175
|
+
coords: [[number, number], [number, number]] | null | undefined;
|
|
176
|
+
startCoords: [[number, number], [number, number]] | undefined;
|
|
177
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
178
|
+
type: "tangent";
|
|
179
|
+
coords: [number, number][] | null | undefined;
|
|
180
|
+
startCoords: [number, number][] | undefined;
|
|
149
181
|
}>;
|
|
150
182
|
correct: import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
151
183
|
type: "angle";
|
|
@@ -206,6 +238,22 @@ export declare const parseInteractiveGraphWidget: import("../parser-types").Pars
|
|
|
206
238
|
type: "sinusoid";
|
|
207
239
|
coords: [number, number][] | null | undefined;
|
|
208
240
|
startCoords: [number, number][] | undefined;
|
|
241
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
242
|
+
type: "exponential";
|
|
243
|
+
coords: [number, number][] | null | undefined;
|
|
244
|
+
asymptote: number | null | undefined;
|
|
245
|
+
startCoords: import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
246
|
+
coords: [[number, number], [number, number]];
|
|
247
|
+
asymptote: number;
|
|
248
|
+
}> | undefined;
|
|
249
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
250
|
+
type: "absolute-value";
|
|
251
|
+
coords: [[number, number], [number, number]] | null | undefined;
|
|
252
|
+
startCoords: [[number, number], [number, number]] | undefined;
|
|
253
|
+
}> | import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
254
|
+
type: "tangent";
|
|
255
|
+
coords: [number, number][] | null | undefined;
|
|
256
|
+
startCoords: [number, number][] | undefined;
|
|
209
257
|
}>;
|
|
210
258
|
lockedFigures: (import("../general-purpose-parsers/object-types").OptionalizeProperties<{
|
|
211
259
|
type: "label";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InteractiveGraphWidget, LockedEllipseType, LockedFunctionType, LockedLabelType, LockedLineType, LockedPointType, LockedPolygonType, LockedVectorType, PerseusGraphTypeAngle, PerseusGraphTypeCircle, PerseusGraphTypeLinear, PerseusGraphTypeLinearSystem, PerseusGraphTypeNone, PerseusGraphTypePoint, PerseusGraphTypePolygon, PerseusGraphTypeQuadratic, PerseusGraphTypeRay, PerseusGraphTypeSegment, PerseusGraphTypeSinusoid, PerseusInteractiveGraphWidgetOptions } from "../../data-schema";
|
|
1
|
+
import type { InteractiveGraphWidget, LockedEllipseType, LockedFunctionType, LockedLabelType, LockedLineType, LockedPointType, LockedPolygonType, LockedVectorType, PerseusGraphTypeAngle, PerseusGraphTypeCircle, PerseusGraphTypeLinear, PerseusGraphTypeLinearSystem, PerseusGraphTypeNone, PerseusGraphTypePoint, PerseusGraphTypePolygon, PerseusGraphTypeQuadratic, PerseusGraphTypeRay, PerseusGraphTypeSegment, PerseusGraphTypeSinusoid, PerseusGraphTypeTangent, PerseusInteractiveGraphWidgetOptions } from "../../data-schema";
|
|
2
2
|
export declare function generateInteractiveGraphWidget(interactiveGraphWidgetProperties?: Partial<Omit<InteractiveGraphWidget, "type">>): InteractiveGraphWidget;
|
|
3
3
|
export declare function generateInteractiveGraphOptions(options?: Partial<PerseusInteractiveGraphWidgetOptions>): PerseusInteractiveGraphWidgetOptions;
|
|
4
4
|
export declare function generateIGAngleGraph(options?: Partial<PerseusGraphTypeAngle>): PerseusGraphTypeAngle;
|
|
@@ -12,6 +12,7 @@ export declare function generateIGQuadraticGraph(options?: Partial<Omit<PerseusG
|
|
|
12
12
|
export declare function generateIGRayGraph(options?: Partial<Omit<PerseusGraphTypeRay, "type">>): PerseusGraphTypeRay;
|
|
13
13
|
export declare function generateIGSegmentGraph(options?: Partial<Omit<PerseusGraphTypeSegment, "type">>): PerseusGraphTypeSegment;
|
|
14
14
|
export declare function generateIGSinusoidGraph(options?: Partial<Omit<PerseusGraphTypeSinusoid, "type">>): PerseusGraphTypeSinusoid;
|
|
15
|
+
export declare function generateIGTangentGraph(options?: Partial<Omit<PerseusGraphTypeTangent, "type">>): PerseusGraphTypeTangent;
|
|
15
16
|
export declare function generateIGLockedPoint(options?: Partial<Omit<LockedPointType, "type">>): LockedPointType;
|
|
16
17
|
export declare function generateIGLockedLine(options?: Partial<Omit<LockedLineType, "type">>): LockedLineType;
|
|
17
18
|
export declare function generateIGLockedVector(options?: Partial<Omit<LockedVectorType, "type">>): LockedVectorType;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PerseusItem, PerseusRenderer } from "../data-schema";
|
|
2
|
+
/**
|
|
3
|
+
* Returns a new PerseusItem whose question and hints contain only widgets
|
|
4
|
+
* referenced by placeholders in their `content` string. Idempotent. Does not
|
|
5
|
+
* mutate its argument.
|
|
6
|
+
*/
|
|
7
|
+
export declare function removeOrphanedWidgetsFromPerseusItem(item: PerseusItem): PerseusItem;
|
|
8
|
+
/**
|
|
9
|
+
* Returns a new PerseusRenderer whose `widgets` map contains only widgets
|
|
10
|
+
* referenced by placeholders in the `content` string. Idempotent. Does not
|
|
11
|
+
* mutate its argument.
|
|
12
|
+
*/
|
|
13
|
+
export declare function removeOrphanedWidgets(renderer: PerseusRenderer): PerseusRenderer;
|
|
@@ -31,160 +31,511 @@
|
|
|
31
31
|
import type { GrapherAnswerTypes, PerseusDropdownChoice, PerseusExpressionAnswerForm, PerseusGradedGroupSetWidgetOptions, PerseusGradedGroupWidgetOptions, PerseusGraphType, PerseusGroupWidgetOptions, PerseusMatrixWidgetAnswers, PerseusNumericInputAnswer, PerseusOrdererWidgetOptions, PerseusRadioChoice, PerseusGraphCorrectType, MakeWidgetMap, PerseusFreeResponseWidgetScoringCriterion, PerseusRenderer } from "./data-schema";
|
|
32
32
|
import type { ErrorCode } from "./error-codes";
|
|
33
33
|
import type { Relationship } from "./types";
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
/**
|
|
35
|
+
* The signature of a widget's client-side validation function. This function
|
|
36
|
+
* runs before the learner submits their attempt, so it will be passed
|
|
37
|
+
* answerless widget option data (as returned by `PublicWidgetOptionsFunction`).
|
|
38
|
+
*
|
|
39
|
+
* Returns an invalid result (ie. empty) if the input is not yet ready to be
|
|
40
|
+
* scored, or null if valid.
|
|
41
|
+
*/
|
|
42
|
+
export type WidgetValidatorFunction = (
|
|
43
|
+
/**
|
|
44
|
+
* The user's input. Undefined if the widget has never been interacted
|
|
45
|
+
* with.
|
|
46
|
+
*/
|
|
47
|
+
userInput: UserInput | undefined,
|
|
48
|
+
/** The answerless data needed to validate the input. */
|
|
49
|
+
validationData: ValidationData,
|
|
50
|
+
/**
|
|
51
|
+
* The user's locale, needed for some validations. For example,
|
|
52
|
+
* commas may be interpreted as decimal separators in some locales.
|
|
53
|
+
*/
|
|
54
|
+
locale: string) => ValidationResult;
|
|
55
|
+
/** The signature of a widget's scoring function. */
|
|
56
|
+
export type WidgetScorerFunction = (
|
|
57
|
+
/**
|
|
58
|
+
* The user's input (guess) data to score. Undefined if the user never
|
|
59
|
+
* interacted with the widget.
|
|
60
|
+
*/
|
|
61
|
+
userInput: UserInput | undefined,
|
|
62
|
+
/** The scoring criteria containing the correct answer. */
|
|
63
|
+
rubric: Rubric,
|
|
64
|
+
/**
|
|
65
|
+
* The locale for locale-sensitive scoring (eg. decimal separators).
|
|
66
|
+
*/
|
|
67
|
+
locale?: string) => PerseusScore;
|
|
68
|
+
/** The result of scoring a widget's user input. */
|
|
36
69
|
export type PerseusScore = {
|
|
70
|
+
/** Indicates the input is not ready to score. */
|
|
37
71
|
type: "invalid";
|
|
72
|
+
/**
|
|
73
|
+
* An error code describing why the input is invalid, or null
|
|
74
|
+
* for a generic invalid state.
|
|
75
|
+
*/
|
|
38
76
|
message?: ErrorCode | null;
|
|
77
|
+
/** When true, suppresses the "almost there" hint. */
|
|
39
78
|
suppressAlmostThere?: boolean | null | undefined;
|
|
40
79
|
} | {
|
|
80
|
+
/**
|
|
81
|
+
* Indicates the input has been scored and points awarded (widgets
|
|
82
|
+
* are free to award any number of points, but the score is only
|
|
83
|
+
* considered "correct" when `earned` >= `total`).
|
|
84
|
+
*/
|
|
41
85
|
type: "points";
|
|
86
|
+
/** The number of points the learner earned. */
|
|
42
87
|
earned: number;
|
|
88
|
+
/** The total possible points for this widget. */
|
|
43
89
|
total: number;
|
|
90
|
+
/** An optional feedback message to display alongside the score. */
|
|
44
91
|
message?: string | null | undefined;
|
|
45
92
|
};
|
|
93
|
+
/**
|
|
94
|
+
* The return type of a widget validator. Null means the input is valid and
|
|
95
|
+
* ready to score; otherwise, an invalid PerseusScore describing why it is not
|
|
96
|
+
* ready.
|
|
97
|
+
*/
|
|
46
98
|
export type ValidationResult = Extract<PerseusScore, {
|
|
47
99
|
type: "invalid";
|
|
48
100
|
}> | null;
|
|
101
|
+
/**
|
|
102
|
+
* The outcome status reported by a self-grading widget (CS Program or IFrame)
|
|
103
|
+
* via postMessage.
|
|
104
|
+
*/
|
|
49
105
|
export type UserInputStatus = "correct" | "incorrect" | "incomplete";
|
|
106
|
+
/** Scoring rubric for the Categorizer widget. */
|
|
50
107
|
export type PerseusCategorizerRubric = {
|
|
108
|
+
/**
|
|
109
|
+
* The correct category index for each item. The array index corresponds to
|
|
110
|
+
* the item; the value is the category index.
|
|
111
|
+
* e.g. [0, 1, 0, 1, 2]
|
|
112
|
+
*/
|
|
51
113
|
values: number[];
|
|
52
114
|
} & PerseusCategorizerValidationData;
|
|
115
|
+
/**
|
|
116
|
+
* User input for the Categorizer widget. Records which category
|
|
117
|
+
* the user assigned to each item.
|
|
118
|
+
*/
|
|
53
119
|
export type PerseusCategorizerUserInput = {
|
|
120
|
+
/**
|
|
121
|
+
* The category index selected for each item, parallel to the
|
|
122
|
+
* rubric's `items` array. Null/undefined means not yet
|
|
123
|
+
* categorized.
|
|
124
|
+
*/
|
|
54
125
|
values: Array<number | null | undefined>;
|
|
55
126
|
};
|
|
127
|
+
/** Validation data for the Categorizer widget. */
|
|
56
128
|
export type PerseusCategorizerValidationData = {
|
|
129
|
+
/**
|
|
130
|
+
* Translatable text; items to categorize.
|
|
131
|
+
* e.g. ["banana", "yellow", "apple", "purple", "shirt"]
|
|
132
|
+
*/
|
|
57
133
|
items: string[];
|
|
58
134
|
};
|
|
135
|
+
/** User input for the CS Program widget. */
|
|
59
136
|
export type PerseusCSProgramUserInput = {
|
|
137
|
+
/**
|
|
138
|
+
* The outcome of the CS program run, as reported by the program
|
|
139
|
+
* itself via postMessage.
|
|
140
|
+
*/
|
|
60
141
|
status: UserInputStatus;
|
|
142
|
+
/** An optional message from the program to display alongside the score. */
|
|
61
143
|
message: string | null;
|
|
62
144
|
};
|
|
145
|
+
/** Scoring rubric for the Dropdown widget. */
|
|
63
146
|
export type PerseusDropdownRubric = {
|
|
147
|
+
/** The list of choices; each has a `correct` flag used for scoring. */
|
|
64
148
|
choices: Array<PerseusDropdownChoice>;
|
|
65
149
|
};
|
|
150
|
+
/** User input for the Dropdown widget. */
|
|
66
151
|
export type PerseusDropdownUserInput = {
|
|
152
|
+
/**
|
|
153
|
+
* The 1-indexed position of the selected choice in the dropdown.
|
|
154
|
+
* A value of 0 indicates nothing is selected.
|
|
155
|
+
*/
|
|
67
156
|
value: number;
|
|
68
157
|
};
|
|
158
|
+
/** Scoring rubric for the Expression widget. */
|
|
69
159
|
export type PerseusExpressionRubric = {
|
|
160
|
+
/**
|
|
161
|
+
* Ordered list of answer forms matched top-to-bottom; the first
|
|
162
|
+
* match determines the score.
|
|
163
|
+
*/
|
|
70
164
|
answerForms: Array<PerseusExpressionAnswerForm>;
|
|
165
|
+
/**
|
|
166
|
+
* Variable names treated as functions (e.g. ["f", "g"]) during KAS
|
|
167
|
+
* parsing.
|
|
168
|
+
*/
|
|
71
169
|
functions: string[];
|
|
72
170
|
extraKeys?: ReadonlyArray<string>;
|
|
73
171
|
};
|
|
172
|
+
/**
|
|
173
|
+
* User input for the Expression widget: the raw math expression
|
|
174
|
+
* string the learner typed, parsed by @khanacademy/kas for scoring.
|
|
175
|
+
*/
|
|
74
176
|
export type PerseusExpressionUserInput = string;
|
|
177
|
+
/**
|
|
178
|
+
* Scoring rubric for the Group widget: the full widget options including all
|
|
179
|
+
* nested widgets and their rubrics.
|
|
180
|
+
*/
|
|
75
181
|
export type PerseusGroupRubric = PerseusGroupWidgetOptions;
|
|
182
|
+
/**
|
|
183
|
+
* Validation data for the Group widget: the full renderer content used to
|
|
184
|
+
* recursively validate nested widgets.
|
|
185
|
+
*/
|
|
76
186
|
export type PerseusGroupValidationData = PerseusRenderer;
|
|
187
|
+
/**
|
|
188
|
+
* User input for the Group widget: a map of widget IDs to each widget's user
|
|
189
|
+
* input. Scored by recursively scoring all contained widgets.
|
|
190
|
+
*/
|
|
77
191
|
export type PerseusGroupUserInput = UserInputMap;
|
|
192
|
+
/** Scoring rubric for the GradedGroup widget. */
|
|
78
193
|
export type PerseusGradedGroupRubric = PerseusGradedGroupWidgetOptions;
|
|
194
|
+
/** Scoring rubric for the GradedGroupSet widget. */
|
|
79
195
|
export type PerseusGradedGroupSetRubric = PerseusGradedGroupSetWidgetOptions;
|
|
196
|
+
/** Scoring rubric for the Grapher widget. */
|
|
80
197
|
export type PerseusGrapherRubric = {
|
|
198
|
+
/** The expected function type and coordinates for a correct answer. */
|
|
81
199
|
correct: GrapherAnswerTypes;
|
|
82
200
|
};
|
|
201
|
+
/**
|
|
202
|
+
* User input for the Grapher widget: the function type and
|
|
203
|
+
* coordinates the learner plotted.
|
|
204
|
+
*/
|
|
83
205
|
export type PerseusGrapherUserInput = GrapherAnswerTypes;
|
|
206
|
+
/** User input for the IFrame widget. */
|
|
84
207
|
export type PerseusIFrameUserInput = {
|
|
208
|
+
/**
|
|
209
|
+
* The outcome of the iframe's interaction, as reported by the iframe via
|
|
210
|
+
* postMessage.
|
|
211
|
+
*/
|
|
85
212
|
status: UserInputStatus;
|
|
213
|
+
/** An optional message from the iframe to display alongside the score. */
|
|
86
214
|
message?: string | null;
|
|
87
215
|
};
|
|
216
|
+
/** Scoring rubric for the InputNumber widget. */
|
|
88
217
|
export type PerseusInputNumberRubric = {
|
|
218
|
+
/**
|
|
219
|
+
* Constrains which numeric forms are accepted (ie. these don't change the
|
|
220
|
+
* correct numerical value - they only restrict which input representation
|
|
221
|
+
* the scorer accepts as valid).
|
|
222
|
+
*
|
|
223
|
+
* Defaults to "number" if unset.
|
|
224
|
+
*
|
|
225
|
+
* - "number" - Any standard numeric form: integer, decimal, proper
|
|
226
|
+
* fraction, improper fraction, or mixed number (note that
|
|
227
|
+
* "percent" and "pi" are _not_ included in this format).
|
|
228
|
+
* - "decimal" - Decimal notation only (e.g. 1.5)
|
|
229
|
+
* - "integer" - Whole numbers only (e.g. 3, -7)
|
|
230
|
+
* - "rational" - Integer, proper fraction, improper fraction, or mixed
|
|
231
|
+
* number — but no decimals
|
|
232
|
+
* - "improper" - Integer, proper or improper fraction — mixed numbers
|
|
233
|
+
* like 1 3/4 are rejected
|
|
234
|
+
* - "mixed" - Integer, proper fraction, or mixed number — standalone
|
|
235
|
+
* improper fractions are rejected
|
|
236
|
+
* - "percent" - All numeric forms plus percent notation (e.g. 50%); all
|
|
237
|
+
* number forms are accepted, but the % sign is required
|
|
238
|
+
* when entering the value as a percentage (e.g. 50%)
|
|
239
|
+
* rather than as the equivalent decimal (e.g. 0.5).
|
|
240
|
+
* - "pi" - Expressions involving π or τ (e.g. 3 pi, 5/6 pi, 2 \pi);
|
|
241
|
+
* the user must express the answer as a multiple of pi. 0
|
|
242
|
+
* is accepted literally since 0·π = 0. Decimal
|
|
243
|
+
* approximations within 0.01 of a multiple of π or τ are
|
|
244
|
+
* also accepted. Any fraction with 7 as the denominator
|
|
245
|
+
* (ie. `x/7`) is resolved to a numeric value and compared
|
|
246
|
+
* against the answer (approximating pi).
|
|
247
|
+
*/
|
|
89
248
|
answerType?: "number" | "decimal" | "integer" | "rational" | "improper" | "mixed" | "percent" | "pi";
|
|
249
|
+
/**
|
|
250
|
+
* When true, approximate answers within `maxError` of the correct
|
|
251
|
+
* value are accepted.
|
|
252
|
+
*/
|
|
90
253
|
inexact?: boolean;
|
|
254
|
+
/**
|
|
255
|
+
* The maximum allowable deviation from the correct value when
|
|
256
|
+
* `inexact` is true.
|
|
257
|
+
*
|
|
258
|
+
* Legacy content encodes this number as a string (eg. "0.5"), encoding the
|
|
259
|
+
* values as the `number` type is preferred!
|
|
260
|
+
*/
|
|
91
261
|
maxError?: number | string;
|
|
262
|
+
/**
|
|
263
|
+
* Controls how the scorer handles unsimplified fractions (e.g. 2/4
|
|
264
|
+
* instead of 1/2) when the numerical value is otherwise correct.
|
|
265
|
+
*
|
|
266
|
+
* - "required" - The unsimplified answer is treated as not yet
|
|
267
|
+
* submitted: the scorer returns an invalid/empty result
|
|
268
|
+
* with a "needs to be simplified" prompt, so the learner
|
|
269
|
+
* must simplify before their attempt is graded.
|
|
270
|
+
* - "optional" - Unsimplified fractions are accepted as fully correct;
|
|
271
|
+
* simplification is not checked.
|
|
272
|
+
* - "enforced" - Unsimplified fractions are scored as wrong (incorrect,
|
|
273
|
+
* not just unsubmitted), with a "needs to be simplified"
|
|
274
|
+
* message shown alongside the incorrect result.
|
|
275
|
+
*/
|
|
92
276
|
simplify: "required" | "optional" | "enforced";
|
|
277
|
+
/** The correct answer value. */
|
|
93
278
|
value: string | number;
|
|
94
279
|
};
|
|
280
|
+
/** User input for the InputNumber widget. */
|
|
95
281
|
export type PerseusInputNumberUserInput = {
|
|
282
|
+
/**
|
|
283
|
+
* The raw value entered by the learner. May be a TeX expression; the
|
|
284
|
+
* scorer parses it before grading.
|
|
285
|
+
*/
|
|
96
286
|
currentValue: string;
|
|
97
287
|
};
|
|
288
|
+
/** Scoring rubric for the InteractiveGraph widget. */
|
|
98
289
|
export type PerseusInteractiveGraphRubric = {
|
|
290
|
+
/**
|
|
291
|
+
* The expected graph state for a correct answer.
|
|
292
|
+
*/
|
|
99
293
|
correct: PerseusGraphCorrectType;
|
|
294
|
+
/**
|
|
295
|
+
* The initial graph state; used to detect an unanswered/empty widget
|
|
296
|
+
* (input equals this when nothing has moved).
|
|
297
|
+
*/
|
|
100
298
|
graph: PerseusGraphType;
|
|
101
299
|
};
|
|
300
|
+
/**
|
|
301
|
+
* User input for the InteractiveGraph widget: the graph type and coordinates
|
|
302
|
+
* the learner positioned.
|
|
303
|
+
*/
|
|
102
304
|
export type PerseusInteractiveGraphUserInput = PerseusGraphType;
|
|
305
|
+
/** Scoring rubric for the LabelImage widget. */
|
|
103
306
|
export type PerseusLabelImageRubric = {
|
|
307
|
+
/**
|
|
308
|
+
* The expected answers for each labeled region in the image, parallel to
|
|
309
|
+
* the user input's markers.
|
|
310
|
+
*/
|
|
104
311
|
markers: Array<{
|
|
312
|
+
/** The set of correct answer labels for this marker. */
|
|
105
313
|
answers: string[];
|
|
314
|
+
/** The label text identifying this marker in the image. */
|
|
106
315
|
label: string;
|
|
107
316
|
}>;
|
|
108
317
|
};
|
|
318
|
+
/** User input for a single image marker in the LabelImage widget. */
|
|
109
319
|
export type PerseusLabelImageUserInputMarker = {
|
|
320
|
+
/** The answer labels the user selected for this marker. */
|
|
110
321
|
selected?: string[];
|
|
322
|
+
/** The label identifying this marker in the image. */
|
|
111
323
|
label: string;
|
|
112
324
|
};
|
|
325
|
+
/** User input for the LabelImage widget. */
|
|
113
326
|
export type PerseusLabelImageUserInput = {
|
|
327
|
+
/**
|
|
328
|
+
* The user's selections for each image marker, parallel to the
|
|
329
|
+
* rubric's markers array.
|
|
330
|
+
*/
|
|
114
331
|
markers: PerseusLabelImageUserInputMarker[];
|
|
115
332
|
};
|
|
333
|
+
/** Scoring rubric for the Matcher widget. */
|
|
116
334
|
export type PerseusMatcherRubric = {
|
|
335
|
+
/**
|
|
336
|
+
* Static concepts for the left column. e.g. ["Fruit", "Color", "Clothes"]
|
|
337
|
+
*
|
|
338
|
+
* Translatable text.
|
|
339
|
+
*/
|
|
117
340
|
left: string[];
|
|
341
|
+
/**
|
|
342
|
+
* the correct right-column values, ordered to match the left. e.g.
|
|
343
|
+
* ["Banana", "Red", "Shirt"]
|
|
344
|
+
*
|
|
345
|
+
* Translatable markup.
|
|
346
|
+
*/
|
|
118
347
|
right: string[];
|
|
119
348
|
};
|
|
349
|
+
/** User input for the Matcher widget. */
|
|
120
350
|
export type PerseusMatcherUserInput = {
|
|
351
|
+
/** The left-column items in the learner's current arrangement. */
|
|
121
352
|
left: string[];
|
|
353
|
+
/**
|
|
354
|
+
* The right-column items in the learner's arrangement. Must match the
|
|
355
|
+
* rubric's right column to be scored correct.
|
|
356
|
+
*/
|
|
122
357
|
right: string[];
|
|
123
358
|
};
|
|
359
|
+
/** Scoring rubric for the Matrix widget. */
|
|
124
360
|
export type PerseusMatrixRubric = {
|
|
361
|
+
/** The correct 2D matrix of answers. */
|
|
125
362
|
answers: PerseusMatrixWidgetAnswers;
|
|
126
363
|
} & PerseusMatrixValidationData;
|
|
364
|
+
/**
|
|
365
|
+
* Validation data for the Matrix widget; currently empty — validation is
|
|
366
|
+
* performed from user input alone.
|
|
367
|
+
*/
|
|
127
368
|
export type PerseusMatrixValidationData = Empty;
|
|
369
|
+
/** User input for the Matrix widget. */
|
|
128
370
|
export type PerseusMatrixUserInput = {
|
|
371
|
+
/**
|
|
372
|
+
* A 2D array of cell values entered by the learner; each string may be a
|
|
373
|
+
* numeric expression.
|
|
374
|
+
*/
|
|
129
375
|
answers: string[][];
|
|
130
376
|
};
|
|
377
|
+
/** Scoring rubric for the NumberLine widget. */
|
|
131
378
|
export type PerseusNumberLineRubric = {
|
|
379
|
+
/**
|
|
380
|
+
* The correct inequality relation (e.g. "lt", "ge"), or null
|
|
381
|
+
* for an equality question.
|
|
382
|
+
*/
|
|
132
383
|
correctRel: string | null | undefined;
|
|
384
|
+
/** The correct numeric position on the number line. */
|
|
133
385
|
correctX: number;
|
|
386
|
+
/** The [min, max] extent of the number line. */
|
|
134
387
|
range: number[];
|
|
388
|
+
/** The starting position of the point. Defaults to range[0] if null. */
|
|
135
389
|
initialX: number | null | undefined;
|
|
390
|
+
/** When true, the widget shows a shaded region and a relation selector. */
|
|
136
391
|
isInequality: boolean;
|
|
392
|
+
/** When true, the learner can adjust the number of tick-mark divisions. */
|
|
137
393
|
isTickCtrl?: boolean;
|
|
394
|
+
/** The [min, max] allowed number of divisions when isTickCtrl is true. */
|
|
138
395
|
divisionRange: number[];
|
|
139
396
|
};
|
|
397
|
+
/** User input for the NumberLine widget. */
|
|
140
398
|
export type PerseusNumberLineUserInput = {
|
|
399
|
+
/**
|
|
400
|
+
* The actual numeric axis value where the learner placed the point
|
|
401
|
+
* (e.g. `3.5` on a `[0, 10]` number line). Clamped to the rubric's
|
|
402
|
+
* range and snapped to the nearest tick increment.
|
|
403
|
+
*/
|
|
141
404
|
numLinePosition: number;
|
|
405
|
+
/**
|
|
406
|
+
* The inequality relationship selected by the learner (e.g. "lt", "gt",
|
|
407
|
+
* "le", "ge", or "eq" for a standard point).
|
|
408
|
+
*/
|
|
142
409
|
rel: Relationship | "eq";
|
|
410
|
+
/**
|
|
411
|
+
* The number of tick-mark divisions the learner has set.
|
|
412
|
+
* Validated against the rubric's divisionRange when
|
|
413
|
+
* isTickCtrl is enabled.
|
|
414
|
+
*/
|
|
143
415
|
numDivisions: number;
|
|
144
416
|
};
|
|
417
|
+
/** Scoring rubric for the NumericInput widget. */
|
|
145
418
|
export type PerseusNumericInputRubric = {
|
|
419
|
+
/**
|
|
420
|
+
* A list of correct and incorrect answers. Each answer can have a
|
|
421
|
+
* message explaining why it is correct/incorrect. There may be
|
|
422
|
+
* multiple correct answers if multiple formats are accepted. For
|
|
423
|
+
* example, some questions might accept either a fraction or a
|
|
424
|
+
* decimal as correct.
|
|
425
|
+
*
|
|
426
|
+
* The first answer that matches (correct or incorrect) is the scoring
|
|
427
|
+
* result (so order of answers is very important).
|
|
428
|
+
*/
|
|
146
429
|
answers: PerseusNumericInputAnswer[];
|
|
430
|
+
/**
|
|
431
|
+
* When true, allows shorthand coefficient entry: `"-"` means `-1`
|
|
432
|
+
* and an empty string (`""`) means `1`.
|
|
433
|
+
*/
|
|
147
434
|
coefficient: boolean;
|
|
148
435
|
};
|
|
436
|
+
/** User input for the NumericInput widget. */
|
|
149
437
|
export type PerseusNumericInputUserInput = {
|
|
438
|
+
/**
|
|
439
|
+
* The raw value the learner typed. May be a TeX expression or a percent
|
|
440
|
+
* string (e.g. "75%"); the scorer normalizes it before grading.
|
|
441
|
+
*/
|
|
150
442
|
currentValue: string;
|
|
151
443
|
};
|
|
444
|
+
/** User input for the FreeResponse widget. */
|
|
152
445
|
export type PerseusFreeResponseUserInput = {
|
|
446
|
+
/** The free-text string entered by the learner. */
|
|
153
447
|
currentValue: string;
|
|
154
448
|
};
|
|
449
|
+
/** Scoring rubric for the FreeResponse widget. */
|
|
155
450
|
export type PerseusFreeResponseRubric = {
|
|
451
|
+
/** The question text shown to the learner. */
|
|
156
452
|
question: string;
|
|
453
|
+
/** The criteria used for AI-assisted scoring of the learner's response. */
|
|
157
454
|
scoringCriteria: ReadonlyArray<PerseusFreeResponseWidgetScoringCriterion>;
|
|
158
455
|
};
|
|
456
|
+
/**
|
|
457
|
+
* Scoring rubric for the Orderer widget: the full widget options
|
|
458
|
+
* including the correct ordering.
|
|
459
|
+
*/
|
|
159
460
|
export type PerseusOrdererRubric = PerseusOrdererWidgetOptions;
|
|
461
|
+
/** User input for the Orderer widget. */
|
|
160
462
|
export type PerseusOrdererUserInput = {
|
|
463
|
+
/**
|
|
464
|
+
* The content strings of the items in the learner's current order,
|
|
465
|
+
* compared against the rubric's correctOptions to score.
|
|
466
|
+
*/
|
|
161
467
|
current: string[];
|
|
162
468
|
};
|
|
469
|
+
/** Scoring rubric for the Plotter widget. */
|
|
163
470
|
export type PerseusPlotterRubric = {
|
|
471
|
+
/** The expected Y-axis values representing the correct answer. */
|
|
164
472
|
correct: number[];
|
|
165
473
|
} & PerseusPlotterValidationData;
|
|
474
|
+
/** Validation data for the Plotter widget. */
|
|
166
475
|
export type PerseusPlotterValidationData = {
|
|
476
|
+
/** The initial Y-axis values the chart is pre-populated with. */
|
|
167
477
|
starting: number[];
|
|
168
478
|
};
|
|
479
|
+
/**
|
|
480
|
+
* User input for the Plotter widget: an array of Y-axis values, one
|
|
481
|
+
* per bar or data point, as set by the learner.
|
|
482
|
+
*/
|
|
169
483
|
export type PerseusPlotterUserInput = number[];
|
|
484
|
+
/** Scoring rubric for the Radio widget. */
|
|
170
485
|
export type PerseusRadioRubric = {
|
|
486
|
+
/** The answer choices shown to the learner; each has a `correct` flag. */
|
|
171
487
|
choices: PerseusRadioChoice[];
|
|
488
|
+
/**
|
|
489
|
+
* When true, the learner must select exactly as many choices as there
|
|
490
|
+
* are correct answers before the answer is graded. Only has an effect
|
|
491
|
+
* when there are multiple correct answers.
|
|
492
|
+
*/
|
|
172
493
|
countChoices?: boolean;
|
|
173
494
|
};
|
|
495
|
+
/** User input for the Radio widget. */
|
|
174
496
|
export type PerseusRadioUserInput = {
|
|
497
|
+
/**
|
|
498
|
+
* The IDs of the choices the learner selected. Each ID corresponds to a
|
|
499
|
+
* choice's `id` field in the rubric. Order is insignificant — scoring
|
|
500
|
+
* uses set membership, not position. IDs are stable and do not reflect
|
|
501
|
+
* the display order, which may be shuffled.
|
|
502
|
+
*/
|
|
175
503
|
selectedChoiceIds: string[];
|
|
176
504
|
};
|
|
505
|
+
/** Scoring rubric for the Sorter widget. */
|
|
177
506
|
export type PerseusSorterRubric = {
|
|
507
|
+
/**
|
|
508
|
+
* Translatable text; the correct ordering of the cards. The
|
|
509
|
+
* learner sees them in a randomized order.
|
|
510
|
+
*/
|
|
178
511
|
correct: string[];
|
|
179
512
|
};
|
|
513
|
+
/** User input for the Sorter widget. */
|
|
180
514
|
export type PerseusSorterUserInput = {
|
|
515
|
+
/**
|
|
516
|
+
* The content strings of the sortable cards in the learner's current
|
|
517
|
+
* order, compared to the rubric's correct to score.
|
|
518
|
+
*/
|
|
181
519
|
options: string[];
|
|
520
|
+
/**
|
|
521
|
+
* Whether the learner has moved any cards from their initial randomized
|
|
522
|
+
* positions. The widget is invalid (considered empty) until true.
|
|
523
|
+
*/
|
|
182
524
|
changed: boolean;
|
|
183
525
|
};
|
|
526
|
+
/** Scoring rubric for the Table widget. */
|
|
184
527
|
export type PerseusTableRubric = {
|
|
528
|
+
/** Translatable text; the correct 2D array of cell values. */
|
|
185
529
|
answers: string[][];
|
|
186
530
|
};
|
|
531
|
+
/**
|
|
532
|
+
* User input for the Table widget: a 2D array of cell values
|
|
533
|
+
* entered by the learner, scored against the rubric's answers.
|
|
534
|
+
*/
|
|
187
535
|
export type PerseusTableUserInput = string[][];
|
|
536
|
+
/**
|
|
537
|
+
* A registry mapping widget type names to their rubric types.
|
|
538
|
+
*/
|
|
188
539
|
export interface RubricRegistry {
|
|
189
540
|
categorizer: PerseusCategorizerRubric;
|
|
190
541
|
dropdown: PerseusDropdownRubric;
|
|
@@ -222,6 +573,7 @@ export type RubricMap = {
|
|
|
222
573
|
options: RubricRegistry[Property];
|
|
223
574
|
};
|
|
224
575
|
};
|
|
576
|
+
/** A union of all widget rubric types. */
|
|
225
577
|
export type Rubric = RubricRegistry[keyof RubricRegistry];
|
|
226
578
|
/**
|
|
227
579
|
* This is an interface so that it can be extended if a widget is created
|
|
@@ -257,6 +609,10 @@ export type UserInput = UserInputRegistry[keyof UserInputRegistry];
|
|
|
257
609
|
* of the widget ID).
|
|
258
610
|
*/
|
|
259
611
|
export type UserInputMap = MakeWidgetMap<UserInputRegistry>;
|
|
612
|
+
/**
|
|
613
|
+
* A registry mapping widget type names to their client-side
|
|
614
|
+
* validation data types.
|
|
615
|
+
*/
|
|
260
616
|
export interface ValidationDataTypes {
|
|
261
617
|
categorizer: PerseusCategorizerValidationData;
|
|
262
618
|
group: PerseusGroupValidationData;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Shared Perseus infrastructure",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "23.
|
|
6
|
+
"version": "23.7.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"tiny-invariant": "1.3.1",
|
|
40
|
-
"@khanacademy/kas": "2.2.
|
|
41
|
-
"@khanacademy/perseus-utils": "2.1.
|
|
42
|
-
"@khanacademy/pure-markdown": "2.2.
|
|
40
|
+
"@khanacademy/kas": "2.2.1",
|
|
41
|
+
"@khanacademy/perseus-utils": "2.1.5",
|
|
42
|
+
"@khanacademy/pure-markdown": "2.2.7"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@khanacademy/wonder-stuff-core": "3.0.0",
|