Commit Graph

65 Commits

Author SHA1 Message Date
Thomas Hallock b94f5338e5 feat(skills-modal): add spring animations and UX improvements
- Add smooth spring-animated accordion expand/collapse using react-spring
- Add dynamic scroll indicators that show when content is scrolled
- Auto-scroll to show expanded category content optimally
- Replace ambiguous arrows with "Show/Hide" + rotating chevron
- Make modal full-screen on mobile, centered on desktop
- Add sticky category headers within scroll container
- Fix z-index layering using shared constants
- Add optimistic updates for skill mutations (instant UI feedback)
- Fix React Query cache sync for live skill updates

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 07:23:14 -06:00
Thomas Hallock 6a4dd694a2 feat(blog): add interactive ECharts for BKT validation blog post
- Add ValidationCharts with tabbed interface for A/B trajectory data
  - "All Skills" tab: shows 6 skills at once, toggle Adaptive/Classic
  - "Single Skill" tab: interactive skill selector for individual comparison
  - "Convergence" tab: bar chart comparing sessions to 80% mastery
  - "Data Table" tab: summary with advantage calculations
- Add SkillDifficultyCharts for skill difficulty model visualization
- Create snapshot-based test infrastructure for trajectory data
  - skill-difficulty.test.ts generates A/B mastery trajectories
  - Snapshots capture session-by-session mastery for 6 deficient skills
- Add generator scripts to convert snapshots to JSON for blog charts
  - generateMasteryTrajectoryData.ts → ab-mastery-trajectories.json
  - generateSkillDifficultyData.ts → skill-difficulty-report.json
- Add skill-specific difficulty multipliers to SimulatedStudent
  - Basic skills: 0.8-0.9x (easier)
  - Five-complements: 1.2-1.3x (moderate)
  - Ten-complements: 1.6-2.0x (harder)
- Document SimulatedStudent model in SIMULATED_STUDENT_MODEL.md

Results show adaptive mode reaches 80% mastery faster for all 6 tested
skills (4-0 at 50% threshold, 6-0 at 80% threshold).

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 13:16:47 -06:00
Thomas Hallock f74db216da chore: remove abandoned 3d-printing feature
Remove dead code from abandoned 3D printing initiative:
- Delete jobManager.ts (had unbounded memory growth)
- Delete openscad.worker.ts (unused)
- Delete 3D model files from public/
- Remove openscad-wasm-prebuilt dependency
- Clean up doc references

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-09 12:48:13 -06:00
Thomas Hallock 1e6153ee8b feat(know-your-world): add fire tracer animation for learning mode takeover
Add animated fire/sparkle tracer effect around region outlines in learning mode:
- Tracer follows simplified path (using simplify-js) for smooth coastlines
- Supports multiple islands with simultaneous animations
- Progressive intensity as letters are typed:
  - Speed increases exponentially (15s → 0.075s, 200x faster)
  - Opacity fades in (25% → 100%)
  - Sparkle count grows exponentially (1 → 48)
- 750ms laser effect delay after final letter before dismissing
- Uses react-spring for smooth transitions
- Performance optimized: removed filters from particles

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 09:44:03 -06:00
Thomas Hallock 5318d0dd89 feat(know-your-world): add mobile virtual keyboard and space-skipping
- Add SimpleLetterKeyboard component for touch devices (react-simple-keyboard)
- Skip spaces when counting letters for name confirmation
  (e.g., "US Virgin Islands" → type U, S, V)
- Hide MyAbacus floating button when virtual keyboard is shown
- Add Storybook stories for testing letter confirmation flow

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-30 13:08:39 -06:00
Thomas Hallock 743adae92d refactor: use flatten-js for precise geometric intersection detection
Replace point sampling with proper computational geometry using @flatten-js/core.

Previous approach (strategic points):
- Tested 9 hardcoded points (corners + center + edge midpoints)
- Still sampling-based, just smarter sampling
- Could miss edge cases with complex shapes

