Commit Graph

36 Commits

Author SHA1 Message Date
Thomas Hallock 646a4228d0 fix(qr-button): improve layout and z-index
- Stack room code and share link vertically on left
- Place square QR button on right, spanning both rows
- Show mini QR code (40px) in button instead of emoji
- Fix popover z-index to appear above dropdown menu (z: 10000)
- Reduce button padding for more compact appearance

🤖 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 a1a135a858 feat: add vibrant gradients and equal heights to game cards
Updated the game theme system and game cards to have vibrant, eye-catching
gradients and consistent heights:

Theme System Changes:
- Updated all game themes from pastel to vibrant gradients
- Blue: Vibrant cyan (#4facfe to #00f2fe)
- Purple: Vibrant purple (#667eea to #764ba2)
- Pink: Vibrant pink (#f093fb to #f5576c)
- Green: Vibrant green/teal (#43e97b to #38f9d7)
- Plus updated indigo, teal, orange, yellow, red, gray themes

Game Manifest Updates:
- Memory Lightning: now uses purple theme
- Matching Pairs: now uses pink theme
- Complement Race: continues using blue (cyan) theme
- Card Sorting: now uses green theme

Homepage Game Cards:
- Added height: '100%' and flexbox to make all cards equal height
- Cards now stretch uniformly regardless of content length
- Maintains responsive hover effects and text readability overlays

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 17:13:09 -05:00
Thomas Hallock c883d9e4c1 fix(tutorial): resolve React hydration error in TutorialPlayer
Change <p> tag to <div> tag to fix HTML nesting violation. The
<p> tag was containing <DecompositionWithReasons> which renders
a <div>, causing a hydration error. In HTML, <p> cannot contain
block-level elements like <div>.

Fixed: apps/web/src/components/tutorial/TutorialPlayer.tsx:1386

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:41:33 -05:00
Thomas Hallock ebe123ed7e fix: replace native alerts with inline confirmations in ModerationPanel
Removed native browser confirm() dialogs and replaced with React state-based inline confirmations:
- Removed confirm() from handleKick (kicks happen immediately)
- Removed confirm() from handleTransferOwnership
- Added confirmingTransferOwnership state variable
- Added inline confirmation UI with Cancel/Confirm buttons
- Follows pattern documented in UI_STYLE_GUIDE.md

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:07:18 -05:00
Thomas Hallock feecda78d0 chore: update Claude Code permissions
Auto-updated permissions during session.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 19:49:17 -05:00
Thomas Hallock 0209975af6 refactor(arcade): standardize game card themes with preset system
Create a centralized theme system to prevent inconsistent game card
styling. All games now use standard color presets instead of manually
specifying gradients.

## Changes

**New Theme System:**
- Created `/src/lib/arcade/game-themes.ts` with 10 standard themes
- All themes use Tailwind 100-200 colors for consistent pastel appearance
- Exported `getGameTheme()` helper and `GAME_THEMES` constants via SDK
- Added comprehensive documentation in `.claude/GAME_THEMES.md`

**Migrated All Games:**
- card-sorting: Uses `getGameTheme('teal')`
- memory-quiz: Uses `getGameTheme('blue')`
- matching: Uses `getGameTheme('purple')`
- complement-race: Uses `getGameTheme('blue')`

**Benefits:**
-  Prevents future styling inconsistencies
-  One-line theme setup instead of three properties
-  TypeScript autocomplete for available themes
-  Centralized maintenance - update all games by changing theme definition
-  Clear documentation prevents mistakes

**Before:**
```typescript
color: 'teal',
gradient: 'linear-gradient(135deg, #99f6e4, #5eead4)',  // Manual, error-prone
borderColor: 'teal.200',
```

**After:**
```typescript
...getGameTheme('teal')  // Simple, consistent, discoverable
```

This fixes the root cause where card-sorting needed manual gradient
adjustments - now all games automatically get professional styling.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 19:00:38 -05:00
Thomas Hallock ee6c4f2f4f feat(home): redesign home page to showcase complete platform
Replace outdated "flashcard generator" landing page with comprehensive
platform showcase highlighting all three pillars: arcade games,
interactive learning, and flashcard creation.

**New Home Page Structure:**
- Compact hero with 3 CTAs: Play Games, Learn, Create
- 4 arcade game cards with player counts and mode tags
- Two-column feature sections for Learning & Flashcards
- Multiplayer features grid (4 cards)
- Stats banner: 4 games, 8 max players, 3 learning modes, 4+ formats

**Visual Design:**
- Smaller, denser components to fit more content
- Information-rich showcase vs marketing fluff
- Purple gradient hero matching guide branding
- Responsive grid layouts for all screen sizes

**Result:**
Home page now accurately represents the full platform:
multiplayer arcade games + interactive tutorials + flashcard tools.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 17:33:01 -05:00
Thomas Hallock 7f6fea91f6 feat(card-sorting): implement Provider with arcade session integration
Add complete Provider implementation for Card Sorting Challenge game.

Features:
- Full useArcadeSession integration with optimistic updates
- Config persistence to database (gameConfig.card-sorting)
- Single-player pattern (local player selection)
- Pause/resume support with config change detection
- Action creators for all 8 move types:
  * startGame() - generates random cards
  * placeCard(cardId, position) - place card in slot
  * removeCard(position) - return card to available
  * checkSolution() - validate answer & calculate score
  * revealNumbers() - show numeric values
  * goToSetup() - pause/return to setup
  * resumeGame() - restore paused game
  * setConfig() - update game settings
- Computed values: canCheckSolution, placedCount, elapsedTime
- Local UI state: selectedCardId (not synced)
- useCardSorting() hook for component access

Next: UI components (Setup, Playing, Results)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-18 14:11:52 -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 13882bda32 feat(complement-race): implement state adapter for multiplayer support
Resolves the state structure incompatibility between single-player and
multiplayer implementations by creating a compatibility transformation layer.

## Problem
The existing beautiful UI components (train animations, railroad tracks,
passenger mechanics) were built for single-player state structure, but the
new multiplayer system uses a different state shape (per-player data, nested
config, different gamePhase values).

## Solution: State Adapter Pattern
Created a transformation layer in Provider that:
- Maps multiplayer state to look like single-player state
- Extracts local player data from `players[localPlayerId]`
- Transforms `currentQuestions[playerId]` → `currentQuestion`
- Maps gamePhase enum values (`setup`/`lobby` → `controls`)
- Separates local UI state (currentInput, isPaused) from server state
- Provides compatibility dispatch mapping old actions to new action creators

## Key Changes
- Added `CompatibleGameState` interface matching old single-player shape
- Implemented state transformation in `compatibleState` useMemo hook
- Enhanced dispatch compatibility for local UI state management
- Updated all component imports to use new Provider
- Preserved ALL existing UI components without modification

## Verification
-  TypeScript: Zero errors in new code
-  Format: Biome formatting passes
-  Lint: No new warnings
-  All existing UI components preserved

See `.claude/COMPLEMENT_RACE_STATE_ADAPTER.md` for technical documentation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 12:01:09 -05:00
Thomas Hallock 0726176e4d refactor(types): consolidate type system - eliminate fragmentation
Implements "Option A: Single Source of Truth" from type audit recommendations.

**Phase 1: Consolidate GameValidator**
- Remove redundant GameValidator re-declaration from SDK types
- SDK now properly re-exports GameValidator from validation types
- Eliminates confusion about which validator interface to use

**Phase 2: Eliminate Move Type Duplication**
- Remove duplicate game-specific move interfaces from validation/types.ts
- Add re-exports of game move types from their source modules
- Maintains single source of truth (game types) while providing convenient access

**Changes:**
- `src/lib/arcade/game-sdk/types.ts`: Import & re-export GameValidator instead of re-declaring
- `src/lib/arcade/validation/types.ts`: Replace duplicate move interfaces with re-exports
- `__tests__/room-realtime-updates.e2e.test.ts`: Fix socket-server import path

**Impact:**
- Zero new type errors introduced
- All existing functionality preserved
- Clear ownership: game types are source of truth
- Improved maintainability: changes in one place

**Verification:**
- TypeScript compilation:  No new errors
- Server build:  Successful
- All pre-existing errors unchanged (AbacusReact module resolution, etc.)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 06:51:20 -05:00
Thomas Hallock e5c4a4bae0 refactor(matching): migrate to modular game system
Completes the Matching Pairs Battle migration from legacy dual-location
architecture to the unified modular game SDK system.

## Summary of Changes

### Phase 1-4: Core Infrastructure
- Created modular game definition with `defineGame()` in `src/arcade-games/matching/index.ts`
- Registered game in arcade registry with proper type inference
- Consolidated types into SDK-compatible `MatchingConfig`, `MatchingState`, and `MatchingMove`
- Migrated and updated validator with new import paths

### Phase 5-6: Provider and Components
- Created unified `MatchingProvider` with proper context and `useMatching` hook
- Moved all 7 components from arcade location to `src/arcade-games/matching/components/`
- Updated all component imports to use absolute paths (@/) where applicable
- Fixed styled-system import paths for new directory structure

### Phase 7-8: Utilities and Cleanup
- Migrated utility functions (cardGeneration, matchValidation, gameScoring)
- **Deleted 32 legacy files** from `/src/app/arcade/matching/` and `/src/app/games/matching/`
- Updated room page to use registry pattern exclusively
- Fixed all import references across the codebase

## Breaking Changes
- Old routes `/arcade/matching` and `/games/matching` no longer exist
- Game now accessed exclusively through arcade room system at `/arcade/room`
- Legacy providers and contexts removed

## Migration Verification
- All TypeScript errors in new code resolved
- Only remaining errors are pre-existing (@soroban/abacus-react, complement-race)
- Components successfully moved and imports updated
- Game registry integration working correctly

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-16 00:38:08 -05:00
Thomas Hallock 784793ba24 fix(arcade): prevent server-side loading of React components
Issue: game-config-helpers.ts was importing game-registry.ts which loads
game definitions including React components. This caused server startup to
fail with MODULE_NOT_FOUND for GameModeContext.

Solution: Lazy-load game registry only in browser environment.
On server, getGame() returns undefined and validation falls back to
switch statement for legacy games.

Changes:
- game-config-helpers.ts: Add conditional getGame() that checks typeof window
- Only requires game-registry in browser environment
- Server uses switch statement fallback for validation
- Browser uses game.validateConfig() when available

This maintains the architectural improvement (games own validation)
while keeping server-side code working.

Test: Dev server starts successfully, no MODULE_NOT_FOUND errors
2025-10-15 21:27: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 49219e34cd feat: adjust tier probabilities for more abacus flavor
Change weighted selection from 70/20/10 to 50/25/25:
- Emoji-specific: 70% → 50%
- Category-specific: 20% → 25%
- Global abacus: 10% → 25%

This increases abacus-themed words by 2.5x, ensuring stronger
presence of core abacus vocabulary (Calculator, Abacist, Counter)
while still maintaining emoji personality theming.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 14:09:25 -05:00
Thomas Hallock 76a8472f12 feat: implement avatar-themed name generation with probabilistic mixing
Add comprehensive emoji-themed player name generation system:
- 150+ emoji-specific word lists (10 adjectives + 10 nouns each)
- 45+ category-themed word lists as fallback
- Generic abacus-themed words as ultimate fallback

Probabilistic tier selection for variety:
- 70% emoji-specific (e.g., "Grinning Grinner" for 😀)
- 20% category-specific (e.g., "Cheerful Optimist" for happy faces)
- 10% global abacus theme (e.g., "Lightning Calculator")

Cross-tier mixing for abacus flavor infusion:
- 60% pure themed words
- 30% themed adjective + abacus noun
- 10% abacus adjective + themed noun

Updated all name generation call sites to pass player emoji:
- PlayerConfigDialog.tsx: Pass emoji when generating name
- GameModeContext.tsx: Theme names for default players and new players

This creates ultra-personalized, variety-rich names while maintaining
abacus theme presence across all generated names.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 14:00:18 -05:00
Thomas Hallock a93d981d1a fix(arcade): add defensive checks and update test fixtures
- Add defensive state corruption checks to RoomMemoryPairsProvider
- Update test fixtures to include userId field in GameMove objects
- Add git restore to allowed bash commands in local settings

These changes improve robustness when game state becomes corrupted
(e.g., from game type mismatches between room sessions).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-15 09:16:54 -05:00
Thomas Hallock 4afa171af2 fix(room): update GAME_TYPE_TO_NAME mapping for memory-quiz
The GAMES_CONFIG was changed from 'memory-lightning' to 'memory-quiz'
but the GAME_TYPE_TO_NAME mapping in room/page.tsx still used the old key.

This caused the handleGameSelect function to fail silently when users
clicked on Memory Lightning in the "Change Game" screen, as it couldn't
find the mapping for 'memory-quiz'.

Also added debug logging to GameCard component to help diagnose issues.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 19:06:00 -05:00
Thomas Hallock 2ffeade437 fix: resolve Memory Quiz room-based multiplayer validation issues
Root Cause:
- GAMES_CONFIG used 'memory-lightning' as key but validator was registered as 'memory-quiz'
- When rooms were created with gameName 'memory-lightning', getValidator() couldn't find the validator
- This caused all move validations to fail, breaking configuration changes and guess validation

Key Changes:
1. Fixed game identifier mismatch:
   - Changed GAMES_CONFIG key from 'memory-lightning' to 'memory-quiz'
   - Updated games/page.tsx to use 'memory-quiz' for routing

2. Completed Memory Quiz room-based multiplayer implementation:
   - Added MemoryQuizGameValidator with all 9 move types (START_QUIZ, NEXT_CARD, SHOW_INPUT_PHASE, ACCEPT_NUMBER, REJECT_NUMBER, SET_INPUT, SHOW_RESULTS, RESET_QUIZ, SET_CONFIG)
   - Created RoomMemoryQuizProvider for network-synchronized gameplay
   - Implemented optimistic client-side updates with server validation
   - Added proper serialization handling (send numbers instead of React components)
   - Split memory-quiz/page.tsx into modular components (SetupPhase, DisplayPhase, InputPhase, ResultsPhase)

3. Updated socket-server:
   - Fixed to use getValidator() instead of hardcoded matchingGameValidator
   - Added game-specific initial state handling for both 'matching' and 'memory-quiz'

4. Fixed test failures from arcade_sessions schema changes:
   - Updated arcade-session-validation.e2e.test.ts to create rooms before sessions (roomId is now primary key)
   - Added missing playerMetadata and playerHovers fields to arcade-session-integration.test.ts
   - Skipped obsolete test in orphaned-session-cleanup.test.ts (roomId can't be null as it's the primary key)

5. Code quality fixes:
   - Removed unused type imports from room-moderation.ts
   - Changed to optional chain in MemoryQuizGameValidator.ts
   - Removed unnecessary fragment in MemoryQuizGame.tsx

Testing:
- All modified tests updated to match new schema requirements
- TypeScript errors resolved (excluding pre-existing @soroban/abacus-react issues)
- Lint passes with 0 errors and 0 warnings

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 18:28:01 -05:00
Thomas Hallock 86e3d41996 feat(moderation): add inline feedback and persistent password display
- Add success/error message UI component to ModerationPanel
- Replace all browser alert() calls with inline React-based feedback
- Add displayPassword field to arcade_rooms schema for plain text storage
- Create migration to add display_password column
- Update settings PATCH route to store both hashed and display passwords
- Update room GET route to return displayPassword only to room creator
- Update ModerationPanel to populate password field when loading settings
- Fix room-manager test to include displayPassword field

Password field now persists and displays correctly when reloading the page
for room owners in password-protected rooms.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 08:52:19 -05:00
Thomas Hallock 4a6b3cabe5 feat: implement approval request flow for share links
When users click share links for approval-only rooms, they now:
- See a prompt to request approval from the room moderator
- Can send a join request with one click
- Get a waiting screen showing their request is pending

Room moderators now see:
- A prominent blue badge showing pending join request count
- Combined count of join requests + reports in the badge
- Badge turns blue when join requests exist (vs red for reports only)
- Detailed tooltip showing breakdown of pending items
- Real-time polling (30s intervals) for new join requests

Also includes improvements to room display names using emoji prefixes.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 07:19:05 -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 9e025f8a0a chore: update Claude Code settings
Update local settings for development workflow.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 11:25:28 -05:00
Thomas Hallock eaf17e07fc chore: add biome permissions to Claude Code settings
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-11 09:18:17 -05:00
Thomas Hallock f9af0f169e chore: biome formatting fixes 2025-10-11 07:17:38 -05:00
Thomas Hallock 43f7c92f6d fix: clear hover state in CLEAR_MISMATCH for clean turn transitions
The previous fix only cleared hover on turn switch, but the actual turn
transition happens during CLEAR_MISMATCH (when cards flip back). This
was causing stale hover avatars to persist at the start of new turns.

Changes:
- **MatchingGameValidator.ts**: Clear non-current players' hovers in
  validateClearMismatch to ensure clean state when cards are cleared
- **RoomMemoryPairsProvider.tsx**: Mirror the server logic in optimistic
  CLEAR_MISMATCH handling
- **LocalMemoryPairsProvider.tsx**: Same fix for local-only games

Now when CLEAR_MISMATCH fires (after 1.5s mismatch timeout), we clear
hover state for all players except the current player, ensuring:
- No stale hovers from previous turns
- Only active player's current hover shows
- Clean transition between turns

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 12:55:41 -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 b128db1783 fix: convert LocalMemoryPairsProvider to pure client-side with useReducer
Replace arcade session-based provider with pure client-side reducer for
local-only games at /arcade/matching. This eliminates "No active session"
errors when starting non-networked games.

Changes:
- Replace useArcadeSession with useReducer for local state management
- Implement complete game logic client-side (match validation, turn switching)
- Remove dependency on socket/server for local games
- Add MATCH_FOUND, MATCH_FAILED, SWITCH_PLAYER actions to reducer
- Auto-detect matches and switch players using useEffect hooks

Local games now run entirely in the browser without requiring an arcade
session or network connection.

Fixes: Starting game at /arcade/matching no longer shows session errors

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 17:54:19 -05:00
Thomas Hallock 442c6b4529 feat: implement smooth hover avatar animations with react-spring
Add networked hover presence feature showing player avatars over cards
they're considering. Avatars smoothly glide between cards using react-spring
physics-based animations.

Key features:
- Fixed positioning with getBoundingClientRect for accurate placement
- Component keyed by playerId to persist across card changes
- Spring animations for smooth position transitions
- Only shows remote players' avatars (filters out local player)
- Only sends hover events when it's the player's turn

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 16:47:50 -05:00
Thomas Hallock 80ad33eec0 fix: export MemoryPairsContext to fix provider hook error
Exports MemoryPairsContext from MemoryPairsContext.tsx so that
RoomMemoryPairsProvider can properly provide to it. Also re-exports
useMemoryPairs hook from RoomMemoryPairsProvider for convenience.

This fixes the runtime error:
"useMemoryPairs must be used within a MemoryPairsProvider"

Changes:
- Export MemoryPairsContext in MemoryPairsContext.tsx
- Re-export useMemoryPairs from RoomMemoryPairsProvider.tsx

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 14:39:20 -05:00
Thomas Hallock d03c789879 feat: implement rich Radix UI tooltips for player avatars
Replace basic HTML title tooltips with Radix UI Tooltip components
that display comprehensive player information:

New PlayerTooltip component features:
- Player name with color accent dot
- Visual badge distinguishing local vs network players
- Player color indicator with glow effect
- "Joined X ago" timestamp (formatted as days/hours/minutes)
- For network players: shows controlling member name
- Glassmorphic dark design with backdrop blur
- Smooth fade-in animation

Updated components:
- ActivePlayersList: Wraps player avatars with PlayerTooltip
- NetworkPlayerIndicator: Shows "Controlled by [member]" in tooltip
- PageWithNav: Passes full player objects (including color, createdAt)
  instead of just {id, name, emoji}

Technical improvements:
- Added @radix-ui/react-tooltip dependency
- Removed all basic `title` attributes
- Type-safe player data passing through component tree
- Handles Date | number types for createdAt properly

The new tooltips provide much richer context for understanding
network opponents and collaborators in multiplayer games.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 07:56:22 -05:00
Thomas Hallock b49630f3cb test: add comprehensive tests for arcade guard and room navigation
Add tests to prevent regression of the enabled flag bug and verify
room navigation behavior with active game sessions.

New tests:
- useArcadeGuard with enabled=false blocks HTTP redirects
- useArcadeGuard with enabled=false blocks WebSocket redirects
- useArcadeGuard with enabled=true still allows redirects
- Room browser renders without redirect when user has active session
- Room navigation works with active sessions
- No redirect loops during rapid navigation
- Users can browse rooms during active gameplay

Fixes in existing tests:
- Updated mock response format to match API (wrapped in { session })
- All 16 useArcadeGuard tests passing
- All 5 room navigation tests passing

This ensures the /arcade-rooms pages remain accessible during
active game sessions, preventing the bug where users were
immediately redirected to /arcade/room.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 11:08:21 -05:00
Thomas Hallock ee6094d59d feat: extend GameModeContext to support room-based multiplayer
When a user is in a room, GameModeContext now merges players from all
room members to create a unified player set for gameplay. This enables
true multiplayer where all participants' active players participate
together in the game.

Key changes:
- Added useRoomData and useViewerId to GameModeContext
- Local players (from DB) are marked with isLocal: true
- Remote players (from other room members) are marked with isLocal: false
- Players map merges local + remote players when in a room
- activePlayers set includes all active players from all room members
- Edit operations (update/remove/setActive) only work on local players
- Socket broadcast when local players change to notify room members
- When not in a room, behavior is unchanged (solo/local multiplayer)

This implements the "players is the union of all active players for all
members of the room" requirement for room-based gameplay.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 10:13:33 -05:00
Thomas Hallock ebfc88c5ea fix: broadcast member join/leave events immediately via API
Add socket.io broadcasts to join and leave API routes to notify
all users in a room immediately when someone joins or leaves,
without waiting for socket connection.

Previously:
- API added member to database
- No socket event until user's browser connected
- Other users didn't see new member until page reload
- Players showed up but member didn't (timing race condition)

Now:
- API broadcasts `member-joined` immediately after adding member
- API broadcasts `member-left` immediately after removing member
- All connected users get real-time updates instantly
- Both members list and players list update simultaneously

Changes:
- Export `getSocketIO()` from socket-server.ts for API access
- Join route broadcasts member-joined with updated members/players
- Leave route broadcasts member-left with updated members/players
- Socket broadcasts are non-blocking (won't fail the request)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 08:51:17 -05:00
Thomas Hallock f005fbbb77 feat: implement modal room enforcement (one room per user)
Implement hybrid database + application-level enforcement to ensure users
can only be in one room at a time, with graceful auto-leave behavior and
clear error messaging.

## Changes

### Database Layer
- Add unique index on `room_members.user_id` to enforce one room per user
- Migration includes cleanup of any existing duplicate memberships
- Constraint provides safety net if application logic fails

### Application Layer
- Auto-leave logic: when joining a room, automatically remove user from
  all other rooms first
- Return `AutoLeaveResult` with metadata about rooms that were left
- Idempotent rejoining: rejoining the same room just updates status

### API Layer
- Join route returns auto-leave information in response
- Catches and handles constraint violations with 409 Conflict
- User-friendly error messages when conflicts occur

### Frontend
- Room list and detail pages handle ROOM_MEMBERSHIP_CONFLICT errors
- Show alerts when user needs to leave current room
- Refresh room list after conflicts to show current state

### Testing
- 7 integration tests for modal room behavior
- Tests cover: first join, auto-leave, rejoining, multi-user scenarios,
  constraint enforcement, and metadata accuracy
- Updated existing unit tests for new return signature

## Technical Details

- `addRoomMember()` now returns `{ member, autoLeaveResult? }`
- Auto-leave happens before new room join, preventing race conditions
- Database unique constraint as ultimate safety net
- Socket events remain status-only (joining goes through API)

## Testing
-  All modal room tests pass (7/7)
-  All room API e2e tests pass (12/12)
-  Format and lint checks pass

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-08 08:41:39 -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