@khanacademy/perseus 23.1.0 → 23.3.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/index.d.ts CHANGED
@@ -51,7 +51,7 @@ export { default as KhanMath } from "./util/math";
51
51
  export { default as LoadingContext } from "./loading-context";
52
52
  export { default as mediaQueries } from "./styles/media-queries";
53
53
  export { default as PerseusMarkdown } from "./perseus-markdown";
54
- export { PerseusExpressionAnswerFormConsidered, plotterPlotTypes, ItemExtras, lockedFigureColors, } from "./perseus-types";
54
+ export { PerseusExpressionAnswerFormConsidered, plotterPlotTypes, ItemExtras, lockedFigureColors, lockedEllipseFillStyles, } from "./perseus-types";
55
55
  export { traverse } from "./traversal";
56
56
  export { isItemRenderableByVersion } from "./renderability";
57
57
  export { violatingWidgets } from "./a11y";
@@ -87,7 +87,7 @@ export type { ILogger, LogErrorOptions, ErrorKind } from "./logging/log";
87
87
  export type { ServerItemRenderer as ServerItemRendererComponent } from "./server-item-renderer";
88
88
  export type { Alignment, APIOptions, APIOptionsWithDefaults, ChangeHandler, DeviceType, DomInsertCheckFn, EditorMode, FocusPath, Hint, ImageDict, ImageUploader, JiptLabelStore, JiptRenderer, PerseusDependencies, PerseusScore, Version, VideoData, VideoKind, WidgetDict, WidgetExports, } from "./types";
89
89
  export type { ParsedValue } from "./util";
90
- export type { LockedFigure, LockedFigureColor, LockedFigureType, LockedPointType, LockedLineType, LockedCircleType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
90
+ export type { LockedFigure, LockedFigureColor, LockedFigureType, LockedPointType, LockedLineType, LockedEllipseType, LockedEllipseFillType, LockedVectorType, PerseusGraphType, PerseusAnswerArea, PerseusExpressionWidgetOptions, Range, Size, MathFormat, InputNumberWidget, // TODO(jeremy): remove?
91
91
  PerseusImageBackground, PerseusInputNumberWidgetOptions, PerseusInteractiveGraphWidgetOptions, PerseusItem, PerseusPlotterWidgetOptions, PerseusPythonProgramWidgetOptions, PerseusRadioWidgetOptions, PerseusExampleWidgetOptions, PerseusExampleGraphieWidgetOptions, PerseusSimpleMarkdownTesterWidgetOptions, PerseusRenderer, PerseusWidget, PerseusWidgetsMap, } from "./perseus-types";
92
92
  export type { Coord } from "./interactive2/types";
93
93
  export type { MarkerType } from "./widgets/label-image/types";
package/dist/index.js CHANGED
@@ -10928,6 +10928,33 @@ function intersects(ab, cd) {
10928
10928
  }
10929
10929
  return false;
10930
10930
  }