New approach (flatten-js):
- Convert SVG path to Polygon using getPointAtLength() (50 samples)
- Convert detection box to Box in SVG coordinates
- Use polygon.intersect(box) for precise intersection test
- Also checks boxContainsRegion for tiny regions fully inside box
- Proper computational geometry, not heuristics

Algorithm:
1. Sample 50 points evenly along SVG path using getTotalLength()
2. Create Polygon from sampled points
3. Transform detection box corners to SVG coordinates
4. Create Box from transformed corners
5. Check: polygon.intersect(box) || box.contains(polygon.box)

Benefits:
- Mathematically correct intersection detection
- No guessing about where to sample
- Handles all cases: intersecting, contained, disjoint
- Fallback to 9-point sampling if flatten-js fails

Dependencies:
- Added @flatten-js/core for 2D computational geometry

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-25 07:05:57 -06:00
Thomas Hallock 9ad4517f2a Revert "feat: use big.js for arbitrary precision in cursor and zoom math"
This reverts commit 8e9c0548c8.
2025-11-19 19:24:07 -06:00
Thomas Hallock 8e9c0548c8 feat: use big.js for arbitrary precision in cursor and zoom math
Problem:
- JavaScript's 64-bit floats lose precision with ultra-small movements
- Vatican City (~0.05px) requires 0.03x dampened cursor (0.001px precision)
- At 1000x zoom with tiny deltas, floating-point rounding causes "one pixel works"
- Coordinate transformations compound precision loss

Solution:
- Install big.js for arbitrary-precision decimal arithmetic
- Use Big for cursor position tracking (cursorPositionRef stores Big values)
- Use Big for movement delta calculations (e.movementX * 0.03)
- Use Big for container → SVG coordinate transformations
- Use Big for zoom viewport calculations at extreme zoom levels (1000x)
- Convert to numbers only when interfacing with DOM/display

Technical details:
- cursorPositionRef: { x: Big; y: Big } instead of { x: number; y: number }
- Cursor deltas: new Big(e.movementX).times(currentMultiplier)
- SVG coordinates: cursorXBig.minus(offset).times(scale).plus(viewBoxX)
- Zoom viewport: new Big(viewBoxWidth).div(testZoom)
- Maintains full precision through entire calculation chain

This should allow Vatican City to be clickable from multiple cursor positions
instead of requiring exact pixel-perfect positioning.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 18:45:38 -06:00
Thomas Hallock 3bf127f344 feat: add precision controls for tiny regions in Know Your World
Implement automatic cursor dampening, super zoom on hover, and quick-escape to make sub-pixel regions (Gibraltar 0.08px, Jersey 0.82px) clickable. Fix crosshair accuracy to match dampened cursor position, add excluded region visualization (gray pre-labeled), and increase unfound region contrast (0.3→0.7 opacity).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 18:06:34 -06:00
Thomas Hallock 25e24a7cbc feat: add Know Your World geography quiz game
Add new arcade game for testing geography knowledge:

Game Features:
- 4 phases: Setup, Study, Playing, Results
- 3 multiplayer modes: Cooperative, Race, Turn-Based
- 2 maps: World countries, USA states
- Configurable study mode (0, 30, 60, or 120 seconds)
- Return to Setup and New Game options in game menu
- Small region labels with arrows for improved visibility

Map Rendering:
- 8-color deterministic palette with hash-based assignment
- Opacity-based states (20-27% unfound, 100% found)
- Enhanced label visibility with text shadows
- Smart bounding box calculation for small regions
- Supports both easy (outlines always visible) and hard (outlines on hover/found) difficulty

Game Modes:
- Cooperative: All players work together to find all regions
- Race: First to click gets the point
- Turn-Based: Players take turns finding regions

Study Phase:
- Optional timed study period before quiz starts
- Shows all region labels for memorization
- Countdown timer with skip option

