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>
The Python flashcard generator requires qpdf for PDF processing.
Without it, the script exits with code 1 even though it prints
a warning saying it will skip linearization.
Added qpdf to Alpine packages to fix PDF generation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Typst was failing with "input file not found" error because the templates
directory containing flashcards-input.typ was missing from the Docker image.
Added COPY command to include packages/templates in the production image.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Auto-generated fresh SVG examples and unified gallery from latest templates.
Includes comprehensive crop mark demonstrations with before/after comparisons.
Files updated:
- packages/templates/gallery-unified.html
🤖 Generated with GitHub Actions
Co-Authored-By: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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>
Implement visual indicators and disabled states for spectator mode to
make it clear when users are watching vs playing.
**Provider.tsx**:
- Expose `localPlayerId` and `isSpectating` in context
- `isSpectating = !localPlayerId` (room members without active players)
**GameComponent.tsx**:
- Add spectator banner ("👀 Spectating [Player]'s game")
- Shows during playing/results phases for spectators
- Yellow gradient background with clear visual feedback
**PlayingPhase.tsx**:
- Disable all interactive elements for spectators:
- Available cards (opacity: 0.5, cursor: not-allowed)
- Position slots (opacity: 0.5, cursor: not-allowed)
- Insert buttons (disabled, visual feedback)
- Action buttons (Reveal, Check Solution, End Game)
- Block handlers early: `if (isSpectating) return`
- Remove hover effects when spectating
**User Experience**:
- Spectators see real-time game state updates
- All controls visually disabled (grayed out, not-allowed cursor)
- Cannot interact with game (click handlers blocked)
- Clear banner indicates spectator role
Completes spectator mode implementation per ARCADE_ARCHITECTURE.md.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Created comprehensive specification for optional spectator mode UI/UX
improvements to make spectator status clear and controls visually disabled.
**Five Enhancement Areas**:
1. **Context Exposure**
- Add `localPlayerId` and `isSpectating` to context
- Enable components to make spectator-aware UI decisions
2. **Spectator Indicator Banner**
- "👀 Spectating [Player]'s game" during playing/results
- "👤 Add a Player to Start" during setup
- Soft blue styling, non-intrusive
3. **Visual Disabled States**
- All buttons: opacity 0.5, cursor not-allowed
- All cards: opacity 0.6, pointer-events none
- Setup, playing, and results phases
4. **Spectator Mode Tests**
- Banner visibility tests
- Disabled control interaction tests
- State synchronization tests
- Context exposure tests
5. **Player Ownership Tests**
- Validate correct player can move
- Reject moves from non-active players
- Reject moves when user doesn't own player
- Reject spectator move attempts
**Includes**:
- Detailed code examples for all changes
- Visual mockups of UI states
- Implementation checklist (20 tasks)
- Success criteria
- Questions for user before implementation
Game is production-ready without these - enhancements are for improved UX.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Document spectator mode as a first-class feature in the arcade architecture.
**ARCADE_ARCHITECTURE.md**:
- Added "SPECTATOR" to core terminology
- Restructured sync modes to three patterns:
- Local Play (No Network Sync)
- Room-Based with Spectator Mode (RECOMMENDED)
- Pure Multiplayer (Room-Only)
- Added complete "Spectator Mode" section (297-603):
- Implementation patterns
- UI/UX considerations (indicators, disabled controls)
- When to use spectator mode
- Example scenarios (Family Game Night, Classroom)
- Server-side validation
- Testing requirements
- Migration path
**CARD_SORTING_AUDIT.md**:
- Updated to reflect room-based sync is CORRECT for spectator mode
- Changed from "CRITICAL ISSUE" to "CORRECT IMPLEMENTATION"
- Removed incorrect recommendation to use `roomId: undefined`
- Added enhancement recommendations (UI indicators, tests)
- Updated compliance checklist: 9/13 items passing
Card Sorting correctly enables spectator mode - room members without active
players can watch games in real-time, creating social/collaborative experiences.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Python 3.11+ prevents global pip installs by default. Since this is a
controlled Docker environment, use --break-system-packages to allow installing
the flashcard generation dependencies.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The pip3 command was not available, causing the Docker build to fail when
trying to install Python dependencies from requirements.txt. Added py3-pip
to the Alpine package installation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added critical section to .claude/CLAUDE.md documenting that this
project uses Panda CSS, NOT Tailwind CSS.
This prevents future confusion and incorrect references to Tailwind
in code, comments, or documentation.
Includes:
- Framework identification and configuration locations
- Import patterns and token syntax
- Common mistakes to avoid with examples
- Reference to GAME_THEMES.md for arcade styling
This mistake was made earlier in this session when documenting the
game theme system, so documenting it now to prevent recurrence.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Corrected documentation and comments that incorrectly referenced
Tailwind CSS. This project uses Panda CSS for styling.
Fixed in:
- `/src/lib/arcade/game-themes.ts` - Updated all comments
- `.claude/GAME_THEMES.md` - Fixed documentation references
The color system uses Panda CSS's preset colors (blue.100, blue.200, etc.)
not Tailwind. The hex values are the same, but we should be accurate
about which framework we're using.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
The Python scripts in packages/core require pyyaml, pillow, imagehash, and
other dependencies to generate flashcards. Install these from requirements.txt
during Docker build.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change card sorting game to use the same blue gradient as Memory Lightning
and Speed Complement Race to ensure consistent appearance on game chooser.
Updated to: linear-gradient(135deg, #dbeafe, #bfdbfe) (blue-100 to blue-200)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The flashcard generator was failing in production because the packages/core
directory (which contains the Python scripts for PDF generation) wasn't being
copied to the production Docker image. The SorobanGenerator class needs these
scripts at runtime to generate flashcards.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change card sorting game gradient from bright teal (teal-200 to teal-300)
to softer pastel teal (teal-100 to teal-200) to match the lighter gradient
style used by other arcade games (Memory Lightning, Matching Pairs).
Updated gradient: linear-gradient(135deg, #ccfbf1, #99f6e4)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The flashcard generator requires both Python and Typst to work.
Added typst to the runtime dependencies in the Dockerfile.
This fixes the issue where flashcards work in dev but fail in production.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed stats banner section as platform is still in development.
Also removed unused StatItem component.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed customStyles approach that wasn't working.
Added color: #1f2937 to container div - numerals inherit from parent.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
SVG foreign objects use CSS color property, not fill.
Changed numerals customStyle from fill to color for dark gray text.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added customStyles to make numerals dark gray (#1f2937) with bold weight
for good contrast on white background.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added useAbacusConfig() hook to get app-wide settings
- AbacusReact now respects beadShape, colorScheme, hideInactiveBeads from app config
- Removed "Click the beads to interact!" instruction text
- Simplified pane layout (just centered)
Now matches the styling/settings used throughout the rest of the app.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed background from dark to white for better contrast
- Removed duplicate number display (abacus has built-in numerals)
- Removed custom styles (defaults work well on light background)
- Updated instruction text color to gray.700 for readability
Abacus component isn't designed for dark mode yet, so light pane is simpler.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed critical issues based on storybook examples:
- Changed callback syntax: onValueChange prop directly (not in callbacks object)
- Added reckoningBar styling: golden stroke (#fbbf24, 4px width)
- Added columnPosts styling: purple stroke (#a78bfa, 3px width)
- Now structural elements are visible on dark background and interactive works
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Make card tiles larger to properly contain AbacusReact SVGs without overflow.
**Issue:**
Card tiles were too small (90px × 90px and 90px × 110px) to contain the
AbacusReact SVGs, causing abacuses to overflow their containers.
**Fix:**
- **Available cards**: Increased from 90px × 90px to 140px × 140px
- **Position slots**: Increased from 90px × 110px to 140px × 160px
**Result:**
Abacus SVGs now fit comfortably within their card containers without
overflowing, while maintaining the overflow: hidden constraint as a
safety measure.
src/arcade-games/card-sorting/components/PlayingPhase.tsx:283-284,419-420
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Make AbacusReact SVGs larger by allowing them to fill their containers.
**Issue:**
SVG containers were set to fixed small sizes (74px × 74px and 70px × 70px),
making the abacuses appear too small within their cards.
**Fix:**
- **Available cards**: Changed SVG container from 74px × 74px to 100% × 100%
to fill the entire 90px card (accounting for 8px padding)
- **Position slots**: Changed SVG container from 70px × 70px to flex: 1,
width: 100% to fill available space while leaving room for label
**Result:**
Abacuses now appear larger and fill their card containers better while
still being constrained by overflow: hidden to prevent breaking out.
src/arcade-games/card-sorting/components/PlayingPhase.tsx:314-329,456-473
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed back to controlled value (with state update) to display initial 1234567
- Added customStyles for numerals: golden color (#fbbf24), bold, larger font
- Numerals now have excellent contrast against dark background
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed from controlled (value) to uncontrolled (defaultValue) pattern
to allow AbacusReact to fully manage its own state and be interactive.
Still tracks value changes via onValueChange callback for display.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>