Commit Graph

12 Commits

Author SHA1 Message Date
Thomas Hallock b36df3a40c fix(worksheets): ten-frames not rendering in mastery mode
Fixed two critical bugs preventing ten-frames from rendering:

1. **Mastery mode not handled** (typstGenerator.ts:61)
   - Code only checked for 'smart' | 'manual' modes
   - Mastery mode fell into manual path, tried to use boolean flags that don't exist
   - Resulted in all display options being `undefined`
   - Fix: Check for both 'smart' OR 'mastery' modes (both use displayRules)

2. **Typst array membership syntax** (already fixed in previous commit)
   - Used `(i in array)` which doesn't work in Typst
   - Changed to `array.contains(i)`

Added comprehensive unit tests (tenFrames.test.ts):
- Problem analysis tests (regrouping detection)
- Display rule evaluation tests
- Full Typst template generation tests
- Mastery mode specific tests
- All 14 tests now passing

Added debug logging to trace display rules resolution:
- displayRules.ts: Shows rule evaluation per problem
- typstGenerator.ts: Shows enriched problems and Typst data
- Helps diagnose future issues

The issue was that mastery mode (which uses displayRules like smart mode)
was being treated as manual mode (which uses boolean flags), resulting in
undefined display options.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:06:27 -06:00
Thomas Hallock cd75df7221 chore: restore stashed work from previous session
Recover all changes from stash including:
- Linter/formatter updates across codebase
- Settings permission updates for git checkout

This commit captures the complete state of work that was
stashed during the previous session's git operations.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 14:59:40 -06:00
Thomas Hallock b6c3d6bda4 refactor: use package-level cropToActiveBeads in generateDayIcon script
Replace custom bounding box calculations with the new cropToActiveBeads prop:
- Remove 100+ lines of manual bbox calculation code (getAbacusBoundingBox)
- Use AbacusStatic instead of AbacusReact for simpler SSR
- Let cropToActiveBeads handle all cropping logic with padding config
- Simplify script to just parse dimensions from cropped SVG and center in canvas

This eliminates code duplication and leverages the precise cropping utilities
now available in the package.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 10:01:46 -06:00
Thomas Hallock 41a3707841 fix: replace regex HTML parsing with deterministic bead position calculations in icon generation
- Replace fragile regex parsing in generateDayIcon.tsx with proper bead
  position calculations using numberToAbacusState(), calculateStandardDimensions(),
  and calculateBeadPosition() from @soroban/abacus-react
- Add query parameter support to /icon route for testing different days (e.g. /icon?day=15)
- Fix icon cropping to properly show only active beads with dynamic viewBox
- Validate day parameter (1-31) and return 400 for invalid values
- Different cache duration for production (1 hour) vs testing (1 minute)

Results:
- Day 1: 48.88px height (minimal)
- Day 5: 48.88px height (single heaven bead)
- Day 25: 100.18px height (many active beads)
- Day 31: 93.88px height

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 09:10:17 -06:00
Thomas Hallock 944ad6574e feat: switch to royal color theme with transparent background
Update favicon design based on user preference:
- Ones place: Gold (#fbbf24, stroke #f59e0b)
- Tens place: Purple (#a855f7, stroke #7e22ce)
- Remove background circle for clean, transparent appearance

The royal color scheme provides better contrast and a more
sophisticated look while maintaining excellent legibility at small sizes.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 0e529be789 fix: reduce padding to minimize gap below last bead
Use asymmetric padding to crop tighter to the active bead region:
- Top: 8px (some breathing room above)
- Bottom: 2px (minimal gap below last bead)
- Sides: 5px

This eliminates the visible gap of column post structure below the
last active bead while maintaining clean spacing above.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 440b492e85 fix: use nested SVG viewBox for actual cropping, not just scaling
Previous approach used transforms to scale the entire SVG, which didn't
actually crop anything - just scaled everything uniformly.

New approach uses nested SVG with viewBox to truly crop the content:
- Outer SVG: 100x100 canvas with background circle
- Inner SVG: Uses viewBox to show only the cropped region
- viewBox dimensions vary per day based on active bead positions

Results:
- Day 1 (1 bead): viewBox height ~59px (narrow vertical crop)
- Day 15 (mixed): viewBox height ~88px (medium)
- Day 31 (many): viewBox height ~104px (tall vertical crop)
- Horizontal: Always ~110px (both columns with posts)

This actually maximizes bead size by showing only the relevant vertical region.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 83090df4df fix: separate horizontal and vertical bounding box logic
Clarify that horizontal and vertical cropping use different criteria:
- Horizontal (X): Always show full width of both columns (from structural rects)
- Vertical (Y): Crop dynamically to active bead positions + heights

This ensures consistent horizontal scale (0.8) across all days, with only
vertical positioning adjusting based on where active beads are located.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 0b2f48106a fix: include column posts in favicon bounding box
Refined the cropping algorithm to include structural elements (column posts)
in the bounding box calculation, not just active beads. This ensures the
complete abacus structure is visible.

Algorithm changes:
- X range: Full width from leftmost to rightmost post (all columns)
- Y range: From top active bead to bottom active bead (tight vertical crop)

Results:
- Consistent scale of 0.8 across all days (vs original 0.48)
- Shows complete column structure with posts
- Vertical position adjusts based on active bead positions
- Maintains visual context while maximizing bead visibility

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 567032296a feat: dynamically crop favicon to active beads for maximum size
Implement bounding box calculation to crop the favicon SVG to show only
the active beads, maximizing their size within the 100x100 icon canvas.

Algorithm:
1. Parse rendered SVG to find all active bead positions (via regex)
2. Calculate bounding box with 15px padding
3. Compute optimal scale to fit within 96x96 (leaving border room)
4. Apply transform: translate + scale + translate to crop and center

Results:
- Day 1-9 (few beads): scale ~1.14 (2.4x larger than before)
- Day 31 (many beads): scale ~0.74 (1.5x larger than before)
- Original fixed scale: 0.48

This uses no external dependencies - just regex parsing of the rendered
SVG to extract active bead coordinates.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock e1369fa275 fix: configure favicon metadata and improve bead visibility
Metadata fixes:
- Update icon path from /icon.svg to /icon to match route handler
- Ensure proper MIME type (image/svg+xml) is set in both metadata and response

Favicon visibility improvements:
- Increase AbacusReact scaleFactor from 1.0 to 1.8 for larger beads
- Add hideInactiveBeads prop to hide inactive beads
- Add hide-inactive-mode class to wrapper for CSS to take effect
- Adjust outer scale to 0.48 to fit larger abacus in 100x100 viewBox
- Reposition abacus with translate(28, -2) for proper centering

CSS cleanup:
- Strip !important declarations from generated SVG (production code policy)
- Apply same fix to OG image generator

User needs to restart dev server to clear in-memory cache.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00
Thomas Hallock 4d0795a9df feat: dynamic day-of-month favicon using subprocess pattern
- Create scripts/generateDayIcon.tsx for on-demand icon generation
- Route handler calls script via execSync (avoids Next.js react-dom/server restriction)
- Implement in-memory caching to minimize subprocess overhead
- Show current day (01-31) on 2-column abacus in US Central Time
- High-contrast design: blue/green beads, 2px strokes, gold border
- Document SSR pattern in .claude/CLAUDE.md for future reference

Fixes the Next.js limitation where route handlers cannot import react-dom/server
by using subprocess pattern - scripts CAN use react-dom/server, route handlers
call them via execSync and cache the results.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 08:59:14 -06:00