@khanacademy/perseus-core 0.0.0-PR3095-20251205200751 → 0.0.0-PR3096-20251208220000

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
@@ -137,6 +137,10 @@ export { default as PerseusFeatureFlags, isFeatureOn } from "./feature-flags";
137
137
  export { traverse } from "./traversal";
138
138
  export { isItemAccessible, violatingWidgets } from "./accessibility";
139
139
  export { isLabeledSVG, getRealImageUrl, getBaseUrl, getSvgUrl, getDataUrl, getImageSizeModern, } from "./utils/util.graphie";
140
+ export { generateDropdownOptions, generateDropdownWidget, } from "./utils/generators/dropdown-widget-generator";
141
+ export { generateExpressionOptions, generateExpressionAnswerForm, generateExpressionWidget, } from "./utils/generators/expression-widget-generator";
140
142
  export { generateFreeResponseOptions, generateFreeResponseWidget, } from "./utils/generators/free-response-widget-generator";
141
143
  export { generateImageOptions, generateImageWidget, } from "./utils/generators/image-widget-generator";
144
+ export { generateNumericInputOptions, generateNumericInputAnswer, generateNumericInputWidget, } from "./utils/generators/numeric-input-widget-generator";
142
145
  export { generateVideoWidget } from "./utils/generators/video-widget-generator";
146
+ export { getAnswersFromWidgets, injectWidgets, getPerseusAIData, } from "./utils/extract-perseus-ai-data";
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ require('tiny-invariant');
7
7
  var KAS = require('@khanacademy/kas');
8
8
  var perseusUtils = require('@khanacademy/perseus-utils');
9
9
  var pureMarkdown = require('@khanacademy/pure-markdown');
10
+ var wonderStuffCore = require('@khanacademy/wonder-stuff-core');
10
11
 
11
12
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
12
13
 