Dependencies:
- Add @svg-maps/world and @svg-maps/usa packages

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 20:04:52 -06:00
Thomas Hallock 7b4c7c3fb6 feat: add worksheet sharing infrastructure with database persistence
Add complete sharing system for worksheet configurations:

Database Schema:
- New worksheet_shares table with short share IDs (7-char base62)
- Stores worksheetType, config JSON, views, creator IP hash, optional title
- Migration 0021 creates the table

Share ID Generation:
- Cryptographically secure base62 IDs (3.5 trillion combinations)
- Collision detection with retry logic (max 5 attempts)
- Validation function for ID format checking

API Endpoints:
- POST /api/worksheets/share - Creates share, returns URL
- GET /api/worksheets/share/[id] - Retrieves config, increments views
- Uses serializeAdditionConfig() for consistent config formatting

Share Modal:
- Auto-generates share link on open (no button needed)
- Displays QR code for mobile sharing (theme-aware colors)
- Copy to clipboard functionality with visual feedback
- Loading states during generation

Dependencies:
- Added qrcode + @types/qrcode for QR code generation

Config Serialization:
- Share uses same serializeAdditionConfig() as database auto-save
- Ensures version field and structure consistency
- Shared configs match database-saved settings exactly

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 11:17:44 -06:00
Thomas Hallock 0040b57829 revert: remove ngrok and LAN IP detection
Remove ngrok tunnel integration - requires account signup.
User will test camera locally with incognito mode instead.

Also removed LAN IP detection feature:
- Reverted QRCodeDisplay to use window.location.origin directly
- Deleted /api/network/lan-ip endpoint
- Simplified dev script back to original

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 05:06:48 -06:00
Thomas Hallock ab2bfde9c2 feat: add ngrok tunnel to dev server for HTTPS testing
Run ngrok automatically alongside the dev server to enable HTTPS
access for testing camera features on phones.

Changes:
- Install ngrok and concurrently packages
- Update dev script to run ngrok tunnel on port 3000
- Add dev:no-tunnel fallback script (no HTTPS tunnel)

Usage:
  pnpm run dev           # Dev server + ngrok HTTPS tunnel
  pnpm run dev:no-tunnel # Dev server only (HTTP)

When running with tunnel:
- ngrok will display HTTPS URL in console
- Use that URL for testing camera on phones
- Camera access works over HTTPS (ngrok URL)
- LAN IP detection still works for localhost testing

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 05:01:31 -06:00
Thomas Hallock 2e0f99f98a refactor(worksheets): extract utility functions
- dateFormatting.ts: getDefaultDate() for consistent date formatting
- layoutCalculations.ts: getDefaultColsForProblemsPerPage() and calculateDerivedState()
- Pure functions, easy to test
- Removes 32 lines from main component
2025-11-07 22:09:35 -06:00
Thomas Hallock ceefb2f1bd fix: add missing blog dependencies to package.json
Add gray-matter, remark, remark-gfm, and remark-html dependencies
that were missing from the blog feature commits, causing webpack
"Module not found" errors in CI/CD.

These packages are required by src/lib/blog.ts for markdown processing.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-07 12:19:00 -06:00
Thomas Hallock eaaf17cd4c feat: add client-side OpenSCAD WASM support for 3D preview
Migrate 3D abacus preview generation from server-side OpenSCAD to client-side
WASM execution using web workers.

Changes:
- Add openscad-wasm-prebuilt dependency for browser-based rendering
- Refactor STLPreview to use web worker for non-blocking WASM execution
- Create openscad.worker.ts for isolated 3D model generation
- Add abacus-inline.scad template for parametric abacus generation
- Improve preview debouncing (500ms) and error handling

Benefits:
- No server-side OpenSCAD dependency required
- Faster preview updates (no network roundtrip)
- Better UX with non-blocking worker execution
- Consistent rendering across environments

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 08:43:52 -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 ffae9c1bdb fix(web): move tsx to production dependencies for calendar generation
Calendar generation scripts need tsx at runtime to execute TypeScript files,
but tsx was in devDependencies which are not installed in production builds.

