Commit Graph

40 Commits

Author SHA1 Message Date
Thomas Hallock
3656800534 refactor(worksheets): extract ConfigPanel helper components (Phase 1)
Created config-panel/ subdirectory and extracted reusable UI components
from the monolithic 2550-line ConfigPanel.tsx file.

Extracted components:
- utils.tsx: getScaffoldingSummary() function
- SubOption.tsx: Nested toggle UI component
- ToggleOption.tsx: Main toggle option with description

Changes:
- Created src/app/create/worksheets/addition/components/config-panel/ directory
- Moved 3 helper functions/components to separate files (~240 lines)
- Updated ConfigPanel.tsx to import from new locations
- Removed unused imports (Checkbox, findNearestPreset)
- File size reduced: 2550 → 2306 lines (-244 lines)

Zero functionality change - all components work identically.
Part 1 of 5-phase refactoring plan.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 11:50:49 -06:00
Thomas Hallock
fcbf0f5421 fix(abacus-react): remove duplicate numeral rendering and fix dark mode colors
Two critical issues fixed:

1. **Duplicate numerals**: Both AbacusSVGRenderer and AbacusReact were rendering
   numerals when showNumbers={true}, causing two overlapping number displays.
   - Disabled SVG text numerals in AbacusSVGRenderer (line 436: added `false &&`)
   - NumberFlow provides better animated numerals, keep only those

2. **White numerals in dark mode**: NumberFlow components were inheriting CSS
   color from parent, turning white in dark mode (unreadable on light frames).
   - Added explicit color style to NumberFlow: uses themeAwareCustomStyles
   - Now consistently dark (rgba(0,0,0,0.8)) regardless of page theme

This was the root cause of the "white numerals everywhere" issue - the
NumberFlow components were inheriting dark mode CSS colors.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-08 09:43:18 -06:00
Thomas Hallock
b191bb9a82 feat(worksheets): Phase 5 - Update typstGenerator for operator support
Updates typstGenerator.ts to handle both addition and subtraction:
- Import WorksheetProblem union type instead of just AdditionProblem
- Import subtraction rendering and analysis functions
- Update calculateMaxDigits to handle both operators
- Update problem enrichment to analyze addition vs subtraction
- Generate Typst problem data with operator discriminator
- Dispatch to correct rendering function based on operator
- Update displayRules to handle both ProblemMeta and SubtractionProblemMeta

Also updates:
- problemGenerator.ts: Add operator field to AdditionProblem
- displayRules.ts: Support AnyProblemMeta union type
- validation.ts: Add operator to shared config fields

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 22:30:15 -06:00
Thomas Hallock
904701da2b feat: optimize ten-frame blog examples for dark theme
- Remove all scaffolding (carry boxes, answer boxes, place value colors) from ten-frame examples
- Show only the core addition problems with ten-frame visualization
- Add transparent background support for blog examples
- Implement smart color replacement for dark theme visibility:
  - White operator symbols (+) and horizontal bars (visible on dark background)
  - Black digits on white cells (preserved contrast)
  - White structural borders and lines
- Update generateTenFrameExamples.ts with post-processing to handle SVG colors
- Regenerate all 4 ten-frame example SVGs with clean, minimal presentation

The examples now focus purely on demonstrating the ten-frame concept
without distracting extra scaffolding elements.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 14:05:24 -06:00
Thomas Hallock
7c33d0246f fix: prevent undefined displayRules error in worksheet generator
Fixes production error "Cannot read properties of undefined (reading 'carryBoxes')"
that occurred when users tried to adjust difficulty settings.

Root cause: displayRules was undefined for new users or users with old V1 config
in database. Difficulty adjustment buttons accessed displayRules.carryBoxes without
checking if displayRules existed first.

Changes:
- AdditionWorksheetClient: Initialize displayRules with defaults when missing
- ConfigPanel: Use null-coalescing operators instead of non-null assertions
- ConfigPanel: Add error logging when required fields are missing
- NEW: WorksheetErrorBoundary component to catch all errors in worksheet page
- page.tsx: Wrap client component with error boundary

