Skip to content

Conversation

@ivyolamit
Copy link
Contributor

@ivyolamit ivyolamit commented Dec 18, 2025

Summary:

Deprecate passage-related widgets (passage, passage-ref, passage-ref-target)

Pre-work related PRs/work:

Things done:

  1. created pre-work PRs that can be updated safely in preparation for deprecating the passage related widgets
  2. global search keyword passage and deleted references and css styles
  3. global search filename with keyword passage deleted passage related widgets and editors files
  4. global search strings used in passage related code and deleted unused translations
  5. added relevant comments to code to follow-up e.g. in radio component that has special handling for passage

To reviewer:

  • Since this is one of the oldest code, please do double check for any other logic/code/relevant repository that is missed from the keyword search

Issue: LEMS-3124

Test plan:

@ivyolamit ivyolamit self-assigned this Dec 18, 2025
@github-actions github-actions bot added the schema-change Attached to PRs when we detect Perseus Schema changes in it label Dec 18, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 18, 2025

🗄️ 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-01-08 20:46:34.075108374 +0000
+++ /home/runner/work/_temp/branch-compare/pr/schema.d.ts	2026-01-08 20:46:21.680214810 +0000
@@ -275,9 +275,6 @@
     "number-line": NumberLineWidget;
     "numeric-input": NumericInputWidget;
     orderer: OrdererWidget;
-    "passage-ref-target": RefTargetWidget;
-    "passage-ref": PassageRefWidget;
-    passage: PassageWidget;
     "phet-simulation": PhetSimulationWidget;
     "python-program": PythonProgramWidget;
     plotter: PlotterWidget;
@@ -285,6 +282,9 @@
     sorter: SorterWidget;
     table: TableWidget;
     video: VideoWidget;
+    "passage-ref-target": DeprecatedStandinWidget;
+    "passage-ref": DeprecatedStandinWidget;
+    passage: DeprecatedStandinWidget;
     "lights-puzzle": DeprecatedStandinWidget;
     sequence: DeprecatedStandinWidget;
     simulator: DeprecatedStandinWidget;
@@ -488,14 +488,6 @@
     "orderer",
     PerseusOrdererWidgetOptions
   >;
-  export type PassageRefWidget = WidgetOptions<
-    "passage-ref",
-    PerseusPassageRefWidgetOptions
-  >;
-  export type PassageWidget = WidgetOptions<
-    "passage",
-    PerseusPassageWidgetOptions
-  >;
   export type PhetSimulationWidget = WidgetOptions<
     "phet-simulation",
     PerseusPhetSimulationWidgetOptions
@@ -522,10 +514,6 @@
     "molecule-renderer",
     PerseusMoleculeRendererWidgetOptions
   >;
-  export type RefTargetWidget = WidgetOptions<
-    "passage-ref-target",
-    PerseusPassageRefTargetWidgetOptions
-  >;
   export type VideoWidget = WidgetOptions<"video", PerseusVideoWidgetOptions>;
   export type DeprecatedStandinWidget = WidgetOptions<
     "deprecated-standin",
@@ -1125,18 +1113,6 @@
     height: "normal" | "auto";
     layout: "horizontal" | "vertical";
   };
-  export type PerseusPassageWidgetOptions = {
-    footnotes: string;
-    passageText: string;
-    passageTitle: string;
-    showLineNumbers: boolean;
-    static: boolean;
-  };
-  export type PerseusPassageRefWidgetOptions = {
-    passageNumber: number;
-    referenceNumber: number;
-    summaryText?: string;
-  };
   export const plotterPlotTypes: readonly [
     "bar",
     "line",
@@ -1396,9 +1372,6 @@
     rotationAngle?: number;
     smiles?: string;
   };
-  export type PerseusPassageRefTargetWidgetOptions = {
-    content: string;
-  };
   export type PerseusWidgetOptions =
     | PerseusCategorizerWidgetOptions
     | PerseusCSProgramWidgetOptions