This caused calendar preview and PDF generation to fail in production with:
"EACCES: permission denied, mkdir '/nonexistent'" when trying to run npx tsx

Moving tsx to dependencies ensures it's available in production for:
- apps/web/scripts/generateCalendarComposite.tsx
- apps/web/scripts/generateCalendarAbacus.tsx

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-04 14:00:26 -06:00
Thomas Hallock 293390ae35 fix(web): generate styled-system artifacts during build
- Add @pandacss/dev to build script before Next.js build
- Ensures styled-system directory is generated in CI/CD pipeline
- Fixes "Cannot find module styled-system/css" build errors

🤖 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 dafdfdd233 feat: add 3D printing support for abacus models
Add comprehensive 3D printing capabilities for generating custom
abacus models in STL format:

- OpenSCAD integration in Docker container
- API endpoint: POST /api/abacus/generate-stl
- Background job processing with status monitoring
- STL file preview with Three.js
- Interactive abacus customization page at /create/abacus
- Configurable parameters: columns, bead shapes, dimensions, colors
- Export formats: STL (for 3D printing), SCAD (for editing)

Components:
- STLPreview: Real-time 3D model viewer
- JobMonitor: Background job status tracking
- AbacusCustomizer: Interactive configuration UI

Docker: Add OpenSCAD and necessary 3D printing tools
Dependencies: Add three, @react-three/fiber, @react-three/drei

Generated models stored in public/3d-models/
Documentation: 3D_PRINTING_DOCKER.md

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 946e5d1910 feat: install embla-carousel-autoplay for games carousel
Add embla-carousel-autoplay dependency to enable smooth automatic
rotation of games in the hero carousel.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 10:53:54 -06:00
Thomas Hallock 642ae95738 feat: install embla-carousel-react for player profile carousel
Add embla-carousel-react dependency to enable smooth carousel
functionality for displaying player profiles on the games page.

🤖 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 bf48ed1757 chore: add type definitions for react-textfit
Installed @types/react-textfit to resolve TypeScript compilation error
in PlayingGuideModal.tsx. This package provides type definitions for
the react-textfit library used in the Rithmomachia guide component.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 13:45:52 -06:00
Thomas Hallock 9fd54067ce feat(rithmomachia): auto-size tab labels with react-textfit
Replace ellipsized tab labels with react-textfit library:
- Install react-textfit package
- Use Textfit component for tab text labels
- Auto-scales font size (8px-14px) using binary search to fit width
- No more ellipsis (...) on narrow tabs
- Full tab labels always visible and readable
- Icon stays fixed size, only text scales

Tab labels now automatically shrink to fit available space while
remaining fully readable.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 12:23:24 -06:00
Thomas Hallock f457f1a1c2 feat(rithmomachia): add guide docking with resizable panels
Implement ability to dock the Rithmomachia playing guide to left or right
side of the board screen. When docked, guide and board are in separate
panels with a resizable divider between them.

