@khanacademy/perseus 66.4.0 → 67.0.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/es/index.css +5 -3
- package/dist/es/index.css.map +1 -1
- package/dist/es/index.js +12 -12
- package/dist/es/index.js.map +1 -1
- package/dist/index.css +5 -3
- package/dist/index.css.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +12 -11
- package/dist/index.js.map +1 -1
- package/dist/server-item-renderer.d.ts +0 -1
- package/dist/types.d.ts +0 -10
- package/dist/user-input-manager.d.ts +1 -1
- package/dist/widgets/expression/expression.d.ts +0 -2
- package/dist/widgets/grapher/grapher.d.ts +0 -1
- package/dist/widgets/interactive-graphs/interactive-graph.d.ts +3 -14
- package/dist/widgets/mock-widgets/mock-widget.d.ts +0 -1
- package/dist/widgets/number-line/number-line.d.ts +0 -1
- package/dist/widgets/numeric-input/numeric-input.class.d.ts +0 -11
- package/dist/widgets/numeric-input/numeric-input.d.ts +0 -1
- package/dist/widgets/numeric-input/utils.d.ts +6 -1
- package/dist/widgets/plotter/plotter.d.ts +0 -1
- package/dist/widgets/radio/choice-indicator.new.d.ts +10 -4
- package/dist/widgets/radio/choice.new.d.ts +2 -1
- package/dist/widgets/radio/multiple-choice-component.new.d.ts +13 -1
- package/dist/widgets/radio/radio-question-builder.d.ts +4 -0
- package/dist/widgets/radio/radio.ff.d.ts +0 -1
- package/dist/widgets/table/table.d.ts +0 -1
- package/package.json +9 -9
|
@@ -159,7 +159,6 @@ declare const _default: React.ForwardRefExoticComponent<Omit<Pick<SharedRenderer
|
|
|
159
159
|
canScrollPage?: boolean;
|
|
160
160
|
editorChangeDelay?: number;
|
|
161
161
|
flags?: Record<"new-radio-widget", boolean>;
|
|
162
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
163
162
|
}> | undefined;
|
|
164
163
|
linterContext?: PerseusLinter.LinterContextProps | undefined;
|
|
165
164
|
onRendered?: ((isRendered: boolean) => void) | undefined;
|
package/dist/types.d.ts
CHANGED
|
@@ -224,16 +224,6 @@ export type APIOptions = Readonly<{
|
|
|
224
224
|
* Define the feature flag name in packages/perseus-core/src/feature-flags.ts
|
|
225
225
|
*/
|
|
226
226
|
flags?: Record<(typeof PerseusFeatureFlags)[number], boolean>;
|
|
227
|
-
/**
|
|
228
|
-
* This is a callback function that returns all of the Widget props
|
|
229
|
-
* after they have been transformed by the widget's transform function.
|
|
230
|
-
* This is useful for when we need to know how a widget has shuffled its
|
|
231
|
-
* the available choices.
|
|
232
|
-
*
|
|
233
|
-
* @deprecated [LEMS-3185] this is externalizing an internal implementation
|
|
234
|
-
* detail similar to serialized state
|
|
235
|
-
*/
|
|
236
|
-
onWidgetStartProps?: (widgets: PerseusWidgetsMap) => void;
|
|
237
227
|
}>;
|
|
238
228
|
type TeXProps = {
|
|
239
229
|
children: string;
|
|
@@ -33,7 +33,7 @@ export declare function sharedInitializeUserInput(widgetOptions: PerseusWidgetsM
|
|
|
33
33
|
*
|
|
34
34
|
* @deprecated - do not use in new code.
|
|
35
35
|
*/
|
|
36
|
-
export declare function
|
|
36
|
+
export declare function deriveUserInputFromSerializedState(serializedState: unknown, widgetsMap: PerseusWidgetsMap): UserInputMap;
|
|
37
37
|
/**
|
|
38
38
|
* UserInputManager is a stateful wrapper for handling and storing user input.
|
|
39
39
|
* Ideally we'll get to a place where widgets are functional components
|
|
@@ -101,7 +101,6 @@ export declare class Expression extends React.Component<Props> implements Widget
|
|
|
101
101
|
canScrollPage?: boolean;
|
|
102
102
|
editorChangeDelay?: number;
|
|
103
103
|
flags?: Record<"new-radio-widget", boolean>;
|
|
104
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
105
104
|
}> & {
|
|
106
105
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
107
106
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -194,7 +193,6 @@ declare const _default: {
|
|
|
194
193
|
canScrollPage?: boolean;
|
|
195
194
|
editorChangeDelay?: number;
|
|
196
195
|
flags?: Record<"new-radio-widget", boolean>;
|
|
197
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
198
196
|
}> & {
|
|
199
197
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
200
198
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -92,7 +92,6 @@ declare class Grapher extends React.Component<Props> implements Widget {
|
|
|
92
92
|
canScrollPage?: boolean;
|
|
93
93
|
editorChangeDelay?: number;
|
|
94
94
|
flags?: Record<"new-radio-widget", boolean>;
|
|
95
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
96
95
|
}> & {
|
|
97
96
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
98
97
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -6,7 +6,7 @@ import type { WidgetProps } from "../../types";
|
|
|
6
6
|
import type { InteractiveGraphPromptJSON } from "../../widget-ai-utils/interactive-graph/interactive-graph-ai-utils";
|
|
7
7
|
import type { UnsupportedWidgetPromptJSON } from "../../widget-ai-utils/unsupported-widget";
|
|
8
8
|
import type { QuadraticCoefficient, SineCoefficient, Range } from "@khanacademy/kmath";
|
|
9
|
-
import type { PerseusGraphType, PerseusGraphTypeAngle, PerseusGraphTypePoint, PerseusGraphTypeSegment, PerseusInteractiveGraphWidgetOptions, GraphRange, InteractiveGraphPublicWidgetOptions, LockedFigure, PerseusImageBackground, MarkingsType, PerseusInteractiveGraphUserInput, AxisLabelLocation } from "@khanacademy/perseus-core";
|
|
9
|
+
import type { PerseusGraphType, PerseusGraphTypeAngle, PerseusGraphTypePoint, PerseusGraphTypeSegment, PerseusInteractiveGraphWidgetOptions, GraphRange, InteractiveGraphPublicWidgetOptions, LockedFigure, PerseusImageBackground, MarkingsType, PerseusInteractiveGraphUserInput, AxisLabelLocation, ShowAxisArrows } from "@khanacademy/perseus-core";
|
|
10
10
|
type RenderProps = {
|
|
11
11
|
/**
|
|
12
12
|
* Where the little black axis lines & labels (ticks) should render.
|
|
@@ -87,12 +87,7 @@ type RenderProps = {
|
|
|
87
87
|
/**
|
|
88
88
|
* Whether to show the arrows on the axis.
|
|
89
89
|
*/
|
|
90
|
-
showAxisArrows:
|
|
91
|
-
xMin: boolean;
|
|
92
|
-
xMax: boolean;
|
|
93
|
-
yMin: boolean;
|
|
94
|
-
yMax: boolean;
|
|
95
|
-
};
|
|
90
|
+
showAxisArrows: ShowAxisArrows;
|
|
96
91
|
/**
|
|
97
92
|
* The type of graph
|
|
98
93
|
*/
|
|
@@ -153,12 +148,7 @@ declare class InteractiveGraph extends React.Component<Props, State> {
|
|
|
153
148
|
rulerLabel?: string;
|
|
154
149
|
rulerTicks?: number;
|
|
155
150
|
range: GraphRange;
|
|
156
|
-
showAxisArrows:
|
|
157
|
-
xMin: boolean;
|
|
158
|
-
xMax: boolean;
|
|
159
|
-
yMin: boolean;
|
|
160
|
-
yMax: boolean;
|
|
161
|
-
};
|
|
151
|
+
showAxisArrows: ShowAxisArrows;
|
|
162
152
|
correct?: PerseusGraphType;
|
|
163
153
|
lockedFigures: LockedFigure[];
|
|
164
154
|
fullGraphAriaLabel?: string;
|
|
@@ -203,7 +193,6 @@ declare class InteractiveGraph extends React.Component<Props, State> {
|
|
|
203
193
|
canScrollPage?: boolean;
|
|
204
194
|
editorChangeDelay?: number;
|
|
205
195
|
flags?: Record<"new-radio-widget", boolean>;
|
|
206
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
207
196
|
}> & {
|
|
208
197
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
209
198
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -76,7 +76,6 @@ declare class MockWidgetComponent extends React.Component<Props> implements Widg
|
|
|
76
76
|
canScrollPage?: boolean;
|
|
77
77
|
editorChangeDelay?: number;
|
|
78
78
|
flags?: Record<"new-radio-widget", boolean>;
|
|
79
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
80
79
|
}> & {
|
|
81
80
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
82
81
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -121,7 +121,6 @@ declare class NumberLine extends React.Component<Props, State> implements Widget
|
|
|
121
121
|
canScrollPage?: boolean;
|
|
122
122
|
editorChangeDelay?: number;
|
|
123
123
|
flags?: Record<"new-radio-widget", boolean>;
|
|
124
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
125
124
|
}> & {
|
|
126
125
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
127
126
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -13,14 +13,6 @@ export type NumericInputProps = ExternalProps & {
|
|
|
13
13
|
linterContext: NonNullable<ExternalProps["linterContext"]>;
|
|
14
14
|
};
|
|
15
15
|
type DefaultProps = Pick<NumericInputProps, "size" | "rightAlign" | "apiOptions" | "coefficient" | "answerForms" | "labelText" | "linterContext" | "userInput">;
|
|
16
|
-
type RenderProps = {
|
|
17
|
-
answerForms: ReadonlyArray<PerseusNumericInputAnswerForm>;
|
|
18
|
-
labelText?: string;
|
|
19
|
-
size: string;
|
|
20
|
-
coefficient: boolean;
|
|
21
|
-
rightAlign?: boolean;
|
|
22
|
-
static: boolean;
|
|
23
|
-
};
|
|
24
16
|
/**
|
|
25
17
|
* The NumericInput widget is a numeric input field that supports a variety of
|
|
26
18
|
* answer forms, including integers, decimals, fractions, and mixed numbers.
|
|
@@ -47,7 +39,6 @@ export declare class NumericInput extends React.Component<NumericInputProps> imp
|
|
|
47
39
|
*/
|
|
48
40
|
getSerializedState(): {
|
|
49
41
|
currentValue: string;
|
|
50
|
-
answers: import("@khanacademy/perseus-core").PerseusNumericInputAnswer[];
|
|
51
42
|
labelText: string;
|
|
52
43
|
size: string;
|
|
53
44
|
coefficient: boolean;
|
|
@@ -92,7 +83,6 @@ export declare class NumericInput extends React.Component<NumericInputProps> imp
|
|
|
92
83
|
canScrollPage?: boolean;
|
|
93
84
|
editorChangeDelay?: number;
|
|
94
85
|
flags?: Record<"new-radio-widget", boolean>;
|
|
95
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
96
86
|
}> & {
|
|
97
87
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
98
88
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -132,7 +122,6 @@ declare const _default: {
|
|
|
132
122
|
name: string;
|
|
133
123
|
displayName: string;
|
|
134
124
|
widget: typeof NumericInput;
|
|
135
|
-
transform: (widgetOptions: PerseusNumericInputWidgetOptions) => RenderProps;
|
|
136
125
|
isLintable: true;
|
|
137
126
|
getOneCorrectAnswerFromRubric(rubric: PerseusNumericInputRubric): string | null | undefined;
|
|
138
127
|
getStartUserInput: typeof getStartUserInput;
|
|
@@ -42,7 +42,6 @@ export declare const NumericInputComponent: React.ForwardRefExoticComponent<impo
|
|
|
42
42
|
canScrollPage?: boolean;
|
|
43
43
|
editorChangeDelay?: number;
|
|
44
44
|
flags?: Record<"new-radio-widget", boolean>;
|
|
45
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
46
45
|
}> & {
|
|
47
46
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
48
47
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PerseusStrings } from "../../strings";
|
|
2
|
-
import type { PerseusNumericInputAnswerForm } from "@khanacademy/perseus-core";
|
|
2
|
+
import type { PerseusNumericInputAnswerForm, PerseusNumericInputWidgetOptions } from "@khanacademy/perseus-core";
|
|
3
3
|
/**
|
|
4
4
|
* The full list of available strings for the numeric input widget,
|
|
5
5
|
* based on whether the Content Creator has specified that the answer must be simplified.
|
|
@@ -23,3 +23,8 @@ export declare const shouldShowExamples: (answerForms: readonly PerseusNumericIn
|
|
|
23
23
|
* `formExamples` forms from above.
|
|
24
24
|
*/
|
|
25
25
|
export declare const unionAnswerForms: (answerFormsList: ReadonlyArray<ReadonlyArray<PerseusNumericInputAnswerForm>>) => ReadonlyArray<PerseusNumericInputAnswerForm>;
|
|
26
|
+
/**
|
|
27
|
+
* Filter out the correct answers and map them to the answer forms
|
|
28
|
+
* so that we can generate the examples for the widget.
|
|
29
|
+
*/
|
|
30
|
+
export declare function normalizeCorrectAnswerForms(answers: PerseusNumericInputWidgetOptions["answers"]): ReadonlyArray<PerseusNumericInputAnswerForm>;
|
|
@@ -130,7 +130,6 @@ export declare class Plotter extends React.Component<Props, State> implements Wi
|
|
|
130
130
|
canScrollPage?: boolean;
|
|
131
131
|
editorChangeDelay?: number;
|
|
132
132
|
flags?: Record<"new-radio-widget", boolean>;
|
|
133
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
134
133
|
}> & {
|
|
135
134
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
136
135
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
export
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface IndicatorContent {
|
|
3
|
+
visible: string;
|
|
4
|
+
screenReader: string;
|
|
5
|
+
labelledBy?: string;
|
|
6
|
+
describedBy?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface IndicatorProps {
|
|
3
9
|
buttonRef: React.Ref<HTMLButtonElement>;
|
|
4
10
|
checked: boolean;
|
|
5
|
-
content:
|
|
11
|
+
content: IndicatorContent;
|
|
6
12
|
shape: "circle" | "square";
|
|
7
13
|
showCorrectness?: "correct" | "wrong";
|
|
8
14
|
updateChecked: (isChecked: boolean) => void;
|
|
9
|
-
}
|
|
15
|
+
}
|
|
10
16
|
declare const Indicator: (props: IndicatorProps) => React.JSX.Element;
|
|
11
17
|
export default Indicator;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import type { IndicatorContent } from "./choice-indicator.new";
|
|
2
3
|
export interface IndicatorProps {
|
|
3
4
|
checked: boolean;
|
|
4
5
|
children: React.ReactNode | React.ReactNode[];
|
|
5
|
-
indicatorContent:
|
|
6
|
+
indicatorContent: IndicatorContent;
|
|
6
7
|
isMultiSelect: boolean;
|
|
7
8
|
showCorrectness?: "correct" | "wrong";
|
|
8
9
|
updateChecked: (isChecked: boolean) => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import React from "react";
|
|
2
2
|
import type { ChoiceType } from "./multiple-choice-widget.new";
|
|
3
3
|
/**
|
|
4
4
|
* Props for the MultipleChoiceComponent
|
|
@@ -20,6 +20,18 @@ export interface MultipleChoiceComponentProps {
|
|
|
20
20
|
*
|
|
21
21
|
* Supports both radio button (single select) and checkbox (multiple select) modes.
|
|
22
22
|
*
|
|
23
|
+
* NOTE: This widget does not implement standard radio button or checkbox elements.
|
|
24
|
+
* The key difference is that the single-select (aka radio button) should
|
|
25
|
+
* allow the user to deselect a choice, whereas regular radio buttons
|
|
26
|
+
* can only be deselected by selecting another choice.
|
|
27
|
+
* Another difference is that keyboard navigation with single-select is
|
|
28
|
+
* done with the tab key, whereas regular radio buttons use the arrow keys,
|
|
29
|
+
* and the radio buttons are automatically selected when navigating to them.
|
|
30
|
+
* Since interactions with the single-select version of this widget don't
|
|
31
|
+
* follow the standard radio button behavior, we decided to use toggle
|
|
32
|
+
* button semantics for both versions of the widget,
|
|
33
|
+
* and implement additional ARIA attributes where needed.
|
|
34
|
+
*
|
|
23
35
|
* Created as part of the Radio Revitalization Project (LEMS-2933).
|
|
24
36
|
*/
|
|
25
37
|
declare const MultipleChoiceComponent: ({ choices, countChoices, multipleSelect, numCorrect, onChoiceChange, reviewMode, }: MultipleChoiceComponentProps) => React.ReactElement;
|
|
@@ -7,12 +7,16 @@ declare class RadioQuestionBuilder {
|
|
|
7
7
|
private hasNoneOfTheAbove?;
|
|
8
8
|
private multipleSelect?;
|
|
9
9
|
private randomize?;
|
|
10
|
+
private static?;
|
|
11
|
+
private numCorrect?;
|
|
10
12
|
build(): PerseusRenderer;
|
|
11
13
|
withContent(content: string): RadioQuestionBuilder;
|
|
12
14
|
withCountChoices(countChoices: boolean): RadioQuestionBuilder;
|
|
13
15
|
withHasNoneOfTheAbove(hasNoneOfTheAbove: boolean): RadioQuestionBuilder;
|
|
14
16
|
withMultipleSelect(multipleSelect: boolean): RadioQuestionBuilder;
|
|
15
17
|
withRandomize(randomize: boolean): RadioQuestionBuilder;
|
|
18
|
+
withStatic(isStatic: boolean): RadioQuestionBuilder;
|
|
19
|
+
withNumCorrect(numCorrect: number): RadioQuestionBuilder;
|
|
16
20
|
addChoice(content: string, options?: {
|
|
17
21
|
correct?: boolean;
|
|
18
22
|
rationale?: string;
|
|
@@ -78,7 +78,6 @@ declare class Radio extends RadioOld {
|
|
|
78
78
|
canScrollPage?: boolean;
|
|
79
79
|
editorChangeDelay?: number;
|
|
80
80
|
flags?: Record<"new-radio-widget", boolean>;
|
|
81
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
82
81
|
}> & {
|
|
83
82
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
84
83
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
|
@@ -85,7 +85,6 @@ declare class Table extends React.Component<Props> implements Widget {
|
|
|
85
85
|
canScrollPage?: boolean;
|
|
86
86
|
editorChangeDelay?: number;
|
|
87
87
|
flags?: Record<"new-radio-widget", boolean>;
|
|
88
|
-
onWidgetStartProps?: (widgets: import("@khanacademy/perseus-core").PerseusWidgetsMap) => void;
|
|
89
88
|
}> & {
|
|
90
89
|
baseElements: NonNullable<import("../..").APIOptions["baseElements"]>;
|
|
91
90
|
canScrollPage: NonNullable<import("../..").APIOptions["canScrollPage"]>;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Core Perseus API (includes renderers and widgets)",
|
|
4
4
|
"author": "Khan Academy",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "
|
|
6
|
+
"version": "67.0.0",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -42,15 +42,15 @@
|
|
|
42
42
|
"tiny-invariant": "1.3.1",
|
|
43
43
|
"uuid": "^10.0.0",
|
|
44
44
|
"@khanacademy/kas": "2.1.0",
|
|
45
|
-
"@khanacademy/keypad-context": "3.1.
|
|
46
|
-
"@khanacademy/kmath": "2.1.
|
|
47
|
-
"@khanacademy/math-input": "26.1.
|
|
48
|
-
"@khanacademy/perseus-core": "18.
|
|
49
|
-
"@khanacademy/perseus-linter": "4.2.
|
|
50
|
-
"@khanacademy/perseus-score": "7.6.
|
|
45
|
+
"@khanacademy/keypad-context": "3.1.5",
|
|
46
|
+
"@khanacademy/kmath": "2.1.5",
|
|
47
|
+
"@khanacademy/math-input": "26.1.6",
|
|
48
|
+
"@khanacademy/perseus-core": "18.8.0",
|
|
49
|
+
"@khanacademy/perseus-linter": "4.2.5",
|
|
50
|
+
"@khanacademy/perseus-score": "7.6.2",
|
|
51
51
|
"@khanacademy/perseus-utils": "2.1.0",
|
|
52
|
-
"@khanacademy/
|
|
53
|
-
"@khanacademy/
|
|
52
|
+
"@khanacademy/simple-markdown": "2.1.0",
|
|
53
|
+
"@khanacademy/pure-markdown": "2.2.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@khanacademy/wonder-blocks-banner": "4.2.14",
|