@@ -1422,9 +1395,6 @@
     | PerseusNumberLineWidgetOptions
     | PerseusNumericInputWidgetOptions
     | PerseusOrdererWidgetOptions
-    | PerseusPassageRefTargetWidgetOptions
-    | PerseusPassageRefWidgetOptions
-    | PerseusPassageWidgetOptions
     | PerseusPhetSimulationWidgetOptions
     | PerseusPlotterWidgetOptions
     | PerseusRadioWidgetOptions

@github-actions
Copy link
Contributor

github-actions bot commented Dec 18, 2025

🛠️ Item Splitting: 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/index.item-splitting.js /home/runner/work/_temp/branch-compare/pr/index.item-splitting.js
--- /home/runner/work/_temp/branch-compare/base/index.item-splitting.js	2026-01-08 20:47:07.935476980 +0000
+++ /home/runner/work/_temp/branch-compare/pr/index.item-splitting.js	2026-01-08 20:46:38.712296561 +0000
@@ -122,10 +122,6 @@
 
 function parseRenderer(rawValue,ctx){return parsePerseusRenderer(rawValue,ctx)}const largeToAuto=(height,ctx)=>{if(height==="large"){return ctx.success("auto")}return ctx.success(height)};const parseOrdererWidget=parseWidget(constant("orderer"),object({options:defaulted(array(parseRenderer),()=>[]),correctOptions:defaulted(array(parseRenderer),()=>[]),otherOptions:defaulted(array(parseRenderer),()=>[]),height:pipeParsers(enumeration("normal","auto","large")).then(largeToAuto).parser,layout:defaulted(enumeration("horizontal","vertical"),()=>"horizontal")}));
 
-const parsePassageRefWidget=parseWidget(constant("passage-ref"),object({passageNumber:number,referenceNumber:number,summaryText:optional(string)}));
-
-const parsePassageWidget=parseWidget(constant("passage"),object({footnotes:defaulted(string,()=>""),passageText:string,passageTitle:defaulted(string,()=>""),showLineNumbers:boolean,static:defaulted(boolean,()=>false)}));
-
 const parsePhetSimulationWidget=parseWidget(constant("phet-simulation"),object({url:string,description:string}));
 
 const parsePlotterWidget=parseWidget(constant("plotter"),object({labels:array(string),categories:array(string),type:enumeration(...plotterPlotTypes),maxY:number,scaleY:defaulted(number,()=>1),labelInterval:optional(nullable(number)),snapsPerLine:defaulted(number,()=>2),starting:array(number),correct:defaulted(array(number),()=>[]),picUrl:optional(nullable(string)),picSize:optional(nullable(number)),picBoxHeight:optional(nullable(number)),plotDimensions:defaulted(array(number),()=>[380,300])}));
@@ -144,7 +140,7 @@
 
 const parseStringToNonNegativeInt=(rawValue,ctx)=>{if(typeof rawValue!=="string"||!/^(0|[1-9][0-9]*)$/.test(rawValue)){return ctx.failure("a string representing a non-negative integer",rawValue)}return ctx.success(+rawValue)};const parseWidgetIdComponents=pair(string,parseStringToNonNegativeInt);
 