@@ -258,7 +259,7 @@ const parseUserInputMap=(rawValue,ctx)=>{if(!isPlainObject(rawValue)){return ctx
258
259
 
259
260
  function parsePerseusItem(json){if(isRealJSONParse(JSON.parse)){return JSON.parse(json)}throw new Error("Something went wrong.")}function parseAndMigratePerseusItem(data){throwErrorIfCheatingDetected();const object=typeof data==="string"?JSON.parse(data):data;const result=parse(object,parsePerseusItem$1);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}function parseAndMigratePerseusArticle(data){throwErrorIfCheatingDetected();const object=typeof data==="string"?JSON.parse(data):data;const result=parse(object,parsePerseusArticle);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}function parseAndMigrateUserInputMap(data){const object=typeof data==="string"?JSON.parse(data):data;const result=parse(object,parseUserInputMap);if(isFailure(result)){return failure({message:result.detail,invalidObject:object})}return result}function throwErrorIfCheatingDetected(){if(!isRealJSONParse(JSON.parse)){throw new Error("Something went wrong.")}}
260
261
 
261
- const libName="@khanacademy/perseus-core";const libVersion="20.3.0";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
262
+ const libName="@khanacademy/perseus-core";const libVersion="20.4.0";perseusUtils.addLibraryVersionToPerseusDebug(libName,libVersion);
262
263
 
263
264
  const Errors=Object.freeze({Unknown:"Unknown",Internal:"Internal",InvalidInput:"InvalidInput",NotAllowed:"NotAllowed",TransientService:"TransientService",Service:"Service"});
264
265
 
@@ -413,12 +414,28 @@ const noop=function(){};const deepCallbackFor=function(contentCallback,widgetCal
413
414
 
414
415
  function violatingWidgets(itemData){const widgetTypes=[];traverse(itemData.question,null,function(info){if(info.type&&!isAccessible(info.type,info.options)){widgetTypes.push(info.type);}});return [...new Set(widgetTypes)]}function isItemAccessible(itemData){const ast=pureMarkdown.parse(itemData.question.content);const widgetIdsInUse=new Set;let hasInaccessibleImage=false;pureMarkdown.traverseContent(ast,node=>{if(node.type==="image"&&(node.alt==null||node.alt==="")){hasInaccessibleImage=true;return}if(node.type==="widget"){widgetIdsInUse.add(node.id);}});if(hasInaccessibleImage){return false}const itemDataWithOnlyActiveWidgets={...itemData,question:{...itemData.question,widgets:Object.fromEntries(Object.entries(itemData.question.widgets).filter(([id])=>widgetIdsInUse.has(id)))}};return violatingWidgets(itemDataWithOnlyActiveWidgets).length===0}
415
416
 
417
+ function generateDropdownOptions(options){return {...dropdownWidgetLogic.defaultWidgetOptions,...options}}function generateDropdownWidget(dropdownWidgetProperties){return {type:"dropdown",graded:true,version:{major:0,minor:0},static:false,alignment:"default",options:generateDropdownOptions(),...dropdownWidgetProperties}}
418
+
419
+ function generateExpressionOptions(options){return {...expressionWidgetLogic.defaultWidgetOptions,...options}}function generateExpressionAnswerForm(answerFormOptions){return {value:"",form:false,simplify:false,considered:"wrong",...answerFormOptions}}function generateExpressionWidget(expressionWidgetProperties){return {type:"expression",graded:true,version:{major:0,minor:0},static:false,alignment:"default",options:generateExpressionOptions(),...expressionWidgetProperties}}
420
+
416
421
  function generateFreeResponseOptions(options){return {...freeResponseWidgetLogic.defaultWidgetOptions,...options}}function generateFreeResponseWidget(freeResponseWidgetProperties){return {type:"free-response",graded:true,version:{major:0,minor:0},static:false,alignment:"default",options:generateFreeResponseOptions(),...freeResponseWidgetProperties}}
417
422
 
418
423
  function generateImageOptions(options){const defaultImageOptions={backgroundImage:{}};return {...defaultImageOptions,...options}}function generateImageWidget(imageWidgetProperties){return {type:"image",graded:true,version:{major:0,minor:0},static:false,alignment:"default",options:generateImageOptions({}),...imageWidgetProperties}}
419
424
 
425
+ function generateNumericInputOptions(options){return {...numericInputWidgetLogic.defaultWidgetOptions,static:false,...options}}function generateNumericInputAnswer(answerOptions){return {...numericInputWidgetLogic.defaultWidgetOptions.answers[0],...answerOptions}}function generateNumericInputWidget(numericInputWidgetProperties){return {type:"numeric-input",graded:true,version:{major:0,minor:0},static:false,alignment:"default",options:generateNumericInputOptions(),...numericInputWidgetProperties}}
426
+
420
427
  function generateVideoWidget(videoWidgetProperties){return {type:"video",graded:true,version:{major:0,minor:0},static:false,alignment:"default",options:{location:""},...videoWidgetProperties}}
421
428
 
429
+ const joinOptionContents=options=>options.map(({content})=>content).join("\n");const toOptionLetter=index=>String.fromCharCode("A".charCodeAt(0)+index);function getAnswersFromWidgets(widgets){const answers=[];wonderStuffCore.keys(widgets).forEach(widgetID=>{const widget=widgets[widgetID];if(!widget.options){return}switch(widget.type){case "radio":const radio=widget;const options=radio.options;if(options?.choices?.length){for(const choice of options.choices){if(choice?.correct){answers.push(choice.content);}}}break;case "categorizer":const categorizer=widget;if(categorizer.options?.categories&&categorizer.options?.items&&categorizer.options?.values){const categories=categorizer.options?.categories;const items=categorizer.options?.items;const values=categorizer.options?.values;answers.push(...values.map((value,index)=>`${items[index]}: ${categories[value]}`));}break;case "dropdown":const dropdown=widget;if(dropdown.options?.choices){for(const choice of dropdown.options.choices){if(choice.correct){answers.push(choice.content);}}}break;case "numeric-input":const numericInput=widget;if(numericInput.options?.answers){for(const ans of numericInput.options.answers){if(ans.status==="correct"&&ans.value!=null){answers.push(ans.value.toString());}}}break;case "input-number":const inputNumber=widget;if(inputNumber.options?.value){answers.push(inputNumber.options.value.toString());}break;case "expression":const expression=widget;if(expression.options?.answerForms){answers.push(...expression.options.answerForms.map(answer=>answer.value));}break;case "group":case "graded-group":const gradedGroup=widget;if(gradedGroup.options?.widgets){answers.push(...getAnswersFromWidgets(gradedGroup.options.widgets));}break;case "plotter":const plotter=widget;if(plotter.options?.categories&&plotter.options?.correct&&plotter.options.categories.length===plotter.options.correct.length){const{categories,correct}=plotter.options;answers.push(`{${categories.map((category,index)=>`${category}: ${correct[index]}`).join(", ")}}`);}break;case "interactive-graph":case "grapher":const grapher=widget;if(grapher.options?.correct?.coords){answers.push(`There should be point(s) on [${grapher.options.correct?.coords.join("], [")}]`);}break;case "orderer":const orderer=widget;if(orderer.options?.correctOptions){answers.push(joinOptionContents(orderer.options.correctOptions));}break;case "sorter":const sorter=widget;if(sorter.options?.correct){answers.push(sorter.options.correct.join(", "));}break;case "label-image":const labelImage=widget;if(labelImage.options?.markers){answers.push(...labelImage.options.markers.map(m=>`{label: "${m.label}", position: {${m.x},${m.y}}, answer: "${m.answers.join(", ")}"}`));}break;case "number-line":const numberLine=widget;if(numberLine.options?.correctX!=null){answers.push(numberLine.options.correctX.toString());}break;case "matrix":const matrix=widget;if(matrix.options?.answers){answers.push(`[${matrix.options.answers.join("], [")}]`);}break;case "matcher":const matcher=widget;if(matcher.options?.left&&matcher.options?.right){const{left,right}=matcher.options;const[leftHeader,rightHeader]=matcher.options.labels;const tableHeader=`| ${leftHeader} | ${rightHeader} |
430
+ | --- | --- |`;const tableRows=left.map((leftItem,index)=>{return `| ${leftItem} | ${right[index]} |`});const table=[tableHeader,...tableRows].join("\n");answers.push(table);}break}});return answers}function injectWidgets(content,widgets,widgetProps){if(!content){return ""}if(!widgets){return content}let context=content;wonderStuffCore.keys(widgets).forEach(widgetID=>{const widget=widgets[widgetID];if(!widget.options){return}switch(widget.type){case "radio":const radio=widget;const radioProps=widgetProps?.[widgetID];if(radio.options?.choices?.length){let radioContext=joinOptionContents(radioProps?radioProps.choices.map(({content},i)=>({content:`Option ${toOptionLetter(i)}: ${content}`})):radio.options.choices);if(!radioProps&&radio.options?.randomize){radioContext+="\nThose options are displayed in a different order to the user. If the user says the letter, number, or ordinal number, always ask them clarify which option they are referring to.\n";}context=context.replace(`[[☃ ${widgetID}]]`,radioContext);}break;case "image":const image=widget;if(image.options?.alt){context=context.replace(`[[☃ ${widgetID}]]`,`<img id="${widgetID}" alt="${image.options.alt}">`);}break;case "label-image":const labelImage=widget;if(labelImage.options?.imageAlt){context=context.replace(`[[☃ ${widgetID}]]`,`[An image with dots that user needs to label. Label choices: [${labelImage.options.choices.join(", ")}]. Image alt text: ${labelImage.options?.imageAlt??""}]`);}break;case "explanation":const explanation=widget;if(explanation.options?.explanation){context=context.replace(`[[☃ ${widgetID}]]`,injectWidgets(explanation.options.explanation,explanation.options.widgets));}break;case "passage":const passage=widget;if(passage.options?.passageTitle||passage.options?.passageText){const{passageTitle,passageText}=passage.options;context=context.replace(`[[☃ ${widgetID}]]`,`# ${passageTitle}
431
+
432
+ ${passageText}`);}break;case "group":case "graded-group":const group=widget;if(group.options?.widgets&&group.options.content){context=context.replace(`[[☃ ${widgetID}]]`,injectWidgets(group.options.content,group.options.widgets));}break;case "graded-group-set":const gradedGroup=widget;if(gradedGroup.options?.gradedGroups){const gradedGroups=gradedGroup.options.gradedGroups;const gradedGroupsContent=gradedGroups.reduce((acc,group)=>{if(group.widgets&&group.content){acc+=injectWidgets(group.content,group.widgets)+"\n";}return acc},"");context=context.replace(`[[☃ ${widgetID}]]`,gradedGroupsContent);}break;case "categorizer":const categorizer=widget;if(categorizer.options?.categories&&categorizer.options.items){const categories=categorizer.options.categories;const items=categorizer.options.items;context=context.replace(`[[☃ ${widgetID}]]`,`For each item, select the correct category. Categories: ${categories.join(", ")}.
433
+ Items:
434
+ ${items.join("\n")}
435
+ `);}break;case "dropdown":const dropdown=widget;if(dropdown.options?.choices){const choices=dropdown.options.choices.map(choice=>choice.content);context=context.replace(`[[☃ ${widgetID}]]`,`[${choices.join(" | ")}]`);}break;case "definition":const definition=widget;if(definition.options?.togglePrompt){context=context.replace(`[[☃ ${widgetID}]]`,definition.options.togglePrompt);}break;case "orderer":const orderer=widget;if(orderer.options?.options){context=context.replace(`[[☃ ${widgetID}]]`,joinOptionContents(orderer.options.options));}break;case "sorter":const sorter=widget;if(sorter.options?.correct){const choices=sorter.options.correct;context=context.replace(`[[☃ ${widgetID}]]`,`[${choices.join(" | ")}]`);}break;case "interactive-graph":const interactiveGraph=widget;if(interactiveGraph.options?.range.length===2){const[x,y]=interactiveGraph.options.range;context=context.replace(`[[☃ ${widgetID}]]`,`[Graph with an x range of ${x[0]} to ${x[1]} and y range of ${y[0]} to ${y[1]}]`);}break;case "number-line":const numberLine=widget;if(numberLine.options?.range.length===2&&numberLine.options?.tickStep&&numberLine.options?.initialX){const[min,max]=numberLine.options.range;const step=numberLine.options.tickStep;const initialPosition=numberLine.options.initialX;context=context.replace(`[[☃ ${widgetID}]]`,`[Number line with a range of ${min} to ${max}, a step of ${step}, and an initial position of ${initialPosition}]`);}break;case "matrix":const matrix=widget;if(matrix.options?.matrixBoardSize.length===2){const[rows,columns]=matrix.options.matrixBoardSize;context=context.replace(`[[☃ ${widgetID}]]`,`[Matrix with ${rows} rows and ${columns} columns. The user can click on each cell to enter a value]`);}break;case "matcher":const matcher=widget;if(matcher.options?.left&&matcher.options?.right){const{left,right}=matcher.options;const[leftHeader,rightHeader]=matcher.options.labels;const tableHeader=`| ${leftHeader} | ${rightHeader} |
436
+ | --- | --- |`;const tableRows=left.map((leftItem,index)=>{return `| ${leftItem} | ${right[index]} |`});const table=[tableHeader,...tableRows].join("\n");const matcherWidgetExplanation="The user needs to move items in the right column to match the correct option on the left. The order of items on the right side will be different from what the user sees.";context=context.replace(`[[☃ ${widgetID}]]`,`${matcherWidgetExplanation}
437
+ ${table}`);}break;case "numeric-input":case "input-number":case "expression":context=context.replace(`[[☃ ${widgetID}]]`,"?");break;default:context=context.replace(`[[☃ ${widgetID}]]`,`[[Unsupported ${widget.type} widget: Explain to the user that you are unable to understand the content in this widget and ask them to describe it.]]`);}});return context}function getPerseusAIData(perseusItem){const answers=getAnswersFromWidgets(perseusItem.question.widgets);const hints=perseusItem.hints.map(hint=>injectWidgets(hint.content,hint.widgets));return {answers,hints}}
438
+
422
439
  registerCoreWidgets();
423
440
 
424
441
  exports.CoreWidgetRegistry = coreWidgetRegistry;
@@ -445,13 +462,22 @@ exports.dropdownLogic = dropdownWidgetLogic;
445
462
  exports.explanationLogic = explanationWidgetLogic;
446
463
  exports.expressionLogic = expressionWidgetLogic;
447
464
  exports.freeResponseLogic = freeResponseWidgetLogic;
465
+ exports.generateDropdownOptions = generateDropdownOptions;
466
+ exports.generateDropdownWidget = generateDropdownWidget;
467
+ exports.generateExpressionAnswerForm = generateExpressionAnswerForm;
468
+ exports.generateExpressionOptions = generateExpressionOptions;
469
+ exports.generateExpressionWidget = generateExpressionWidget;
448
470
  exports.generateFreeResponseOptions = generateFreeResponseOptions;
449
471
  exports.generateFreeResponseWidget = generateFreeResponseWidget;
450
472
  exports.generateImageOptions = generateImageOptions;
451
473
  exports.generateImageWidget = generateImageWidget;
474
+ exports.generateNumericInputAnswer = generateNumericInputAnswer;
475
+ exports.generateNumericInputOptions = generateNumericInputOptions;
476
+ exports.generateNumericInputWidget = generateNumericInputWidget;
452
477
  exports.generateTestPerseusItem = generateTestPerseusItem;
453
478
  exports.generateTestPerseusRenderer = generateTestPerseusRenderer;
454
479
  exports.generateVideoWidget = generateVideoWidget;
480
+ exports.getAnswersFromWidgets = getAnswersFromWidgets;
455
481
  exports.getBaseUrl = getBaseUrl;
456
482
  exports.getCSProgramPublicWidgetOptions = getCSProgramPublicWidgetOptions;
457
483
  exports.getCategorizerPublicWidgetOptions = getCategorizerPublicWidgetOptions;
@@ -475,6 +501,7 @@ exports.getMatrixSize = getMatrixSize;
475
501
  exports.getNumberLinePublicWidgetOptions = getNumberLinePublicWidgetOptions;
476
502
  exports.getNumericInputPublicWidgetOptions = getNumericInputPublicWidgetOptions;
477
503
  exports.getOrdererPublicWidgetOptions = getOrdererPublicWidgetOptions;
504
+ exports.getPerseusAIData = getPerseusAIData;
478
505
  exports.getPlotterPublicWidgetOptions = getPlotterPublicWidgetOptions;
479
506
  exports.getRadioPublicWidgetOptions = getRadioPublicWidgetOptions;
480
507
  exports.getRealImageUrl = getRealImageUrl;
@@ -489,6 +516,7 @@ exports.grapherLogic = grapherWidgetLogic;
489
516
  exports.groupLogic = groupWidgetLogic;
490
517
  exports.iframeLogic = iframeWidgetLogic;
491
518
  exports.imageLogic = imageWidgetLogic;
519
+ exports.injectWidgets = injectWidgets;
492
520
  exports.inputNumberLogic = inputNumberWidgetLogic;
493
521
  exports.interactionLogic = interactionWidgetLogic;
494
522
  exports.interactiveGraphLogic = interactiveGraphWidgetLogic;