feat: add infrastructure for borrowing hints toggle

Add new showBorrowingHints toggle to guide students through borrowing:
- Add showBorrowingHints field to V4 manual config schema
- Add toggle to ConfigPanel UI (shows for subtraction/mixed only)
- Wire through validation, auto-save, preview, and example route
- Update typstGenerator and typstHelpers to accept parameter
- Default to false for backward compatibility

This commit adds the plumbing/infrastructure. The actual Typst rendering
logic for arrows and calculations will be implemented next.

🤖 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 06:18:05 -06:00
parent 42c9c9dd7e
commit 74c67566d2
8 changed files with 28 additions and 6 deletions

View File

@@ -32,6 +32,7 @@ interface ExampleRequest {
showTenFrames?: boolean
showTenFramesForAll?: boolean
showBorrowNotation?: boolean
showBorrowingHints?: boolean
fontSize?: number
operator?: WorksheetOperator
// For addition
@@ -59,6 +60,7 @@ function generateExampleTypst(config: ExampleRequest): string {
const showTenFrames = config.showTenFrames ?? false
const showTenFramesForAll = config.showTenFramesForAll ?? false
const showBorrowNotation = config.showBorrowNotation ?? true
const showBorrowingHints = config.showBorrowingHints ?? false
if (operator === 'addition') {
// Use custom addends if provided, otherwise generate a problem
@@ -128,6 +130,7 @@ ${generateProblemStackFunction(cellSize, 3)}
#let show-ten-frames = ${showTenFrames ? 'true' : 'false'}
#let show-ten-frames-for-all = ${showTenFramesForAll ? 'true' : 'false'}
#let show-borrow-notation = ${showBorrowNotation ? 'true' : 'false'}
#let show-borrowing-hints = ${showBorrowingHints ? 'true' : 'false'}
${generateTypstHelpers(cellSize)}
@@ -137,7 +140,7 @@ ${generateSubtractionProblemStackFunction(cellSize, 3)}
#let subtrahend = ${subtrahend}
#align(center + horizon)[
#subtraction-problem-stack(minuend, subtrahend, if show-numbers { 0 } else { none }, show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation)
#subtraction-problem-stack(minuend, subtrahend, if show-numbers { 0 } else { none }, show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation, show-borrowing-hints)
]
`
}

View File

@@ -2372,6 +2372,15 @@ export function ConfigPanel({ formState, onChange }: ConfigPanelProps) {
/>
)}
{(formState.operator === 'subtraction' || formState.operator === 'mixed') && (
<ToggleOption
checked={formState.showBorrowingHints ?? false}
onChange={(checked) => onChange({ showBorrowingHints: checked })}
label="Borrowing Hints"
description="Show arrows and calculations guiding the borrowing process"
/>
)}
<ToggleOption
checked={formState.showTenFrames ?? false}
onChange={(checked) => {

View File

@@ -180,6 +180,7 @@ export function DisplayOptionsPreview({ formState }: DisplayOptionsPreviewProps)
showTenFrames: formState.showTenFrames ?? false,
showTenFramesForAll: formState.showTenFramesForAll ?? false,
showBorrowNotation: formState.showBorrowNotation ?? true,
showBorrowingHints: formState.showBorrowingHints ?? false,
operator,
}
@@ -217,6 +218,7 @@ export function DisplayOptionsPreview({ formState }: DisplayOptionsPreviewProps)
formState.showTenFrames,
formState.showTenFramesForAll,
formState.showBorrowNotation,
formState.showBorrowingHints,
formState.operator,
operands,
])

View File

@@ -64,6 +64,7 @@ export function useWorksheetAutoSave(
showTenFrames,
showTenFramesForAll,
showBorrowNotation,
showBorrowingHints,
fontSize,
mode,
difficultyProfile,
@@ -95,6 +96,7 @@ export function useWorksheetAutoSave(
showTenFrames,
showTenFramesForAll,
showBorrowNotation,
showBorrowingHints,
fontSize,
mode,
difficultyProfile,

View File

@@ -85,6 +85,7 @@ function generatePageTypst(
...p,
...displayOptions,
showBorrowNotation: false, // Smart mode doesn't have borrow notation (yet)
showBorrowingHints: false, // Smart mode doesn't have borrowing hints (yet)
}
} else {
// Manual mode: Uniform display across all problems
@@ -110,6 +111,7 @@ function generatePageTypst(
showProblemNumbers: config.showProblemNumbers,
showCellBorder: config.showCellBorder,
showBorrowNotation: 'showBorrowNotation' in config ? config.showBorrowNotation : true,
showBorrowingHints: 'showBorrowingHints' in config ? config.showBorrowingHints : false,
}
}
})
@@ -118,9 +120,9 @@ function generatePageTypst(
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}),`
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}),`
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')
@@ -217,7 +219,8 @@ ${generateSubtractionProblemStackFunction(cellSize, maxDigits)}
problem.showPlaceValueColors,
problem.showTenFrames,
problem.showProblemNumbers,
problem.showBorrowNotation
problem.showBorrowNotation,
problem.showBorrowingHints
)
}
]

View File

@@ -355,8 +355,8 @@ export function generateSubtractionProblemStackFunction(
return String.raw`
// Subtraction problem rendering function (supports 1-${maxDigits} digit problems)
// Returns the stack/grid structure for rendering a single subtraction problem
// Per-problem display flags: show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation
#let subtraction-problem-stack(minuend, subtrahend, index-or-none, show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation) = {
// Per-problem display flags: show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation, show-borrowing-hints
#let subtraction-problem-stack(minuend, subtrahend, index-or-none, show-borrows, show-answers, show-colors, show-ten-frames, show-numbers, show-borrow-notation, show-borrowing-hints) = {
// Place value colors array for dynamic lookup
let place-colors = (${placeColors.join(', ')})

View File

@@ -169,6 +169,7 @@ export function validateWorksheetConfig(formState: WorksheetFormState): Validati
showCellBorder: formState.showCellBorder ?? true,
showTenFramesForAll: formState.showTenFramesForAll ?? false,
showBorrowNotation: formState.showBorrowNotation ?? true,
showBorrowingHints: formState.showBorrowingHints ?? false,
manualPreset: formState.manualPreset,
...sharedFields,
}

View File

@@ -330,6 +330,7 @@ const additionConfigV4ManualSchema = additionConfigV4BaseSchema.extend({
showCellBorder: z.boolean(),
showTenFramesForAll: z.boolean(),
showBorrowNotation: z.boolean(), // Scratch boxes for borrowing work
showBorrowingHints: z.boolean(), // Visual hints showing what to write when borrowing
// Optional: Which manual preset is selected
manualPreset: z.string().optional(),
@@ -507,6 +508,7 @@ function migrateAdditionV3toV4(v3: AdditionConfigV3): AdditionConfigV4 {
showCellBorder: v3.showCellBorder,
showTenFramesForAll: v3.showTenFramesForAll,
showBorrowNotation: true, // V4: Default to true for backward compatibility
showBorrowingHints: false, // V4: Default to false for backward compatibility
manualPreset: v3.manualPreset,
}
}