Skip to content

[AbsoluteValue] PR4 - scoring#3351

Merged
SonicScrewdriver merged 7 commits intomainfrom
LEMS-3347/absolute-value-pr4
Mar 23, 2026
Merged

[AbsoluteValue] PR4 - scoring#3351
SonicScrewdriver merged 7 commits intomainfrom
LEMS-3347/absolute-value-pr4

Conversation

@handeyeco
Copy link
Copy Markdown
Contributor

@handeyeco handeyeco commented Mar 13, 2026

Everything is AI

Summary:

This is part of a series of PRs as defined in the first PR:

Initial implementation experiment is Perseus#3304.

Issue: LEMS-3347


Add scoring for absolute-value graph type

Implements scoring for the absolute-value graph, isolated from rendering.
Depends on PR 1 for the type definitions. Can be developed in parallel with
PR 3.

Changes:

  • kmath/src/coefficients.ts — Adds getAbsoluteValueCoefficients(coords), a
    canonical coefficient extractor returning [m, h, v] as an
    AbsoluteValueCoefficient tuple (or undefined if the two points share the
    same x). Lives in kmath alongside getSinusoidCoefficients and
    getQuadraticCoefficients for reuse by the scorer without taking a
    dependency on the rendering package.
  • kmath/src/index.ts — Exports the new AbsoluteValueCoefficient type.
  • data-schema.ts — Adds AbsoluteValueGraphCorrect (type: "absolute-value",
    coords: [Coord, Coord]) and includes it in the PerseusGraphCorrectType
    union, which is required for the rubric type to accept absolute-value
    correct answers.
  • score-interactive-graph.ts — Adds an else if branch for "absolute-value":
    extracts [m, h, v] coefficients from both the user input and rubric
    coords, then uses approximateDeepEqual to compare them. No canonical
    normalization is needed — the vertex uniquely defines position and m
    uniquely defines shape and orientation.
  • score-interactive-graph.test.ts — Six tests covering: undefined guess
    (invalid), missing coords (invalid), wrong slope (incorrect), correct
    answer, symmetry (p2 on either arm of the same V scores correctly), and
    downward-opening vs upward-opening mismatch (incorrect).

@handeyeco handeyeco self-assigned this Mar 13, 2026
@handeyeco handeyeco changed the title Implement PR 4 [AbsoluteValue] PR4 - scoring Mar 13, 2026
@github-actions github-actions Bot added the schema-change Attached to PRs when we detect Perseus Schema changes in it label Mar 13, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 13, 2026

🗄️ Schema Change: Changes Detected ⚠️

Usually this means you need to update the Go parser
that Content Platform maintains!!!

See this list of post-mortems for more information.

This PR contains critical changes to Perseus. Please review
the changes and note that you may need to coordinate
deployment of these changes with other teams at Khan Academy.

diff --unified /home/runner/work/_temp/branch-compare/base/schema.d.ts /home/runner/work/_temp/branch-compare/pr/schema.d.ts
--- /home/runner/work/_temp/branch-compare/base/schema.d.ts	2026-03-23 20:38:21.122849324 +0000
+++ /home/runner/work/_temp/branch-compare/pr/schema.d.ts	2026-03-23 20:38:08.655547806 +0000
@@ -728,6 +728,10 @@
     coords?: CollinearTuple | null;
     startCoords?: CollinearTuple;
   };
+  type AbsoluteValueGraphCorrect = {
+    type: "absolute-value";
+    coords: [Coord, Coord];
+  };
   type AngleGraphCorrect = {
     type: "angle";
     allowReflexAngles: boolean;
@@ -780,6 +784,7 @@
     coords: CollinearTuple;
   };
   export type PerseusGraphCorrectType =
+    | AbsoluteValueGraphCorrect
     | AngleGraphCorrect
     | CircleGraphCorrect
     | LinearGraphCorrect

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 13, 2026

🛠️ Item Splitting: No Changes ✅

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 13, 2026

Size Change: +113 B (+0.02%)

Total Size: 490 kB