10931
+
10932
+ // Whether any two sides of a polygon intersect each other
10933
+ function polygonSidesIntersect(vertices) {
10934
+ for (let i = 0; i < vertices.length; i++) {
10935
+ for (let k = i + 1; k < vertices.length; k++) {
10936
+ // If any two vertices are the same point, sides overlap
10937
+ if (kmath.point.equal(vertices[i], vertices[k])) {
10938
+ return true;
10939
+ }
10940
+
10941
+ // Find the other end of the sides starting at vertices i and k
10942
+ const iNext = (i + 1) % vertices.length;
10943
+ const kNext = (k + 1) % vertices.length;
10944
+
10945
+ // Adjacent sides always intersect (at the vertex); skip those
10946
+ if (iNext === k || kNext === i) {
10947
+ continue;
10948
+ }
10949
+ const side1 = [vertices[i], vertices[iNext]];
10950
+ const side2 = [vertices[k], vertices[kNext]];
10951
+ if (intersects(side1, side2)) {
10952
+ return true;
10953
+ }
10954
+ }
10955
+ }
10956
+ return false;
10957
+ }
10931
10958
  function vector(a, b) {
10932
10959
  return ___default["default"].map(___default["default"].zip(a, b), function (pair) {
10933
10960
  return pair[0] - pair[1];
@@ -28922,25 +28949,27 @@ const lockedFigureColors = {
28922
28949
  pink: "#B25071",
28923
28950
  red: "#D92916"
28924
28951
  };
28952
+ const lockedEllipseFillStyles = {
28953
+ none: 0,
28954
+ solid: 1,
28955
+ translucent: 0.4
28956
+ };
28925
28957
  const plotterPlotTypes = ["bar", "line", "pic", "histogram", "dotplot"];
28926
28958
 
28927
- const LockedCircle = props => {
28959
+ const LockedEllipse = props => {
28928
28960
  const {
28929
28961
  center,
28930
28962
  radius,
28963
+ angle,
28931
28964
  color,
28932
28965
  fillStyle,
28933
28966
  strokeStyle
28934
28967
  } = props;
28935
- const fill = {
28936
- none: 0,
28937
- solid: 1,
28938
- translucent: 0.4
28939
- };
28940
- return /*#__PURE__*/React__namespace.createElement(mafs.Circle, {
28968
+ return /*#__PURE__*/React__namespace.createElement(mafs.Ellipse, {
28941
28969
  center: center,
28942
28970
  radius: radius,
28943
- fillOpacity: fill[fillStyle],
28971
+ angle: angle,
28972
+ fillOpacity: lockedEllipseFillStyles[fillStyle],
28944
28973
  strokeStyle: strokeStyle,
28945
28974
  color: lockedFigureColors[color]
28946
28975
  });
@@ -29199,6 +29228,21 @@ const LockedPoint = props => {
29199
29228
  });
29200
29229
  };
29201
29230
 
29231
+ const LockedVector = props => {
29232
+ const {
29233
+ color,
29234
+ points
29235
+ } = props;
29236
+ const [tail, tip] = points;
29237
+ return /*#__PURE__*/React__namespace.createElement("g", {
29238
+ className: "locked-vector"
29239
+ }, /*#__PURE__*/React__namespace.createElement(Vector, {
29240
+ tail: tail,
29241
+ tip: tip,
29242
+ color: lockedFigureColors[color]
29243
+ }));
29244
+ };
29245
+
29202
29246
  const GraphLockedLayer = props => {
29203
29247
  const {
29204
29248
  lockedFigures
@@ -29214,9 +29258,13 @@ const GraphLockedLayer = props => {
29214
29258
  key: `line-${index}`,
29215
29259
  range: props.range
29216
29260
  }, figure));
29217
- case "circle":
29218
- return /*#__PURE__*/React__namespace.createElement(LockedCircle, _extends({
29219
- key: `circle-${index}`
29261
+ case "ellipse":
29262
+ return /*#__PURE__*/React__namespace.createElement(LockedEllipse, _extends({
29263
+ key: `ellipse-${index}`
29264
+ }, figure));
29265
+ case "vector":
29266
+ return /*#__PURE__*/React__namespace.createElement(LockedVector, _extends({
29267
+ key: `vector-${index}`
29220
29268
  }, figure));
29221
29269
  default:
29222
29270
  /**
@@ -29345,7 +29393,8 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
29345
29393
  point,
29346
29394
  color = wonderBlocksTokens.color.blue,
29347
29395
  dragging,
29348
- focusBehavior
29396
+ focusBehavior,
29397
+ cursor
29349
29398
  } = props;
29350
29399
 
29351
29400
  // WB Tooltip requires a color name for the background color.
@@ -29380,7 +29429,8 @@ const MovablePointView = /*#__PURE__*/React.forwardRef((props, hitboxRef) => {
29380
29429
  ref: hitboxRef,
29381
29430
  className: pointClasses,
29382
29431
  style: {
29383
- "--movable-point-color": color
29432
+ "--movable-point-color": color,
29433
+ cursor
29384
29434
  },
29385
29435
  "data-testid": "movable-point",
29386
29436
  tabIndex: tabIndex(focusBehavior)
@@ -29628,7 +29678,33 @@ const SegmentGraph = props => {
29628
29678
  })));
29629
29679
  };
29630
29680
 
29631
- const LinearGraph = props => {
29681
+ const LinearGraph = (props, key) => {
29682
+ const {
29683
+ dispatch
29684
+ } = props;
29685
+ const {
29686
+ coords: line
29687
+ } = props.graphState;
29688
+
29689
+ // Linear graphs only have one line
29690
+ // (LEMS-2050): Update the reducer so that we have a separate action for moving one line
29691
+ // and another action for moving multiple lines
29692
+ return /*#__PURE__*/React__namespace.createElement(MovableLine, {
29693
+ key: 0,
29694
+ points: line,
29695
+ onMoveLine: delta => {
29696
+ dispatch(moveLine(0, delta));
29697
+ },
29698
+ extend: {
29699
+ start: true,
29700
+ end: true
29701
+ },
29702
+ onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, 0)),
29703
+ color: "var(--movable-line-stroke-color)"
29704
+ });
29705
+ };
29706
+
29707
+ const LinearSystemGraph = props => {
29632
29708
  const {
29633
29709
  dispatch
29634
29710
  } = props;
@@ -29648,7 +29724,7 @@ const LinearGraph = props => {
29648
29724
  },
29649
29725
  onMovePoint: (endpointIndex, destination) => dispatch(moveControlPoint(endpointIndex, destination, i)),
29650
29726
  color: colors[i]
29651
- })));
29727
+ })), ";");
29652
29728
  };