This ensures users see helpful error messages instead of blank pages,
and never need to open the browser console to understand what went wrong.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 13:01:54 -06:00
Thomas Hallock
b628a34605 feat(blog): generate worksheet examples showing scaffolding progression
- Fix script to use correct displayRules property access
- Use valid rule modes (whenRegrouping, when3PlusDigits, whenMultipleRegroups)
- Generate 4 examples with same problem complexity but different scaffolding
- Add 'as const' assertions for TypeScript literal type inference

Generated SVGs:
- full-scaffolding.svg
- medium-scaffolding.svg
- minimal-scaffolding.svg
- no-scaffolding.svg

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 08:49:41 -06:00
Thomas Hallock
d72009776a chore(worksheets): add difficulty progression trace script
Add debugging script to visualize the constrained 2D difficulty progression:

- Traces 30 steps from beginner → maximum difficulty
- Shows path through (regrouping, scaffolding) space as 2D ASCII grid
- Marks preset positions (BEG, EAR, INT, ADV, EXP)
- Visualizes constraint band boundaries
- Helps verify navigation completeness and detect cycles

Usage: npx tsx scripts/traceDifficultyPath.ts

Output shows diagonal progression through pedagogical constraint band,
confirming no dead ends or invalid states.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 06:26:04 -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
379698fea3 refactor(web): move calendar generators to src/utils for proper compilation
The calendar generation scripts were in scripts/ directory which wasn't
included in tsconfig.server.json, so they weren't being compiled during
build. This required tsx in production just to import .tsx files at runtime.