Filename Size Change
packages/kmath/dist/es/index.js 6.08 kB +56 B (+0.93%)
packages/perseus-score/dist/es/index.js 9.5 kB +57 B (+0.6%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 20.5 kB
packages/keypad-context/dist/es/index.js 1 kB
packages/math-input/dist/es/index.js 98.5 kB
packages/math-input/dist/es/strings.js 1.61 kB
packages/perseus-core/dist/es/index.item-splitting.js 11.9 kB
packages/perseus-core/dist/es/index.js 25 kB
packages/perseus-editor/dist/es/index.js 100 kB
packages/perseus-linter/dist/es/index.js 8.82 kB
packages/perseus-utils/dist/es/index.js 403 B
packages/perseus/dist/es/index.js 190 kB
packages/perseus/dist/es/strings.js 7.84 kB
packages/pure-markdown/dist/es/index.js 1.39 kB
packages/simple-markdown/dist/es/index.js 6.71 kB

compressed-size-action

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 13, 2026

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (2c8e427) and published it to npm. You
can install it using the tag PR3351.

Example:

pnpm add @khanacademy/perseus@PR3351

If you are working in Khan Academy's frontend, you can run the below command.

./dev/tools/bump_perseus_version.ts -t PR3351

If you are working in Khan Academy's webapp, you can run the below command.

./dev/tools/bump_perseus_version.js -t PR3351

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — straightforward extension of existing graph scoring patterns to support absolute-value graphs.

Extended reasoning...

Overview

This PR adds absolute-value graph support to the interactive graph scoring system across 5 files: a new getAbsoluteValueCoefficients function in kmath, its type export, an AbsoluteValueGraphCorrect type in the data schema union, scoring logic in score-interactive-graph.ts, and comprehensive tests.

Security risks

None. This is pure math/scoring logic with no I/O, auth, or external interactions.

Level of scrutiny

Low scrutiny warranted. The changes mechanically follow the exact same pattern used for quadratic and sinusoid graph scoring. The coefficient computation is straightforward: Math.abs(rise/run) with sign determined by whether p2 is above or below the vertex, which correctly handles points on either arm of the V. The scoring branch mirrors the quadratic branch (compute coefficients, compare with approximateDeepEqual).

Other factors

  • Good test coverage: 6 tests covering invalid input (undefined guess, missing coords), wrong answer, exact match, symmetry (point on other arm), and direction mismatch (upward vs downward).
  • The new AbsoluteValueGraphCorrect type is correctly added to the PerseusGraphCorrectType union, which is the type used by PerseusInteractiveGraphRubric.correct.
  • The PerseusGraphTypeAbsoluteValue type already existed in the data schema, so this PR just adds the scoring support.
  • PR description is empty, but the code is self-explanatory given the established patterns.

Copy link
Copy Markdown
Contributor

@ivyolamit ivyolamit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes looks logical to me 👍🏼

Comment thread packages/kmath/src/coefficients.ts
Comment thread packages/perseus-score/src/widgets/interactive-graph/score-interactive-graph.ts Outdated
SonicScrewdriver pushed a commit that referenced this pull request Mar 23, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- ▶️ [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

Author: handeyeco

Reviewers: claude[bot], handeyeco, ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3346
SonicScrewdriver pushed a commit that referenced this pull request Mar 23, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- ▶️ [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Add type definitions and schema for absolute-value graph type

  Adds the foundational type scaffolding needed to support an absolute-value
  graph in the Interactive Graph widget. This is a pure additive change — no
  runtime behavior is altered and all existing tests continue to pass.

  Changes:

  - data-schema.ts — Adds PerseusGraphTypeAbsoluteValue (type:
  "absolute-value", coords, startCoords) and includes it in the
  PerseusGraphType union.
  - interactive-graph-widget.ts — Registers
  parsePerseusGraphTypeAbsoluteValue with parsePerseusGraphType so the new
  type round-trips through the parser.
  - types.ts — Adds AbsoluteValueGraphState (mirrors SinusoidGraphState:
  vertex + arm point) and adds it to the InteractiveGraphState union.
  - interactive-graph-ai-utils.ts — Adds AbsoluteValueGraphOptions and
  AbsoluteValueUserInput types and wires them into getGraphOptionsForProps /
  getUserInput to satisfy exhaustiveness checks.
  - Stubs out "absolute-value" cases with throw new Error("Not implemented")
  in initializeGraphState, mafsStateToInteractiveGraph, renderGraphElements,
  and getEquationString to keep TypeScript exhaustiveness errors clear until
  subsequent PRs fill in the implementation.
  - Adds "absolute-value" to the mergeGraphs switch and
  shouldShowStartCoordsUI in the editor to satisfy exhaustiveness checks
  there as well.

Author: handeyeco

Reviewers: claude[bot], handeyeco, ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ✅ 10 checks were successful, ⏭️  1 check has been skipped

Pull Request URL: #3348
SonicScrewdriver pushed a commit that referenced this pull request Mar 23, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- ▶️ [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Implements the full state pipeline for the absolute-value graph:
  initialization, reducer transitions, serialization, and test
  infrastructure. Depends on PR 1. No rendering yet — the renderGraphElements
   stub from PR 1 remains in place.

  Changes:

  - initialize-graph-state.ts — Replaces the PR 1 stub with a real
  initializeGraphState case. Adds getAbsoluteValueCoords(graph, range, step):
   returns graph.coords if set, then graph.startCoords, then normalized
  defaults [[0.5, 0.5], [0.75, 0.75]] (same pattern as sinusoid/quadratic).
  Note: currently unexported — PR 5 exports it for editor use.
  - interactive-graph-action.ts — Adds actions.absoluteValue.movePoint by
  reusing the shared movePoint action factory (same approach as sinusoid).
  - interactive-graph-reducer.ts — Adds a case "absolute-value" in
  doMovePoint that snaps/bounds the destination, then rejects the move if the
   two points would share the same x-coordinate (slope would be undefined).
  - interactive-graph-state.ts — Adds a branch in getGradableGraph() that
  serializes AbsoluteValueGraphState back to PerseusGraphTypeAbsoluteValue
  for scoring.
  - mafs-state-to-interactive-graph.ts — Replaces the PR 1 stub with a real
  case that maps state coords back to the graph type for live interaction
  (e.g. equation display).
  - interactive-graph-question-builder.ts — Adds withAbsoluteValue(options?)
  builder method and AbsoluteValueGraphConfig class so test fixtures can
  construct absolute-value questions.

Author: handeyeco

Reviewers: claude[bot], ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3349
@SonicScrewdriver SonicScrewdriver force-pushed the LEMS-3347/absolute-value-pr3 branch from 16655f3 to 442bfca Compare March 23, 2026 20:16
SonicScrewdriver pushed a commit that referenced this pull request Mar 23, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- ▶️ [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Implement rendering and accessibility for absolute-value graph type

  Adds the visual component, screen reader strings, equation display, and
  unit tests for the absolute-value graph. Depends on PR 1 + PR 2. After this
   PR the graph is fully interactive and renderable.

  Changes:

  - graphs/absolute-value.tsx (new file) — Implements
  renderAbsoluteValueGraph, the AbsoluteValueGraph component, and two
  exported helpers:
    - getAbsoluteValueCoefficients(coords) — extracts {m, h, v} from the two
  control points, returning undefined if both share the same x. Uses a
  coeffRef to cache the last valid result for transient mid-drag states.
    - getAbsoluteValueKeyboardConstraint(coords, snapStep, pointIndex) —
  keyboard movement constraint that skips any position where the moved point
  would share an x-coordinate with the other point.
    - Renders a single <Plot.OfX y={(x) => m * Math.abs(x - h) + v} /> and
  two <MovablePoint> components with appropriate aria labels and an
  <SRDescInSVG> description.
  - graphs/absolute-value.test.tsx (new file) — Unit tests covering
  coefficient extraction (upward/downward V, varying slopes, same-x returns
  undefined, left/right arm equivalence), keyboard constraint skip behaviour,
   and all screen reader strings (graph label, point labels, description,
  interactive elements text).
  - strings.ts — Adds five new screen reader strings (srAbsoluteValueGraph,
  srAbsoluteValueVertexPoint, srAbsoluteValueSecondPoint,
  srAbsoluteValueDescription, srAbsoluteValueInteractiveElements) with i18n
  context comments, English defaults, and mockStrings implementations.
  - mafs-graph.tsx — Replaces the PR 1 throw stub with
  renderAbsoluteValueGraph.
  - interactive-graph.tsx — Implements getAbsoluteValueEquationString
  (returns y = m.toFixed(3)|x - h.toFixed(3)| + v.toFixed(3)) and wires it
  into getEquationString.
  - types.ts — Exports AbsoluteValueGraphState (previously unexported),
  required by the new component file.

Author: handeyeco

Reviewers: claude[bot], handeyeco, ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ✅ 10 checks were successful, ⏭️  1 check has been skipped

Pull Request URL: #3350
@SonicScrewdriver SonicScrewdriver changed the base branch from LEMS-3347/absolute-value-pr3 to main March 23, 2026 20:23
An error occurred while trying to automatically change base from LEMS-3347/absolute-value-pr3 to main March 23, 2026 20:23
@SonicScrewdriver SonicScrewdriver force-pushed the LEMS-3347/absolute-value-pr4 branch from 0b6dc63 to 2c8e427 Compare March 23, 2026 20:37
@SonicScrewdriver SonicScrewdriver merged commit 005e13d into main Mar 23, 2026
11 checks passed
SonicScrewdriver pushed a commit that referenced this pull request Mar 23, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- ▶️ [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Add editor support for absolute-value graph type

  Surfaces the absolute-value graph in the content creator UI. Depends on PR
  1 + PR 2 (for getAbsoluteValueCoords). No new components needed — the
  two-point shape fits existing patterns.

  Changes:

  - initialize-graph-state.ts — Exports getAbsoluteValueCoords (previously
  file-private) so the editor can import it via @khanacademy/perseus.
  - perseus/src/index.ts — Re-exports getAbsoluteValueCoords alongside the
  other getXxxCoords helpers.
  - graph-type-selector.tsx — Adds <OptionItem value="absolute-value"
  label="Absolute value" /> to the answer type dropdown.
  - start-coords/types.ts — Adds {type: "absolute-value"} to
  GraphTypesThatHaveStartCoords so the StartCoords type includes its
  startCoords field.
  - start-coords-settings.tsx — Adds case "absolute-value" using
  getAbsoluteValueCoords + StartCoordsPoint (the two-point shape maps
  directly onto the existing generic point coordinate UI).
  - start-coords/util.ts — Adds case "absolute-value" in
  getDefaultGraphStartCoords so the "Use default start coordinates" reset
  button returns the correct defaults.

Author: handeyeco

Reviewers: claude[bot], ivyolamit, handeyeco, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3352
SonicScrewdriver pushed a commit that referenced this pull request Mar 23, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- ▶️ [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

Author: handeyeco

Reviewers: claude[bot], ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: SonicScrewdriver

Checks: ✅ 10 checks were successful, ⏭️  1 check has been skipped

Pull Request URL: #3380
ivyolamit added a commit that referenced this pull request Mar 24, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @khanacademy/perseus-editor@30.0.0

### Major Changes

- [#3332](#3332)
[`604b3a6c25`](604b3a6)
Thanks [@benchristel](https://github.com/benchristel)! - The `options`
parameter of the `serialize` method of `EditorPage` and `Editor` has
been removed.


- [#3386](#3386)
[`7e76fbbc2f`](7e76fbb)
Thanks [@benchristel](https://github.com/benchristel)! - The `serialize`
methods of classes in `@khanacademy/perseus-editor` no longer use arrow
function syntax. Callers should not unbind them from the class instance.

Additionally, the `Editor` component no longer accepts a `replace` prop
(used for hints), and its serialize method no longer returns `replace`.
The `replace` prop was only used in `serialize`. Users of the `Editor`
component should manage hints' `replace` setting themselves.

### Minor Changes

- [#3395](#3395)
[`97223334ea`](9722333)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of Editor support for Exponential Graph


- [#3352](#3352)
[`b681e00a4f`](b681e00)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add editor support
for AbsoluteValue


- [#3348](#3348)
[`b1557c2a73`](b1557c2)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add schema for
AbsoluteValue graph


- [#3345](#3345)
[`dde985f3b5`](dde985f)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent type
definitions, this is the initial implementation for supporting Tangent
graph in Interactive Graph


- [#3358](#3358)
[`8c503171b1`](8c50317)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
option in the Interactive Graph Editor


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- [#3396](#3396)
[`35fa9133db`](35fa913)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Add a
linter warning for images with no size


- [#3390](#3390)
[`d22c50dc2a`](d22c50d)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Make
the 125 character alt text warning less aggressive


- [#3372](#3372)
[`3cdb09813d`](3cdb098)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (UX) |
Upscale Graphies within Explore Image Modal


- [#3391](#3391)
[`2f285ee161`](2f285ee)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Add
character counter to alt text field


- [#3374](#3374)
[`cd73c99ba3`](cd73c99)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Remove incorrect
usage of the feature flag setting in one of the test

- Updated dependencies
\[[`f18c0d9b6f`](f18c0d9),
[`a022e751d6`](a022e75),
[`35fa9133db`](35fa913),
[`54db3fd4bd`](54db3fd),
[`97223334ea`](9722333),
[`027a5edbda`](027a5ed),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`3cdb09813d`](3cdb098),
[`afcff9f96f`](afcff9f),
[`75f184e5a7`](75f184e),
[`4b2a7c85db`](4b2a7c8),
[`5e1acd01f8`](5e1acd0),
[`b681e00a4f`](b681e00),
[`d99f1c0259`](d99f1c0),
[`54eee35d65`](54eee35),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`56e7dbe9a2`](56e7dbe),
[`85f9cd46fc`](85f9cd4),
[`8c503171b1`](8c50317),
[`3aca3dcdf4`](3aca3dc),
[`9f29bc7161`](9f29bc7),
[`7034844845`](7034844),
[`8aa0a77886`](8aa0a77),
[`003aca7612`](003aca7)]:
    -   @khanacademy/perseus-linter@4.9.0
    -   @khanacademy/perseus-score@8.4.0
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/perseus@76.1.0
    -   @khanacademy/kmath@2.3.0
    -   @khanacademy/keypad-context@3.2.40
    -   @khanacademy/math-input@26.4.10

## @khanacademy/kmath@2.3.0

### Minor Changes

- [#3351](#3351)
[`005e13d784`](005e13d)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add scoring for
AbsoluteValue


- [#3347](#3347)
[`d99f1c0259`](d99f1c0)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add the tangent
math utilities to kmath for supporting Tangent graph in Interactive
Graph


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0

## @khanacademy/perseus@76.1.0

### Minor Changes

- [#3350](#3350)
[`75f184e5a7`](75f184e)
Thanks [@handeyeco](https://github.com/handeyeco)! - Implement
AbsoluteValue rendering


- [#3354](#3354)
[`4b2a7c85db`](4b2a7c8)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Created the tangent
graph visual component, add Storybook coverage, SR strings, and equation
string for supporting Tangent graph in Interactive Graph


- [#3353](#3353)
[`5e1acd01f8`](5e1acd0)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
state management and reducer for supporting Tangent graph in Interactive
Graph


- [#3352](#3352)
[`b681e00a4f`](b681e00)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add editor support
for AbsoluteValue


- [#3348](#3348)
[`b1557c2a73`](b1557c2)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add schema for
AbsoluteValue graph


- [#3345](#3345)
[`dde985f3b5`](dde985f)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent type
definitions, this is the initial implementation for supporting Tangent
graph in Interactive Graph


- [#3349](#3349)
[`56e7dbe9a2`](56e7dbe)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add state
management for AbsoluteValue


- [#3377](#3377)
[`85f9cd46fc`](85f9cd4)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of state management logic for new Exponential graph


- [#3358](#3358)
[`8c503171b1`](8c50317)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
option in the Interactive Graph Editor


- [#3393](#3393)
[`9f29bc7161`](9f29bc7)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Rendering logic for new Exponential Graph


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- [#3329](#3329)
[`027a5edbda`](027a5ed)
Thanks [@Myranae](https://github.com/Myranae)! - Fix image bug by
batching setState calls in setupGraphie


- [#3372](#3372)
[`3cdb09813d`](3cdb098)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (UX) |
Upscale Graphies within Explore Image Modal


- [#3365](#3365)
[`afcff9f96f`](afcff9f)
Thanks [@jeremywiebe](https://github.com/jeremywiebe)! - Improve
ordering of Props type for `Renderer` component


- [#3367](#3367)
[`54eee35d65`](54eee35)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (UX) | Show
image in explore modal even when size is undefined


- [#3407](#3407)
[`3aca3dcdf4`](3aca3dc)
Thanks [@Myranae](https://github.com/Myranae)! - Improve a11y with
graded group set


- [#3385](#3385)
[`003aca7612`](003aca7)
Thanks [@Myranae](https://github.com/Myranae)! - Small fix to prevent
pip duplication in Graded Group Sets

- Updated dependencies
\[[`f18c0d9b6f`](f18c0d9),
[`a022e751d6`](a022e75),
[`35fa9133db`](35fa913),
[`54db3fd4bd`](54db3fd),
[`97223334ea`](9722333),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`d99f1c0259`](d99f1c0),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`7034844845`](7034844),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-linter@4.9.0
    -   @khanacademy/perseus-score@8.4.0
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/kmath@2.3.0
    -   @khanacademy/keypad-context@3.2.40
    -   @khanacademy/math-input@26.4.10

## @khanacademy/perseus-core@23.7.0

### Minor Changes

- [#3405](#3405)
[`54db3fd4bd`](54db3fd)
Thanks [@benchristel](https://github.com/benchristel)! -
`@khanacademy/perseus-core` now exports a
`removeOrphanedWidgetsFromPerseusItem` function, which removes
unreferenced widgets from a `PerseusItem`'s question and hints.


- [#3351](#3351)
[`005e13d784`](005e13d)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add scoring for
AbsoluteValue


- [#3348](#3348)
[`b1557c2a73`](b1557c2)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add schema for
AbsoluteValue graph


- [#3345](#3345)
[`dde985f3b5`](dde985f)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent type
definitions, this is the initial implementation for supporting Tangent
graph in Interactive Graph


- [#3376](#3376)
[`8aa0a77886`](8aa0a77)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Creation of new Types, Schema, and Kmath utilities for Exponential Graph

### Patch Changes

- [#3357](#3357)
[`ae0538d0a7`](ae0538d)
Thanks [@jeremywiebe](https://github.com/jeremywiebe)! - Improve code
documentation for all data-schema and user-input types

## @khanacademy/perseus-linter@4.9.0

### Minor Changes

- [#3381](#3381)
[`f18c0d9b6f`](f18c0d9)
Thanks [@anakaren-rojas](https://github.com/anakaren-rojas)! - Adds new
linters for parsed objects


- [#3395](#3395)
[`97223334ea`](9722333)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of Editor support for Exponential Graph

### Patch Changes

- [#3396](#3396)
[`35fa9133db`](35fa913)
Thanks [@nishasy](https://github.com/nishasy)! - [Image] | (CX) | Add a
linter warning for images with no size

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`d99f1c0259`](d99f1c0),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/kmath@2.3.0

## @khanacademy/perseus-score@8.4.0

### Minor Changes

- [#3356](#3356)
[`a022e751d6`](a022e75)
Thanks [@ivyolamit](https://github.com/ivyolamit)! - Add tangent graph
scoring to support the Tangent graph in Interactive Graph


- [#3351](#3351)
[`005e13d784`](005e13d)
Thanks [@handeyeco](https://github.com/handeyeco)! - Add scoring for
AbsoluteValue


- [#3394](#3394)
[`7034844845`](7034844)
Thanks [@SonicScrewdriver](https://github.com/SonicScrewdriver)! -
Implementation of new scoring logic for Exponential Graph

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`d99f1c0259`](d99f1c0),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/kmath@2.3.0

## @khanacademy/keypad-context@3.2.40

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0

## @khanacademy/math-input@26.4.10

### Patch Changes

- Updated dependencies
\[[`54db3fd4bd`](54db3fd),
[`ae0538d0a7`](ae0538d),
[`005e13d784`](005e13d),
[`b1557c2a73`](b1557c2),
[`dde985f3b5`](dde985f),
[`8aa0a77886`](8aa0a77)]:
    -   @khanacademy/perseus-core@23.7.0
    -   @khanacademy/keypad-context@3.2.40
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- ▶️ [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

Author: handeyeco

Reviewers: claude[bot], handeyeco, ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3346
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- ▶️ [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Add type definitions and schema for absolute-value graph type

  Adds the foundational type scaffolding needed to support an absolute-value
  graph in the Interactive Graph widget. This is a pure additive change — no
  runtime behavior is altered and all existing tests continue to pass.

  Changes:

  - data-schema.ts — Adds PerseusGraphTypeAbsoluteValue (type:
  "absolute-value", coords, startCoords) and includes it in the
  PerseusGraphType union.
  - interactive-graph-widget.ts — Registers
  parsePerseusGraphTypeAbsoluteValue with parsePerseusGraphType so the new
  type round-trips through the parser.
  - types.ts — Adds AbsoluteValueGraphState (mirrors SinusoidGraphState:
  vertex + arm point) and adds it to the InteractiveGraphState union.
  - interactive-graph-ai-utils.ts — Adds AbsoluteValueGraphOptions and
  AbsoluteValueUserInput types and wires them into getGraphOptionsForProps /
  getUserInput to satisfy exhaustiveness checks.
  - Stubs out "absolute-value" cases with throw new Error("Not implemented")
  in initializeGraphState, mafsStateToInteractiveGraph, renderGraphElements,
  and getEquationString to keep TypeScript exhaustiveness errors clear until
  subsequent PRs fill in the implementation.
  - Adds "absolute-value" to the mergeGraphs switch and
  shouldShowStartCoordsUI in the editor to satisfy exhaustiveness checks
  there as well.

Author: handeyeco

Reviewers: claude[bot], handeyeco, ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ✅ 10 checks were successful, ⏭️  1 check has been skipped

Pull Request URL: #3348
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- ▶️ [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Implements the full state pipeline for the absolute-value graph:
  initialization, reducer transitions, serialization, and test
  infrastructure. Depends on PR 1. No rendering yet — the renderGraphElements
   stub from PR 1 remains in place.

  Changes:

  - initialize-graph-state.ts — Replaces the PR 1 stub with a real
  initializeGraphState case. Adds getAbsoluteValueCoords(graph, range, step):
   returns graph.coords if set, then graph.startCoords, then normalized
  defaults [[0.5, 0.5], [0.75, 0.75]] (same pattern as sinusoid/quadratic).
  Note: currently unexported — PR 5 exports it for editor use.
  - interactive-graph-action.ts — Adds actions.absoluteValue.movePoint by
  reusing the shared movePoint action factory (same approach as sinusoid).
  - interactive-graph-reducer.ts — Adds a case "absolute-value" in
  doMovePoint that snaps/bounds the destination, then rejects the move if the
   two points would share the same x-coordinate (slope would be undefined).
  - interactive-graph-state.ts — Adds a branch in getGradableGraph() that
  serializes AbsoluteValueGraphState back to PerseusGraphTypeAbsoluteValue
  for scoring.
  - mafs-state-to-interactive-graph.ts — Replaces the PR 1 stub with a real
  case that maps state coords back to the graph type for live interaction
  (e.g. equation display).
  - interactive-graph-question-builder.ts — Adds withAbsoluteValue(options?)
  builder method and AbsoluteValueGraphConfig class so test fixtures can
  construct absolute-value questions.

Author: handeyeco

Reviewers: claude[bot], ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3349
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- ▶️ [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Implement rendering and accessibility for absolute-value graph type

  Adds the visual component, screen reader strings, equation display, and
  unit tests for the absolute-value graph. Depends on PR 1 + PR 2. After this
   PR the graph is fully interactive and renderable.

  Changes:

  - graphs/absolute-value.tsx (new file) — Implements
  renderAbsoluteValueGraph, the AbsoluteValueGraph component, and two
  exported helpers:
    - getAbsoluteValueCoefficients(coords) — extracts {m, h, v} from the two
  control points, returning undefined if both share the same x. Uses a
  coeffRef to cache the last valid result for transient mid-drag states.
    - getAbsoluteValueKeyboardConstraint(coords, snapStep, pointIndex) —
  keyboard movement constraint that skips any position where the moved point
  would share an x-coordinate with the other point.
    - Renders a single <Plot.OfX y={(x) => m * Math.abs(x - h) + v} /> and
  two <MovablePoint> components with appropriate aria labels and an
  <SRDescInSVG> description.
  - graphs/absolute-value.test.tsx (new file) — Unit tests covering
  coefficient extraction (upward/downward V, varying slopes, same-x returns
  undefined, left/right arm equivalence), keyboard constraint skip behaviour,
   and all screen reader strings (graph label, point labels, description,
  interactive elements text).
  - strings.ts — Adds five new screen reader strings (srAbsoluteValueGraph,
  srAbsoluteValueVertexPoint, srAbsoluteValueSecondPoint,
  srAbsoluteValueDescription, srAbsoluteValueInteractiveElements) with i18n
  context comments, English defaults, and mockStrings implementations.
  - mafs-graph.tsx — Replaces the PR 1 throw stub with
  renderAbsoluteValueGraph.
  - interactive-graph.tsx — Implements getAbsoluteValueEquationString
  (returns y = m.toFixed(3)|x - h.toFixed(3)| + v.toFixed(3)) and wires it
  into getEquationString.
  - types.ts — Exports AbsoluteValueGraphState (previously unexported),
  required by the new component file.

Author: handeyeco

Reviewers: claude[bot], handeyeco, ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ✅ 10 checks were successful, ⏭️  1 check has been skipped

Pull Request URL: #3350
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- ▶️ [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

Add scoring for absolute-value graph type

  Implements scoring for the absolute-value graph, isolated from rendering.
  Depends on PR 1 for the type definitions. Can be developed in parallel with
   PR 3.

  Changes:

  - kmath/src/coefficients.ts — Adds getAbsoluteValueCoefficients(coords), a
  canonical coefficient extractor returning [m, h, v] as an
  AbsoluteValueCoefficient tuple (or undefined if the two points share the
  same x). Lives in kmath alongside getSinusoidCoefficients and
  getQuadraticCoefficients for reuse by the scorer without taking a
  dependency on the rendering package.
  - kmath/src/index.ts — Exports the new AbsoluteValueCoefficient type.
  - data-schema.ts — Adds AbsoluteValueGraphCorrect (type: "absolute-value",
  coords: [Coord, Coord]) and includes it in the PerseusGraphCorrectType
  union, which is required for the rubric type to accept absolute-value
  correct answers.
  - score-interactive-graph.ts — Adds an else if branch for "absolute-value":
   extracts [m, h, v] coefficients from both the user input and rubric
  coords, then uses approximateDeepEqual to compare them. No canonical
  normalization is needed — the vertex uniquely defines position and m
  uniquely defines shape and orientation.
  - score-interactive-graph.test.ts — Six tests covering: undefined guess
  (invalid), missing coords (invalid), wrong slope (incorrect), correct
  answer, symmetry (p2 on either arm of the same V scores correctly), and
  downward-opening vs upward-opening mismatch (incorrect).

Author: handeyeco

Reviewers: claude[bot], ivyolamit, handeyeco, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3351
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- ▶️ [Perseus#3352: PR5 - editor support](#3352)
- [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

---

  Add editor support for absolute-value graph type

  Surfaces the absolute-value graph in the content creator UI. Depends on PR
  1 + PR 2 (for getAbsoluteValueCoords). No new components needed — the
  two-point shape fits existing patterns.

  Changes:

  - initialize-graph-state.ts — Exports getAbsoluteValueCoords (previously
  file-private) so the editor can import it via @khanacademy/perseus.
  - perseus/src/index.ts — Re-exports getAbsoluteValueCoords alongside the
  other getXxxCoords helpers.
  - graph-type-selector.tsx — Adds <OptionItem value="absolute-value"
  label="Absolute value" /> to the answer type dropdown.
  - start-coords/types.ts — Adds {type: "absolute-value"} to
  GraphTypesThatHaveStartCoords so the StartCoords type includes its
  startCoords field.
  - start-coords-settings.tsx — Adds case "absolute-value" using
  getAbsoluteValueCoords + StartCoordsPoint (the two-point shape maps
  directly onto the existing generic point coordinate UI).
  - start-coords/util.ts — Adds case "absolute-value" in
  getDefaultGraphStartCoords so the "Use default start coordinates" reset
  button returns the correct defaults.

Author: handeyeco

Reviewers: claude[bot], ivyolamit, handeyeco, SonicScrewdriver

Required Reviewers:

Approved By: ivyolamit

Checks: ⏭️  1 check has been skipped, ✅ 10 checks were successful

Pull Request URL: #3352
catandthemachines pushed a commit that referenced this pull request Apr 1, 2026
Everything is AI

## Summary:

This is part of a series of PRs as defined in the first PR:
- [Perseus#3346: PR implementation guide](#3346)
- [Perseus#3348: PR1 - type definitions and schema](#3348)
- [Perseus#3349: PR2 - state management](#3349)
- [Perseus#3350: PR3 - rendering & accessibility](#3350)
- [Perseus#3351: PR4 - scoring](#3351)
- [Perseus#3352: PR5 - editor support](#3352)
- ▶️ [Perseus#3380: Convert todo into a long-term doc](#3380)

Initial implementation experiment is [Perseus#3304](#3304).

Issue: LEMS-3347

Author: handeyeco

Reviewers: claude[bot], ivyolamit, SonicScrewdriver

Required Reviewers:

Approved By: SonicScrewdriver

Checks: ✅ 10 checks were successful, ⏭️  1 check has been skipped

Pull Request URL: #3380
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

olc-5.0.97b91 schema-change Attached to PRs when we detect Perseus Schema changes in it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants