feat: apply skill-specific scaffolding and fix mini card heights
**Issue 1: Too much scaffolding on mastery skills** - Mastery skills have `recommendedScaffolding` defined but weren't being applied - "Five-digit mastery" should have zero scaffolding (no carry boxes, no answer boxes) **Issue 2: Variable card heights in mixed mode** - Mini skill cards used `minHeight` which allowed them to grow - Description text of varying lengths caused buttons to jump around **Fix 1: Auto-apply recommendedScaffolding in mastery mode** - validation.ts: Query skill by ID and apply its recommendedScaffolding - Single operator: Use skill's exact scaffolding rules - Mixed mode: Merge both skills, taking least scaffolding (most restrictive) - 5d-mastery skills now correctly show: - carryBoxes: 'never' - answerBoxes: 'never' - placeValueColors: 'when3PlusDigits' (shown for 5-digit, hidden otherwise) - tenFrames: 'never' **Fix 2: Enforce fixed height for mini cards** - Changed `minHeight` → `height: '5.5rem'` - Added `overflow: 'hidden'` to container - Added `flexShrink: 0` to header and title - Description truncates with ellipsis after 2 lines (`WebkitLineClamp: 2`) - Buttons now stay perfectly aligned Files changed: - `validation.ts`: Apply recommendedScaffolding from skills in mastery mode - `MasteryModePanel.tsx`: Fixed height (5.5rem) with 2-line description clamp 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -287,9 +287,10 @@ export function MasteryModePanel({ formState, onChange, isDark = false }: Master
|
|||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: isDark ? 'gray.600' : 'gray.300',
|
borderColor: isDark ? 'gray.600' : 'gray.300',
|
||||||
backgroundColor: isDark ? 'gray.600' : 'white',
|
backgroundColor: isDark ? 'gray.600' : 'white',
|
||||||
minHeight: '5.5rem',
|
height: '5.5rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -300,11 +301,19 @@ export function MasteryModePanel({ formState, onChange, isDark = false }: Master
|
|||||||
marginBottom: '0.25rem',
|
marginBottom: '0.25rem',
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: '0.025em',
|
letterSpacing: '0.025em',
|
||||||
|
flexShrink: 0,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
Addition
|
Addition
|
||||||
</div>
|
</div>
|
||||||
<div className={css({ display: 'flex', alignItems: 'center', gap: '0.375rem' })}>
|
<div
|
||||||
|
className={css({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '0.375rem',
|
||||||
|
flexShrink: 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<h4
|
<h4
|
||||||
className={css({
|
className={css({
|
||||||
fontSize: '0.8125rem',
|
fontSize: '0.8125rem',
|
||||||
@@ -333,6 +342,11 @@ export function MasteryModePanel({ formState, onChange, isDark = false }: Master
|
|||||||
color: isDark ? 'gray.400' : 'gray.600',
|
color: isDark ? 'gray.400' : 'gray.600',
|
||||||
marginTop: '0.25rem',
|
marginTop: '0.25rem',
|
||||||
lineHeight: '1.3',
|
lineHeight: '1.3',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
display: '-webkit-box',
|
||||||
|
WebkitLineClamp: 2,
|
||||||
|
WebkitBoxOrient: 'vertical',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{currentAdditionSkill.description}
|
{currentAdditionSkill.description}
|
||||||
@@ -432,9 +446,10 @@ export function MasteryModePanel({ formState, onChange, isDark = false }: Master
|
|||||||
border: '1px solid',
|
border: '1px solid',
|
||||||
borderColor: isDark ? 'gray.600' : 'gray.300',
|
borderColor: isDark ? 'gray.600' : 'gray.300',
|
||||||
backgroundColor: isDark ? 'gray.600' : 'white',
|
backgroundColor: isDark ? 'gray.600' : 'white',
|
||||||
minHeight: '5.5rem',
|
height: '5.5rem',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
overflow: 'hidden',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@@ -445,11 +460,19 @@ export function MasteryModePanel({ formState, onChange, isDark = false }: Master
|
|||||||
marginBottom: '0.25rem',
|
marginBottom: '0.25rem',
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
letterSpacing: '0.025em',
|
letterSpacing: '0.025em',
|
||||||
|
flexShrink: 0,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
Subtraction
|
Subtraction
|
||||||
</div>
|
</div>
|
||||||
<div className={css({ display: 'flex', alignItems: 'center', gap: '0.375rem' })}>
|
<div
|
||||||
|
className={css({
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: '0.375rem',
|
||||||
|
flexShrink: 0,
|
||||||
|
})}
|
||||||
|
>
|
||||||
<h4
|
<h4
|
||||||
className={css({
|
className={css({
|
||||||
fontSize: '0.8125rem',
|
fontSize: '0.8125rem',
|
||||||
@@ -478,6 +501,11 @@ export function MasteryModePanel({ formState, onChange, isDark = false }: Master
|
|||||||
color: isDark ? 'gray.400' : 'gray.600',
|
color: isDark ? 'gray.400' : 'gray.600',
|
||||||
marginTop: '0.25rem',
|
marginTop: '0.25rem',
|
||||||
lineHeight: '1.3',
|
lineHeight: '1.3',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
display: '-webkit-box',
|
||||||
|
WebkitLineClamp: 2,
|
||||||
|
WebkitBoxOrient: 'vertical',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{currentSubtractionSkill.description}
|
{currentSubtractionSkill.description}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import type { WorksheetFormState, WorksheetConfig, ValidationResult } from './types'
|
import type { WorksheetFormState, WorksheetConfig, ValidationResult } from './types'
|
||||||
import type { DisplayRules } from './displayRules'
|
import type { DisplayRules } from './displayRules'
|
||||||
|
import { getSkillById } from './skills'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current date formatted as "Month Day, Year"
|
* Get current date formatted as "Month Day, Year"
|
||||||
@@ -150,7 +151,9 @@ export function validateWorksheetConfig(formState: WorksheetFormState): Validati
|
|||||||
|
|
||||||
if (mode === 'smart' || mode === 'mastery') {
|
if (mode === 'smart' || mode === 'mastery') {
|
||||||
// Smart & Mastery modes: Use displayRules for conditional scaffolding
|
// Smart & Mastery modes: Use displayRules for conditional scaffolding
|
||||||
const displayRules: DisplayRules = {
|
|
||||||
|
// Default display rules
|
||||||
|
let baseDisplayRules: DisplayRules = {
|
||||||
carryBoxes: 'whenRegrouping',
|
carryBoxes: 'whenRegrouping',
|
||||||
answerBoxes: 'always',
|
answerBoxes: 'always',
|
||||||
placeValueColors: 'always',
|
placeValueColors: 'always',
|
||||||
@@ -159,6 +162,67 @@ export function validateWorksheetConfig(formState: WorksheetFormState): Validati
|
|||||||
cellBorders: 'always',
|
cellBorders: 'always',
|
||||||
borrowNotation: 'whenRegrouping', // Subtraction: show when borrowing
|
borrowNotation: 'whenRegrouping', // Subtraction: show when borrowing
|
||||||
borrowingHints: 'never', // Subtraction: no hints by default
|
borrowingHints: 'never', // Subtraction: no hints by default
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mastery mode: Apply recommendedScaffolding from current skill(s)
|
||||||
|
if (mode === 'mastery') {
|
||||||
|
const operator = formState.operator ?? 'addition'
|
||||||
|
|
||||||
|
if (operator === 'mixed') {
|
||||||
|
// Mixed mode: Merge scaffolding from both skills
|
||||||
|
const addSkillId = formState.currentAdditionSkillId
|
||||||
|
const subSkillId = formState.currentSubtractionSkillId
|
||||||
|
|
||||||
|
if (addSkillId && subSkillId) {
|
||||||
|
const addSkill = getSkillById(addSkillId as any)
|
||||||
|
const subSkill = getSkillById(subSkillId as any)
|
||||||
|
|
||||||
|
if (addSkill?.recommendedScaffolding && subSkill?.recommendedScaffolding) {
|
||||||
|
// Use the LEAST scaffolding from both (most restrictive)
|
||||||
|
// This ensures mastery-level problems have minimal scaffolding
|
||||||
|
baseDisplayRules = {
|
||||||
|
// Take 'never' if either skill recommends it
|
||||||
|
carryBoxes:
|
||||||
|
addSkill.recommendedScaffolding.carryBoxes === 'never' ||
|
||||||
|
subSkill.recommendedScaffolding.carryBoxes === 'never'
|
||||||
|
? 'never'
|
||||||
|
: 'whenRegrouping',
|
||||||
|
answerBoxes:
|
||||||
|
addSkill.recommendedScaffolding.answerBoxes === 'never' ||
|
||||||
|
subSkill.recommendedScaffolding.answerBoxes === 'never'
|
||||||
|
? 'never'
|
||||||
|
: 'always',
|
||||||
|
placeValueColors:
|
||||||
|
addSkill.recommendedScaffolding.placeValueColors === 'never' ||
|
||||||
|
subSkill.recommendedScaffolding.placeValueColors === 'never'
|
||||||
|
? 'never'
|
||||||
|
: addSkill.recommendedScaffolding.placeValueColors,
|
||||||
|
tenFrames: 'never', // Always off for mastery
|
||||||
|
problemNumbers: 'always',
|
||||||
|
cellBorders: 'always',
|
||||||
|
borrowNotation: subSkill.recommendedScaffolding.borrowNotation,
|
||||||
|
borrowingHints: 'never',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Single operator: Use its recommendedScaffolding
|
||||||
|
const skillId =
|
||||||
|
operator === 'addition'
|
||||||
|
? formState.currentAdditionSkillId
|
||||||
|
: formState.currentSubtractionSkillId
|
||||||
|
|
||||||
|
if (skillId) {
|
||||||
|
const skill = getSkillById(skillId as any)
|
||||||
|
if (skill?.recommendedScaffolding) {
|
||||||
|
baseDisplayRules = { ...skill.recommendedScaffolding }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayRules: DisplayRules = {
|
||||||
|
...baseDisplayRules,
|
||||||
...((formState.displayRules as any) ?? {}), // Override with provided rules if any
|
...((formState.displayRules as any) ?? {}), // Override with provided rules if any
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user