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 types for internal use
import type { DisplayOptions } from './typstHelpers/shared/types' 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 // Re-export everything from modular structure
export type { export type {
DisplayOptions,
CellDimensions, CellDimensions,
DisplayOptions,
} from './typstHelpers/shared/types' } from './typstHelpers/shared/types'
export { generateTypstHelpers } from './typstHelpers/shared/helpers'
export { generatePlaceValueColors } from './typstHelpers/shared/colors'
export { generateSubtractionProblemStackFunction } from './typstHelpers/subtraction/problemStack' export { generateSubtractionProblemStackFunction } from './typstHelpers/subtraction/problemStack'
/** /**
@@ -103,12 +103,14 @@ export function generateProblemStackFunction(cellSize: number, maxDigits: number
dir: ttb, dir: ttb,
spacing: 0pt, spacing: 0pt,
problem-number-display, problem-number-display,
grid( // Wrap grid in a box to enable place() overlay for operator
columns: column-list, box[
gutter: 0pt, #grid(
columns: column-list,
gutter: 0pt,
// Carry boxes row (one per place value, right to left) // Carry boxes row (one per place value, right to left)
[], // Empty cell for + sign column [], // Empty cell for + sign column
..for i in range(0, actual-digits).rev() { ..for i in range(0, actual-digits).rev() {
// DEBUG: Show which place values get carry boxes and why // DEBUG: Show which place values get carry boxes and why
let show-carry = show-carries and i > 0 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) // Second addend row (operator sign rendered separately via place() for proper layering)
box(width: 0.5em, height: ${cellSizeIn})[#align(center + horizon)[#text(size: ${(cellSizePt * 0.8).toFixed(1)}pt)[+]]], [], // Empty cell for operator column (operator overlaid later)
..for i in range(0, actual-digits).rev() { ..for i in range(0, actual-digits).rev() {
let digit = b-digits.at(i) let digit = b-digits.at(i)
let place-color = place-colors.at(i) // Dynamic color lookup by place value 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 { getPlaceValueColorNames } from '../shared/colors'
import type { CellDimensions } from '../shared/types' import type { CellDimensions } from '../shared/types'
import { generateAnswerBoxesRow, generateLineRow, generateTenFramesRow } from './answerRow'
import { generateBorrowBoxesRow } from './borrowBoxes' import { generateBorrowBoxesRow } from './borrowBoxes'
import { generateMinuendRow } from './minuendRow' import { generateMinuendRow } from './minuendRow'
import { generateOperatorOverlay } from './operatorOverlay'
import { generateSubtrahendRow } from './subtrahendRow' import { generateSubtrahendRow } from './subtrahendRow'
import { generateLineRow, generateTenFramesRow, generateAnswerBoxesRow } from './answerRow'
/** /**
* Generate the main subtraction problem stack function for Typst * Generate the main subtraction problem stack function for Typst
@@ -115,9 +116,11 @@ export function generateSubtractionProblemStackFunction(
dir: ttb, dir: ttb,
spacing: 0pt, spacing: 0pt,
problem-number-display, problem-number-display,
grid( // Wrap grid in a box to enable place() overlay for operator
columns: column-list, box[
gutter: 0pt, #grid(
columns: column-list,
gutter: 0pt,
${generateBorrowBoxesRow(cellDimensions)} ${generateBorrowBoxesRow(cellDimensions)}
@@ -130,7 +133,9 @@ ${generateLineRow(cellDimensions)}
${generateTenFramesRow(cellDimensions)} ${generateTenFramesRow(cellDimensions)}
${generateAnswerBoxesRow(cellDimensions)} ${generateAnswerBoxesRow(cellDimensions)}
) )
${generateOperatorOverlay(cellDimensions)}
]
) )
} }
` `

View File

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