Features:
- Drag guide to left/right edge (within 100px) to dock it
- Resizable divider using react-resizable-panels library
- Guide defaults to 35% width, resizable between 20-50%
- Board takes remaining space (minimum 50%)
- Undock button (⛶) returns guide to floating modal mode
- No overlap between board and guide when docked
- Dragging disabled when docked
- Styling adjusted for docked mode (no shadows, no border-radius)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-02 10:17:26 -06:00
Thomas Hallock 9016b76024 feat(i18n): migrate from react-i18next to next-intl
- Install next-intl package
- Configure next-intl middleware for cookie-based locale detection
- Set up routing config with supported locales (en, de, ja, hi, es, la)
- Create request config for server-side locale resolution
- Migrate Rithmomachia guide sections to next-intl
- Update translation calls to use next-intl's useTranslations hook
- Remove old react-i18next config

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-01 14:47:25 -05:00
Thomas Hallock 91154d9364 fix(rithmomachia): add missing i18next dependencies
Added i18next and react-i18next to package.json to fix Docker build failure.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-31 20:32:01 -05:00
Thomas Hallock d568955d6a feat(arcade): add yjs-demo collaborative game and Yjs persistence layer
- Add yjs-demo arcade game with collaborative state management
- Add Yjs persistence layer for real-time sync
- Update socket server with Yjs support
- Update Rithmomachia game component
- Add yjs type definitions

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 09:46:20 -05:00
Thomas Hallock c367e0ceec feat(card-sorting): add react-spring animations for real-time sync
Add smooth physics-based animations for card and arrow movements:
- Add @react-spring/web dependency
- Create AnimatedCard component with spring animations
- Create AnimatedArrow component with spring animations
- Add viewport resize handling with debounced isResizing flag
- Conditionally disable animations during drag and resize (immediate mode)
- Enable spring animations only for socket position updates
- Add throttled position syncing (100ms) during drag operations
- Convert all positioning to viewport percentages (0-100)
- Track viewport dimensions and reposition cards on resize

Cards and arrows now smoothly animate when positions update from other
connected windows, while maintaining instant feedback during local
drag operations and viewport resizes.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 14:33:30 -05:00
Thomas Hallock 349290ac6a feat(room-share): add QR code button for easy mobile joining
Add a QR code sharing option alongside existing copy buttons.
When clicked, opens a popover with:
- QR code encoding the room's share URL
- "Scan to Join" heading
- Clickable copy button for the URL

Uses qrcode.react library with Radix UI popover component.
Button styled with orange gradient to differentiate from existing
blue link and purple code copy buttons.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-23 10:35:22 -05:00
Thomas Hallock e711c52757 feat(homepage): add interactive draggable flashcards with physics
Add a fun, interactive flashcard display to the homepage's flashcard
generator section. Users can drag and throw 8-15 randomly generated
flashcards around with realistic physics-based momentum.

Features:
- Drag and drop flashcards with mouse/touch
- Throw cards with velocity-based physics
- 8-15 randomly generated flashcards (100-999 range)
- Real AbacusReact components for each card
- Client-side rendering to avoid hydration errors

Technical implementation:
- Uses @use-gesture/react for drag gesture handling
- Uses @react-spring/web for smooth physics animations
- Cards generated client-side with useEffect to prevent SSR mismatch
- Each card maintains its own spring-based position and rotation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 17:37:48 -05:00
Thomas Hallock 22426f677f refactor: remove dead Python bridge and unused packages
Removed abandoned SVG generation code that was never used in production:

**Deleted Files:**
- packages/core/src/bridge.py (302 lines) - Python-shell bridge for SVG generation
- packages/core/client/node/src/soroban-generator-bridge.ts - TypeScript wrapper
- packages/core/client/typescript/ - Entire unused @soroban/client package
- packages/core/client/browser/ - Empty package

**Dependencies Removed:**
- python-shell - Only used by abandoned bridge code
- @types/minimatch - Only needed by removed TypeScript packages
- @soroban/client from apps/web

**Code Cleanup:**
- Simplified packages/core/client/node/src/index.ts exports
- Removed SorobanGeneratorBridge, BridgeFlashcardConfig, BridgeFlashcardResult exports

**Impact:**
- ~800 lines of dead TypeScript code removed
- 302 lines of unused Python code removed
- 2 npm dependencies removed
- Build verified successful - no functionality affected

**What Remains Active:**
- generate.py - PDF generation via Typst CLI (actively used by /api/generate)
- soroban-generator.ts - CLI wrapper for PDF generation
- api.py - Optional FastAPI server
- generate_examples.py - Documentation image generator
- Web app uses @soroban/abacus-react for all SVG rendering

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 09:11:59 -05:00
Thomas Hallock e135d92abb refactor(db): remove database schema coupling for game names
BREAKING CHANGE: Database schemas now accept any string for game names

