Skip to content

Conversation

@ivyolamit
Copy link
Contributor

Summary:

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

Issue: LEMS-3124

Test plan:

…ecate passage-related widgets (passage, passage-ref, passage-ref-target)
…assage-related widgets (passage, passage-ref, passage-ref-target)
@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

🗄️ Schema Change: Changes Detected ⚠️

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	2025-12-18 00:16:39.371092370 +0000
+++ /home/runner/work/_temp/branch-compare/pr/schema.d.ts	2025-12-18 00:16:27.560999661 +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;
@@ -479,14 +479,6 @@
     "orderer",
     PerseusOrdererWidgetOptions
   >;
-  export type PassageRefWidget = WidgetOptions<
-    "passage-ref",
-    PerseusPassageRefWidgetOptions
-  >;
-  export type PassageWidget = WidgetOptions<
-    "passage",
-    PerseusPassageWidgetOptions
-  >;
   export type PhetSimulationWidget = WidgetOptions<
     "phet-simulation",
     PerseusPhetSimulationWidgetOptions
@@ -513,10 +505,6 @@
     "molecule-renderer",
     PerseusMoleculeRendererWidgetOptions
   >;
-  export type RefTargetWidget = WidgetOptions<
-    "passage-ref-target",
-    PerseusPassageRefTargetWidgetOptions
-  >;
   export type VideoWidget = WidgetOptions<"video", PerseusVideoWidgetOptions>;
   export type DeprecatedStandinWidget = WidgetOptions<
     "deprecated-standin",
@@ -1116,18 +1104,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",
@@ -1387,9 +1363,6 @@
     rotationAngle?: number;
     smiles?: string;
   };
-  export type PerseusPassageRefTargetWidgetOptions = {
-    content: string;
-  };
   export type PerseusWidgetOptions =
     | PerseusCategorizerWidgetOptions
     | PerseusCSProgramWidgetOptions
@@ -1413,9 +1386,6 @@
     | PerseusNumberLineWidgetOptions
     | PerseusNumericInputWidgetOptions
     | PerseusOrdererWidgetOptions
-    | PerseusPassageRefTargetWidgetOptions
-    | PerseusPassageRefWidgetOptions
-    | PerseusPassageWidgetOptions
     | PerseusPhetSimulationWidgetOptions
     | PerseusPlotterWidgetOptions
     | PerseusRadioWidgetOptions

@github-actions
Copy link
Contributor

🛠️ Item Splitting: Changes Detected ⚠️

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	2025-12-18 00:17:04.790393358 +0000
+++ /home/runner/work/_temp/branch-compare/pr/index.item-splitting.js	2025-12-18 00:16:37.924722699 +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

Size Change: -10 kB (-2.01%)

Total Size: 488 kB

Filename Size Change
packages/perseus-core/dist/es/index.item-splitting.js 13 kB -88 B (-0.67%)
packages/perseus-core/dist/es/index.js 25.3 kB -310 B (-1.21%)
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%)
ℹ️ 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.3 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/perseus/dist/es/strings.js 7.78 kB
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

npm Snapshot: Published

Good news!! We've packaged up the latest commit from this PR (7ec2040) 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

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.

2 participants