feat: implement precise inline highlighting of pedagogical terms

- Add termPosition field to ExpectedStep interface
- Update renderHighlightedDecomposition to use precise string positions
- Replace problematic indexOf approach with exact position data
- Integrate position information from unified step generator
- Fix issue with repeated mathematical terms in decomposition
- Improve pedagogical feedback by highlighting current term precisely

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock
2025-09-23 08:39:49 -05:00
parent 52323aeba8
commit 538d356f03

View File

@@ -29,6 +29,7 @@ interface ExpectedStep {
startValue: number
description: string
mathematicalTerm?: string // Pedagogical term like "10", "(5 - 1)", "-6"
termPosition?: { startIndex: number; endIndex: number } // Position in full decomposition
}
type TutorialPlayerAction =
@@ -187,7 +188,7 @@ export function TutorialPlayer({
}
// Define the static expected steps using our unified step generator
const expectedSteps: ExpectedStep[] = useMemo(() => {
const { expectedSteps, fullDecomposition } = useMemo(() => {
try {
const unifiedSequence = generateUnifiedInstructionSequence(currentStep.startValue, currentStep.targetValue)
@@ -198,12 +199,19 @@ export function TutorialPlayer({
targetValue: step.expectedValue,
startValue: index === 0 ? currentStep.startValue : unifiedSequence.steps[index - 1].expectedValue,
description: step.englishInstruction,
mathematicalTerm: step.mathematicalTerm // Add the pedagogical term
mathematicalTerm: step.mathematicalTerm, // Add the pedagogical term
termPosition: step.termPosition // Add the precise position information
}))
return steps
return {
expectedSteps: steps,
fullDecomposition: unifiedSequence.fullDecomposition
}
} catch (error) {
return []
return {
expectedSteps: [],
fullDecomposition: ''
}
}
}, [currentStep.startValue, currentStep.targetValue])
@@ -273,6 +281,40 @@ export function TutorialPlayer({
// Get current step summary for real-time user feedback
const currentStepSummary = getCurrentStepSummary()
// Helper function to highlight the current mathematical term in the full decomposition
const renderHighlightedDecomposition = useCallback(() => {
if (!fullDecomposition || expectedSteps.length === 0) return null
const currentStep = expectedSteps[currentMultiStep]
if (!currentStep?.mathematicalTerm || !currentStep?.termPosition) {
return <span>{fullDecomposition}</span>
}
// Use the precise position information from the unified generator
const { startIndex, endIndex } = currentStep.termPosition
const beforeTerm = fullDecomposition.substring(0, startIndex)
const term = fullDecomposition.substring(startIndex, endIndex)
const afterTerm = fullDecomposition.substring(endIndex)
return (
<span>
{beforeTerm}
<span className={css({
bg: 'blue.100',
color: 'blue.800',
px: 1,
borderRadius: 'sm',
border: '2px solid',
borderColor: 'blue.300',
fontWeight: 'bold'
})}>
{term}
</span>
{afterTerm}
</span>
)
}, [fullDecomposition, expectedSteps, currentMultiStep])
// Event logging - now just notifies parent, state is managed by reducer
const notifyEvent = useCallback((event: TutorialEvent) => {
onEvent?.(event)
@@ -816,6 +858,34 @@ export function TutorialPlayer({
maxW: '600px',
w: 'full'
})}>
{/* Pedagogical decomposition with current term highlighted */}
{fullDecomposition && (
<div className={css({
mb: 3,
p: 2,
bg: 'gray.50',
border: '1px solid',
borderColor: 'gray.200',
borderRadius: 'md'
})}>
<p className={css({
fontSize: 'xs',
fontWeight: 'medium',
color: 'gray.700',
mb: 1
})}>
Problem Breakdown:
</p>
<p className={css({
fontSize: 'sm',
color: 'gray.800',
fontFamily: 'mono'
})}>
{renderHighlightedDecomposition()}
</p>
</div>
)}
<p className={css({
fontSize: 'sm',
fontWeight: 'medium',