Files
soroban-abacus-flashcards/apps/web/scripts/generateSubtractionExamples.ts
Thomas Hallock b36df3a40c fix(worksheets): ten-frames not rendering in mastery mode
Fixed two critical bugs preventing ten-frames from rendering:

1. **Mastery mode not handled** (typstGenerator.ts:61)
   - Code only checked for 'smart' | 'manual' modes
   - Mastery mode fell into manual path, tried to use boolean flags that don't exist
   - Resulted in all display options being `undefined`
   - Fix: Check for both 'smart' OR 'mastery' modes (both use displayRules)

2. **Typst array membership syntax** (already fixed in previous commit)
   - Used `(i in array)` which doesn't work in Typst
   - Changed to `array.contains(i)`

Added comprehensive unit tests (tenFrames.test.ts):
- Problem analysis tests (regrouping detection)
- Display rule evaluation tests
- Full Typst template generation tests
- Mastery mode specific tests
- All 14 tests now passing

Added debug logging to trace display rules resolution:
- displayRules.ts: Shows rule evaluation per problem
- typstGenerator.ts: Shows enriched problems and Typst data
- Helps diagnose future issues

The issue was that mastery mode (which uses displayRules like smart mode)
was being treated as manual mode (which uses boolean flags), resulting in
undefined display options.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:06:27 -06:00

258 lines
7.7 KiB
TypeScript

// Script to generate subtraction worksheet examples for the blog post
// Shows different scaffolding levels for subtraction problems
import fs from 'fs'
import path from 'path'
import { generateWorksheetPreview } from '../src/app/create/worksheets/addition/generatePreview'
// Output directory
const outputDir = path.join(process.cwd(), 'public', 'blog', 'subtraction-examples')
// Ensure output directory exists
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true })
}
// Generate examples showing different subtraction scaffolding options
const examples = [
{
name: 'subtraction-no-borrowing',
filename: 'no-borrowing.svg',
description: 'Simple subtraction (no borrowing needed)',
config: {
operator: 'subtraction' as const,
pAllStart: 0.0, // No borrowing problems
pAnyStart: 0.0,
digitRange: { min: 2, max: 2 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'never' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: false,
showBorrowingHints: false,
},
},
{
name: 'subtraction-with-borrow-notation',
filename: 'with-borrow-notation.svg',
description: 'Subtraction with borrow notation boxes',
config: {
operator: 'subtraction' as const,
pAllStart: 0.0,
pAnyStart: 1.0, // Some borrowing
digitRange: { min: 2, max: 2 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'always' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: true,
showBorrowingHints: false,
},
},
{
name: 'subtraction-with-hints',
filename: 'with-borrowing-hints.svg',
description: 'Subtraction with borrow notation and hints',
config: {
operator: 'subtraction' as const,
pAllStart: 0.0,
pAnyStart: 1.0, // Some borrowing
digitRange: { min: 2, max: 2 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'always' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: true,
showBorrowingHints: true,
},
},
{
name: 'subtraction-multiple-borrows',
filename: 'multiple-borrows.svg',
description: 'Complex subtraction with multiple borrows',
config: {
operator: 'subtraction' as const,
pAllStart: 1.0, // All problems require borrowing
pAnyStart: 1.0,
digitRange: { min: 3, max: 3 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'always' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: true,
showBorrowingHints: false,
},
},
{
name: 'subtraction-single-borrow',
filename: 'single-borrow-ones.svg',
description: 'Single borrow in ones place only',
config: {
operator: 'subtraction' as const,
pAllStart: 0.0,
pAnyStart: 1.0, // Some borrowing
digitRange: { min: 2, max: 2 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'always' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: true,
showBorrowingHints: false,
},
},
{
name: 'subtraction-comparison-no-notation',
filename: 'comparison-no-notation.svg',
description: 'Borrowing problems WITHOUT notation boxes',
config: {
operator: 'subtraction' as const,
pAllStart: 0.0,
pAnyStart: 1.0,
digitRange: { min: 2, max: 2 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'never' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: false,
showBorrowingHints: false,
},
},
{
name: 'subtraction-comparison-with-notation',
filename: 'comparison-with-notation.svg',
description: 'Same problems WITH notation boxes',
config: {
operator: 'subtraction' as const,
pAllStart: 0.0,
pAnyStart: 1.0,
digitRange: { min: 2, max: 2 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'never' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: true,
showBorrowingHints: false,
seed: 12345, // Same seed as above for identical problems
},
},
{
name: 'subtraction-cascading-borrows',
filename: 'cascading-borrows.svg',
description: 'Cascading borrows across multiple places',
config: {
operator: 'subtraction' as const,
pAllStart: 1.0,
pAnyStart: 1.0,
digitRange: { min: 4, max: 4 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'always' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: true,
showBorrowingHints: false,
},
},
{
name: 'subtraction-hints-detail',
filename: 'hints-detail.svg',
description: 'Detailed view of borrowing hints',
config: {
operator: 'subtraction' as const,
pAllStart: 0.0,
pAnyStart: 1.0,
digitRange: { min: 3, max: 3 },
mode: 'manual' as const,
displayRules: {
carryBoxes: 'never' as const,
answerBoxes: 'always' as const,
placeValueColors: 'always' as const,
tenFrames: 'never' as const,
problemNumbers: 'always' as const,
cellBorders: 'always' as const,
},
showBorrowNotation: true,
showBorrowingHints: true,
problemsPerPage: 2, // Fewer problems for detail
cols: 1,
},
},
] as const
console.log('Generating subtraction example worksheets...\n')
for (const example of examples) {
console.log(`Generating ${example.description}...`)
const config = {
problemsPerPage: 4,
pages: 1,
cols: 2,
seed: 12345, // Fixed seed for consistent examples
...example.config, // Spread example config last so it can override defaults
}
try {
const result = generateWorksheetPreview(config)
if (!result.success || !result.pages || result.pages.length === 0) {
console.error(`Failed to generate ${example.name}:`, result.error)
console.error(`Details:`, result.details)
continue
}
// Get the first page's SVG
const svg = result.pages[0]
// Save to file
const outputPath = path.join(outputDir, example.filename)
fs.writeFileSync(outputPath, svg, 'utf-8')
console.log(` ✓ Saved to ${outputPath}`)
} catch (error) {
console.error(` ✗ Error generating ${example.name}:`, error)
}
}
console.log('\nDone! Subtraction example worksheets generated.')
console.log(`\nFiles saved to: ${outputDir}`)