-const parseWidgetsMap=(rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx.failure("PerseusWidgetsMap",rawValue)}const widgetsMap={};for(const key of Object.keys(rawValue)){const entryResult=parseWidgetsMapEntry([key,rawValue[key]],widgetsMap,ctx.forSubtree(key));if(isFailure(entryResult)){return entryResult}}return ctx.success(widgetsMap)};const parseWidgetsMapEntry=([id,widget],widgetMap,ctx)=>{const idComponentsResult=parseWidgetIdComponents(id.split(" "),ctx.forSubtree("(widget ID)"));if(isFailure(idComponentsResult)){return idComponentsResult}const[type,n]=idComponentsResult.value;function parseAndAssign(key,parse){const widgetResult=parse(widget,ctx);if(isFailure(widgetResult)){return widgetResult}widgetMap[key]=widgetResult.value;return ctx.success(undefined)}switch(type){case "categorizer":return parseAndAssign(`categorizer ${n}`,parseCategorizerWidget);case "cs-program":return parseAndAssign(`cs-program ${n}`,parseCSProgramWidget);case "definition":return parseAndAssign(`definition ${n}`,parseDefinitionWidget);case "dropdown":return parseAndAssign(`dropdown ${n}`,parseDropdownWidget);case "explanation":return parseAndAssign(`explanation ${n}`,parseExplanationWidget);case "expression":return parseAndAssign(`expression ${n}`,parseExpressionWidget);case "free-response":return parseAndAssign(`free-response ${n}`,parseFreeResponseWidget);case "grapher":return parseAndAssign(`grapher ${n}`,parseGrapherWidget);case "group":return parseAndAssign(`group ${n}`,parseGroupWidget);case "graded-group":return parseAndAssign(`graded-group ${n}`,parseGradedGroupWidget);case "graded-group-set":return parseAndAssign(`graded-group-set ${n}`,parseGradedGroupSetWidget);case "iframe":return parseAndAssign(`iframe ${n}`,parseIframeWidget);case "image":return parseAndAssign(`image ${n}`,parseImageWidget);case "input-number":return parseAndAssign(`input-number ${n}`,parseInputNumberWidget);case "interaction":return parseAndAssign(`interaction ${n}`,parseInteractionWidget);case "interactive-graph":return parseAndAssign(`interactive-graph ${n}`,parseInteractiveGraphWidget);case "label-image":return parseAndAssign(`label-image ${n}`,parseLabelImageWidget);case "matcher":return parseAndAssign(`matcher ${n}`,parseMatcherWidget);case "matrix":return parseAndAssign(`matrix ${n}`,parseMatrixWidget);case "measurer":return parseAndAssign(`measurer ${n}`,parseMeasurerWidget);case "molecule-renderer":return parseAndAssign(`molecule-renderer ${n}`,parseMoleculeRendererWidget);case "number-line":return parseAndAssign(`number-line ${n}`,parseNumberLineWidget);case "numeric-input":return parseAndAssign(`numeric-input ${n}`,parseNumericInputWidget);case "orderer":return parseAndAssign(`orderer ${n}`,parseOrdererWidget);case "passage":return parseAndAssign(`passage ${n}`,parsePassageWidget);case "passage-ref":return parseAndAssign(`passage-ref ${n}`,parsePassageRefWidget);case "passage-ref-target":return parseAndAssign(`passage-ref-target ${n}`,any);case "phet-simulation":return parseAndAssign(`phet-simulation ${n}`,parsePhetSimulationWidget);case "plotter":return parseAndAssign(`plotter ${n}`,parsePlotterWidget);case "python-program":return parseAndAssign(`python-program ${n}`,parsePythonProgramWidget);case "radio":return parseAndAssign(`radio ${n}`,parseRadioWidget);case "sorter":return parseAndAssign(`sorter ${n}`,parseSorterWidget);case "table":return parseAndAssign(`table ${n}`,parseTableWidget);case "video":return parseAndAssign(`video ${n}`,parseVideoWidget);case "sequence":return parseAndAssign(`sequence ${n}`,parseDeprecatedWidget);case "lights-puzzle":return parseAndAssign(`lights-puzzle ${n}`,parseDeprecatedWidget);case "simulator":return parseAndAssign(`simulator ${n}`,parseDeprecatedWidget);case "transformer":return parseAndAssign(`transformer ${n}`,parseDeprecatedWidget);default:return parseAndAssign(`${type} ${n}`,parseWidget(constant(type),any))}};const parseDeprecatedWidget=parseWidget((_,ctx)=>ctx.success("deprecated-standin"),object({}));
+const parseWidgetsMap=(rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx.failure("PerseusWidgetsMap",rawValue)}const widgetsMap={};for(const key of Object.keys(rawValue)){const entryResult=parseWidgetsMapEntry([key,rawValue[key]],widgetsMap,ctx.forSubtree(key));if(isFailure(entryResult)){return entryResult}}return ctx.success(widgetsMap)};const parseWidgetsMapEntry=([id,widget],widgetMap,ctx)=>{const idComponentsResult=parseWidgetIdComponents(id.split(" "),ctx.forSubtree("(widget ID)"));if(isFailure(idComponentsResult)){return idComponentsResult}const[type,n]=idComponentsResult.value;function parseAndAssign(key,parse){const widgetResult=parse(widget,ctx);if(isFailure(widgetResult)){return widgetResult}widgetMap[key]=widgetResult.value;return ctx.success(undefined)}switch(type){case "categorizer":return parseAndAssign(`categorizer ${n}`,parseCategorizerWidget);case "cs-program":return parseAndAssign(`cs-program ${n}`,parseCSProgramWidget);case "definition":return parseAndAssign(`definition ${n}`,parseDefinitionWidget);case "dropdown":return parseAndAssign(`dropdown ${n}`,parseDropdownWidget);case "explanation":return parseAndAssign(`explanation ${n}`,parseExplanationWidget);case "expression":return parseAndAssign(`expression ${n}`,parseExpressionWidget);case "free-response":return parseAndAssign(`free-response ${n}`,parseFreeResponseWidget);case "grapher":return parseAndAssign(`grapher ${n}`,parseGrapherWidget);case "group":return parseAndAssign(`group ${n}`,parseGroupWidget);case "graded-group":return parseAndAssign(`graded-group ${n}`,parseGradedGroupWidget);case "graded-group-set":return parseAndAssign(`graded-group-set ${n}`,parseGradedGroupSetWidget);case "iframe":return parseAndAssign(`iframe ${n}`,parseIframeWidget);case "image":return parseAndAssign(`image ${n}`,parseImageWidget);case "input-number":return parseAndAssign(`input-number ${n}`,parseInputNumberWidget);case "interaction":return parseAndAssign(`interaction ${n}`,parseInteractionWidget);case "interactive-graph":return parseAndAssign(`interactive-graph ${n}`,parseInteractiveGraphWidget);case "label-image":return parseAndAssign(`label-image ${n}`,parseLabelImageWidget);case "matcher":return parseAndAssign(`matcher ${n}`,parseMatcherWidget);case "matrix":return parseAndAssign(`matrix ${n}`,parseMatrixWidget);case "measurer":return parseAndAssign(`measurer ${n}`,parseMeasurerWidget);case "molecule-renderer":return parseAndAssign(`molecule-renderer ${n}`,parseMoleculeRendererWidget);case "number-line":return parseAndAssign(`number-line ${n}`,parseNumberLineWidget);case "numeric-input":return parseAndAssign(`numeric-input ${n}`,parseNumericInputWidget);case "orderer":return parseAndAssign(`orderer ${n}`,parseOrdererWidget);case "phet-simulation":return parseAndAssign(`phet-simulation ${n}`,parsePhetSimulationWidget);case "plotter":return parseAndAssign(`plotter ${n}`,parsePlotterWidget);case "python-program":return parseAndAssign(`python-program ${n}`,parsePythonProgramWidget);case "radio":return parseAndAssign(`radio ${n}`,parseRadioWidget);case "sorter":return parseAndAssign(`sorter ${n}`,parseSorterWidget);case "table":return parseAndAssign(`table ${n}`,parseTableWidget);case "video":return parseAndAssign(`video ${n}`,parseVideoWidget);case "sequence":return parseAndAssign(`sequence ${n}`,parseDeprecatedWidget);case "lights-puzzle":return parseAndAssign(`lights-puzzle ${n}`,parseDeprecatedWidget);case "simulator":return parseAndAssign(`simulator ${n}`,parseDeprecatedWidget);case "transformer":return parseAndAssign(`transformer ${n}`,parseDeprecatedWidget);case "passage":return parseAndAssign(`passage ${n}`,parseDeprecatedWidget);case "passage-ref":return parseAndAssign(`passage-ref ${n}`,parseDeprecatedWidget);case "passage-ref-target":return parseAndAssign(`passage-ref-target ${n}`,parseDeprecatedWidget);default:return parseAndAssign(`${type} ${n}`,parseWidget(constant(type),any))}};const parseDeprecatedWidget=parseWidget((_,ctx)=>ctx.success("deprecated-standin"),object({}));
 
 const parsePerseusRenderer=defaulted(object({content:defaulted(string,()=>""),widgets:defaulted((rawVal,ctx)=>parseWidgetsMap(rawVal,ctx),()=>({})),images:parseImages,metadata:any}),()=>({content:"",widgets:{},images:{}}));
 

