@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.
@@ -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
- export type WidgetValidatorFunction = (userInput: UserInput | undefined, validationData: ValidationData, locale: string) => ValidationResult;
35
- export type WidgetScorerFunction = (userInput: UserInput | undefined, rubric: Rubric, locale?: string) => PerseusScore;
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.0",
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.0",
41
- "@khanacademy/perseus-utils": "2.1.4",
42
- "@khanacademy/pure-markdown": "2.2.6"
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",