fix(worksheets): render operators last for proper layering

Use Typst's place() function to overlay + and − operators on top of
all other problem elements. This ensures operators are always visible
and properly layered over carry/borrow boxes, scratch work, and other
decorations.

Changes:
- Addition: wrap grid in box, use place() for + sign overlay
- Subtraction: extract operator to operatorOverlay.ts, use place()
- Both operators positioned at correct row using dy offset

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-12-05 09:46:46 -06:00
parent 2ae5fbfac9
commit cdd0de797f
4 changed files with 77 additions and 22 deletions

View File

@@ -7,13 +7,13 @@
// Import types for internal use
import type { DisplayOptions } from './typstHelpers/shared/types'
export { generatePlaceValueColors } from './typstHelpers/shared/colors'
export { generateTypstHelpers } from './typstHelpers/shared/helpers'
// Re-export everything from modular structure
export type {
DisplayOptions,
CellDimensions,
DisplayOptions,
} from './typstHelpers/shared/types'
export { generateTypstHelpers } from './typstHelpers/shared/helpers'
export { generatePlaceValueColors } from './typstHelpers/shared/colors'
export { generateSubtractionProblemStackFunction } from './typstHelpers/subtraction/problemStack'
/**
@@ -103,12 +103,14 @@ export function generateProblemStackFunction(cellSize: number, maxDigits: number
dir: ttb,
spacing: 0pt,
problem-number-display,
grid(
columns: column-list,
gutter: 0pt,
// Wrap grid in a box to enable place() overlay for operator
box[
#grid(
columns: column-list,
gutter: 0pt,
// Carry boxes row (one per place value, right to left)
[], // Empty cell for + sign column
// Carry boxes row (one per place value, right to left)
[], // Empty cell for + sign column
..for i in range(0, actual-digits).rev() {
// DEBUG: Show which place values get carry boxes and why
let show-carry = show-carries and i > 0
@@ -152,8 +154,8 @@ export function generateProblemStackFunction(cellSize: number, maxDigits: number
],)
},
// Second addend row with + sign (right to left)
box(width: 0.5em, height: ${cellSizeIn})[#align(center + horizon)[#text(size: ${(cellSizePt * 0.8).toFixed(1)}pt)[+]]],
// Second addend row (operator sign rendered separately via place() for proper layering)
[], // Empty cell for operator column (operator overlaid later)
..for i in range(0, actual-digits).rev() {
let digit = b-digits.at(i)
let place-color = place-colors.at(i) // Dynamic color lookup by place value
@@ -240,7 +242,20 @@ export function generateProblemStackFunction(cellSize: number, maxDigits: number
],)
}
},
)
)
// Operator overlay - rendered last for proper layering
// Position: left edge, at second addend row vertical position
#place(
left + top,
dx: 0pt,
dy: ${cellSizeIn} * 2, // Skip carry boxes row + first addend row
box(width: 0.5em, height: ${cellSizeIn})[
#align(center + horizon)[
#text(size: ${(cellSizePt * 0.8).toFixed(1)}pt)[+]
]
]
)
]
)
}
`

View File

@@ -0,0 +1,39 @@
// Operator overlay for subtraction problems
// Rendered last to ensure proper layering over all other elements
import type { CellDimensions } from '../shared/types'
/**
* Generate Typst code for the operator overlay
*
* The operator ( sign) is rendered using place() to overlay it on top of
* all other problem elements, ensuring proper layering regardless of
* borrow boxes, scratch work, or other decorations.
*
* @param cellDimensions - Cell sizing information
* @returns Typst code for operator overlay using place()
*/
export function generateOperatorOverlay(cellDimensions: CellDimensions): string {
const { cellSizeIn, cellSizePt } = cellDimensions
// The operator should be positioned at the subtrahend row level
// Borrow boxes row height + minuend row height = 2 * cellSize from top
// We position relative to the grid, so we need to account for:
// - Row 0: borrow boxes (height: cellSize)
// - Row 1: minuend row (height: cellSize)
// - Row 2: subtrahend row (where operator should appear)
return String.raw`
// Operator overlay - rendered last for proper layering
// Position: left edge, at subtrahend row vertical position
#place(
left + top,
dx: 0pt,
dy: ${cellSizeIn} * 2, // Skip borrow boxes row + minuend row
box(width: 0.5em, height: ${cellSizeIn})[
#align(center + horizon)[
#text(size: ${(cellSizePt * 0.8).toFixed(1)}pt)[]
]
]
)`
}

View File

@@ -3,10 +3,11 @@
import { getPlaceValueColorNames } from '../shared/colors'
import type { CellDimensions } from '../shared/types'
import { generateAnswerBoxesRow, generateLineRow, generateTenFramesRow } from './answerRow'
import { generateBorrowBoxesRow } from './borrowBoxes'
import { generateMinuendRow } from './minuendRow'
import { generateOperatorOverlay } from './operatorOverlay'
import { generateSubtrahendRow } from './subtrahendRow'
import { generateLineRow, generateTenFramesRow, generateAnswerBoxesRow } from './answerRow'
/**
* Generate the main subtraction problem stack function for Typst
@@ -115,9 +116,11 @@ export function generateSubtractionProblemStackFunction(
dir: ttb,
spacing: 0pt,
problem-number-display,
grid(
columns: column-list,
gutter: 0pt,
// Wrap grid in a box to enable place() overlay for operator
box[
#grid(
columns: column-list,
gutter: 0pt,
${generateBorrowBoxesRow(cellDimensions)}
@@ -130,7 +133,9 @@ ${generateLineRow(cellDimensions)}
${generateTenFramesRow(cellDimensions)}
${generateAnswerBoxesRow(cellDimensions)}
)
)
${generateOperatorOverlay(cellDimensions)}
]
)
}
`

View File

@@ -18,12 +18,8 @@ export function generateSubtrahendRow(cellDimensions: CellDimensions): string {
const { cellSize, cellSizeIn, cellSizePt } = cellDimensions
return String.raw`
// Subtrahend row with sign
box(width: 0.5em, height: ${cellSizeIn})[
#align(center + horizon)[
#text(size: ${(cellSizePt * 0.8).toFixed(1)}pt)[]
]
],
// Subtrahend row (operator sign rendered separately via place() for proper layering)
[], // Empty cell for operator column (operator overlaid later)
..for i in range(0, grid-digits).rev() {
let digit = s-digits.at(i)
let place-color = place-colors.at(i)