29653
29729
 
29654
29730
  const RayGraph = props => {
@@ -29656,14 +29732,14 @@ const RayGraph = props => {
29656
29732
  dispatch
29657
29733
  } = props;
29658
29734
  const {
29659
- coords: lines
29735
+ coords: line
29660
29736
  } = props.graphState;
29661
29737
  const handleMoveLine = delta => dispatch(moveLine(0, delta));
29662
29738
  const handleMovePoint = (pointIndex, newPoint) => dispatch(moveControlPoint(pointIndex, newPoint, 0));
29663
- return /*#__PURE__*/React__namespace.createElement(MovableLine
29664
- // a ray only has one line
29665
- , {
29666
- points: lines[0],
29739
+
29740
+ // Ray graphs only have one line
29741
+ return /*#__PURE__*/React__namespace.createElement(MovableLine, {
29742
+ points: line,
29667
29743
  onMoveLine: handleMoveLine,
29668
29744
  onMovePoint: handleMovePoint,
29669
29745
  extend: {
@@ -29850,6 +29926,7 @@ const StyledMovablePoint = props => {
29850
29926
  const {
29851
29927
  point,
29852
29928
  onMove,
29929
+ cursor,
29853
29930
  color = wonderBlocksTokens.color.blue
29854
29931
  } = props;
29855
29932
  const {
@@ -29868,7 +29945,8 @@ const StyledMovablePoint = props => {
29868
29945
  focusBehavior: {
29869
29946
  type: "uncontrolled",
29870
29947
  tabIndex: 0
29871
- }
29948
+ },
29949
+ cursor: cursor
29872
29950
  });
29873
29951
  };
29874
29952
 
@@ -29974,26 +30052,42 @@ function initializeGraphState(params) {
29974
30052
  return {
29975
30053
  ...shared,
29976
30054
  type: "segment",
29977
- coords: getDefaultSegments({
30055
+ coords: getSegmentCoords({
29978
30056
  graph,
29979
30057
  step,
29980
30058
  range
29981
30059
  })
29982
30060
  };
29983
30061
  case "linear":
29984
- case "linear-system":
30062
+ return {
30063
+ ...shared,
30064
+ type: graph.type,
30065
+ coords: getLineCoords({
30066
+ graph,
30067
+ range,
30068
+ step
30069
+ })
30070
+ };
29985
30071
  case "ray":
29986
30072
  return {
29987
30073
  ...shared,
29988
30074
  type: graph.type,
29989
- // Linear and ray graphs have a single tuple of points, while a
29990
- // linear system has two tuples of points.
29991
30075
  coords: getLineCoords({
29992
30076
  graph,
29993
30077
  range,
29994
30078
  step
29995
30079
  })
29996
30080
  };
30081
+ case "linear-system":
30082
+ return {
30083
+ ...shared,
30084
+ type: graph.type,
30085
+ coords: getLinearSystemCoords({
30086
+ graph,
30087
+ range,
30088
+ step
30089
+ })
30090
+ };
29997
30091
  case "polygon":
29998
30092
  return {
29999
30093
  ...shared,
@@ -30020,8 +30114,7 @@ function initializeGraphState(params) {
30020
30114
  return {
30021
30115
  ...shared,
30022
30116
  type: graph.type,
30023
- center: [0, 0],
30024
- radiusPoint: [1, 0]
30117
+ ...getCircleCoords(graph)
30025
30118
  };
30026
30119
  case "quadratic":
30027
30120
  return {
@@ -30105,13 +30198,13 @@ function getGradableGraph(state, initialGraph) {
30105
30198
  if (state.type === "linear" && initialGraph.type === "linear") {
30106
30199
  return {
30107
30200
  ...initialGraph,
30108
- coords: state.coords[0]
30201
+ coords: state.coords
30109
30202
  };
30110
30203
  }
30111
30204
  if (state.type === "ray" && initialGraph.type === "ray") {
30112
30205
  return {
30113
30206
  ...initialGraph,
30114
- coords: state.coords[0]
30207
+ coords: state.coords
30115
30208
  };
30116
30209
  }
30117
30210
  if (state.type === "polygon" && initialGraph.type === "polygon") {
@@ -30147,12 +30240,15 @@ function getGradableGraph(state, initialGraph) {
30147
30240
  }
30148
30241
  throw new Error("Mafs is not yet implemented for graph type: " + initialGraph.type);
30149
30242
  }
30150
- const getDefaultSegments = _ref2 => {
30243
+ const getSegmentCoords = _ref2 => {
30151
30244
  let {
30152
30245
  graph,
30153
30246
  range,
30154
30247
  step
30155
30248
  } = _ref2;
30249
+ if (graph.coords) {
30250
+ return graph.coords;
30251
+ }
30156
30252
  const ys = n => {
30157
30253
  switch (n) {
30158
30254
  case 2:
@@ -30184,23 +30280,28 @@ const getLineCoords = _ref3 => {
30184
30280
  range,
30185
30281
  step
30186
30282
  } = _ref3;
30187
- // Return two lines for a linear system, one for a ray or linear
30188
- switch (graph.type) {
30189
- case "linear-system":
30190
- return defaultLinearCoords.map(points => normalizePoints(range, step, points));
30191
- case "linear":
30192
- case "ray":
30193
- return [normalizePoints(range, step, defaultLinearCoords[0])];
30194
- default:
30195
- throw new wonderStuffCore.UnreachableCaseError(graph);
30283
+ if (graph.coords) {
30284
+ return graph.coords;
30196
30285
  }
30286
+ return normalizePoints(range, step, defaultLinearCoords[0]);
30197
30287
  };
30198
- const getPolygonCoords = _ref4 => {
30288
+ const getLinearSystemCoords = _ref4 => {
30199
30289
  let {
30200
30290
  graph,
30201
30291
  range,
30202
30292
  step
30203
30293
  } = _ref4;
30294
+ if (graph.coords) {
30295
+ return graph.coords;
30296
+ }
30297
+ return defaultLinearCoords.map(points => normalizePoints(range, step, points));
30298
+ };
30299
+ const getPolygonCoords = _ref5 => {
30300
+ let {
30301
+ graph,
30302
+ range,
30303
+ step
30304
+ } = _ref5;
30204
30305
  let coords = graph.coords?.slice();
30205
30306
  if (coords) {
30206
30307
  return coords;
@@ -30225,14 +30326,31 @@ const getPolygonCoords = _ref4 => {
30225
30326
  return coords;
30226
30327
  };
30227
30328
  const getSinusoidCoords = (graph, range, step) => {
30329
+ if (graph.coords) {
30330
+ return [graph.coords[0], graph.coords[1]];
30331
+ }
30228
30332
  let coords = [[0.5, 0.5], [0.65, 0.6]];
30229
30333
  coords = normalizePoints(range, step, coords, true);
30230
30334
  return coords;
30231
30335
  };
30232
30336
  const getQuadraticCoords = (graph, range, step) => {
30233
- let coords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
30234
- coords = normalizePoints(range, step, coords, true);
30235
- return coords;
30337
+ if (graph.coords) {
30338
+ return graph.coords;
30339
+ }
30340
+ const defaultCoords = [[0.25, 0.75], [0.5, 0.25], [0.75, 0.75]];
30341
+ return normalizePoints(range, step, defaultCoords, true);
30342
+ };
30343
+ const getCircleCoords = (graph, range, step) => {
30344
+ if (graph.center != null && graph.radius != null) {
30345
+ return {
30346
+ center: graph.center,
30347
+ radiusPoint: mafs.vec.add(graph.center, [graph.radius, 0])
30348
+ };
30349
+ }
30350
+ return {
30351
+ center: [0, 0],
30352
+ radiusPoint: [1, 0]
30353
+ };
30236
30354
  };
30237
30355
 
30238
30356
  /**
@@ -30262,6 +30380,7 @@ function CircleGraph(props) {
30262
30380
  onMove: newCenter => dispatch(moveCenter(newCenter))
30263
30381
  }), /*#__PURE__*/React__namespace.createElement(StyledMovablePoint, {
30264
30382
  point: radiusPoint,
30383
+ cursor: "ew-resize",
30265
30384
  onMove: newRadiusPoint => {
30266
30385
  dispatch(moveRadiusPoint(newRadiusPoint));
30267
30386
  }
@@ -31570,15 +31689,9 @@ function interactiveGraphReducer(state, action) {
31570
31689
  }
31571
31690
  }
31572
31691
  function doMoveControlPoint(state, action) {
31573
- const {
31574
- snapStep,
31575
- range
31576
- } = state;
31577
31692
  switch (state.type) {
31578
31693
  case "segment":
31579
- case "linear":
31580
31694
  case "linear-system":
31581
- case "ray":
31582
31695
  {
31583
31696
  const newCoords = updateAtIndex({
31584
31697
  array: state.coords,
@@ -31586,11 +31699,7 @@ function doMoveControlPoint(state, action) {
31586
31699
  update: tuple => setAtIndex({
31587
31700
  array: tuple,
31588
31701
  index: action.pointIndex,
31589
- newValue: snap(snapStep, bound$1({
31590
- snapStep,
31591
- range,
31592
- point: action.destination
31593
- }))
31702
+ newValue: boundAndSnapToGrid(action.destination, state)
31594
31703
  })
31595
31704
  });
31596
31705
  const coordsToCheck = newCoords[action.itemIndex];
@@ -31603,6 +31712,20 @@ function doMoveControlPoint(state, action) {
31603
31712
  coords: newCoords
31604
31713
  };
31605
31714
  }
31715
+ case "linear":
31716
+ case "ray":
31717
+ {
31718
+ const newCoords = setAtIndex({
31719
+ array: state.coords,
31720
+ index: action.pointIndex,
31721
+ newValue: boundAndSnapToGrid(action.destination, state)
31722
+ });
31723
+ return {
31724
+ ...state,
31725
+ hasBeenInteractedWith: true,
31726
+ coords: newCoords
31727
+ };
31728
+ }
31606
31729
  case "circle":
31607
31730
  throw new Error("FIXME implement circle reducer");
31608
31731
  case "point":
@@ -31614,6 +31737,9 @@ function doMoveControlPoint(state, action) {
31614
31737
  throw new wonderStuffCore.UnreachableCaseError(state);
31615
31738
  }
31616
31739
  }
31740
+
31741
+ // (LEMS-2050): Update the reducer so that we have a separate action for moving one line
31742
+ // and another action for moving multiple lines
31617
31743
  function doMoveLine(state, action) {
31618
31744
  const {
31619
31745
  snapStep,
@@ -31621,9 +31747,7 @@ function doMoveLine(state, action) {
31621
31747
  } = state;
31622
31748
  switch (state.type) {
31623
31749
  case "segment":
31624
- case "linear":
31625
31750
  case "linear-system":
31626
- case "ray":
31627
31751
  {
31628
31752
  if (action.itemIndex === undefined) {
31629
31753
  throw new Error("Please provide index of line to move");
@@ -31649,6 +31773,22 @@ function doMoveLine(state, action) {
31649
31773
  coords: newCoords
31650
31774
  };
31651
31775
  }
31776
+ case "linear":
31777
+ case "ray":
31778
+ {
31779
+ const currentLine = state.coords;
31780
+ const change = getChange(currentLine, action.delta, {
31781
+ snapStep,
31782
+ range
31783
+ });
31784
+ const newLine = [snap(snapStep, mafs.vec.add(currentLine[0], change)), snap(snapStep, mafs.vec.add(currentLine[1], change))];
31785
+ return {
31786
+ ...state,
31787
+ type: state.type,
31788
+ hasBeenInteractedWith: true,
31789
+ coords: newLine
31790
+ };
31791
+ }
31652
31792
  default:
31653
31793
  // The MoveLine action doesn't make sense for other graph types;
31654
31794
  // ignore it if it somehow happens
@@ -31682,6 +31822,21 @@ function doMoveAll(state, action) {
31682
31822
  function doMovePoint(state, action) {
31683
31823
  switch (state.type) {
31684
31824
  case "polygon":
31825
+ const newCoords = setAtIndex({
31826
+ array: state.coords,
31827
+ index: action.index,
31828
+ newValue: boundAndSnapToGrid(action.destination, state)
31829
+ });
31830
+
31831
+ // Reject the move if it would cause the sides of the polygon to cross
31832
+ if (polygonSidesIntersect(newCoords)) {
31833
+ return state;
31834
+ }
31835
+ return {
31836
+ ...state,
31837
+ hasBeenInteractedWith: true,
31838
+ coords: newCoords
31839
+ };
31685
31840
  case "point":
31686
31841
  {
31687
31842
  return {
@@ -31690,11 +31845,7 @@ function doMovePoint(state, action) {
31690
31845
  coords: setAtIndex({
31691
31846
  array: state.coords,
31692
31847
  index: action.index,
31693
- newValue: snap(state.snapStep, bound$1({
31694
- snapStep: state.snapStep,
31695
- range: state.range,
31696
- point: action.destination
31697
- }))
31848
+ newValue: boundAndSnapToGrid(action.destination, state)
31698
31849
  })
31699
31850
  };
31700
31851
  }
@@ -31714,11 +31865,7 @@ function doMovePoint(state, action) {
31714
31865
  coords: setAtIndex({
31715
31866
  array: state.coords,
31716
31867
  index: action.index,
31717
- newValue: snap(state.snapStep, bound$1({
31718
- snapStep: state.snapStep,
31719
- range: state.range,
31720
- point: destination
31721
- }))
31868
+ newValue: boundAndSnapToGrid(destination, state)
31722
31869
  })
31723
31870
  };
31724
31871
  }
@@ -31730,11 +31877,7 @@ function doMovePoint(state, action) {
31730
31877
  coords: setAtIndex({
31731
31878
  array: state.coords,
31732
31879
  index: action.index,
31733
- newValue: snap(state.snapStep, bound$1({
31734
- snapStep: state.snapStep,
31735
- range: state.range,
31736
- point: action.destination
31737
- }))
31880
+ newValue: boundAndSnapToGrid(action.destination, state)
31738
31881
  })
31739
31882
  };
31740
31883
  }
@@ -31843,14 +31986,26 @@ const getChange = (coords, delta, constraintOpts) => {
31843
31986
  const dy = clamp(deltaY, minYMove, maxYMove);
31844
31987
  return [dx, dy];
31845
31988
  };
31989
+ function boundAndSnapToGrid(point, _ref) {
31990
+ let {
31991
+ snapStep,
31992
+ range
31993
+ } = _ref;
31994
+ return snap(snapStep, bound$1({
31995
+ snapStep,
31996
+ range,
31997
+ point
31998
+ }));
31999
+ }
32000
+
31846
32001
  // Returns the closest point to the given `point` that is within the graph
31847
32002
  // bounds given in `state`.
31848
- function bound$1(_ref) {
32003
+ function bound$1(_ref2) {
31849
32004
  let {
31850
32005
  snapStep,
31851
32006
  range,
31852
32007
  point
31853
- } = _ref;
32008
+ } = _ref2;
31854
32009
  const [requestedX, requestedY] = point;
31855
32010
  const [snapX, snapY] = snapStep;
31856
32011
  const [[minX, maxX], [minY, maxY]] = range;
@@ -31858,12 +32013,12 @@ function bound$1(_ref) {
31858
32013
  }
31859
32014
 
31860
32015
  // Returns the vector from the given point to the top-right corner of the graph
31861
- function maxMove(_ref2) {
32016
+ function maxMove(_ref3) {
31862
32017
  let {
31863
32018
  snapStep,
31864
32019
  range,
31865
32020
  point
31866
- } = _ref2;
32021
+ } = _ref3;
31867
32022
  const topRight = bound$1({
31868
32023
  snapStep,
31869
32024
  range,
@@ -31874,12 +32029,12 @@ function maxMove(_ref2) {
31874
32029
 
31875
32030
  // Returns the vector from the given point to the bottom-left corner of the
31876
32031
  // graph
31877
- function minMove(_ref3) {
32032
+ function minMove(_ref4) {
31878
32033
  let {
31879
32034
  snapStep,
31880
32035
  range,
31881
32036
  point
31882
- } = _ref3;
32037
+ } = _ref4;
31883
32038
  const bottomLeft = bound$1({
31884
32039
  snapStep,
31885
32040
  range,
@@ -31935,8 +32090,12 @@ const renderGraph = props => {
31935
32090
  graphState: state,
31936
32091
  dispatch: dispatch
31937
32092
  });
31938
- case "linear":
31939
32093
  case "linear-system":
32094
+ return /*#__PURE__*/React__namespace.createElement(LinearSystemGraph, {
32095
+ graphState: state,
32096
+ dispatch: dispatch
32097
+ });
32098
+ case "linear":
31940
32099
  return /*#__PURE__*/React__namespace.createElement(LinearGraph, {
31941
32100
  graphState: state,
31942
32101
  dispatch: dispatch
@@ -44184,7 +44343,7 @@ const init = function (options) {
44184
44343
 
44185
44344
  // This file is processed by a Rollup plugin (replace) to inject the production
44186
44345
  const libName = "@khanacademy/perseus";
44187
- const libVersion = "23.1.0";
44346
+ const libVersion = "23.3.0";
44188
44347
  perseusCore.addLibraryVersionToPerseusDebug(libName, libVersion);
44189
44348
 
44190
44349
  const apiVersion = {
@@ -47284,6 +47443,7 @@ exports.itemDataVersion = itemDataVersion;
47284
47443
  exports.itemToTree = itemToTree;
47285
47444
  exports.itemVersion = ItemVersion;
47286
47445
  exports.libVersion = libVersion;
47446
+ exports.lockedEllipseFillStyles = lockedEllipseFillStyles;
47287
47447
  exports.lockedFigureColors = lockedFigureColors;
47288
47448
  exports.mediaQueries = mediaQueries;
47289
47449
  exports.parsePerseusItem = parsePerseusItem;