@github-actions
Copy link
Contributor

github-actions bot commented Dec 18, 2025

Size Change: -10.4 kB (-2.08%)

Total Size: 489 kB

Filename Size Change
packages/perseus-core/dist/es/index.item-splitting.js 13.1 kB -88 B (-0.67%)
packages/perseus-core/dist/es/index.js 25.8 kB -318 B (-1.22%)
packages/perseus-editor/dist/es/index.js 96.7 kB -836 B (-0.86%)
packages/perseus/dist/es/index.js 191 kB -8.79 kB (-4.39%)
packages/perseus/dist/es/strings.js 7.44 kB -338 B (-4.35%)
ℹ️ View Unchanged
Filename Size
packages/kas/dist/es/index.js 20.8 kB
packages/keypad-context/dist/es/index.js 1 kB
packages/kmath/dist/es/index.js 5.98 kB
packages/math-input/dist/es/index.js 98.5 kB
packages/math-input/dist/es/strings.js 1.61 kB
packages/perseus-linter/dist/es/index.js 8.65 kB
packages/perseus-score/dist/es/index.js 9.25 kB
packages/perseus-utils/dist/es/index.js 403 B
packages/pure-markdown/dist/es/index.js 1.39 kB
packages/simple-markdown/dist/es/index.js 6.72 kB

