fix: refactor worksheet config persistence to blacklist approach + Storybook stories
This commit addresses a critical bug where shared worksheets showed incorrect page counts,
and refactors the config extraction architecture to prevent future sharing bugs.
## Bug Fix: Shared Worksheet Page Count
**Problem:** When sharing a 100-page worksheet, opening the share link showed only 4 pages.
**Root Cause:**
- `extractConfigFields()` correctly excluded `total` (derived state)
- `validateWorksheetConfig()` incorrectly used `formState.total ?? 20` as fallback
- Result: Shared worksheets defaulted to `total: 20` instead of calculating from `pages × problemsPerPage`
**Solution:**
- Calculate `total = problemsPerPage × pages` from PRIMARY state
- Never use `formState.total` as source of truth - it's derived!
```typescript
// Before (bug)
const total = formState.total ?? 20
// After (fix)
const problemsPerPage = formState.problemsPerPage ?? 20
const pages = formState.pages ?? 1
const total = problemsPerPage * pages // 100 pages × 20 problems = 2000 ✓
```
## Architecture: Blacklist Refactor
**Problem:** Multiple incidents where new config fields broke shared worksheets because
we forgot to update the extraction whitelist.
**Old Approach (FRAGILE):**
```typescript
// Manually list every field - easy to forget new ones!
return {
problemsPerPage: formState.problemsPerPage,
cols: formState.cols,
// ... 30+ fields manually listed
}
```
**New Approach (ROBUST):**
```typescript
// Automatically include everything except derived/ephemeral fields
const { rows, total, date, ...persistedFields } = formState
return persistedFields
```
**Benefits:**
- ✅ New config fields automatically work in shared worksheets
- ✅ Only need to update if adding DERIVED fields (rare)
- ✅ Much harder to accidentally break sharing
## Documentation
Added comprehensive docs explaining PRIMARY vs DERIVED state:
**Architecture docs:**
- `.claude/WORKSHEET_CONFIG_PERSISTENCE.md` - Full architecture, examples, history
- `src/app/create/worksheets/README_CONFIG_PERSISTENCE.md` - Quick reference
**In-code docs:**
- `extractConfigFields.ts` - Blacklist approach, field categories, usage
- `validation.ts` - PRIMARY → DERIVED state calculation with examples
- `types.ts` - Field category markers (PRIMARY/DERIVED/EPHEMERAL)
## Storybook Stories
Added comprehensive Storybook stories for worksheet generator components:
**Component Stories:**
- `AdditionWorksheetClient.stories.tsx` - Full generator with virtual loading demos
- `ConfigSidebar.stories.tsx` - Configuration panel with all tabs
- `ResponsivePanelLayout.stories.tsx` - Desktop/mobile layout variations
- `WorksheetPreview.stories.tsx` - Preview component with different states
- `PagePlaceholder.stories.tsx` - Animated loading placeholders
**Config Panel Stories:**
- `OverallDifficultySlider.stories.tsx` - Difficulty slider variations
- `RuleDropdown.stories.tsx` - Display rule dropdown states
- `ToggleOption.stories.tsx` - Toggle option component
**Placeholder Enhancements:**
- Added fast-cycling animated problem variations (0.5-1s cycles)
- Bigger, bolder, higher-contrast math elements
- Removed redundant "Loading page..." overlay
- Added keyframes: wiggle, shimmer, fadeInScale, slideInRight, morphWidth, colorShift
**Storybook Config:**
- Renamed `preview.ts` → `preview.tsx` for JSX support
- Updated `main.ts` for Panda CSS integration
## Testing
Manual test checklist for sharing:
1. Create 100-page worksheet (2000 problems)
2. Click "Share"
3. Open share link in incognito
4. Verify: Shows 100 pages, 2000 total problems ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>