This implements Critical Fix #1 from AUDIT_2_ARCHITECTURE_QUALITY.md

Changes:
- Remove hardcoded enums from all database schemas
- arcade-rooms.ts: gameName now accepts any string
- arcade-sessions.ts: currentGame now accepts any string
- room-game-configs.ts: gameName now accepts any string

Runtime Validation:
- Add isValidGameName() helper to validate against registry
- Add assertValidGameName() helper for fail-fast validation
- Update settings API to use runtime validation instead of hardcoded array

Benefits:
 No schema migration needed when adding new games
 No TypeScript compilation errors for new games
 Single source of truth: validator registry
 "Just register and go" - no database changes required

Migration Impact:
- Existing data is compatible (strings remain strings)
- No data migration needed
- TypeScript will now allow any string, but runtime validation enforces correctness

This eliminates the most critical architectural issue identified in the audit.
Future games can be added by:
1. Register validator in validators.ts
2. Register game in game-registry.ts
That's it! No database schema changes needed.
2025-10-15 21:17:00 -05:00
Thomas Hallock de30bec479 feat(arcade): add modular game SDK and registry system
Create foundation for modular arcade game architecture:

**Game SDK** (`/src/lib/arcade/game-sdk/`):
- Stable API surface that games can safely import
- Type-safe game definition with `defineGame()` helper
- Controlled hook exports (useArcadeSession, useRoomData, etc.)
- Player ownership and metadata utilities
- Error boundary component for game crashes

**Manifest System**:
- YAML-based game manifests with Zod validation
- Game metadata (name, icon, description, difficulty, etc.)
- Type-safe manifest loading with `loadManifest()`

**Game Registry**:
- Central registry for all arcade games
- Explicit registration pattern via `registerGame()`
- Helper functions to query available games

**Type Safety**:
- Full TypeScript contracts for games
- GameValidator, GameState, GameMove, GameConfig types
- Compile-time validation of game implementations

This establishes the plugin system for drop-in arcade games.
Next: Create demo games to exercise the system.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 18:09:17 -05:00
Thomas Hallock 6ff21c4f1d feat: add room access modes and ownership transfer
Add comprehensive access control system for arcade rooms with 6 modes:
- open: Anyone can join (default)
- locked: Only current members allowed
- retired: Room no longer functions
- password: Requires password to join
- restricted: Only users with pending invitations can join
- approval-only: Requires host approval via join request system

Database Changes:
- Add accessMode field to arcade_rooms (replaces isLocked boolean with enum)
- Add password field to arcade_rooms (hashed with bcrypt)
- Create room_join_requests table for approval-only mode

New API Endpoints:
- PATCH /api/arcade/rooms/:roomId/settings - Update room access mode and password (host only)
- POST /api/arcade/rooms/:roomId/transfer-ownership - Transfer ownership to another member (host only)
- POST /api/arcade/rooms/:roomId/join-request - Request to join approval-only room
- GET /api/arcade/rooms/:roomId/join-requests - Get pending join requests (host only)
- POST /api/arcade/rooms/:roomId/join-requests/:requestId/approve - Approve join request (host only)
- POST /api/arcade/rooms/:roomId/join-requests/:requestId/deny - Deny join request (host only)

Updated Endpoints:
- POST /api/arcade/rooms/:roomId/join - Now validates access modes before allowing join:
  * locked: Rejects all joins
  * retired: Rejects all joins (410 Gone)
  * password: Requires password validation
  * restricted: Requires valid pending invitation
  * approval-only: Requires approved join request
  * open: Allows anyone (existing behavior)

Libraries:
- Add room-join-requests.ts for managing join request lifecycle
- Ownership transfer updates room.createdBy and member.isCreator flags
- Socket.io events for join request notifications and ownership transfers

Migration: 0007_access_modes.sql

