diff --git a/apps/web/src/app/create/worksheets/addition/components/DisplayOptionsPreview.tsx b/apps/web/src/app/create/worksheets/addition/components/DisplayOptionsPreview.tsx
index 1e4d0380..c7f69603 100644
--- a/apps/web/src/app/create/worksheets/addition/components/DisplayOptionsPreview.tsx
+++ b/apps/web/src/app/create/worksheets/addition/components/DisplayOptionsPreview.tsx
@@ -9,6 +9,128 @@ interface DisplayOptionsPreviewProps {
formState: WorksheetFormState
}
+interface MathSentenceProps {
+ operands: number[]
+ operator: string
+ onChange: (operands: number[]) => void
+ labels?: string[]
+}
+
+/**
+ * Flexible math sentence component supporting operators with arity 1-3
+ * Examples:
+ * Arity 1 (unary): [64] with "√" → "√64"
+ * Arity 2 (binary): [45, 27] with "+" → "45 + 27"
+ * Arity 3 (ternary): [5, 10, 15] with "between" → "5 < 10 < 15"
+ */
+function MathSentence({ operands, operator, onChange, labels }: MathSentenceProps) {
+ const handleOperandChange = (index: number, value: string) => {
+ const numValue = Number.parseInt(value, 10)
+ if (!Number.isNaN(numValue) && numValue >= 0 && numValue <= 99) {
+ const newOperands = [...operands]
+ newOperands[index] = numValue
+ onChange(newOperands)
+ }
+ }
+
+ const renderInput = (value: number, index: number) => (
+ handleOperandChange(index, e.target.value)}
+ aria-label={labels?.[index] || `operand ${index + 1}`}
+ className={css({
+ width: '3.5em',
+ px: '1',
+ py: '0.5',
+ fontSize: 'sm',
+ fontWeight: 'medium',
+ textAlign: 'center',
+ border: '1px solid',
+ borderColor: 'transparent',
+ rounded: 'sm',
+ outline: 'none',
+ transition: 'border-color 0.2s',
+ _hover: {
+ borderColor: 'gray.300',
+ },
+ _focus: {
+ borderColor: 'brand.500',
+ ring: '1px',
+ ringColor: 'brand.200',
+ },
+ })}
+ />
+ )
+
+ // Render based on arity
+ if (operands.length === 1) {
+ // Unary operator (prefix): √64 or ±5
+ return (
+
+ {operator}
+ {renderInput(operands[0], 0)}
+
+ )
+ }
+
+ if (operands.length === 2) {
+ // Binary operator (infix): 45 + 27
+ return (
+
+ {renderInput(operands[0], 0)}
+ {operator}
+ {renderInput(operands[1], 1)}
+
+ )
+ }
+
+ if (operands.length === 3) {
+ // Ternary operator: 5 < 10 < 15 or similar
+ return (
+
+ {renderInput(operands[0], 0)}
+ {operator}
+ {renderInput(operands[1], 1)}
+ {operator}
+ {renderInput(operands[2], 2)}
+
+ )
+ }
+
+ return null
+}
+
async function fetchExample(options: {
showCarryBoxes: boolean
showAnswerBoxes: boolean
@@ -17,6 +139,8 @@ async function fetchExample(options: {
showCellBorder: boolean
showTenFrames: boolean
showTenFramesForAll: boolean
+ addend1: number
+ addend2: number
}): Promise {
const response = await fetch('/api/create/worksheets/addition/example', {
method: 'POST',
@@ -36,6 +160,9 @@ async function fetchExample(options: {
}
export function DisplayOptionsPreview({ formState }: DisplayOptionsPreviewProps) {
+ // Local state for operands (not debounced - we want immediate feedback)
+ const [operands, setOperands] = useState([45, 27])
+
// Debounce the display options to avoid hammering the server
const [debouncedOptions, setDebouncedOptions] = useState({
showCarryBoxes: formState.showCarryBoxes ?? true,
@@ -45,6 +172,8 @@ export function DisplayOptionsPreview({ formState }: DisplayOptionsPreviewProps)
showCellBorder: formState.showCellBorder ?? true,
showTenFrames: formState.showTenFrames ?? false,
showTenFramesForAll: formState.showTenFramesForAll ?? false,
+ addend1: operands[0],
+ addend2: operands[1],
})
useEffect(() => {
@@ -57,6 +186,8 @@ export function DisplayOptionsPreview({ formState }: DisplayOptionsPreviewProps)
showCellBorder: formState.showCellBorder ?? true,
showTenFrames: formState.showTenFrames ?? false,
showTenFramesForAll: formState.showTenFramesForAll ?? false,
+ addend1: operands[0],
+ addend2: operands[1],
})
}, 300) // 300ms debounce
@@ -69,6 +200,7 @@ export function DisplayOptionsPreview({ formState }: DisplayOptionsPreviewProps)
formState.showCellBorder,
formState.showTenFrames,
formState.showTenFramesForAll,
+ operands,
])
const { data: svg, isLoading } = useQuery({
@@ -81,7 +213,7 @@ export function DisplayOptionsPreview({ formState }: DisplayOptionsPreviewProps)
- Preview
+
+ Preview
+
+
{isLoading ? (