compressed-size-action

@github-actions
Copy link
Contributor

github-actions bot commented Dec 18, 2025

npm Snapshot: Published

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

Example:

pnpm add @khanacademy/perseus@PR3147

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

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

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

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

@ivyolamit ivyolamit force-pushed the LEMS-3124/deprecate-passage-related-widgets-2 branch from 7ec2040 to 92d0fb2 Compare January 6, 2026 17:55
let nextPassageRefId = 1;
const widgets: Record<string, any> = {};

// passage-ref is already deprecated see LEMS-3124
Copy link
Contributor Author

Choose a reason for hiding this comment

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

will revisit this in a different PR (if needed) to check if it's safe to remove this. This is also related to the comment in renderer.tsx regarding alwaysUpdate

@ivyolamit
Copy link
Contributor Author

@ivyolamit ivyolamit marked this pull request as ready for review January 6, 2026 21:30
Copy link
Contributor

@anakaren-rojas anakaren-rojas left a comment

Choose a reason for hiding this comment

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

Overall LGTM, left a non-blocking question

Copy link
Member

@catandthemachines catandthemachines left a comment

Choose a reason for hiding this comment

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

Added a comment for a fix or a potential follow-up bug for some logic. Excited to see this guy gone!! 🥳

Base automatically changed from deploy/perseus-january-2026 to main January 8, 2026 20:15
@ivyolamit ivyolamit force-pushed the LEMS-3124/deprecate-passage-related-widgets-2 branch from 432fe55 to 3e7c2e1 Compare January 8, 2026 20:45
@ivyolamit
Copy link
Contributor Author

👆🏼 rebased from latest master (after deploy/perseus-january-2026 branch was merged in main)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

item-splitting-change olc-5.0.07850 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.

4 participants