Next Steps (UI not included in this commit):
- RoomSettingsModal for configuring access mode and password
- Join request approval UI in ModerationPanel
- Ownership transfer UI in ModerationPanel
- Password input in join flow

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 19:19:49 -05:00
Thomas Hallock 83b9a4d976 fix: compile TypeScript server files to JavaScript for production
- Add tsconfig.server.json to compile server-side TypeScript
- Install tsc-alias to resolve path aliases (@/*) in compiled JS
- Update build script to run tsc + tsc-alias before Next.js build
- Update dev script to compile server files before starting
- Remove tsx runtime dependencies from server.js
- Add compiled JS files for socket-server, db, and arcade modules

This enables production builds to run with pure Node.js without
requiring tsx or ts-node at runtime, as required for Docker deployment.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 07:55:18 -05:00
Thomas Hallock dd1104310f docs: establish mandatory code quality regime for Claude Code
Add comprehensive documentation and tooling to enforce code quality
checks before every commit. This regime persists across all Claude
Code sessions via `.claude/` directory files.

**The Regime** (mandatory before every commit):
1. TypeScript type checking (0 errors)
2. Biome formatting (auto-applied)
3. Linting with auto-fix (0 errors, 0 warnings)
4. Final verification

**Implementation:**
- `.claude/CLAUDE.md`: Quick reference for Claude Code sessions
- `.claude/CODE_QUALITY_REGIME.md`: Detailed regime documentation
- `npm run pre-commit`: Single command to run all checks

**Why no pre-commit hooks:**
Avoided for religious reasons. Claude Code is responsible for
enforcing quality checks through session-persistent documentation.

**Usage:**
```bash
# Before every commit
npm run pre-commit

# Or run steps individually
npm run type-check
npm run format
npm run lint:fix
npm run lint
```

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:52:29 -05:00
Thomas Hallock fc1838f4f5 feat: add Biome + ESLint linting setup
Add Biome for formatting and general linting, with minimal ESLint
configuration for React Hooks rules only. This provides:

- Fast formatting via Biome (10-100x faster than Prettier)
- General JS/TS linting via Biome
- React Hooks validation via ESLint (rules-of-hooks)
- Import organization via Biome

Configuration files:
- biome.jsonc: Biome config with custom rule overrides
- eslint.config.js: Minimal flat config for React Hooks only
- .gitignore: Added Biome cache exclusion
- LINTING.md: Documentation for the setup

Scripts added to package.json:
- npm run lint: Check all files
- npm run lint:fix: Auto-fix issues
- npm run format: Format all files
- npm run check: Full Biome check

🤖 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 eedce28572 feat: remove typst dependencies
Remove @myriaddreamin/typst-* packages that are no longer needed.
This eliminates Docker overlay conflicts with hoisted node_modules.

Removed packages (-365):
- @myriaddreamin/typst-all-in-one.ts
- @myriaddreamin/typst-ts-renderer
- @myriaddreamin/typst-ts-web-compiler
- @myriaddreamin/typst.ts

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 10:22:48 -05:00
Thomas Hallock 22541df99f fix: use UUID player IDs in session creation fallback
- Changed CreateSessionOptions.activePlayers from number[] to string[]
- Updated socket-server.ts fallback from [1] to [data.userId]
- Added debug logging to validateFlipCard to diagnose turn validation issues

This ensures that when a session is created without explicit activePlayers,
it uses the actual UUID of the requesting player instead of the numeric value 1.
2025-10-06 13:04:33 -05:00
Thomas Hallock 5d5afd4e68 feat: set up Drizzle ORM with SQLite database
Phase 1.1 Complete: Database & Auth Infrastructure

- Configure Drizzle with SQLite and better-sqlite3
- Create schema for users, players, and user_stats tables
- Set up database client with foreign keys and WAL mode enabled
- Add migration runner and package.json scripts
- Generate initial migration (0000_third_carnage.sql)

Database Features:
- Users table with guestId for guest sessions
- Players table with userId FK (cascade delete)
- UserStats table with userId FK (cascade delete)
- Indexes on foreign keys for performance
- Type-safe schema with Drizzle ORM

Testing:
- 20 unit + e2e tests all passing
- Schema validation tests
- Migration idempotency tests
- Foreign key constraint tests
- Cascade delete tests

Scripts added:
- pnpm db:generate - Generate migration from schema
- pnpm db:migrate - Run pending migrations
- pnpm db:push - Push schema directly (dev)
- pnpm db:studio - Visual DB browser
- pnpm db:drop - Drop migration (dev)

User tests verified:
 Migration runs successfully
 Database tables created with correct schema
 Migration is idempotent (can run multiple times)
2025-10-05 17:01:27 -05:00
Thomas Hallock a3878a8537 feat: add React Query setup with api helper
- Install @tanstack/react-query
- Create QueryClientProvider in ClientProviders with stable client instance
- Add queryClient.ts with createQueryClient() and api() helper
- Add api() helper that wraps fetch with automatic /api prefix
- Add example.ts with complete CRUD hook examples
- Configure sensible defaults (5min staleTime, retry once)

All API routes are now prefixed with /api automatically via api() helper.
2025-10-05 16:55:03 -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
Thomas Hallock 42b73cf8ee chore: add nanoid dependency
Add nanoid for generating universally unique player IDs
2025-10-04 17:06:53 -05:00
Thomas Hallock a935e5aed8 fix: resolve TypeScript errors in PlayerStatusBar component
- Fix duplicate color properties in single player mode styling
- Fix className prop passing to css() function in both single and multiplayer modes
- Replace undefined 'super-bounce' animation with 'gentle-bounce'

The make-plural pluralization integration is working correctly with proper
display of "1 pair" vs "2 pairs", "1 move" vs "3 moves", etc.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-29 13:21:45 -05:00
Thomas Hallock d58053fad3 feat: add comprehensive E2E testing with Playwright
- Add Playwright configuration for cross-browser testing
- Implement navigation slot persistence tests
- Add sound settings persistence E2E tests
- Ensure robust testing of navigation state management
- Cover edge cases for mini-nav behavior across routes

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-29 09:44:36 -05:00
Thomas Hallock afec22ac3f feat: add cosmic fullscreen mode to abacus style dropdown
- Updated AbacusDisplayDropdown to accept isFullscreen prop
- Added dark/cosmic styling that adapts to fullscreen mode
- Updated trigger button, content panel, and all form controls
- Enhanced FormField, SwitchField, and RadioGroupField components
- Integrated with AppNavBar to pass fullscreen state
- Ensures consistent cosmic theme across entire navigation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 16:52:21 -05:00
Thomas Hallock 31df87d3fc fix: restore workspace dependencies and fix TypeScript errors
- Restore @soroban/core and @soroban/client dependencies in apps/web
- Correct workspace configuration to include all existing packages
- Disable problematic example-server.ts to fix TypeScript build
- Update Dockerfile to copy all required package.json files

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 09:08:46 -05:00
Thomas Hallock 39526eb496 fix: correct workspace configuration and remove non-existent packages
- Fix pnpm-workspace.yaml to use packages/* pattern
- Remove references to non-existent @soroban/core and @soroban/client packages
- Update Dockerfile to only copy existing package.json files
- Clean up dependencies after workspace changes

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 09:06:57 -05:00
Thomas Hallock eb8ed8b22c feat: add complete NAS deployment system for apps/web
- Add Dockerfile with multi-stage build for monorepo
- Add GitHub Actions workflow for automated CI/CD
- Add NAS deployment configuration for abaci.one
- Configure Porkbun DDNS integration
- Add Watchtower for auto-updates
- Fix Next.js standalone output configuration
- Add missing dependencies for package builds

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-27 08:42:41 -05:00