fix(worksheets): enable borrowNotation and borrowingHints in smart difficulty mode

Remove hardcoded false values for showBorrowNotation and showBorrowingHints
that were preventing these subtraction-specific scaffolds from displaying
in smart difficulty mode.

The displayOptions object already includes these fields from the resolved
display rules, so spreading displayOptions now correctly applies them.

This was work from a previous session that got lost during a git stash/pull.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-11-08 14:50:05 -06:00
parent e5262e5007
commit 8020ee835e

View File

@@ -1,24 +1,27 @@
// Typst document generator for addition worksheets
import type { WorksheetProblem, WorksheetConfig } from './types'
import type { WorksheetProblem, WorksheetConfig } from "./types";
import {
generateTypstHelpers,
generateProblemStackFunction,
generateSubtractionProblemStackFunction,
generatePlaceValueColors,
} from './typstHelpers'
import { analyzeProblem, analyzeSubtractionProblem } from './problemAnalysis'
import { resolveDisplayForProblem } from './displayRules'
} from "./typstHelpers";
import { analyzeProblem, analyzeSubtractionProblem } from "./problemAnalysis";
import { resolveDisplayForProblem } from "./displayRules";
/**
* Chunk array into pages of specified size
*/
function chunkProblems(problems: WorksheetProblem[], pageSize: number): WorksheetProblem[][] {
const pages: WorksheetProblem[][] = []
function chunkProblems(
problems: WorksheetProblem[],
pageSize: number,
): WorksheetProblem[][] {
const pages: WorksheetProblem[][] = [];
for (let i = 0; i < problems.length; i += pageSize) {
pages.push(problems.slice(i, i + pageSize))
pages.push(problems.slice(i, i + pageSize));
}
return pages
return pages;
}
/**
@@ -26,22 +29,22 @@ function chunkProblems(problems: WorksheetProblem[], pageSize: number): Workshee
* Returns max digits across all operands (handles both addition and subtraction)
*/
function calculateMaxDigits(problems: WorksheetProblem[]): number {
let maxDigits = 1
let maxDigits = 1;
for (const problem of problems) {
if (problem.operator === '+') {
const digitsA = problem.a.toString().length
const digitsB = problem.b.toString().length
const maxProblemDigits = Math.max(digitsA, digitsB)
maxDigits = Math.max(maxDigits, maxProblemDigits)
if (problem.operator === "+") {
const digitsA = problem.a.toString().length;
const digitsB = problem.b.toString().length;
const maxProblemDigits = Math.max(digitsA, digitsB);
maxDigits = Math.max(maxDigits, maxProblemDigits);
} else {
// Subtraction
const digitsMinuend = problem.minuend.toString().length
const digitsSubtrahend = problem.subtrahend.toString().length
const maxProblemDigits = Math.max(digitsMinuend, digitsSubtrahend)
maxDigits = Math.max(maxDigits, maxProblemDigits)
const digitsMinuend = problem.minuend.toString().length;
const digitsSubtrahend = problem.subtrahend.toString().length;
const maxProblemDigits = Math.max(digitsMinuend, digitsSubtrahend);
maxDigits = Math.max(maxDigits, maxProblemDigits);
}
}
return maxDigits
return maxDigits;
}
/**
@@ -51,48 +54,60 @@ function generatePageTypst(
config: WorksheetConfig,
pageProblems: WorksheetProblem[],
problemOffset: number,
rowsPerPage: number
rowsPerPage: number,
): string {
console.log('[typstGenerator] generatePageTypst called with config:', {
console.log("[typstGenerator] generatePageTypst called with config:", {
mode: config.mode,
displayRules: config.mode === 'smart' ? config.displayRules : 'N/A (manual mode)',
showTenFrames: config.mode === 'manual' ? config.showTenFrames : 'N/A (smart mode)',
})
displayRules:
config.mode === "smart" ? config.displayRules : "N/A (manual mode)",
showTenFrames:
config.mode === "manual" ? config.showTenFrames : "N/A (smart mode)",
});
// Calculate maximum digits for proper column layout
const maxDigits = calculateMaxDigits(pageProblems)
console.log('[typstGenerator] Max digits on this page:', maxDigits)
const maxDigits = calculateMaxDigits(pageProblems);
console.log("[typstGenerator] Max digits on this page:", maxDigits);
// Enrich problems with display options based on mode
const enrichedProblems = pageProblems.map((p, index) => {
if (config.mode === 'smart') {
if (config.mode === "smart") {
// Smart mode: Per-problem conditional display based on problem complexity
const meta =
p.operator === '+'
p.operator === "+"
? analyzeProblem(p.a, p.b)
: analyzeSubtractionProblem(p.minuend, p.subtrahend)
const displayOptions = resolveDisplayForProblem(config.displayRules, meta)
: analyzeSubtractionProblem(p.minuend, p.subtrahend);
const displayOptions = resolveDisplayForProblem(
config.displayRules,
meta,
);
if (index === 0) {
const problemStr = p.operator === '+' ? `${p.a} + ${p.b}` : `${p.minuend} ${p.subtrahend}`
console.log('[typstGenerator] Smart mode - First problem display options:', {
problem: problemStr,
meta,
displayOptions,
})
const problemStr =
p.operator === "+"
? `${p.a} + ${p.b}`
: `${p.minuend} ${p.subtrahend}`;
console.log(
"[typstGenerator] Smart mode - First problem display options:",
{
problem: problemStr,
meta,
displayOptions,
},
);
}
return {
...p,
...displayOptions,
showBorrowNotation: false, // Smart mode doesn't have borrow notation (yet)
showBorrowingHints: false, // Smart mode doesn't have borrowing hints (yet)
}
...displayOptions, // Now includes showBorrowNotation and showBorrowingHints from resolved rules
};
} else {
// Manual mode: Uniform display across all problems
if (index === 0) {
const problemStr = p.operator === '+' ? `${p.a} + ${p.b}` : `${p.minuend} ${p.subtrahend}`
console.log('[typstGenerator] Manual mode - Uniform display options:', {
const problemStr =
p.operator === "+"
? `${p.a} + ${p.b}`
: `${p.minuend} ${p.subtrahend}`;
console.log("[typstGenerator] Manual mode - Uniform display options:", {
problem: problemStr,
showCarryBoxes: config.showCarryBoxes,
showAnswerBoxes: config.showAnswerBoxes,
@@ -100,7 +115,7 @@ function generatePageTypst(
showTenFrames: config.showTenFrames,
showProblemNumbers: config.showProblemNumbers,
showCellBorder: config.showCellBorder,
})
});
}
return {
@@ -111,40 +126,44 @@ function generatePageTypst(
showTenFrames: config.showTenFrames,
showProblemNumbers: config.showProblemNumbers,
showCellBorder: config.showCellBorder,
showBorrowNotation: 'showBorrowNotation' in config ? config.showBorrowNotation : true,
showBorrowingHints: 'showBorrowingHints' in config ? config.showBorrowingHints : false,
}
showBorrowNotation:
"showBorrowNotation" in config ? config.showBorrowNotation : true,
showBorrowingHints:
"showBorrowingHints" in config ? config.showBorrowingHints : false,
};
}
})
});
// Generate Typst problem data with per-problem display flags
const problemsTypst = enrichedProblems
.map((p) => {
if (p.operator === '+') {
return ` (operator: "+", a: ${p.a}, b: ${p.b}, showCarryBoxes: ${p.showCarryBoxes}, showAnswerBoxes: ${p.showAnswerBoxes}, showPlaceValueColors: ${p.showPlaceValueColors}, showTenFrames: ${p.showTenFrames}, showProblemNumbers: ${p.showProblemNumbers}, showCellBorder: ${p.showCellBorder}, showBorrowNotation: ${p.showBorrowNotation}, showBorrowingHints: ${p.showBorrowingHints}),`
if (p.operator === "+") {
return ` (operator: "+", a: ${p.a}, b: ${p.b}, showCarryBoxes: ${p.showCarryBoxes}, showAnswerBoxes: ${p.showAnswerBoxes}, showPlaceValueColors: ${p.showPlaceValueColors}, showTenFrames: ${p.showTenFrames}, showProblemNumbers: ${p.showProblemNumbers}, showCellBorder: ${p.showCellBorder}, showBorrowNotation: ${p.showBorrowNotation}, showBorrowingHints: ${p.showBorrowingHints}),`;
} else {
return ` (operator: "", minuend: ${p.minuend}, subtrahend: ${p.subtrahend}, showCarryBoxes: ${p.showCarryBoxes}, showAnswerBoxes: ${p.showAnswerBoxes}, showPlaceValueColors: ${p.showPlaceValueColors}, showTenFrames: ${p.showTenFrames}, showProblemNumbers: ${p.showProblemNumbers}, showCellBorder: ${p.showCellBorder}, showBorrowNotation: ${p.showBorrowNotation}, showBorrowingHints: ${p.showBorrowingHints}),`
return ` (operator: "", minuend: ${p.minuend}, subtrahend: ${p.subtrahend}, showCarryBoxes: ${p.showCarryBoxes}, showAnswerBoxes: ${p.showAnswerBoxes}, showPlaceValueColors: ${p.showPlaceValueColors}, showTenFrames: ${p.showTenFrames}, showProblemNumbers: ${p.showProblemNumbers}, showCellBorder: ${p.showCellBorder}, showBorrowNotation: ${p.showBorrowNotation}, showBorrowingHints: ${p.showBorrowingHints}),`;
}
})
.join('\n')
.join("\n");
// Calculate actual number of rows on this page
const actualRows = Math.ceil(pageProblems.length / config.cols)
const actualRows = Math.ceil(pageProblems.length / config.cols);
// Use smaller margins to maximize space
const margin = 0.4
const contentWidth = config.page.wIn - margin * 2
const contentHeight = config.page.hIn - margin * 2
const margin = 0.4;
const contentWidth = config.page.wIn - margin * 2;
const contentHeight = config.page.hIn - margin * 2;
// Calculate grid spacing based on ACTUAL rows on this page
const headerHeight = 0.35 // inches for header
const availableHeight = contentHeight - headerHeight
const problemBoxHeight = availableHeight / actualRows
const problemBoxWidth = contentWidth / config.cols
const headerHeight = 0.35; // inches for header
const availableHeight = contentHeight - headerHeight;
const problemBoxHeight = availableHeight / actualRows;
const problemBoxWidth = contentWidth / config.cols;
// Calculate cell size assuming MAXIMUM possible embellishments
// Check if ANY problem on this page might show ten-frames
const anyProblemMayShowTenFrames = enrichedProblems.some((p) => p.showTenFrames)
const anyProblemMayShowTenFrames = enrichedProblems.some(
(p) => p.showTenFrames,
);
// Calculate cell size to fill the entire problem box
// Base vertical stack: carry row + addend1 + addend2 + line + answer = 5 rows
@@ -153,13 +172,13 @@ function generatePageTypst(
//
// Horizontal constraint: maxDigits columns + 1 for + sign
// Cell size must fit: (maxDigits + 1) * cellSize <= problemBoxWidth
const maxCellSizeForWidth = problemBoxWidth / (maxDigits + 1)
const maxCellSizeForWidth = problemBoxWidth / (maxDigits + 1);
const maxCellSizeForHeight = anyProblemMayShowTenFrames
? problemBoxHeight / 6.0
: problemBoxHeight / 4.5
: problemBoxHeight / 4.5;
// Use the smaller of width/height constraints
const cellSize = Math.min(maxCellSizeForWidth, maxCellSizeForHeight)
const cellSize = Math.min(maxCellSizeForWidth, maxCellSizeForHeight);
return String.raw`
// addition-worksheet-page.typ (auto-generated)
@@ -177,13 +196,13 @@ function generatePageTypst(
#let heavy-stroke = 0.8pt
#let show-ten-frames-for-all = ${
config.mode === 'manual'
config.mode === "manual"
? config.showTenFramesForAll
? 'true'
: 'false'
: config.displayRules.tenFrames === 'always'
? 'true'
: 'false'
? "true"
: "false"
: config.displayRules.tenFrames === "always"
? "true"
: "false"
}
${generatePlaceValueColors()}
@@ -261,7 +280,7 @@ ${problemsTypst}
)
] // End of constrained block
`
`;
}
/**
@@ -269,17 +288,22 @@ ${problemsTypst}
*/
export function generateTypstSource(
config: WorksheetConfig,
problems: WorksheetProblem[]
problems: WorksheetProblem[],
): string[] {
// Use the problemsPerPage directly from config (primary state)
const problemsPerPage = config.problemsPerPage
const rowsPerPage = problemsPerPage / config.cols
const problemsPerPage = config.problemsPerPage;
const rowsPerPage = problemsPerPage / config.cols;
// Chunk problems into discrete pages
const pages = chunkProblems(problems, problemsPerPage)
const pages = chunkProblems(problems, problemsPerPage);
// Generate separate Typst source for each page
return pages.map((pageProblems, pageIndex) =>
generatePageTypst(config, pageProblems, pageIndex * problemsPerPage, rowsPerPage)
)
generatePageTypst(
config,
pageProblems,
pageIndex * problemsPerPage,
rowsPerPage,
),
);
}