Changes:
- Moved generateCalendarComposite.tsx and generateCalendarAbacus.tsx to src/utils/calendar/
- Removed CLI interface code from these files (they're now pure utility modules)
- Updated imports in API routes to use @/utils/calendar/...
- Moved tsx back to devDependencies where it belongs
- Removed scripts/ copy from Dockerfile (no longer needed)

Now these files are compiled to JavaScript during the build process and
don't require tsx at runtime. This fixes the architecture issue properly.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 14:26:17 -06:00
Thomas Hallock
e5ba772fde feat(abacus-react): add shared dimension calculator for consistent sizing
- Add calculateAbacusDimensions utility to AbacusUtils
- Refactor AbacusStatic to use shared dimension calculator
- Update calendar composite generator to use shared utility
- Export calculateAbacusDimensions from index and static entry points
- Ensures consistent sizing between preview and PDF generation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock
867c7ee172 feat(web): add year abacus to calendar header and make grid bolder
- Separate month name and year display in calendar header
- Render year as abacus element (15% of page width)
- Increase grid line weight from 1px to 2px
- Change grid color from light gray (#ddd) to dark (#333)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 10:43:21 -06:00
Thomas Hallock
00a8bc3e5e fix(web): move react-dom/server import to API route to satisfy Next.js
Changes:
- Scripts now export JSX elements (generateAbacusElement) or accept renderToString param
- API route imports react-dom/server (API routes are server-only, allowed)
- renderToStaticMarkup called in API route, not in imported scripts
- CLI interfaces use dynamic require() for react-dom/server when run directly

This satisfies Next.js requirement that react-dom/server not be imported in files that could be client-bundled.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 08:48:14 -06:00
Thomas Hallock
9f1715f085 refactor(web): use direct function imports instead of execSync for calendar generation
Changes:
- Refactored scripts/generateCalendarAbacus.tsx to export generateAbacusSVG function
- Refactored scripts/generateCalendarComposite.tsx to export generateCalendarComposite function
- Updated API route to import and call functions directly instead of spawning subprocesses
- Removed tsx from production Dockerfile (not needed - Next.js transpiles at build time)
- Kept scripts and abacus-react package copies in Dockerfile (needed for imports)

This eliminates the need for tsx at runtime and improves performance by avoiding subprocess overhead.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 08:27:41 -06:00
Thomas Hallock
f9cbee8fcd fix(web): use nested SVG elements to prevent coordinate space conflicts
Fixed persistent overlap by using proper SVG nesting:

Previous approach: Extracted SVG content and used g transforms
- Problem: Inner coordinates remained in original 120x230 space
- Caused overlapping when elements had absolute positioning

New approach: Nested SVG elements with viewBox
- Each abacus rendered at natural scale=1 (120×230)
- Wrapped in <svg> with:
  - x, y: position in parent calendar
  - width, height: scaled display size
  - viewBox="0 0 120 230": maps content to display size
- Creates isolated coordinate space per abacus

This is the correct SVG pattern for embedding scaled content.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 08:27:41 -06:00
Thomas Hallock
448f93c1e2 fix(web): prevent abacus overlap in composite calendar
Fixed spacing issues causing abaci to overlap:

Layout improvements:
- Calculate proper scale based on cell dimensions (CELL_WIDTH / 7, CELL_HEIGHT / 6)
- Account for natural abacus size (120×230) in scale calculation
- Use MAX_SCALE_X and MAX_SCALE_Y with 90% safety margin
- Center abacus in each cell using calculated scaled dimensions
- Add debug cell borders to visualize grid (stroke="#f0f0f0")

Math:
- ABACUS_SCALE = min(MAX_SCALE_X, MAX_SCALE_Y) × 0.9
- Position = cellCenter - (scaledSize / 2)
- This ensures abaci stay within cell boundaries

Before: Hardcoded offsets (-60, -115) caused overlaps
After: Dynamic centering based on actual scaled dimensions

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 20:24:16 -06:00
Thomas Hallock
8ce8038bae feat(web): redesign monthly calendar as single composite SVG
BREAKING FIX: Monthly calendars were overflowing to multiple pages

New "page-first" design approach:
- Generate entire calendar as one composite SVG (850x1100px)
- Includes title, year abacus, weekday headers, and all day abacuses
- Typst scales the single image to fit page (width: 100%, fit: "contain")
- Impossible to overflow - it's just one scalable image

Benefits:
 Guaranteed single-page layout across all paper sizes
 No grid overflow issues
 Consistent rendering regardless of month length
 Fast generation (~97KB SVG vs multiple small files)

Implementation:
- Created generateCalendarComposite.tsx script
- Updated route to use composite for monthly, individual SVGs for daily
- Simplified generateMonthlyTypst to just scale one image

Daily calendars unchanged (intentionally multi-page, one per day).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 20:21:40 -06:00
Thomas Hallock
08c6a419e2 fix(web): use AbacusStatic for calendar SVG generation
- Switch from AbacusReact to AbacusStatic in generateCalendarAbacus.tsx
- Fixes "failed to parse SVG (missing root node)" Typst error
- AbacusReact can't be rendered server-side with renderToStaticMarkup
- AbacusStatic is designed for pure server-side rendering

Root cause: AbacusReact has "use client" directive and uses hooks,
which causes renderToStaticMarkup to generate invalid/empty SVG.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 19:25:18 -06:00
Thomas Hallock
f03d341314 fix: adjust hero abacus position to avoid covering subtitle
Move hero abacus down from 50vh to 60vh to prevent it from overlapping
the subtitle text ("master the ancient art..." etc.) on the home page.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 13:24:15 -06:00
Thomas Hallock
ed9a050d64 fix: resolve z-index layering and hero abacus visibility issues
Fix two critical issues with the trophy abacus system:

1. Hero abacus appearing on all pages:
   - Root cause: HomeHeroProvider now wraps all pages globally
   - Solution: Use pathname check to detect actual home page routes
   - Only show hero mode on: /, /en, /de, /ja, /hi, /es, /la

2. Z-index conflicts causing layering issues:
   - AppNavBar had hardcoded z-index: 1000 (DROPDOWN layer)
   - Should use Z_INDEX.NAV_BAR (100) for proper layering
   - Tooltip had z-index: 50, should use Z_INDEX.TOOLTIP (1000)

This ensures:
- Hero abacus only appears on home page, not all pages
- Trophy abacus (z-index 30001) appears above ALL content
- Nav bar and tooltips use correct z-index constants
- No stacking context conflicts

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 13:24:15 -06:00
Thomas Hallock
423274657c fix: correct hero abacus scroll direction to flow with page content
The hero abacus was scrolling in the opposite direction of page content due to incorrect math. Fixed by subtracting scroll position instead of adding it.

Change: top: calc(50vh - ${scrollY}px) instead of calc(50vh + ${scrollY}px)

Now the abacus properly scrolls up with the page content when scrolling down.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 13:24:15 -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
Thomas Hallock
f2bbd91801 Redesign OG image and favicon using AbacusReact server-side rendering
- Generate icon.svg and og-image.svg from AbacusReact component via scripts/generateAbacusIcons.tsx
- OG image: huge 4-column abacus with place-value colors, dark theme with decorative diamond shapes and math operators at 0.4/0.35 opacity for visibility
- Favicon: single-column abacus showing value 5, dark brown beads, properly centered and scaled (0.7)
- opengraph-image.tsx: read pre-generated og-image.svg instead of using react-dom/server (avoids edge runtime restriction)
- All abacus visualizations now use AbacusReact component consistently

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 07:04:09 -06:00
Thomas Hallock
b07f1c4216 fix: extract pure SVG content from AbacusReact renders
Fixed generateAbacusIcons.tsx to properly extract SVG content from
AbacusReact server-side renders, removing invalid div wrappers that
prevented the abacus from displaying in icon.svg and og-image.svg.

Changes:
- Added extractSvgContent() function to parse rendered markup
- Regenerated icon.svg with proper SVG structure
- Regenerated og-image.svg showing abacus value 123
- Removed unused React import (modern TSX doesn't need it)

The Open Graph image now correctly displays the abacus visualization
alongside the site text, fixing the issue where only text was visible.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 04:25:53 -06:00
Thomas Hallock
0922ea10b7 feat: add comprehensive metadata, SEO, and make AbacusReact SSR-compatible
- Update metadata in layout.tsx with full SEO, Open Graph, Twitter cards, PWA config
- Create sitemap.ts for dynamic sitemap generation
- Create robots.ts for search engine guidance
- Create icon.svg favicon generated from AbacusReact component
- Create opengraph-image.tsx for dynamic Open Graph image generation
- Create public/og-image.svg as fallback Open Graph image
- Make AbacusReact component SSR-compatible by:
  - Detecting server environment and conditionally using animations
  - Using regular SVG elements instead of animated ones when on server
  - Making useSpring and useDrag hooks SSR-safe
- Add generateAbacusIcons.tsx script to generate icons from real AbacusReact component

All icons now use the actual AbacusReact component rendering, ensuring consistency
between static assets and the interactive version.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 04:06:23 -06:00
Thomas Hallock
b5a96eaeb1 fix: board rotation now properly fills height in portrait mode
Fixed board rotation issues when guide is docked:
- Board now prioritizes HEIGHT when in portrait orientation
- Removed CSS constraints (maxWidth, maxHeight, aspectRatio) that were
  interfering with explicit dimensions during rotation
- Board properly fills container whether guide is docked or not
- Smooth transition between portrait and landscape orientations

Technical changes:
- Set maxWidth/maxHeight to 'none' when svgDimensions are explicit
- Set aspectRatio to 'auto' when rotating to prevent override
- Cleaned up debug logging

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 19:06:41 -06:00
Thomas Hallock
3121d8240a feat(rithmomachia): Add interactive playing guide modal
Add comprehensive draggable playing guide modal with quick navigation:
- Draggable modal on desktop, fixed on mobile
- 5 quick-access sections: Overview, Pieces, Capture, Harmony, Victory
- Integrated PieceRenderer SVGs for visual piece examples
- Responsive layout for desktop and mobile
- Modal persists during gameplay
- "How to Play" button on setup page with updated description
- "Guide" button in gameplay controls
- Complete guide content from PLAYING_GUIDE.md

Features:
- Desktop: draggable modal that can be positioned anywhere
- Mobile: full-screen responsive modal
- Quick navigation tabs for easy reference
- Visual piece examples with movement descriptions
- Mathematical capture relations explained
- Harmony progression examples with formulas
- Strategy tips and victory conditions

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 19:59:09 -05:00
Thomas Hallock
c0680cad0f fix(arcade): add host-only game selection with clear messaging
Non-host room members were getting 403 errors when trying to select games.
Added proper UI restrictions and messaging to clarify only the host can
select games.

**Changes**:

1. **Host Detection**: Check if current user is room creator
   - Find `currentMember` in `roomData.members`
   - Check `isCreator` flag

2. **Visual Restrictions**:
   - Game buttons disabled for non-hosts (opacity: 0.4, cursor: not-allowed)
   - No hover effects when disabled
   - Clear visual feedback

3. **Messaging**:
   - **Host**: "👑 You're the room host. Select a game to start playing."
   - **Non-host**: " Waiting for [Host Name] to select a game..."
   - **Error**: "⚠️ Only the room host can select a game. Ask [Host] to choose."

4. **Error Handling**:
   - Client-side check before API call
   - Server error caught and displayed with host name
   - Auto-dismiss after 5 seconds

**UX Flow**:
- Non-hosts see disabled games with clear "waiting for host" message
- If they somehow click, they get clear error message
- Host sees active games with confirmation they can select

Prevents confusing 403 errors and clarifies room permissions.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 11:59:43 -05:00
Thomas Hallock
4b04e43ff8 fix(deployment): pass git info to Docker build for deployment info modal
This commit fixes two critical production issues:

1. **Flashcard generator dependencies** - Added all required dependencies to Dockerfile:
   - Typst for PDF generation
   - Python pip and setuptools
   - Python packages (pyyaml, Pillow, imagehash)
   - packages/core directory with generate.py script

2. **Deployment info modal** - Fixed git commit hash display on production:
   - Modified generate-build-info.js to accept env vars as fallback when .git is unavailable
   - Updated Dockerfile to accept GIT_* build arguments
   - Updated GitHub Actions workflow to pass git information during Docker build

The deployment info modal (Ctrl+Shift+I) will now show the correct commit hash,
branch, and build time on production, matching the behavior on dev.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 21:23:15 -05:00
Thomas Hallock
f9af0f169e chore: biome formatting fixes 2025-10-11 07:17:38 -05:00
Thomas Hallock
bda5bc6c0e fix: prevent database imports from being bundled into client code
**Problem:**
- player-ownership.ts imported drizzle-orm and @/db at top level
- When RoomMemoryPairsProvider imported client-safe utilities, Webpack bundled ALL imports including database code
- This caused hydration error: "The 'original' argument must be of type Function"
- Node.js util.promisify was being called in browser context

**Solution:**
1. Created player-ownership.client.ts with ONLY client-safe utilities
   - No database imports
   - Safe to import from 'use client' components
   - Contains: buildPlayerOwnershipFromRoomData(), buildPlayerMetadata(), helper functions

2. Updated player-ownership.ts to re-export client utilities and add server-only functions
   - Re-exports everything from .client.ts
   - Adds buildPlayerOwnershipMap() (async, database-backed)
   - Safe to import from server components/API routes

3. Updated RoomMemoryPairsProvider to import from .client.ts

**Result:**
- No more hydration errors on /arcade/room
- Client bundle doesn't include database code
- Server code can still use both client and server utilities

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 11:40:46 -05:00
Thomas Hallock
60d70cd2f2 style: apply Biome formatting to entire codebase
Run Biome formatter on all files to ensure consistent code style:
- Single quotes for JS/TS
- Double quotes for JSX
- 2-space indentation
- 100 character line width
- Semicolons as needed
- ES5 trailing commas

This is the result of running: npx @biomejs/biome format . --write

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:48:26 -05:00
Thomas Hallock
416dc897e2 feat: add build info generation script
Add script to capture deployment metadata (git commit, branch, timestamp, version) at build time and integrate it into the build process.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 06:37:00 -05:00