# [2.0.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v1.8.0...abacus-react-v2.0.0) (2025-10-20)
### Bug Fixes
* add dark color for abacus numerals ([73ff32c](73ff32c243)), closes [#1f2937](https://github.com/antialias/soroban-abacus-flashcards/issues/1f2937)
* add POST handler for join requests API endpoint ([d3e5cdf](d3e5cdfc54))
* add Typst to Docker image for flashcard generation ([d9a7694](d9a7694031))
* allow join with pending invitation for restricted rooms ([85b2cf9](85b2cf9816))
* allow password retry when joining via share link ([e469363](e469363699))
* **api:** add 'math-sprint' to settings endpoint validation ([d790e5e](d790e5e278)), closes [#1](https://github.com/antialias/soroban-abacus-flashcards/issues/1)
* **api:** include members and memberPlayers in room creation response ([8320d9e](8320d9e730))
* **arcade-rooms:** navigate to invite link after room creation ([1922b21](1922b2122b))
* **arcade:** add defensive checks and update test fixtures ([a93d981](a93d981d1a))
* **arcade:** add host-only game selection with clear messaging ([22df1b0](22df1b0b66))
* **arcade:** add host-only game selection with clear messaging ([c0680ca](c0680cad0f))
* **arcade:** add Number Guesser to game config helpers ([7d1a351](7d1a351ed6))
* **arcade:** allow room creator to rejoin restricted/approval rooms ([654ba19](654ba19ccc))
* **arcade:** delete old session when room game changes ([98a3a25](98a3a2573d))
* **arcade:** implement settings persistence for matching game ([08fe432](08fe4326a6))
* **arcade:** only notify room creator of join requests ([bc571e3](bc571e3d0d))
* **arcade:** preserve game settings when returning to game selection ([0ee7739](0ee7739091))
* **arcade:** preserve gameConfig when switching games ([2273c71](2273c71a87))
* **arcade:** prevent empty update in settings API when only gameConfig changes ([ffb626f](ffb626f403))
* **arcade:** prevent gameConfig from being overwritten when switching games ([a89d3a9](a89d3a9701))
* **arcade:** prevent server-side loading of React components ([784793b](784793ba24))
* **arcade:** read nested gameConfig correctly when creating sessions ([94ef392](94ef39234d))
* **arcade:** remove broken query param from game URLs ([87631af](87631af678))
* **arcade:** remove legacy master-organizer placeholder ([76d207e](76d207e2e5))
* **arcade:** resolve TypeScript errors in game config helpers ([04c9944](04c9944f2e))
* **build:** resolve Docker build failures preventing deployment ([7801dbb](7801dbb25f))
* **card-sorting:** center AbacusReact SVGs in card tiles ([26edec1](26edec1bbf))
* **card-sorting:** faithfully port UI/UX from Python original ([c92076f](c92076f232)), closes [#2c5f76](https://github.com/antialias/soroban-abacus-flashcards/issues/2c5f76) [#1976d2](https://github.com/antialias/soroban-abacus-flashcards/issues/1976d2)
* **card-sorting:** increase card tile sizes to contain abacuses ([d2a3b7a](d2a3b7ae2e))
* **card-sorting:** increase SVG size to fill card containers ([cf9d893](cf9d893f3f))
* **card-sorting:** match game selector background to other games ([db62519](db62519f9b)), closes [#ccfbf1](https://github.com/antialias/soroban-abacus-flashcards/issues/ccfbf1) [#99f6e4](https://github.com/antialias/soroban-abacus-flashcards/issues/99f6e4)
* **card-sorting:** match Python card layout with flex wrap ([9679d68](9679d68154))
* **card-sorting:** position slots flow horizontally with wrap ([e14ffe4](e14ffe44d6))
* **card-sorting:** use blue gradient matching other game cards ([bdb84f5](bdb84f5d90))
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* **complement-race:** add missing AI commentary cooldown updates ([357aa30](357aa30618))
* **complement-race:** add missing useEffect import ([3054130](30541304dd))
* **complement-race:** add missing useRef import ([d43829a](d43829ad48))
* **complement-race:** add pressure decay system and improve logging ([66992e8](66992e8770))
* **complement-race:** balance AI speeds to match original implementation ([054f0c0](054f0c0d23))
* **complement-race:** clear input state on question transitions ([5872030](587203056a))
* **complement-race:** correct passenger boarding to use multiplayer fields ([7ed1b94](7ed1b94b8f))
* **complement-race:** counter-flip AI speech bubbles to make text readable ([07d5607](07d5607218))
* **complement-race:** flip AI racers to face right in practice mode ([ebfff1a](ebfff1a62f))
* **complement-race:** flip player avatar to face right in practice mode ([fa6b3b6](fa6b3b69d5))
* **complement-race:** implement client-side momentum with continuous decay for smooth train movement ([ea19ff9](ea19ff918b))
* **complement-race:** improve AI speech bubble positioning ([6e436db](6e436db5e7))
* **complement-race:** reduce initial momentum from 50 to 10 to prevent train sailing past first station ([5f146b0](5f146b0daf))
* **complement-race:** remove dual game loop conflict preventing route progression ([84d42e2](84d42e22ac))
* **complement-race:** resolve TypeScript errors in state adapter ([59abcca](59abcca4c4))
* **complement-race:** restore smooth train movement with client-side game loop ([46a80cb](46a80cbcc8))
* **complement-race:** show new passengers when route changes ([ec1c8ed](ec1c8ed263))
* **complement-race:** track physical car indices to prevent boarding issues ([53bbae8](53bbae84af))
* **complement-race:** track previous position to detect route threshold crossing ([a6c20aa](a6c20aab3b))
* **complement-race:** train now moves in sprint mode ([54b46e7](54b46e771e))
* **complement-race:** update passenger display when state changes ([5116364](511636400c))
* **complement-race:** use active local players pattern from navbar ([71cdc34](71cdc342c9))
* **complement-race:** use local player emoji instead of first active player ([76eb051](76eb0517c2))
* correct AbacusReact API usage and add structural styling ([247377f](247377fca3)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78)
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* **db:** add 'math-sprint' to database schema enums ([7b112a9](7b112a98ba)), closes [#1](https://github.com/antialias/soroban-abacus-flashcards/issues/1)
* **deployment:** pass git info to Docker build for deployment info modal ([4b04e43](4b04e43ff8))
* **docker:** add packages/templates for Typst flashcard generation ([1417722](1417722438))
* **docker:** add qpdf for PDF linearization and validation ([c92ff39](c92ff3971c))
* **docker:** bypass PEP 668 externally-managed-environment error ([bb59c61](bb59c61638))
* **docker:** copy core package with Python scripts to production image ([33e9ad2](33e9ad2f79))
* **docker:** include Panda CSS styled-system in production image ([57fabff](57fabffe60))
* **docker:** install py3-pip for Python dependency installation ([0f55909](0f55909533))
* **docker:** install Python dependencies for flashcard generation ([c9b7e92](c9b7e92f39))
* **docker:** remove reference to deleted @soroban/client package ([2953ef8](2953ef8917))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **homepage:** adjust mini abacus container height ([c4066d6](c4066d6879))
* **homepage:** correct positioning of progression arrows in Your Journey section ([3fff9ef](3fff9ef140))
* **homepage:** fix MiniAbacus runtime error and improve sizing ([1fa0df8](1fa0df85f7))
* **homepage:** improve text contrast in Your Journey section ([24d1200](24d120004d))
* **homepage:** use correct AbacusReact API and fix clipping/styling issues ([1432afd](1432afd6e6))
* **homepage:** use direct conditionals for mini abacus padding ([38ef16a](38ef16a8f9))
* **homepage:** use explicit RGBA colors for Your Journey text ([9c51cc9](9c51cc94ee))
* **homepage:** use inline styles for journey level colors ([5d85e89](5d85e898d6)), closes [#4ade80](https://github.com/antialias/soroban-abacus-flashcards/issues/4ade80) [#60a5](https://github.com/antialias/soroban-abacus-flashcards/issues/60a5) [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24)
* **homepage:** use inline styles for Your Journey text contrast ([8e51390](8e51390018)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3) [#d1d5](https://github.com/antialias/soroban-abacus-flashcards/issues/d1d5)
* **home:** use Panda CSS token() for dynamic colors and center arrows properly ([d52ba63](d52ba6373a))
* improve authorization error handling and add missing decline invitation endpoint ([97669ad](97669ad084))
* improve join request approval error handling with actionable messages ([57bf846](57bf8460c8))
* improve kicked modal message for retired room ejections ([f865ce1](f865ce16ec))
* join user socket channel to receive approval notifications ([7d08fdd](7d08fdd906))
* **levels:** use correct AbacusReact API with direct props ([892b377](892b377eb3))
* **levels:** use correct dark mode styling from homepage + docs update ([c38767f](c38767f4d3))
* **matching:** add settings persistence to matching game ([00dcb87](00dcb872b7))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **math-sprint:** remove unused import and autoFocus attribute ([51593eb](51593eb44f))
* **memory-quiz:** fix playMode persistence by updating validator ([de0efd5](de0efd5932))
* **memory-quiz:** persist playMode setting across game switches ([487ca7f](487ca7fba6))
* **memory-quiz:** prevent duplicate card processing from optimistic updates ([51676fc](51676fc15f))
* **memory-quiz:** prevent input lag during rapid typing in room mode ([b45139b](b45139b588))
* **memory-quiz:** scope game settings by game name for proper persistence ([3dfe54f](3dfe54f1cb))
* **memory-quiz:** synchronize card display across all players in multiplayer ([472f201](472f201088))
* **migrations:** add migration 0009 for display_password column ([040d749](040d7495a0))
* **moderation:** don't show pending invitation for users already in room ([fae5920](fae5920e2f))
* **moderation:** improve access mode settings UX ([dd9e657](dd9e657db8))
* move invitations into nav and filter out current/banned rooms ([cfaf82b](cfaf82b2cc))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** close hamburger menu when nested dropdown closes and mouse not hovering ([7d65212](7d652126d0))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** navigate to /arcade/room (not /arcade/rooms/{id}) ([1c55f36](1c55f3630c))
* **nav:** navigate to room after creation from (+) menu ([21e6e33](21e6e33173))
* **nav:** prevent hamburger menu from closing when toggling Style dropdown ([a898fbc](a898fbc187))
* **nav:** prevent style dropdown from closing hamburger menu ([560a052](560a05266e))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** properly prevent nested style dropdown from closing hamburger menu ([c5b6a82](c5b6a82ca4))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* **nav:** update types for registry games with nullable gameName ([a51e539](a51e539d02))
* **number-guesser:** add turn indicators, error feedback, and fix player ordering ([9f62623](9f62623684))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* **player-config:** correct label positioning in player settings dialog ([554cc40](554cc4063b))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove duplicate ModerationNotifications causing double toasts ([c6886a0](c6886a0e59))
* replace isLocked with accessMode and add bcryptjs ([a74b96b](a74b96bb6f))
* replace last remaining isLoading with isPending in CreateRoomModal ([85d13cc](85d13cc552))
* replace native alerts with inline confirmations in ModerationPanel ([ebe123e](ebe123ed7e))
* reset join request toast state when moderation event cleared ([6beb58a](6beb58a7b8))
* resolve Memory Quiz room-based multiplayer validation issues ([2ffeade](2ffeade437))
* resolve TypeScript errors in MemoryGrid and StandardGameLayout ([cabbc82](cabbc82195))
* **room-data:** update query cache when gameConfig changes ([7cea297](7cea297095))
* **rooms:** add real-time ownership transfer updates via WebSocket ([c00cfa3](c00cfa3de0))
* **room:** update GAME_TYPE_TO_NAME mapping for memory-quiz ([4afa171](4afa171af2))
* set color on abacus container div for numeral visibility ([cd47960](cd4796024e)), closes [#1f2937](https://github.com/antialias/soroban-abacus-flashcards/issues/1f2937)
* show initial value and improve numeral contrast ([1b57f6d](1b57f6ddec)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24)
* simplify abacus pane with light background ([30f48ab](30f48ab897))
* **socket-io:** update import path for socket-server module ([1a64dec](1a64decf5a))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* **toast:** scope animations to prevent affecting other UI elements ([245ed8a](245ed8a625))
* **tutorial:** correct column index calculation for variable column counts ([bf1ced4](bf1ced43f8))
* **tutorial:** filter bead highlights when using fewer columns ([4d906ec](4d906ec20e))
* **tutorial:** reduce tooltip z-index to scroll under nav bar ([47640f3](47640f3486))
* **tutorial:** resolve React hydration error in TutorialPlayer ([c883d9e](c883d9e4c1))
* **tutorial:** resolve TypeScript errors in TutorialPlayer ([88f57ce](88f57ce6df))
* **tutorial:** use correct customStyles API for dark mode frame styling ([fdc882c](fdc882cb04))
* update locked room terminology and allow existing members ([1ddf985](1ddf985938))
* use app-wide abacus config and remove instruction text ([0a50c73](0a50c733b0))
* use color instead of fill for numeral styling ([ea10c16](ea10c16811))
* use defaultValue for interactive abacus control ([06aca98](06aca986ac))
* use useCreateRoom hook instead of nonexistent createRoom from useRoomData ([f7d63b3](f7d63b30ac))
### Code Refactoring
* **db:** remove database schema coupling for game names ([e135d92](e135d92abb)), closes [#1](https://github.com/antialias/soroban-abacus-flashcards/issues/1)
### Features
* **abacus-react:** add BigInt support for 30-digit Dan level abacuses ([0ab4cc2](0ab4cc2880))
* add API routes for moderation and invitations ([79a8518](79a8518557))
* add backend library functions for room moderation ([84f3c4b](84f3c4bcfd))
* add common UI components ([cd3115a](cd3115aa6d))
* add database schema for room moderation and invitations ([97d1604](97d16041df))
* add drizzle migration for room_game_configs table ([3bae00b](3bae00b9a9))
* add fun automatic player naming system ([249257c](249257c6c7))
* add invitation system UI components ([fd3a2d1](fd3a2d1f76))
* add moderation panel with unban & invite feature ([a2d0169](a2d0169f80))
* add name generator button and abacus emoji ([07212e4](07212e4df0))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* add prominent join request approval notifications for room moderators ([036da6d](036da6de66))
* add real-time socket updates for moderation events ([86ceba3](86ceba3df3))
* add room access modes and ownership transfer ([6ff21c4](6ff21c4f1d))
* add room creation and join flow UI ([7f95032](7f95032253))
* add socket listener and polling for approval notifications ([35b4a72](35b4a72c8b))
* add waiting state for approval requests in JoinRoomModal ([f9b0429](f9b0429a2e))
* adjust tier probabilities for more abacus flavor ([49219e3](49219e34cd))
* **arcade:** add Card Sorting Challenge game scaffolding ([df37260](df37260e26))
* **arcade:** add Change Game functionality for room hosts ([ee39241](ee39241e3c))
* **arcade:** add game selection screen with navigation to room page ([4124f1c](4124f1cc08))
* **arcade:** add Math Sprint game implementation ([e5be09e](e5be09ef5f))
* **arcade:** add modular game SDK and registry system ([de30bec](de30bec479))
* **arcade:** add Number Guesser demo game with plugin architecture ([0e3c058](0e3c058707))
* **arcade:** broadcast game selection changes to all room members ([b99e754](b99e754395))
* **arcade:** migrate matching pairs - phases 1-4 and 7 complete ([2a3af97](2a3af973f7))
* **arcade:** migrate memory-quiz to modular game system ([f48c37a](f48c37accc))
* **arcade:** register Math Sprint in game system ([0c05a7c](0c05a7c6bb)), closes [#2](https://github.com/antialias/soroban-abacus-flashcards/issues/2) [#3](https://github.com/antialias/soroban-abacus-flashcards/issues/3)
* **card-sorting:** add spectator mode UX enhancements ([4ab093a](4ab093a9d8))
* **card-sorting:** add UI components and fix AbacusReact props ([d249ec0](d249ec0e5f))
* **card-sorting:** implement Provider with arcade session integration ([7f6fea9](7f6fea91f6))
* **complement-race:** add infinite win condition for Steam Sprint mode ([d8fdfee](d8fdfeef74))
* **complement-race:** add mini app navigation bar ([ed0ef2d](ed0ef2d3b8))
* **complement-race:** enable adaptive AI difficulty in arcade ([55010d2](55010d2bcd))
* **complement-race:** implement state adapter for multiplayer support ([13882bd](13882bda32))
* **complement-race:** restore AI opponents in practice and survival modes ([325e07d](325e07de59))
* **homepage:** add animated mini abacus to "Read and set numbers" card ([e028e34](e028e342ad))
* **homepage:** add more visual embellishments to learning cards ([4ec1b95](4ec1b952f2))
* **homepage:** enhance "What You'll Learn" with visual cards ([d142342](d1423420e6))
* **home:** redesign home page to showcase complete platform ([ee6c4f2](ee6c4f2f4f))
* implement approval request flow for share links ([4a6b3ca](4a6b3cabe5))
* implement avatar-themed name generation with probabilistic mixing ([76a8472](76a8472f12))
* implement proper retired room behavior with member expulsion ([a2d5368](a2d53680f2))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* improve room creation UX and add password support for share links ([dcbb507](dcbb5072d8))
* integrate moderation system into arcade pages ([087652f](087652f9e7))
* **levels:** add Dan levels ladder visualization ([c18012c](c18012cb50))
* **levels:** add dark mode styling and responsive scaling to abacus ([92e1e62](92e1e62132))
* **levels:** add informational footer section ([0b1bff7](0b1bff7eab))
* **levels:** add Kyu & Dan levels page with homepage link ([39b1e7d](39b1e7de16))
* **levels:** add kyu level data and cards ([6463a3b](6463a3b2f6))
* **levels:** create true horizontal slider with abacus visualizations ([6d734f1](6d734f1d51))
* **levels:** implement interactive slider for exploring kyu & dan ranks ([eb3b100](eb3b100056))
* **levels:** replace kyu grid with interactive slider and abacus visualizations ([10978e8](10978e890b))
* make home page abacus interactive with audio ([9a53d7e](9a53d7e5db))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **math-sprint:** add game manifest ([1eefcc8](1eefcc89a5))
* **memory-quiz:** add multiplayer support with redesigned scoreboards ([1cf4469](1cf44696c2))
* **memory-quiz:** persist game settings per-game across sessions ([05a8e0a](05a8e0a842))
* **memory-quiz:** show player emojis on cards to indicate who found them ([05bd11a](05bd11a133))
* **moderation:** add inline feedback and persistent password display ([86e3d41](86e3d41996))
* **moderation:** improve password input with copy button ([2580e47](2580e474d0))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* prevent invitations to retired rooms ([a7c3c1f](a7c3c1f4cd))
* redesign home page with component showcase ([29af265](29af265958))
* redesign homepage with educational vision and interactive demo ([2f09cb5](2f09cb5539))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
* replace access mode dropdown with visual button grid ([e5d0672](e5d0672059))
* **tutorial:** add dark mode styling for coaching bar and abacus frame ([7e2f580](7e2f580877))
* **tutorial:** add dark theme and column control props ([d42f9b2](d42f9b2d9a))
* **tutorial:** add fill color support for dark mode column posts and reckoning bar ([2eb3ff3](2eb3ff3406))
* **tutorial:** add hideNavigation prop to TutorialPlayer ([79ea52a](79ea52af80))
* **tutorial:** add hideTooltip prop and improve dark mode coaching bar ([1ee25b3](1ee25b3dd2))
* **tutorial:** add silentErrors prop to suppress error messages ([8835e1c](8835e1c57a))
### Reverts
* **nav:** restore original room creation/join behavior ([710e93c](710e93c997))
### BREAKING CHANGES
* **db:** Database schemas now accept any string for game names
* Added DELETE /api/arcade/rooms/:roomId/invite endpoint for declining invitations
Authorization Error Handling:
- ModerationPanel: Parse and display API error messages (kick, ban, unban, invite, data loading)
- PendingInvitations: Parse and display API error messages (decline, fetch)
- All moderation actions now show specific auth errors like "Only the host can kick users"
New Endpoint:
- DELETE /api/arcade/rooms/:roomId/invite: Allow users to decline their pending invitations
* Validates invitation exists and is pending
* Only invited user can decline their own invitation
* Returns proper error messages for auth failures
Bug Fix:
- Fixed invitations/pending/route.ts ban check query (removed reference to non-existent unbannedAt field)
- Ban records are deleted when unbanned, so any existing ban is active
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add BigInt support to AbacusReact to handle 30-digit numbers without
precision loss (JavaScript's Number.MAX_SAFE_INTEGER is ~16 digits).
Changes:
- Update AbacusReact types to accept `value?: number | bigint`
- Modify useAbacusPlaceStates hook to use string-based digit parsing
- Add conditional BigInt arithmetic for >15 digits (maxPlaceValue > 14)
- Update levels page to pass BigInt for Dan levels (30 columns)
- Fix games page Date comparison (unrelated TypeScript error)
The implementation automatically detects when BigInt is needed based on
the number of digits, maintaining backward compatibility.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed abacus styling issues by matching homepage implementation:
- Use `fill` (not just `stroke`) for columnPosts and reckoningBar
- Changed from all zeros to interesting display value (123456...)
- Removed incorrect color customization causing mixed bead styles
- Now uses exact same darkStyles pattern as homepage MiniAbacus
Documentation update:
- Added MANDATORY section: "Read the Docs Before Customizing"
- Emphasized always reading packages/abacus-react/README.md
- Added references to homepage and storybook examples
- Included concrete example of correct darkStyles usage
- Key point: columnPosts and reckoningBar need `fill` property
This ensures columns and reckoning bar are now visible on dark backgrounds
and provides guidance to prevent similar issues in the future.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improvements to the levels page abacus display:
- Added showNumbers={true} to show place value numbers
- Styled for dark background with light gray columns and reckoning bar
- Colored beads (blue heaven, green earth) for better visibility
- Dynamic scaling: large (2.5x) for Kyu levels, smaller for Dan levels
- Added horizontal overflow for very wide Dan level abacuses (30 columns)
- Formula: scaleFactor = Math.min(2.5, 20 / digits)
The abacus now fits gracefully at all levels and is clearly visible
on the dark page background.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed abacus not rendering by using the correct API:
- Removed non-existent useAbacusConfig import
- Changed from config object to direct props (value, columns, scaleFactor)
- Added scaleFactor=1.5 for better visibility
The abacus now properly displays with the appropriate number of columns
based on the selected kyu/dan level.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace static grid layout with an interactive range slider that allows
users to explore all 21 kyu and dan levels dynamically. The slider updates
a single AbacusReact component showing the appropriate number of columns
(2-30 digits) based on the selected rank.
Features:
- HTML range input slider from 10th Kyu to 10th Dan
- Dynamic abacus visualization using @soroban/abacus-react
- Real-time updates of level metadata (emoji, name, min score)
- Color-coded borders matching progression levels
- Reference markers for key ranks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace carousel with actual horizontal slider showing all 21 ranks:
- Continuous scrollable view of 10 Kyu levels + 11 Dan levels
- Each level card displays:
- Level name, emoji, and metadata
- Visual abacus showing digit mastery (2-30 columns)
- Color-coded by progression (green→blue→violet→amber)
- Simplified abacus columns with proper visual structure
- Visual transition marker between Kyu and Dan sections
- Color legend for all four progression stages
- Fully mobile-responsive horizontal scrolling
Also add documentation note about using @soroban/abacus-react for all
abacus visualizations in the codebase.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace cluttered static grid of kyu level cards with an elegant horizontal slider.
Each level now features:
- Interactive slider with left/right navigation buttons
- Keyboard navigation support (arrow keys)
- Visual abacus showing digit mastery progression (2-10 columns)
- Clickable progress indicators
- Smooth transitions and hover effects
- Mobile-responsive design
The abacus visualizations provide an intuitive representation of the student's
progression through each level, showing the increasing number of digit columns
they master from 10th Kyu (2 digits) to 1st Kyu (10 digits).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive "About This Ranking System" section at the bottom of the
page explaining the Japan Abacus Federation ranking system and its purpose.
Features:
- Educational context about the JAF ranking system
- Explanation of progressive difficulty structure
- Clear disclaimer about educational vs certification purposes
- Professional styling matching the rest of the page
- Mobile-responsive padding and typography
Phase 6 complete: Final polish with educational context and disclaimers.
All phases complete! The /levels page now provides:
- Complete kyu level information (10th to 1st)
- Dan level ladder visualization (Pre-1st Dan to 10th Dan)
- Exam requirements and scoring details
- Educational context and disclaimers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive Dan level section (Pre-1st Dan to 10th Dan) with score-based
ranking system. Display as vertical ladder showing progression from 90 to 290
points.
Features:
- Dan exam requirements box (30-digit problems, 3× 1st Kyu complexity)
- Ladder visualization with 11 Dan ranks
- Japanese names (Shodan, Nidan, etc.)
- Score thresholds for each rank
- Amber/gold color theme for master ranks
- Hover effects on each ladder rung
- Mobile-responsive design
Phase 4 complete: Full Dan ranking system with official JAF requirements.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive kyu level information from 10th to 1st Kyu with detailed
exam requirements. Display data in responsive grid of color-coded cards.
Data includes:
- Duration, pass thresholds, and point requirements
- Problem types with digit complexity for each operation
- Color-coded by difficulty (green → blue → violet)
- Hover effects and mobile-responsive layout
Phase 2 complete: All 10 kyu levels with official JAF requirements.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Transform the journey section into an interactive card that navigates to
/levels. Remove separate button in favor of whole-card clickability with
clear hover feedback.
Changes:
- Wrap entire card in Link component
- Add hover effects: lift, violet border, purple shadow
- Add subtle arrow indicator in top-right corner
- Update text: "Click to learn about the official Japanese ranking system"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create new /levels page with hero section explaining the Japanese soroban
ranking system (10th Kyu to 10th Dan). Add navigation link from homepage
"Your Journey" section with violet-themed button styling.
Phase 1 complete: basic page structure and routing ready for content.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increase emoji size from 3xl to 5xl for better visual presence.
Tighten spacing between emojis and labels by setting gap to 0 and
adding negative top margin (-2) to level labels.
Changes:
- Increase emoji fontSize from '3xl' to '5xl'
- Remove emoji bottom margin (mb: '0')
- Remove gap between emoji and labels (gap: '0')
- Add negative top margin to level labels (mt: '-2')
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed horizontal padding for all skill icon containers to ensure
consistent alignment across the "What You'll Learn" section.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reduced vertical padding from token '5' to '4' in the "Read and set
numbers" card icon container for better visual balance.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace spread operator with direct conditional values for py and px
to ensure proper application with Panda CSS.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use py: '5' and px: '3' instead of uniform padding to create visually
equal spacing on all sides of the animated abacus.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Double the padding from '3' to '5' for more generous vertical spacing
around the animated abacus.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Increase padding from '2' to '3' on the mini abacus icon container
to provide more vertical space between the abacus and the card border.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Set container height to 80px to prevent excess vertical space while
keeping the abacus fully visible and centered.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Use the correct pattern for AbacusReact:
- Call useAbacusConfig() without parameters for global config
- Pass individual props (value, columns, beadShape, styles) instead of config object
- No more timing hacks - the proper API doesn't have initialization issues
Fix display issues:
- Remove fixed height and overflow:hidden to prevent clipping
- Add dark theme styles for columnPosts and reckoningBar
- Reduce scale from 0.7 to 0.6 with proper transform origin
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix "Cannot read properties of undefined (reading 'earthActive')" error by:
- Adding 500ms initialization delay before starting value cycling
- Starting with value 123 instead of 0 to show valid state immediately
- Splitting ready state management into separate useEffect
Also improve container sizing:
- Set explicit width (75px) and height (75px)
- Add transform scale(0.7) to fit better in card icon space
- Add overflow hidden to contain the component
- Increase cycle interval to 2.5s for smoother transitions
The error occurred because the AbacusReact component was trying to access
column states before they were fully initialized. The delay ensures proper
initialization before animation starts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace static 🔢 emoji with a functional 3-column abacus that cycles through
random 3-digit numbers (0-999) every 2 seconds. Uses dark theme styling to
match the homepage aesthetic.
Changes:
- Create MiniAbacus component using AbacusReact
- Cycle through random numbers using useState/useEffect
- Constrain height to ~75px as specified
- Conditionally render in first skill card (i === 0)
- Use theme="dark" to match tutorial abacus styling
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace simple checklist with rich visual cards for each learning objective.
Each card now includes:
- Large prominent icon
- Title and description
- Example/demo text
- Hover effects
- Card-based layout with subtle borders
Makes the section more engaging and less text-heavy, better balanced with
the tutorial demo on the left.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change tutorial tooltip z-index from 1000 to 50 so it scrolls under the
app nav bar (z-index 100) along with the abacus. This prevents the coaching
text from appearing layered above the nav while the abacus it points to is
hidden beneath it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Restructure "Learn by Doing" section to display tutorial and "What You'll
Learn" side by side. Items now display vertically instead of in a 2x2 grid.
Changes:
- Tutorial positioned on left with flex: 1
- "What You'll Learn" section positioned on right
- Items arranged vertically using stack layout
- Increased container max-width from 900px to 1200px
- Responsive: stacks vertically on mobile, side-by-side on md+ screens
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add silentErrors prop to TutorialPlayer to allow suppressing "That's not
the highlighted bead" error messages. Used on homepage to provide a less
intrusive demo experience.
Changes:
- Add silentErrors prop to TutorialPlayerProps interface
- Conditionally skip error dispatch when silentErrors is true
- Pass silentErrors={true} from homepage TutorialPlayer
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed abacusColumns from 2 to 1 for the homepage tutorial demo.
Since the "Friends of 5" (2+3) demonstration only needs the ones place,
a single column is more focused and less distracting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Changes:**
- Use Panda CSS token() function for dynamic color references instead of inline hex values
- Fix arrow positioning to be centered in gap between progression stages
- Document Panda CSS dynamic token usage pattern
**Color Token Fix:**
Panda CSS css() requires static values at build time. For dynamic token references,
use the token() function with inline styles:
- Import: token from styled-system/tokens
- Usage: style={{ color: token(stage.color) }}
- Token paths marked as const for TypeScript literal types
**Arrow Positioning Fix:**
Arrows between stages were positioned based on element width, not gap width.
Now properly centered using:
- left: 100% (position at right edge)
- marginLeft: 0.5rem (half of 1rem gap)
- transform: translate(-50%, -50%) (center on that point)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed progression level text from Panda CSS tokens to inline hex colors:
- 10 Kyu: #4ade80 (green)
- 5 Kyu: #60a5fa (blue)
- 1 Kyu: #a78bfa (purple)
- Dan: #fbbf24 (gold)
This ensures all text displays with proper colors regardless of CSS loading.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The styled-system directory containing generated Panda CSS was being
created during build but not copied to the production image, causing
all Panda CSS classes to be undefined at runtime.
This fix copies the generated styled-system directory from the builder
stage to the production image, ensuring styles.css is available.
Fixes missing CSS definitions for classes like fs_xl, font_bold, text_blue.400
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switched from Panda CSS to direct inline styles with hex colors:
- Labels (Beginner/Intermediate/Advanced/Master): #e5e7eb (light gray)
- Subtitle: #e5e7eb (light gray)
- Arrows: #9ca3af (medium gray)
- Footer: #d1d5db (light gray)
This bypasses any CSS framework issues and applies colors directly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove references to non-existent highlight.columnIndex property
- Remove references to removed currentStep.errorMessages property
- Use placeValue directly for highlight filtering and calculations
- Add generic error message for incorrect bead clicks
All changes maintain existing functionality while fixing type safety issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added position: 'relative' to parent containers to properly anchor the absolutely positioned arrow elements between progression levels. This ensures the arrows display correctly between stages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Repositioned the learning objectives section to appear before the interactive tutorial for better visual hierarchy and user flow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added hideTooltip prop to TutorialPlayer to optionally hide guidance panels
- Enhanced coaching bar text for dark mode (brighter yellow with glow effect)
- Applied hideTooltip to homepage tutorial for cleaner presentation
- Updated dark mode header background for better integration
These changes are specific to the homepage dark theme instance while preserving default behavior for all other uses of the tutorial system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added fill property to ColumnPostStyle and ReckoningBarStyle interfaces in abacus-react to enable high-contrast colors in dark mode. Updated TutorialPlayer to set fill colors for column posts (30% white) and reckoning bar (40% white) when in dark theme mode.
This improves visibility of the abacus frame elements in dark mode on the homepage tutorial.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the dark mode styling to use the correct AbacusReact customStyles API:
Previous (incorrect):
- Used nested `frame` object that doesn't exist in the API
- `frame.column`, `frame.reckoningBar`, `frame.border`
Corrected (per AbacusReact.tsx interface):
- `columnPosts` - Global styling for all column dividers
- `reckoningBar` - Horizontal middle bar styling
Changes:
- Column dividers: rgba(255, 255, 255, 0.2) with 2px stroke
- Reckoning bar: rgba(255, 255, 255, 0.25) with 3px stroke
These properties are at the root level of customStyles, not nested
under a `frame` object. The styling will now properly apply to the
abacus frame elements in dark mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhanced the dark mode theme support for the tutorial player:
Coaching Bar:
- Updated instruction text color to use yellow.300 for dark mode instead of
hardcoded yellow.900
- Ensures coaching instructions are readable against dark backgrounds
Abacus Frame:
- Added custom frame styling for dark mode using customStyles prop
- Column dividers: rgba(255, 255, 255, 0.15) with 2px stroke
- Reckoning bar: rgba(255, 255, 255, 0.2) with 3px stroke
- Outer border: rgba(255, 255, 255, 0.15) with 2px stroke
- Provides subtle, elegant appearance that blends with dark theme
The frame styling is automatically applied when theme="dark" and does not
affect light mode or other tutorial instances.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed a critical bug where tooltip overlays were referencing invalid column
indices when using fewer than 5 columns. The issue occurred because column
index calculations assumed a 5-column layout (0-4), but when using
abacusColumns={2}, the valid indices should be 0-1.
Changes:
- Updated targetColumnIndex calculation to use (abacusColumns - 1) - placeValue
instead of hardcoded 4 - placeValue
- Fixed hasActiveBeadsToLeft logic to use abacusColumns for padding and
column index conversions
- All column index calculations now properly account for the actual number
of columns
This resolves the "Cannot read properties of undefined (reading 'heavenActive')"
error that occurred when using fewer than 5 columns on the homepage tutorial.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix runtime error when abacusColumns < 5 by filtering all bead highlights
to only include columns that actually exist.
Changes:
- Filter highlightBeads prop to only include valid place values
- Filter stepBeadHighlights to only include valid place values
- Filter customStyles column highlights to only include valid columns
- Add abacusColumns to dependencies of relevant useMemo/useCallback
This prevents accessing undefined column states when rendering with
fewer than 5 columns (e.g., abacusColumns={2} for simple demos).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add `theme` and `abacusColumns` props to TutorialPlayer for better customization:
- theme: 'light' | 'dark' controls all color schemes
- abacusColumns: number controls abacus column count (default 5)
Updated homepage to use:
- abacusColumns={2} for simpler 2+3 demo
- theme="dark" for cohesive integration with dark page design
- Vertical layout with "What You'll Learn" below tutorial
Dark theme styling:
- Transparent dark backgrounds for all containers
- Muted text colors (gray.200-gray.400)
- Subtle borders and shadows
- Removed bright yellow/amber gradients
All changes maintain backward compatibility - defaults to light theme with 5 columns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Apply visual improvements to homepage tutorial demo only (not tutorial system):
- Hide close (X) button in CoachBar - not needed on homepage
- Soften white backgrounds to light gray (#f9fafb)
- Mute text colors (h2 to gray.800, p to gray.600)
- Add transparency to guidance box (reduced opacity)
- Improve spacing and padding throughout
- Soften all shadows (reduced opacity)
- Mute amber/slate text colors for better dark theme integration
All changes scoped to .homepage-tutorial-demo wrapper via CSS overrides.
Tutorial system remains unchanged.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add `hideNavigation` prop to TutorialPlayer component that hides
the header and footer navigation controls, allowing the tutorial
content to be embedded cleanly without navigation chrome.
Perfect for single-step tutorial demos like the homepage.
Changes:
- Add hideNavigation prop to TutorialPlayerProps
- Wrap header section in conditional rendering
- Wrap navigation footer in conditional rendering
- Update homepage to use hideNavigation={true}
- Adjust minHeight when navigation is hidden
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Change tutorial container background from bright white to dark
semi-transparent black (rgba(0, 0, 0, 0.4)) with gray border to
match the homepage's dark aesthetic. Improves visual cohesion.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Replaced the simple FriendsOfFiveDemo component with the actual TutorialPlayer
from the existing tutorial system, showing the "Friends of 5" lesson (2+3=5).
Changes:
- Removed FriendsOfFiveDemo.tsx (redundant custom component)
- Updated homepage to use TutorialPlayer with filtered tutorial steps
- Added TUTORIAL_SYSTEM.md documentation explaining the full tutorial system
- Homepage now demonstrates the real learning system instead of a mock
The tutorial system includes:
- Step-by-step guidance with bead highlighting
- Real-time feedback and validation
- Multi-step instruction support
- Pedagogical decomposition
- Auto-advancement on correct completion
- Full tutorial editor at /tutorial-editor
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Redesigned the homepage to communicate the platform's educational
mission: providing a structured, self-directed path to soroban fluency
for students and families.
Key changes:
- New hero section with "Learn → Practice → Play → Master" journey
- Interactive Friends of 5 demo embedded directly on homepage
- Clear sections: Learn by Doing, Available Now, For Kids & Families
- Progression visualization (10 Kyu → Dan levels)
- Honest development status badge
- Removed aspirational language, focused on what's ready now
- Added comprehensive education roadmap documentation
The homepage now serves as both a visual introduction and working
demonstration of the learning system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Documented the project's UI pattern requirements:
- Never use native browser dialogs (alert/confirm/prompt)
- Always use inline React-based confirmations
- Included pattern examples and migration checklist
- Referenced ModerationPanel.tsx for real examples
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When ownership is transferred via the moderation modal, both the old
and new host now see the change immediately without requiring a page
reload.
Added missing socket event handler for 'ownership-transferred' event:
- Server already broadcasts event with updated members (route.ts:82)
- Client now listens and updates React Query cache in real-time
- All components using useRoomData() automatically re-render
- Both sessions see host status changes instantly
Fixes issue where ownership transfer required manual page refresh
to see updated host permissions (game selection, moderation access).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Only room hosts can select games. Added clear visual 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."
Changes:
- Detect host status via currentMember?.isCreator
- Disable game buttons for non-hosts (opacity 0.4, cursor not-allowed)
- Client-side permission check before API call
- Error messages auto-dismiss after 5 seconds
- Error handling in setRoomGame mutation callback
Fixes 403 errors when non-hosts attempt game selection.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Enhanced the hero abacus to be a fully interactive showcase:
- Increased size from default to 2.2x scale factor
- Enabled interactive mode (click beads to change value)
- Enabled smooth animations for bead movements
- Enabled audio feedback with volume at 0.4
- Added live value display below abacus in large golden text
- Added instructional text "Click the beads to interact!"
- Shows place value numbers on each column
The abacus now serves as a "try it now" demo right on the landing page.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced outdated landing page with component-based "storybook" design:
- Large featured AbacusReact component (1,234,567 with place-value colors)
- Color scheme showcase with 4 actual AbacusReact instances
- Dark, fancy theme with purple gradients and golden accents
- All game links now point to /games instead of /arcade
- Hover animations and visual polish throughout
- Dot pattern background texture
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improve centering of abacus SVGs within both available cards and position slots.
**Issue:**
AbacusReact SVGs were not properly centered within their card containers,
appearing off-center or misaligned.
**Fix:**
- **Available cards**: Added maxHeight: '100%' constraint and display: 'block'
with margin: '0 auto' to SVG styling
- **Position slots**: Changed container to use flex: 1 and proper flex centering,
constrained SVG to maxWidth: '70px' with centering styles
**Changes:**
- Available card SVG container: Added display flex with center alignment
- Available card SVG: maxHeight: '100%', display: 'block', margin: '0 auto'
- Position slot SVG container: width: '100%', flex: 1, flex centering
- Position slot SVG: maxWidth: '70px', maxHeight: '100%', display: 'block', margin: '0 auto'
Now AbacusReact SVGs render centered within their card tiles regardless of
the actual SVG dimensions.
src/arcade-games/card-sorting/components/PlayingPhase.tsx:314-330,457-475
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Fix position slots to match Python original - they should flow horizontally
and wrap, not stack vertically.
**Before:**
- Container: display: flex, flexDirection: 'column' (vertical stack)
- Each slot + insert button pair wrapped in a div
- One slot per line with insert button below it
**After (matching Python lines 3101-3111):**
- Container: display: flex, flexWrap: 'wrap' (horizontal flow)
- Slots and insert buttons flow together as siblings
- Wraps naturally based on container width
- Dashed border container with semi-transparent background
**Changes:**
- Position slots container: flex-wrap instead of flex-direction: column
- Removed wrapper div around slot+button pairs
- Added React keys to slots and buttons
- Added container styling (padding, background, border)
Now matches the Python original where all slots and + buttons flow
together horizontally and wrap as needed.
src/arcade-games/card-sorting/components/PlayingPhase.tsx:362-524
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Simplify arcade routing by moving room page to main arcade route.
The /arcade route now handles both game selection and gameplay,
eliminating the need for a separate /arcade/room route.
**Changes:**
- Move /arcade/room/page.tsx → /arcade/page.tsx
- Update import paths for styled-system (room/ → arcade/)
- Remove legacy GAMES_CONFIG handling (now registry-only)
- Delete obsolete EnhancedChampionArena component
**Result:**
- Users navigate directly to /arcade for all arcade features
- Game selection UI shows when no game selected in room
- Selected game renders when room has gameName set
- Simpler, more intuitive URL structure
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix available cards layout to match the Python original implementation.
**Before:**
- Grid layout with responsive columns (1/2/3 cols)
- Cards stretched to fill grid cells
- Each card on its own line on mobile
**After (matching Python web_generator.py lines 3077-3087):**
- Flex container with wrap
- Fixed 90px x 90px card size
- Cards flow naturally and wrap based on container width
- Dashed border container with semi-transparent background
- Revealed numbers positioned absolutely in top-right corner
- Matching hover/selection animations from original
**Visual Changes:**
- Container: flex wrap, centered, dashed border, rgba background
- Cards: 90px x 90px fixed size
- Revealed numbers: absolute positioned badge (top-right)
- Selection state: scale(1.1), blue border, blue background
- Hover: translateY(-5px) lift effect
src/arcade-games/card-sorting/components/PlayingPhase.tsx:265-348
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove ?game= query parameter from GameSelector URLs. The /arcade/room
page doesn't use query params - it uses the room's gameName field from
the database and handles game selection through its own UI.
This was causing the URL to have a stale ?game= param that didn't match
the actual selected game, breaking testing and navigation.
**Changed:**
- GameSelector.tsx: url changed from `/arcade/room?game=${name}` to `/arcade/room`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update Card Sorting Challenge to match the original Python implementation:
**Visual Changes:**
- Add status message display showing game state and instructions
- Update insert buttons to match Python styling:
- Pill-shaped (border-radius: 20px)
- Teal (#2c5f76) with 0.3 opacity when inactive
- Blue (#1976d2) when card is selected
- Smooth scale animation on hover
- Fix position slot sizing to 90px x 110px (matching original)
- Simplify slot content (remove position numbers)
- Add active state highlighting (blue glow) for empty slots when card selected
- Adjust SVG sizing in filled slots to 70px width
**Logic Fixes:**
- Fix INSERT_CARD array shift logic bug in Provider.tsx
- Remove redundant else clause that could create oversized arrays
- Cards that fall off end are properly collected during compaction
- Remove unused useEffect import
**Behavioral Changes:**
- Status messages now update based on game state:
- Card selected: "Selected card with value X. Click a position or + button to place it."
- All placed: "All cards placed! Click 'Check My Solution' to see how you did."
- In progress: "X/Y cards placed. Select a card to continue."
- Empty slot click shows helpful message when no card selected
This brings the implementation in line with the original Python web_generator.py
design (lines 8662-9132) as requested.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the original single-player implementation (10,198 lines across 45 files)
that has been superseded by the modular arcade version.
**Deleted:**
- apps/web/src/app/games/complement-race/ (entire directory)
- 45 files including components, hooks, context, lib
- practice/, sprint/, survival/ sub-routes
- 10,198 lines of code
- 416KB total
**Kept (still in use):**
- /arcade/complement-race/ - Working arcade pages (already use modular provider)
- /arcade-games/complement-race/ - New modular implementation with Provider/Validator
- All imports point to arcade-games version
**Also cleaned:**
- Removed legacy master-organizer config from GameSelector (unused)
- Minor whitespace cleanup in games/page.tsx
**Verified:**
- Arcade version is completely independent (all imports from @/arcade-games/complement-race)
- No shared utilities in /lib
- No references from navigation or other pages
- Arcade pages already fixed in previous commit (used wrong provider path)
Saves ~10,200 lines of duplicate code while preserving all working functionality.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The old "Master Organizer" placeholder with available: false was blocking
users from accessing games. This has been replaced by the new Card Sorting
Challenge game in the game registry (card-sorting).
Fixes: User unable to click on master organizer tile
Related: Card Sorting Challenge implementation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the original 2,071-line single-player implementation that has been
superseded by the modular arcade version.
**Deleted:**
- apps/web/src/app/games/memory-quiz/page.tsx (2,071 lines)
- Removed unused _handleGameClick function from /games page
**Kept (still in use):**
- /lib/memory-quiz-utils.ts - Shared utilities used by arcade version
- /lib/memory-quiz-utils.test.ts - Test coverage
- /arcade-games/memory-quiz/ - New modular arcade implementation
- /arcade/memory-quiz/ - Redirect page to arcade
**Verified:**
- Arcade version is completely independent (imports from @/arcade-games/memory-quiz)
- Arcade version uses shared utility (/lib/memory-quiz-utils.ts)
- No functionality affected
Saves ~2,100 lines of duplicate code while preserving all working functionality.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Cleaned up arcade room game selection by removing two games:
- Removed number-guesser game and all its code
- Removed math-sprint game and all its code
Changes:
- Removed from game registry (game-registry.ts)
- Removed from validator registry (validators.ts)
- Removed type definitions and default configs (game-configs.ts)
- Deleted all game directories and files
Remaining games:
- Memory Quiz (Soroban Lightning)
- Matching (Memory Pairs Battle)
- Complement Race (Speed Complements)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented adaptive AI speed adjustment based on player performance:
- Added UPDATE_AI_SPEEDS handler to update clientAIRacers speeds
- Added UPDATE_DIFFICULTY_TRACKER handler to update local state
- Now matches solo game behavior where AI speeds adapt to challenge
AI speeds adjust based on:
- Player success rate (0.5x to 1.6x multiplier)
- Average response time (faster players get faster AI)
- Current streak (hot streaks increase challenge)
- Learning mode (no adaptation until sufficient data)
This provides dynamic difficulty balancing, making the game
engaging for both beginners and advanced players.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Speech bubbles now:
- Position 15px above AI racers instead of directly over them
- Use zIndex 20 to appear above all racers (player: 10, AI: 5)
This fixes two UX issues:
1. Bubbles were covering AI racer emojis, making them invisible
2. Bubbles appeared under player avatar when racers were close
Applied to both LinearTrack (practice) and CircularTrack (survival)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed critical bug where AI racers would spam speech bubbles every 200ms
instead of respecting the 2-6 second cooldown between comments.
Issue:
The TRIGGER_AI_COMMENTARY action was only updating activeSpeechBubbles
but not updating the AI racer's lastComment timestamp and cooldown value.
This caused getAICommentary() cooldown check to always pass since
lastComment stayed at 0.
Fix:
Added setClientAIRacers() call to update racer.lastComment and
racer.commentCooldown (random 2-6 seconds) when commentary is triggered,
matching the pattern from the original single-player version.
Impact:
- AI commentary now properly throttled (one comment per 2-6 seconds)
- Speech bubbles readable instead of rapidly changing
- Matches original solo game behavior
Provider.tsx:803-814
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When AI racers were flipped with scaleX(-1) to face right, their speech
bubbles were also flipped, making the text appear mirrored/backwards.
Added a wrapper div around SpeechBubble with scaleX(-1) to counter the
parent's flip transform, keeping the text readable while the emoji
remains flipped.
This matches the pattern used in CircularTrack which counter-rotates
speech bubbles to keep them upright.
LinearTrack.tsx:145-154
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed player emoji selection to be consistent with navbar's player
display logic. Both LinearTrack and CircularTrack now use the same
pattern as PageWithNav to get the current user's active local players.
Pattern:
- Get activePlayers Set from GameModeContext
- Map to player objects
- Filter for isLocal !== false (excludes remote players)
- Take first player's emoji
This ensures:
- Consistency with navbar display
- Correct handling of solo vs arcade room modes
- Proper filtering of remote players (isLocal === false)
- Future-proof for multi-player support
Files changed:
- LinearTrack.tsx:23,27-30
- CircularTrack.tsx:20,26-29
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed a bug where both LinearTrack and CircularTrack were displaying
the first active player's emoji instead of the current user's (local)
player emoji. This would cause incorrect avatar display in multi-player
arcade rooms.
Changed from:
- Getting first element of activePlayers array
- Would show wrong player in multi-player scenarios
To:
- Finding player with isLocal === true
- Correctly shows current user's avatar
- Matches pattern used throughout Provider.tsx
Files changed:
- LinearTrack.tsx: Use local player for emoji
- CircularTrack.tsx: Use local player for emoji
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds scaleX(-1) to player racer icon so it faces the same direction as AI racers (racing to the right).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds horizontal flip (scaleX(-1)) to AI racer icons in LinearTrack so they face the correct direction (right) when racing.
CircularTrack doesn't need this fix as racers are already rotated to face the direction they're traveling around the track.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes AI opponents racing away too fast and ending the game in 2 seconds by restoring the original balanced speeds and mode-specific multipliers.
Changes:
- Reduce AI base speeds from 0.8-1.2 to 0.32 (Swift AI) and 0.2 (Math Bot)
- Add mode-specific speedMultipliers: practice (0.7), sprint (0.9), survival (1.0)
- Update AI names to match original: "Swift AI" and "Math Bot"
The original system uses much lower base speeds combined with:
- Random variance (0.6-1.4x per update)
- Rubber-banding (2x speed when >10 units behind)
- Adaptive difficulty adjustments based on player performance
This makes AI opponents challenging but fair, adapting to player skill rather than just racing ahead at fixed high speeds.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two critical issues blocking deployment:
1. **TypeScript build failure**: Added @types/minimatch dependency and created
proper tsconfig.json files for @soroban/core and @soroban/client packages.
The DTS build was failing because TypeScript couldn't find the minimatch
type definitions.
2. **Next.js prerendering error**: Fixed complement-race pages importing from
wrong provider. Pages were using ./context/ComplementRaceContext but game
components were using @/arcade-games/complement-race/Provider, causing
"useComplementRace must be used within ComplementRaceProvider" errors
during static page generation.
Deployment was blocked for 2 days. Container on NAS is from Oct 16th while
latest commits are from Oct 18th.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Migrates AI opponent system from server-side Validator to client-side Provider to align with original single-player implementation and enable sophisticated features.
Changes:
- Remove AI generation/updates from Validator (now returns empty aiOpponents array)
- Add clientAIRacers state to Provider (similar to clientMomentum/clientPosition)
- Initialize AI racers when game starts based on config.enableAI
- Handle UPDATE_AI_POSITIONS dispatch to update client-side AI state
- Map clientAIRacers to compatibleState.aiRacers for components to consume
This enables the existing useAIRacers hook to work properly with:
- Time-based position updates (200ms interval)
- Rubber-banding mechanic (AI speeds up 2x when >10 units behind)
- AI commentary system with personality-based messages
- Context detection and sound effects
AI wins are now detected client-side via useAIRacers hook instead of server-side Validator.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PROBLEM:
Practice Race and Survival Circuit modes had no AI opponents visible, even
though the config had enableAI: true and aiOpponentCount: 2.
ROOT CAUSE:
The Validator's validateStartGame method (line 208) was initializing
aiOpponents as an empty array and never actually generating them, even
when config.enableAI was true.
This was likely lost during the multiplayer migration when the code was
refactored from single-player to multiplayer architecture.
FIX:
1. Added generateAIOpponents() method (lines 782-808)
- Creates AI opponents with names like "Robo-Racer", "Calculator", etc.
- Assigns personality types (competitive/analytical)
- Gives each AI a random speed multiplier (0.8-1.2)
2. Call generateAIOpponents in validateStartGame (lines 209-212)
- Only generates AI when config.enableAI is true and aiOpponentCount > 0
3. Added updateAIOpponents() method (lines 810-840)
- Updates AI positions during the game
- Practice mode: AI moves forward based on speed (simulates answering)
- Survival mode: AI continuously moves forward
- Sprint mode: AI doesn't participate (train journey is single-player)
4. Call updateAIOpponents in validateSubmitAnswer (lines 367-373)
- AI opponents progress each time a human answers a question
5. Updated checkWinCondition (lines 904-909, 970-976)
- Practice mode: Check if any AI reaches position 100
- Survival mode: Check if any AI has highest position when time expires
BEHAVIOR:
- Practice Race now shows 2 AI opponents racing alongside you
- Survival Circuit now has AI competitors to beat
- AI opponents move at slightly randomized speeds for variety
- AI can win the race if they reach the goal first
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PROBLEM:
After a few routes in Steam Sprint mode, the "victory!" page was appearing.
Steam Sprint is designed to be an infinite game that never ends.
ROOT CAUSE:
The default config had:
winCondition: 'route-based'
routeCount: 3
So after completing 3 routes, the validator's checkWinCondition method would
find a winner and trigger the results/victory screen (line 835 in Validator).
FIX:
1. Added 'infinite' as a new valid winCondition type (game-configs.ts:92)
2. Updated default config to use winCondition: 'infinite' (both in
arcade-games/complement-race/index.tsx and lib/arcade/game-configs.ts)
3. Updated checkWinCondition to return null immediately when winCondition === 'infinite'
(Validator.ts:815-818)
BEHAVIOR:
- Steam Sprint now runs indefinitely with infinite routes
- Train automatically advances to next route after completing each one
- Game never ends unless player manually quits or leaves room
- Score and deliveries continue to accumulate across all routes
- Other win conditions (route-based, score-based, time-based) still work for
custom game modes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PROBLEM:
Previous fix broke route progression - train would never advance to next route.
ROOT CAUSE:
After removing the dual game loop, I changed line 97 to:
const trainPosition = state.trainPosition
This meant the route completion check (lines 296-299) became:
if (
state.trainPosition >= threshold &&
state.trainPosition < threshold // Same variable!
)
This is ALWAYS FALSE because a value can't be both >= and < threshold.
The previous code worked because:
- trainPosition was the newly calculated position for this frame
- state.trainPosition was the previous frame's position
- So it could detect threshold crossing: newPos >= threshold && oldPos < threshold
FIX:
1. Added previousTrainPositionRef to track position from previous frame (line 48)
2. Updated route completion check to use previousPosition instead of state.trainPosition (line 300)
3. Store current position in ref at end of each frame (line 323)
4. Reset previousPosition when route changes (line 82)
5. Added debug logging when route completes (line 310-312)
Now the check correctly detects:
if (
trainPosition >= threshold && // Current position
previousPosition < threshold // Previous position
)
This properly detects when the train crosses the exit threshold.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
PROBLEM:
Route progression was broken - train would never advance to next route after
completing Route 1. This was a regression after fixing passenger display issues.
ROOT CAUSE:
There were TWO conflicting game loops both managing train position:
1. Provider.tsx (lines 448-491): Updates clientPosition based on clientMomentum
2. useSteamJourney.ts (lines 87-126): Was calculating its own trainPosition and
dispatching UPDATE_STEAM_JOURNEY
The critical issue: useSteamJourney dispatched UPDATE_STEAM_JOURNEY to update
position, but Provider IGNORES this action (line 764). This meant:
- useSteamJourney calculated a local trainPosition variable each frame
- It used this to check route completion threshold
- But state.trainPosition was stale because the dispatch never updated it
- So the route completion condition never triggered
FIX:
1. useSteamJourney.ts: Removed redundant position calculation (lines 95-125)
- Now just reads state.trainPosition from Provider instead of calculating own
- Game logic (boarding, delivery, route completion) uses authoritative position
2. useTrackManagement.ts: Changed trainPosition < 0 to <= 0 (lines 70, 82)
- Provider resets clientPosition to exactly 0 (not negative)
- This allows track and passenger display to update on route reset
3. Provider.tsx: Added debug logging for route changes and START_NEW_ROUTE
- Helps diagnose route progression issues in console
4. Test files: Fixed TypeScript errors from API changes
- Added missing maxCars and carSpacing parameters
- Added missing name property to mock passengers
ARCHITECTURE NOTE:
The game now has a clear separation of concerns:
- Provider.tsx: Manages visual state (position, momentum, pressure)
- useSteamJourney.ts: Reads visual state and handles game logic
- Single source of truth for train position
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add temporary debug logging to diagnose why passengers aren't appearing
after route 1:
1. Log when START_NEW_ROUTE move is dispatched with route number
2. Log when Provider detects route change and how many passengers exist
This will help identify if the issue is:
- Move not being sent
- Server not generating passengers
- State update not propagating to client
- Display logic not showing passengers
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed bug where passengers wouldn't appear after completing a route.
Problem: When a new route started, the Provider reset trainPosition to 0,
but useTrackManagement was checking `trainPosition < 0` to detect resets.
Since 0 is not < 0, the condition failed and passengers didn't update.
Root cause:
1. Route completes, train at position ~107%
2. Client dispatches START_NEW_ROUTE with routeNumber=2
3. Server validates and broadcasts new state (route=2, new passengers)
4. Provider resets clientPosition to 0
5. useTrackManagement checks:
- trainReset = trainPosition < 0 → FALSE (0 is not < 0!)
- sameRoute = currentRoute === displayRouteRef.current → FALSE (2 !== 1)
6. Neither condition met, so displayPassengers doesn't update
Solution: Changed trainReset condition from `trainPosition < 0` to
`trainPosition <= 0` to treat position 0 as a reset. Also updated
the pending track application logic with the same fix.
Now passengers appear immediately when a new route starts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed critical bug where passengers were missing boarding opportunities
due to array index confusion after deliveries.
Problem: After a passenger was delivered, the currentBoardedPassengers
array would compact but physical car positions wouldn't change. The
occupiedCars map was using array indices instead of physical car numbers,
causing cars to incorrectly appear occupied.
Example bug scenario:
- Train has 4 cars (indices 0-3)
- Kate boards Car 0, Frank Car 1, Mia Car 2, Charlie Car 3
- Kate delivers from Car 0
- Array compacts: [Frank, Mia, Charlie] at indices [0, 1, 2]
- occupiedCars map now shows cars 0,1,2 occupied (WRONG!)
- Physical Car 0 is actually EMPTY, but Alice can't board
Solution: Added carIndex field to Passenger type to track physical car
number (0-N) independently from array position.
Changes:
- Added carIndex: number | null to Passenger type
- Updated Validator to store physical carIndex when boarding
- Updated Provider to pass carIndex in BOARD_PASSENGER moves
- Updated useSteamJourney to use passenger.carIndex for all car
position calculations and occupancy tracking
- Reordered frame processing so DELIVERY moves dispatch before BOARDING
moves to prevent race conditions
- Fixed configuration mismatch: client now uses server's authoritative
maxConcurrentPassengers instead of calculating locally
- Updated visual display to use server's maxConcurrentPassengers
Also includes improved logging for debugging train configuration and
passenger movement.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix visual desync where delivered passengers remained displayed at stations.
The displayPassengers state only updated when train was at start (< 0%)
or in middle of track (10-90%). Passengers delivered at positions > 90%
(e.g., trainPos 103-130%) weren't reflected in the UI even though server
state was correct.
Now detects when passenger states change (boarding or delivery via
claimedBy/deliveredBy fields) and updates display immediately regardless
of train position. This ensures passenger cards disappear from HUD as
soon as passengers are delivered.
Also updated test files to use multiplayer Passenger type with
claimedBy/deliveredBy/carIndex/timestamp instead of old
isBoarded/isDelivered format.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replaced massive per-frame debug logs (20 logs/second) with smart event-based logging:
- One summary log at game start showing passengers and their routes
- One log per boarding event (only when passenger boards)
- One log per delivery event (only when passenger delivers)
This provides actionable diagnostics without flooding the console or context window.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Passengers weren't boarding because the arcade room version was still checking for single-player fields (isBoarded/isDelivered) instead of multiplayer fields (claimedBy/deliveredBy).
Changes:
- Update useSteamJourney to check claimedBy/deliveredBy instead of isBoarded/isDelivered
- Update Validator to skip position validation in sprint mode (position is client-side)
- Trust client-side spatial checking for boarding/delivery in sprint mode
Sprint mode architecture:
- Client (useSteamJourney) continuously checks if train is at stations
- Client sends CLAIM_PASSENGER / DELIVER_PASSENGER moves when conditions met
- Server validates passenger availability (not position, since position is client-side)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The train was starting with too much momentum (50), causing it to sail past the first station without requiring user input. Reduced to 10 for a gentle push that still requires player engagement.
Changes:
- Reduce initial momentum from 50 to 10 in all three locations:
- Initial state (useState)
- Game start initialization
- Route reset when advancing to next route
- Update pressure calculation to match new starting momentum
With momentum=10: speed = 1.5% per second (gentle start requiring answers to progress)
vs momentum=50: speed = 7.5% per second (too aggressive, reaches station without input)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes train jumping backward and pressure not decaying to zero in sprint mode by moving momentum/position/pressure tracking entirely to the client.
Changes:
- Remove momentum/pressure from server PlayerState type (sprint mode only)
- Remove all momentum updates from Validator (server tracks only scoring)
- Add client-side momentum state with 50ms game loop for smooth 20fps movement
- Implement continuous momentum decay based on skill level (2.0-13.0/sec)
- Calculate position and pressure client-side from momentum
- Handle answer boosts (+15 correct, -10 wrong) in client
This matches the arcade room's event-driven architecture where visual elements are client-side and the server maintains authoritative game state (score, streak, passengers).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: Train was jumping discretely on each answer instead of moving smoothly
**Root Cause**: Ported incorrectly - position updated on answer submission instead of continuously
**Original Mechanics** (from useSteamJourney.ts):
- 50ms game loop (20fps) runs continuously
- Position calculated from momentum: `position += (momentum * 0.15 * deltaTime) / 1000`
- Pressure calculated from momentum: `pressure = (momentum / 100) * 150` (0-150 PSI)
- Answers only affect momentum (+15 correct, -10 wrong)
**Fixed Implementation**:
- Client-side game loop at 50ms interval
- Position calculated continuously from server momentum
- Pressure calculated continuously from momentum (0-150 PSI)
- Server only tracks momentum (authoritative)
- Removed discrete position jumps from Validator
- Position/pressure are derived values, not stored
**Files Changed**:
- Provider.tsx: Added client game loop, use calculated position/pressure
- Validator.ts: Removed position updates, only track momentum
- types.ts: Removed pressure field (calculated client-side)
This matches the original smooth movement behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**1. Smart Logging (event-based instead of frame-based)**
- Only logs on answer submission, not every frame
- Format: "🚂 Answer #X: momentum=Y pos=Z pressure=P streak=S"
- Prevents console overflow in real-time game
**2. Pressure Decay System**
- Added `pressure` field to PlayerState type
- Pressure now independent from momentum (was stuck at 100)
- Correct answer: +20 pressure (add steam)
- Wrong answer: +5 pressure (less steam)
- Decay: -8 pressure per answer (steam escapes over time)
- Range: 0-100 with min/max caps
**3. Implementation**
- types.ts: Added pressure field to PlayerState
- Validator.ts: Initialize pressure=60, update with decay
- Provider.tsx: Use actual pressure from server (not calculated)
- Route reset: Reset pressure to 60 on new routes
This fixes the pressure gauge being pinned at 100 constantly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**THE BUG**: Validator was only updating momentum in Sprint mode,
but NEVER updating position! This caused trainPosition to stay at 0.
**THE FIX**: Added position calculation based on momentum:
- moveDistance = momentum / 20
- Starting momentum (50) → 2.5 units per answer
- Max momentum (100) → 5 units per answer
- Creates progression: higher momentum = faster train movement
Position updates per answer now work:
- Correct answer: momentum +15, then position +=(momentum/20)
- Wrong answer: momentum -10, then position +=(momentum/20)
- Position capped at 100 (end of route)
This matches the original single-player behavior where the train
speed was tied to momentum.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed all excessive console logging that was causing console overflow.
**Removed**:
- GameDisplay: All keyboard/answer validation logs (input bug is fixed)
- Context reducer: All action dispatched logs
- Provider: Verbose state transformation details
- Provider: Dispatch compatibility layer logs
**Kept (for train/pressure debugging)**:
- Provider: Sprint-specific values (momentum, trainPosition, pressure)
- SteamTrainJourney: Component props and state
This should give us minimal, focused logs to debug:
1. Why train isn't appearing
2. Why pressure is stuck at 100
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added detailed console logging to debug why train isn't appearing:
**Provider.tsx**:
- State transformation details (localPlayer, all players, game phase)
- Transformed sprint-specific values (momentum, trainPosition, pressure)
**SteamTrainJourney.tsx**:
- Component props (momentum, trainPosition, pressure, etc.)
- State from provider (stations, passengers, currentRoute, gamePhase)
This will help identify:
1. If localPlayer is null/undefined
2. If momentum/position values are 0
3. If stations/passengers are empty
4. What game phase we're in
Note: User reports pressure is pinned at 100 - likely related to formula:
`pressure: localPlayer?.momentum ? Math.min(100, localPlayer.momentum + 10) : 0`
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed bug where previous answer appeared in complement box instead of "?".
Root cause: Provider's localUIState.currentInput wasn't being cleared when
NEXT_QUESTION was dispatched. The sequence was:
1. User types answer (e.g. "5")
2. UPDATE_INPUT sets localUIState.currentInput = "5"
3. Answer correct → NEXT_QUESTION dispatched
4. Server generates new question
5. But localUIState.currentInput still "5" ❌
Solution: Clear localUIState.currentInput in NEXT_QUESTION case.
Added comprehensive debug logging:
- GameDisplay: Render state, keyboard events, answer validation
- Provider: Dispatch actions, input clearing
- Context reducer: All action types, NEXT_QUESTION flow, UPDATE_INPUT
This logging will help identify any remaining state synchronization issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds PageWithNav wrapper to complement-race game for consistency with
other arcade games.
## Changes
- Created `GameComponent.tsx` wrapper that includes PageWithNav
- Wraps existing ComplementRaceGame with navigation bar
- Updates game title and emoji based on selected style:
- Practice mode: "Complement Race" 🏁
- Sprint mode: "Steam Sprint" 🚂
- Survival mode: "Endless Circuit" ♾️
- Provides exit session and new game callbacks
- Emphasizes player selection during setup phase
## Integration
- Updated index.tsx to use new GameComponent instead of direct ComplementRaceGame
- Maintains all existing game functionality
- Navigation bar now matches other arcade games (matching, number-guesser)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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>
Fix import path from '../../socket-server' to '../socket-server'
to point to the TypeScript source file instead of the deleted
compiled file in the root.
Path resolution:
- From: src/lib/socket-io.ts
- Import: '../socket-server'
- Resolves to: src/socket-server.ts
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This compiled file was outdated with old validator imports.
Build system now correctly generates it in dist/socket-server.js
during the TypeScript compilation step (tsc + tsc-alias).
server.js correctly imports from dist/socket-server.js.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete Phase 3 of matching migration plan:
- Update validators.ts to import from @/arcade-games/matching/Validator
- Delete old validator from /lib/arcade/validation/
- Now consistent with other modular games (memory-quiz, number-guesser, math-sprint)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove duplicate game entries by cleaning up legacy GAMES_CONFIG references.
Matching game now accessed exclusively through game registry.
- Removed battle-arena from GAMES_CONFIG
- Removed battle-arena from GAME_TYPE_TO_NAME mapping
- Removed battle-arena navigation logic
Fixes duplicate game entries in game selector.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Fix cardElement type error by converting undefined to null
- Fix className type error by properly concatenating CSS classes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Phases completed:
- Phase 1: Pre-migration audit (arcade version is canonical)
- Phase 2: Create modular game definition with registry
- Phase 3: Move and update validator to modular location
- Phase 4: Consolidate and move SDK-compatible types
- Phase 7: Move utility functions (cardGeneration, matchValidation, gameScoring)
Changes:
- Created /src/arcade-games/matching/ with game definition
- Registered matching game in game registry
- Added type inference for MatchingGameConfig
- Moved validator with updated imports to use local types
- Created SDK-compatible MatchingConfig, MatchingState, MatchingMove types
- Moved utils with updated import paths
Remaining:
- Phase 5: Create unified Provider
- Phase 6: Consolidate and move components
- Phase 8: Update routes and clean up legacy files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Create comprehensive migration plan for Matching Pairs Battle game.
Documents dual-location complexity, 8-phase migration approach, and
key differences from Memory Quiz migration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add detailed migration plan document for the Memory Quiz game migration
to the modular game system. This serves as a reference for future game
migrations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Memory-quiz now only exists in the game registry, eliminating duplicate:
- Removed from GAMES_CONFIG in GameSelector.tsx
- Removed from GAME_TYPE_TO_NAME mapping in room/page.tsx
- Updated /arcade/memory-quiz route to redirect to arcade
- Removed legacy switch case (now handled by registry)
Fixes issue where Memory Lightning appeared twice in game selector.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Changes**: Auto-formatting from Biome formatter.
- Provider: Multi-line formatting for useArcadeSession call
- SetupPhase: Multi-line formatting for long className
No logic changes, purely stylistic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Lint fixes**:
- Removed unused TEAM_MOVE import from Validator.ts
- Removed autoFocus attribute from PlayingPhase input (a11y best practice)
**Reason**: These were flagged by Biome linter as issues.
The unused import was left over from development, and autoFocus
can cause accessibility problems.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Updates**:
- Added "Key Improvements" section highlighting Phase 3
- Updated architecture diagram to show type system layer
- Added validateConfig to GameDefinition interface docs
- Updated Step 6 to include validateConfig example
- Added Step 7c: Config Type Inference guide
- Documented benefits of type inference (10-15 lines saved per game)
**Example shown**:
```typescript
// Before: Manual definition
export interface NumberGuesserGameConfig { ... }
// After: Inferred
export type NumberGuesserGameConfig = InferGameConfig<typeof numberGuesserGame>
```
**Key concept**: defaultConfig serves as source of truth for types.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: Config types were manually defined in game-configs.ts,
requiring 10-15 lines of boilerplate per game.
**Solution**: Use TypeScript's type inference to extract config types
from game definitions' defaultConfig property.
**Changes**:
- Added InferGameConfig<T> utility type
- NumberGuesserGameConfig now inferred from numberGuesserGame
- MathSprintGameConfig now inferred from mathSprintGame
- RoomGameConfig auto-derived from GameConfigByName using mapped type
- Changed RoomGameConfig from interface to type for auto-derivation
**Benefits**:
- Single source of truth (game definition)
- Add game → types automatically available
- No manual type definitions needed
- TypeScript ensures type consistency
**Architecture**: Phase 3 of modular game system improvements.
Legacy games (matching, memory-quiz, complement-race) still use
manual types until migrated to new system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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
This implements Critical Fix#3 from AUDIT_2_ARCHITECTURE_QUALITY.md
Changes:
1. Add validateConfig to GameDefinition type
2. Update defineGame() to accept validateConfig function
3. Add validation functions to Number Guesser and Math Sprint
4. Update game-config-helpers.ts to use registry validation
Before (switch statement in helpers):
- validateGameConfig() had 50+ line switch statement
- Must update helper for every new game
- Validation logic separated from game
After (validation in game definition):
- Games own their validation logic
- validateGameConfig() calls game.validateConfig()
- Switch only for legacy games (matching, memory-quiz, complement-race)
- New games: just add validateConfig to defineGame()
Example (Number Guesser):
function validateNumberGuesserConfig(config: unknown): config is NumberGuesserConfig {
return (
typeof config === 'object' &&
config !== null &&
typeof config.minNumber === 'number' &&
typeof config.maxNumber === 'number' &&
typeof config.roundsToWin === 'number' &&
config.minNumber >= 1 &&
config.maxNumber > config.minNumber &&
config.roundsToWin >= 1
)
}
Benefits:
✅ Eliminates switch statement boilerplate
✅ Single source of truth for validation
✅ Games are self-contained
✅ No helper updates needed for new games
To add a new game now:
1. Define validation function in game index.ts
2. Pass to defineGame({ validateConfig })
That's it! No helper file changes needed.
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.
Add game.yaml with metadata for Math Sprint:
- Display name, icon, description
- Max 6 players
- Difficulty: Beginner
- Tags: Multiplayer, Free-for-All, Math Skills, Speed
- Purple color theme to match UI
- Set available: true
This manifest enables Math Sprint to appear in GameSelector
automatically via the registry system.
Comprehensive audit of modular game system after implementing Math Sprint.
Key Findings:
- Grade: B- (down from B+ after implementation testing)
- SDK design is solid (useArcadeSession, Provider pattern)
- Unified validator registry works well
- BUT: Significant boilerplate and coupling issues
Critical Issues Identified:
1. 🚨 Database Schema Coupling - Must update schema for each game
2. ⚠️ game-config-helpers.ts - Switch statements for defaults/validation
3. ⚠️ game-configs.ts - 5 places to update per game
4. 📊 High Boilerplate Ratio - 12 files touched per game, ~44 lines boilerplate
Files That Required Updates for Math Sprint:
- 3 database schemas (arcade-rooms, arcade-sessions, room-game-configs)
- 1 API endpoint (settings/route.ts)
- 2 config files (game-configs.ts, game-config-helpers.ts)
- 2 registry files (validators.ts, game-registry.ts)
- 8 game implementation files (types, validator, provider, components, etc.)
Recommendations:
- Critical: Fix database schema to accept any string, validate at runtime
- Infer config types from game definitions (single source of truth)
- Move config validation to game definitions (eliminate switch statements)
Developer Experience:
- Time to add a game: 3-5 hours (including boilerplate)
- Pain point: Database schema updates require migration
- Pain point: Easy to forget one of the 12 files
See audit for detailed analysis and architectural recommendations.
Add 'math-sprint' to validGames array in PATCH /api/arcade/rooms/:roomId/settings
Without this change, selecting math-sprint from room page returns:
400 Bad Request - "Invalid game name"
This is another instance of the coupling issue - hardcoded validation
array must be manually updated for each new game.
The TODO comment on line 97 acknowledges this:
"TODO: make this dynamic when we refactor to lazy-load registry"
Addresses Issue #1 from AUDIT_2_ARCHITECTURE_QUALITY.md
Update all database schemas to include 'math-sprint':
- arcade-rooms.ts: Add to gameName enum
- arcade-sessions.ts: Add to currentGame enum
- room-game-configs.ts: Add to gameName enum and documentation
CRITICAL ISSUE DEMONSTRATED:
This is the schema coupling problem (Issue #1 from AUDIT_2).
Must manually update database schemas for every new game.
Breaks modularity - cannot "just register and go".
Without this change, TypeScript compilation fails with:
Type '"math-sprint"' is not assignable to type '...'
Recommendation from audit: Change schemas to accept any string,
validate against registry at runtime instead of compile-time enums.
Register math-sprint in all required places:
- validators.ts: Add mathSprintValidator to registry
- game-registry.ts: Register mathSprintGame
- game-configs.ts: Add MathSprintGameConfig type and defaults
- game-config-helpers.ts: Add config getters and validation
This demonstrates the boilerplate issue documented in AUDIT_2:
Had to update 4 files with switch/case statements and type definitions.
Addresses issue #2 and #3 from architecture audit.
- Implement free-for-all math racing game
- Demonstrates TEAM_MOVE pattern (no specific turn owner)
- Server-generated math questions (addition, subtraction, multiplication)
- Real-time competitive gameplay with scoring
- Three difficulty levels: easy, medium, hard
- Configurable questions per round and time limits
Components:
- SetupPhase: Configure difficulty, questions, time
- PlayingPhase: Answer questions competitively
- ResultsPhase: Display final scores and winner
- Validator: Server-side question generation and validation
Game follows SDK patterns established by Number Guesser.
## Bug Fixes
### 1. Turn Indicators
- Added `currentPlayerId` prop to PageWithNav
- Shows whose turn it is during choosing and guessing phases
- Visual highlighting of active player avatar
- Displays "Your turn" label for current user
**Files**:
- `GameComponent.tsx`: Calculate currentPlayerId based on game phase
- `Provider.tsx`: Expose lastError and clearError to context
### 2. Error Feedback
- Added error banner in GuessingPhase
- Shows server rejection messages (out of bounds, not your turn, etc.)
- Auto-dismisses after 5 seconds
- Clear dismiss button for manual dismissal
**Impact**: Users now see why their moves were rejected instead of
silent failures.
### 3. Player Ordering Consistency
- Fixed player ordering mismatch between UI and game logic
- Removed `.sort()` to keep Set iteration order consistent
- Both UI (PageWithNav) and game logic now use same player order
**Issue**: UI showed players in Set order, but game logic used
alphabetical order, causing "skipped leftmost player" bug.
**Fix**: Use `Array.from(activePlayerIds)` without sorting everywhere.
### 4. Score Display
- Added `playerScores` prop to PageWithNav
- Shows scores for all players in the navigation
## Testing Notes
These fixes address all issues found during manual testing:
- ✅ Turn indicator now shows correctly
- ✅ Error messages display to users
- ✅ Player order matches between UI and game logic
- ✅ Scores visible in navigation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Documentation Updates
### src/arcade-games/README.md
- **Step 7**: Expanded to explain both registration steps
- 7a: Register validator in validators.ts (server-side)
- 7b: Register game in game-registry.ts (client-side)
- Added explanation of why both steps are needed
- Added verification warnings that appear during registration
- Clarified the difference between isomorphic and client-only code
### docs/AUDIT_MODULAR_GAME_SYSTEM.md
- **Status**: Updated from "CRITICAL ISSUES" to "ISSUE RESOLVED"
- **Executive Summary**: Marked system as Production Ready
- **Issue #1**: Marked as RESOLVED with implementation details
- **Issue #2**: Marked as RESOLVED (validators now accessible)
- **Issue #5**: Marked as RESOLVED (GameName auto-derived)
- **Compliance Table**: Updated grade from D to B+
- **Action Items**: Marked critical items 1-3 as completed
## Summary
Documentation now accurately reflects the unified validator registry
implementation, providing clear guidance for developers adding new games.
Related: 9459f37b (implementation commit)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**: Games required dual registration - once in client registry
(game-registry.ts) and again in server validator map (validation/index.ts).
This broke the modular architecture goal.
**Solution**: Created unified isomorphic validator registry that serves
both client and server needs.
## Changes
### New File
- `src/lib/arcade/validators.ts` - Unified validator registry
- Single source of truth for all game validators
- Auto-derives GameName type from registry keys
- Isomorphic (runs on both client and server)
### Updated Files
- `validation/index.ts` - Converted to re-export from unified registry
- Maintains backwards compatibility
- Marked as deprecated
- `validation/types.ts` - GameName now re-exported from validators
- No longer hard-coded union type
- Auto-updates when new games added
- `game-registry.ts` - Added runtime validation
- Checks if validator registered server-side
- Warns on registration mismatch
- `session-manager.ts` - Import from unified registry
- `socket-server.ts` - Import from unified registry
- `route.ts` (rooms API) - Use hasValidator() instead of hard-coded array
- `game-config-helpers.ts` - Handle ExtendedGameName for legacy games
- Supports both registered validators and legacy 'complement-race'
- TODO comment for migration
## Benefits
✅ Single registration point for new games
✅ Auto-derived GameName type (no manual updates)
✅ Type-safe validator access
✅ Backwards compatible with existing code
✅ Clear migration path for old games
## Migration Status
- ✅ number-guesser: Uses new system
- 🔄 matching, memory-quiz: Use new validator registry, not migrated to arcade-games/ yet
- ⏳ complement-race: Legacy game, handled via ExtendedGameName
Addresses critical Issue #1 from AUDIT_MODULAR_GAME_SYSTEM.md
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes TypeScript errors introduced by registry game system:
- Allow gameName to be string | null in nav component types
- Update RecentRoom, AddPlayerButton, GameContextNav, ArcadeRoomInfo interfaces
- Convert null to undefined where needed for legacy function calls
- Fix GameTitleMenu showMenu prop
- Update JoinRoomModal to use separate useGetRoomByCode and useJoinRoom hooks
These changes allow rooms to exist without a selected game (gameName = null).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes server-side session creation for Number Guesser:
- Import DEFAULT_NUMBER_GUESSER_CONFIG
- Add case for 'number-guesser' in getDefaultGameConfig()
- Add validation for number-guesser config
- Include arcade-games validators in server TypeScript build
This resolves the "Unknown game: number-guesser" error when creating sessions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements the first registry-based game to demonstrate the modular plugin system:
Game Features:
- Turn-based number guessing with hot/cold feedback
- 2-4 players with configurable settings
- Round-based scoring system
- Complete game phases: Setup → Choosing → Guessing → Results
Technical Implementation:
- Created Number Guesser game in src/arcade-games/number-guesser/
- Registered NumberGuesserValidator in validation system
- Added 'number-guesser' to database schema enums (arcade_rooms, arcade_sessions, room_game_configs)
- Created NumberGuesserGameConfig type with default configuration
- Integrated game into GameSelector and room page
- Added PageWithNav wrapper for consistent navigation
- Fixed controlled input warnings with fallback values
Registry Integration:
- Game automatically appears in /arcade/room selection UI
- Settings persist to room_game_configs table
- Validator creates and manages server-side game state
- Provider syncs client state via arcade session
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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>
Changed from tier-then-mix approach to per-word-type selection:
- Before: Pick one tier, then optionally mix with abacus words
- After: Pick tier independently for adjective and noun
Benefits:
- Simpler, cleaner code
- More natural variety in name combinations
- Adjective and noun can come from different tiers naturally
- Examples: "Grinning Calculator" (emoji + global), "Ancient Smiler" (global + emoji)
Each word still uses weighted selection:
- 70% emoji-specific
- 20% category-specific
- 10% global abacus
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
When only gameConfig is updated (without accessMode, password, or gameName),
the updateData object remained empty, causing Drizzle to throw "No values to set"
error when attempting to update arcade_rooms table.
Now only updates arcade_rooms if there are actual fields to update, preventing
the 500 error while still allowing gameConfig-only updates to room_game_configs table.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Creates migration 0011 to:
- Create room_game_configs table with proper schema
- Add unique index on (room_id, game_name)
- Migrate existing game_config data from arcade_rooms table
Migration is idempotent and safe to run on any database state:
- Uses IF NOT EXISTS for table and index creation
- Uses INSERT OR IGNORE to avoid duplicate data
- Will work on both fresh databases and existing production
This ensures production will automatically get the new table structure
when the migration runs on deployment.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Manual migration applied on 2025-10-15:
- Created room_game_configs table via sqlite3 CLI
- Migrated 6000 existing configs from arcade_rooms.game_config
- 5991 matching configs + 9 memory-quiz configs
- Table created directly instead of through drizzle migration system
The manually created drizzle migration SQL file has been removed since
the migration was applied directly to the database. See
.claude/MANUAL_MIGRATION_0011.md for complete details on the migration
process, verification steps, and rollback plan.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed three TypeScript compilation errors:
1. game-config-helpers.ts:82 - Cast existing.config to object for spread
2. game-config-helpers.ts:116 - Fixed dynamic field assignment in updateGameConfigField
3. MatchingGameValidator.ts:540 - Use proper Difficulty type in MatchingGameConfig
Changes:
- Import Difficulty and GameType from matching types
- Update MatchingGameConfig to use proper union types
- Cast existing.config as object before spreading
- Rewrite updateGameConfigField to avoid type assertion issue
All TypeScript errors resolved. Compilation passes cleanly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated documentation to reflect the refactored implementation:
- Documented new room_game_configs table structure
- Explained shared type system and benefits
- Updated all code examples to use new helpers
- Revised debugging checklist for new architecture
- Added migration notes and rollback plan
- Clarified the four critical systems (was three, now includes helpers)
The documentation now accurately describes the normalized database
schema approach instead of the old monolithic JSON column.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Schema Changes
- Create `room_game_configs` table with one row per game per room
- Migrate existing gameConfig data from arcade_rooms.game_config JSON column
- Add unique index on (roomId, gameName) for efficient queries
### Benefits
- ✅ Type-safe config access with shared types
- ✅ Smaller rows (only configs for used games)
- ✅ Easier updates (single row vs entire JSON blob)
- ✅ Better concurrency (no lock contention between games)
- ✅ Foundation for per-game audit trail
### Core Changes
1. **Shared Config Types** (`game-configs.ts`)
- `MatchingGameConfig`, `MemoryQuizGameConfig` interfaces
- Default configs for each game
- Single source of truth for all settings
2. **Helper Functions** (`game-config-helpers.ts`)
- `getGameConfig<T>()` - type-safe config retrieval with defaults
- `setGameConfig()` - upsert game config
- `getAllGameConfigs()` - aggregate all game configs for a room
- `validateGameConfig()` - runtime validation
3. **API Routes**
- `/api/arcade/rooms/current`: Aggregates configs from new table
- `/api/arcade/rooms/[roomId]/settings`: Writes to new table
4. **Socket Server** (`socket-server.ts`)
- Uses `getGameConfig()` helper for session creation
- Eliminates manual config extraction and defaults
5. **Validators**
- `MemoryQuizGameValidator.getInitialState(config: MemoryQuizGameConfig)`
- `MatchingGameValidator.getInitialState(config: MatchingGameConfig)`
- Type signatures enforce consistency
### Migration Path
- Existing data migrated automatically (SQL in migration file)
- Old `gameConfig` column preserved temporarily
- Client-side providers unchanged (read from aggregated response)
Next steps:
- Test settings persistence thoroughly
- Drop old `gameConfig` column after validation
- Update documentation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed verbose console.log statements added during settings persistence debugging:
- socket-server.ts: Removed JSON.stringify logging of gameConfig flow
- RoomMemoryQuizProvider.tsx: Removed logging from mergedInitialState useMemo and setConfig
- MemoryQuizGameValidator.ts: Removed logging from validateAcceptNumber
The actual fix (playMode parameter addition) is preserved. Debug logging was only needed to identify the root cause and is no longer necessary.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive documentation for game settings persistence system
after fixing multiple settings bugs (gameType, playMode not persisting).
New documentation:
- .claude/GAME_SETTINGS_PERSISTENCE.md: Complete architecture guide
- How settings are structured (nested by game name)
- Three critical systems that must stay in sync
- Common bugs with detailed solutions
- Debugging checklist
- Step-by-step guide for adding new settings
- .claude/GAME_SETTINGS_REFACTORING.md: Recommended improvements
- Shared config types to prevent type mismatches
- Helper functions to reduce duplication (getGameConfig, updateGameConfig)
- Validator config type enforcement
- Exhaustiveness checking
- Runtime validation
- Migration strategy with priority order
Updated .claude/CLAUDE.md to reference these docs with quick reference guide.
This documentation will prevent similar bugs in the future by making the
architecture explicit and providing clear patterns to follow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
ROOT CAUSE FOUND:
The MemoryQuizGameValidator.getInitialState() method was hardcoding
playMode to 'cooperative' and not accepting it as a config parameter.
Even though socket-server.ts was passing playMode from the saved config,
the validator's TypeScript signature didn't include it:
BEFORE:
```typescript
getInitialState(config: {
selectedCount: number
displayTime: number
selectedDifficulty: DifficultyLevel
}): SorobanQuizState {
return {
// ...
playMode: 'cooperative', // ← ALWAYS HARDCODED!
}
}
```
AFTER:
```typescript
getInitialState(config: {
selectedCount: number
displayTime: number
selectedDifficulty: DifficultyLevel
playMode?: 'cooperative' | 'competitive' // ← NEW!
}): SorobanQuizState {
return {
// ...
playMode: config.playMode || 'cooperative', // ← USES CONFIG VALUE!
}
}
```
Also added comprehensive debug logging throughout the flow:
- socket-server.ts: logs room.gameConfig, extracted config, and resulting playMode
- RoomMemoryQuizProvider.tsx: logs roomData.gameConfig and merged state
- MemoryQuizGameValidator.ts: logs config received and playMode returned
This will help identify any remaining persistence issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The socket-server was missing playMode when creating the initial session
for memory-quiz games. It was only loading selectedCount, displayTime, and
selectedDifficulty from the saved config, causing playMode to always reset
to the default 'cooperative' even when 'competitive' was saved.
Now includes playMode in the initial state config:
- selectedCount
- displayTime
- selectedDifficulty
- playMode (NEW)
This ensures the playMode setting persists when users:
1. Set playMode to 'competitive'
2. Go back to game selection
3. Select memory-quiz again
4. PlayMode is still 'competitive' (not reset to 'cooperative')
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The session initialization was looking for settings at the wrong level:
- Was reading: room.gameConfig.gameType (undefined, falls back to default)
- Should read: room.gameConfig.matching.gameType (saved value)
gameConfig is structured as:
{
"matching": { "gameType": "...", "difficulty": ..., "turnTimer": ... },
"memory-quiz": { "selectedCount": ..., "displayTime": ..., ... }
}
This caused the session to be created with default settings even though
the settings were saved in the database. The client would load the correct
settings from roomData.gameConfig, but then the socket would immediately
overwrite them with the session's default state.
Now properly accesses the nested config for each game type.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When users clicked "back to game selection", the clearRoomGameApi function
was sending both gameName: null AND gameConfig: null to the server. This
destroyed all saved game settings (like gameType, difficulty, etc.).
Now clearRoomGameApi only sends gameName: null and preserves gameConfig,
so settings persist when users select a game again.
Root cause discovered via comprehensive database-level logging that traced
the exact data flow through the system.
Fixes settings persistence bug in room mode.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add detailed logging at every layer to trace gameConfig through the system:
Server-side (Settings API):
- Log incoming PATCH request body
- Log database state BEFORE update
- Log what will be written to database
- Log database state AFTER update
Server-side (Current Room API):
- Log what's READ from database when fetching room
Client-side:
- Track roomData.gameConfig changes with useEffect
This will show us exactly when and where gameConfig is being overwritten.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: setRoomGameApi was sending `gameConfig: {}` when gameConfig
was undefined, which overwrote all saved settings in the database.
Changes:
- Client: Only include gameConfig in request body if explicitly provided
- Server: Only include gameConfig in socket broadcast if provided
- Client handler: Update gameConfig from broadcast if present
This preserves all game settings (difficulty, card count, etc.) when
switching between games in a room.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace collapsed object logging with JSON.stringify to ensure full
object details are visible when console logs are copied/pasted.
This affects all settings persistence logging:
- Loading settings from database
- Saving settings to database
- API calls to server
- Cache updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive console logging to trace the settings persistence flow:
- Load settings from database on initialization
- Save settings to database when changed (gameType, difficulty, turnTimer)
- API calls to server with full request/response logging
This will help diagnose if settings are being persisted correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add useUpdateGameConfig hook and database saves to RoomMemoryPairsProvider
- Load saved settings from gameConfig['matching'] on init
- Save gameType, difficulty, and turnTimer changes to database
- Apply lint fixes: use dot notation instead of bracket notation
Matching game now persists settings when switching between games,
matching the behavior of memory-quiz.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The matching game was not saving settings to the database at all.
When you changed gameType, difficulty, or turnTimer, it only sent
a move to the arcade session but never saved to the database.
This adds the same persistence logic that memory-quiz uses:
**On Load:**
- Reads settings from gameConfig['matching'] in the database
- Merges with initialState
- Passes to useArcadeSession
**On Change:**
- Sends SET_CONFIG move (for real-time sync)
- Saves to gameConfig['matching'] via updateGameConfig
- Updates TanStack Query cache
Changes:
- Import useUpdateGameConfig hook
- Add mergedInitialState with settings from database
- Save settings in setGameType, setDifficulty, setTurnTimer
Now settings persist when switching between games!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**ROOT CAUSE:**
When switching games, setRoomGame was called with gameConfig: {},
which OVERWROTE the entire gameConfig in the database, destroying
all saved settings for ALL games.
**THE FIX:**
Remove gameConfig parameter from setRoomGame call - only change the
game name, preserve all existing settings.
**ADDED DEBUG LOGGING:**
Added detailed logging in RoomMemoryQuizProvider to help diagnose
settings persistence issues:
- Log gameConfig on component init
- Log what settings are being loaded
- Log what settings are being saved
- Log the full updated gameConfig
Changes:
- src/app/arcade/room/page.tsx: Don't pass gameConfig when switching games
- src/app/arcade/memory-quiz/context/RoomMemoryQuizProvider.tsx: Added debug logs
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed excessive console.log statements from:
- RoomMemoryQuizProvider.tsx: Removed ~14 verbose logs related to player
metadata, scores, and move processing
- useRoomData.ts: Removed logs for moderation events and player updates
Kept critical logs for debugging settings persistence:
- Loading saved game config
- Saving game config
- Room game changed
- Cache updates
This cleanup makes console output much more manageable when debugging
settings persistence issues.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The issue was that useUpdateGameConfig was saving settings to the database
but not updating the TanStack Query cache. This meant that when components
re-mounted (e.g., when switching games), they would read stale data from
the cache instead of the newly saved settings.
Changes:
- Added onSuccess callback to useUpdateGameConfig to update the cache
- Added gameConfig field to RoomData interface
- Updated all API functions to include gameConfig in returned data:
- fetchCurrentRoom
- createRoomApi
- joinRoomApi
- getRoomByCodeApi
Now when settings are saved, the cache is immediately updated, so switching
games and returning shows the correct saved settings.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, when creating a new room, users were navigated to
/arcade-rooms/{roomId}, which is the direct room route.
Now users are navigated to /join/{code}, which is the invite link
format. This provides a better user experience as it follows the
same flow as joining via an invite link.
Changes:
- Changed router.push from /arcade-rooms/{id} to /join/{code}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, settings were stored at the root of gameConfig, causing each
game to overwrite the other's settings when switching between games.
Now settings are stored under gameConfig['memory-quiz'], allowing each
game to maintain its own settings independently. When you switch from
memory-quiz to another game and back, the memory-quiz settings are
preserved exactly as you left them.
Changes:
- Load settings from gameConfig['memory-quiz'] instead of root gameConfig
- Save settings to gameConfig['memory-quiz'] to avoid overwriting other games
- Added comments explaining the scoping strategy
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement per-game settings persistence so that when users switch between
games and come back, their settings are restored. Settings are saved to
the room's gameConfig field in the database.
Changes:
- Add useUpdateGameConfig hook to save settings to room
- Load settings from roomData.gameConfig on provider initialization
- Merge saved config with initialState using useMemo
- Save settings to database when setConfig is called
- Settings persist across:
- Game switches (memory-quiz -> matching -> memory-quiz)
- Page refreshes
- New arcade sessions
Settings saved: selectedCount, displayTime, selectedDifficulty, playMode
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix issue where game selection by the host was not synchronized to other
room members. When the host selects a game, all players now see the change
in real-time via socket.io.
Server changes:
- Add 'room-game-changed' socket broadcast when gameName is updated
- Emit to all members in the room channel when game is set/changed
Client changes:
- Add socket listener for 'room-game-changed' event in useRoomData
- Update local cache when game change is received
- Room page automatically re-renders with new game selection
This ensures all players stay synchronized when the host selects or changes
the game for the room.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix race condition where the host would skip cards due to the effect
running twice on the same card index - once for the optimistic update
and potentially again for the server update.
The issue: When the host calls nextCard(), it immediately applies an
optimistic update that changes currentCardIndex. This triggers the effect
to re-run before the timer has even finished. Since isProcessingRef was
set to false right before calling nextCard(), the effect would start
processing the next card immediately, causing cards to be skipped.
Solution: Track the last processed card index in a ref (lastProcessedIndexRef)
and skip the effect if we're trying to process the same index again. This
ensures each card is only shown once, regardless of how many times the
effect runs due to state changes.
- Add lastProcessedIndexRef to track the last card we processed
- Check at start of effect if currentCardIndex === lastProcessedIndexRef
- Skip duplicate processing to prevent race conditions
- Remove unnecessary dependency on state.quizCards[currentCardIndex]
- Add detailed logging to help debug timing issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fix race condition where each player's browser independently timed card
progression, causing desync where different players saw different numbers
of cards during the memorization phase.
Solution: Only the room creator controls card timing by sending NEXT_CARD
moves. All other players react to state.currentCardIndex changes from the
server, ensuring all players see the same cards at the same time.
- Add isRoomCreator flag to MemoryQuizContext
- Detect room creator in RoomMemoryQuizProvider
- Modify DisplayPhase to only call nextCard() if room creator or local mode
- Add debug logging to track timing control
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- 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>
Replace checkmark indicators with player emojis on correctly guessed cards
in the results view. This provides visual feedback about which team found
each number in both cooperative and competitive modes.
- Display team player emojis vertically stacked for multi-player teams
- Use rounded rectangle background instead of circle for better fit
- Set maxHeight and overflow:hidden to prevent clipping issues
- Fallback to checkmark if no player data available
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add multiplayer state tracking (playerMetadata, playerScores, activePlayers)
- Add cooperative and competitive play modes
- Preserve multiplayer state through server-side validation
- Redesign scoreboard layout to stack players vertically with larger stats
- Add live scoreboard during gameplay (competitive mode)
- Add final leaderboard on results screen for both modes
- Track scores by userId to properly handle multi-player teams
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When typing rapidly in room mode, users had to type each digit
8+ times before it registered. This was caused by reading stale
state.currentInput values during rapid keypresses before React
could re-render with the optimistically updated state.
Solution: Use a ref to track the current input value and update
it immediately when keys are pressed, before waiting for the
network round-trip and React re-render.
Changes:
- Add currentInputRef to track input value immediately
- Update ref in useEffect to stay in sync with state
- Use ref instead of state.currentInput in keyboard handlers
- Clear ref immediately when accepting/rejecting numbers
This fixes the async network validation issue where local state
updates were too slow for rapid user input.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When changing a room's game via the settings API, the old arcade
session was persisting with the previous game's state. This caused
users to still see the old game after selecting a new one.
Changes:
- Delete existing arcade session when gameName is updated in room settings
- Add debug logging to room page game selection handler
- Ensure fresh session is created with new game settings
This fixes the issue where clicking Memory Lightning would not
properly switch the game from Battle Arena.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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>
Reorganizes layout so labels appear under their corresponding elements:
- Character count under name input
- "Random name" under dice button
Previously labels were misaligned and confusing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Improves clarity by renaming the prop to better describe its purpose:
highlighting the player selection/roster UI in the navigation bar.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Wraps game selection in PageWithNav for consistent navigation
- Adds game type mapping (GameType keys to internal game names)
- Enables player selection mode on game selection screen
- Adds navigation to "unsupported game" screen
- Fixes 400 error when selecting games like "Matching Pairs Battle"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Allows room hosts to return to game selection screen by clearing the
room's game selection. Adds useClearRoomGame hook and "Change Game"
menu item in room dropdown (only visible when a game is selected).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 2: UI and workflow updates
- Update room settings API to support setting game via PATCH
- Add useSetRoomGame hook for client-side game selection
- Update /arcade/room page to show game selection when no game set
- Create beautiful game selection UI with gradient cards
- Update AddPlayerButton to create rooms without games
- Navigate to /arcade/room after creating or joining rooms
- Remove dependency on local-only play - all games now room-based
Workflow:
1. User clicks "Create Room" from (+) menu
2. Room is created without a game (gameName = null)
3. User is navigated to /arcade/room
4. Game selection screen is shown
5. User clicks a game
6. Room game is set via API
7. Game loads - URL never changes, it's always /arcade/room
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 1: Database and API updates
- Create migration 0010 to make game_name and game_config nullable
- Update arcade_rooms schema to support rooms without games
- Update RoomData interface to make gameName optional
- Update CreateRoomParams to make gameName optional
- Update room creation API to allow null gameName
- Update all room data parsing to handle null gameName
This allows rooms to be created without a game selected, enabling
users to choose a game inside the room itself. The URL remains
/arcade/room regardless of selection, setup, or gameplay state.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Reverts navigation changes that broke lifted state popover behavior.
Original behavior (now restored):
- Create room: Keep popover open, switch to invite tab to share code
- Join room: Close popover, stay on current page
The navigation changes caused the popover to close immediately,
breaking the lifted state pattern that was intentionally designed
to keep the popover open for sharing room codes after creation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The toast CSS animations were using overly broad selectors like
[data-state='open'] which affected ANY element with data-state
attributes, causing nav items and other components to trigger the
toast slide-in/slide-out animations on hover.
Fixed by:
- Renaming animations: slideIn → toastSlideIn, etc.
- Scoping selectors: [data-radix-toast-viewport] [data-state='open']
- Now only toast elements within the viewport are animated
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Rooms are modal and use a single route /arcade/room that fetches
the user's current room. Fixed navigation for both:
- Creating a new room
- Joining an existing room
Both now navigate to /arcade/room instead of /arcade/rooms/{id}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When creating a room from the /arcade page using the (+) button:
- Add room to recent rooms list
- Close the popover
- Navigate to the room page immediately
This fixes the UX issue where users would create a room but
remain on the /arcade page without any clear indication of
how to access their new room.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The client expects the POST /api/arcade/rooms response to include
members and memberPlayers fields, but the API was only returning
room and joinUrl. This caused room creation to fail on the client.
Fixes the "failed to create room" error on production.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Create 0009_add_display_password.sql migration
- Add entry to drizzle journal
- This adds the display_password column that was missing in production
The plan is to nuke the production database and let all migrations
run from scratch on container restart.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes issue where ALL room members were seeing join request approval
toasts, but only the creator can approve them, leading to confusing
error messages when non-creators clicked approve/deny.
Changes:
- Join request notifications now sent only to room creator's user channel
- Changed from broadcasting to entire room to targeted user notification
- Uses `user:${room.createdBy}` channel instead of `room:${roomId}`
Non-host users will no longer see approval toasts they cannot act on.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixes catch-22 where room creator who leaves their own approval-only
or restricted room cannot rejoin because:
- Approval-only: They need approval but can't approve themselves
- Restricted: They need an invitation but can't invite themselves
Changes:
- Room creator now bypasses invitation check for restricted rooms
- Room creator now bypasses approval check for approval-only rooms
- Other users still require proper authorization
This ensures hosts can always access their own rooms regardless of
access mode restrictions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- 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>
Enhances the password-protected room settings UX:
Changes:
1. Password input now stays visible and editable
- Plain text input (not hidden) for easy viewing
- Focus state with orange border
- Clear placeholder text
2. Copy button next to password input
- 📋 Copy icon with text
- Visual feedback: changes to "✓ Copied!" for 2 seconds
- Disabled state when no password entered
- Green success color after copying
3. Better labeling and hints
- "Room Password" label above input
- Helper text: "Share this password with guests to allow them to join"
- More descriptive placeholder
Note: Passwords are hashed in the database for security, so existing
passwords cannot be retrieved. This UI is for setting/changing passwords
and easily copying them to share with guests.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhances the room moderation settings UX to prevent accidental closure
with unsaved changes:
Changes:
1. "Update Access Mode" button now only appears when there are changes
- Tracks original access mode on load
- Compares current selection to detect changes
- Button hidden when no changes made
2. "Close" button disabled when there are unsaved access mode changes
- Prevents accidentally losing changes
- Visual feedback: dimmed appearance, orange border
- Hover state brightens orange border to draw attention
3. Tooltip on disabled "Close" button
- Shows "Please update access mode settings before closing"
- Helps users understand why close is disabled
This prevents the frustrating UX issue where users want to close but
are forced to update settings they didn't intend to change.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Adds two enhancements to player customization:
1. Name generator button in PlayerConfigDialog
- Dice emoji (🎲) button next to name input
- Generates new themed names on click
- Excludes current player from collision check
- Maintains auto-save behavior
2. Abacus emoji option
- Added 🧮 (abacus) as first emoji choice
- Perfect thematic fit for the application
Now players can easily try different generated names without leaving
the settings dialog.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implements automatic generation of creative player names combining
adjectives with nouns/roles (e.g., "Swift Ninja", "Cosmic Wizard").
Changes:
- Created playerNames utility with 50 adjectives and 50 nouns
- Generates unique names with collision detection
- Applied to default player creation and addPlayer function
- Replaces generic "Player 1", "Player 2" with fun names
- Manual override still available via PlayerConfigDialog
- Added comprehensive unit tests (10 passing tests)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Root Cause:**
- ModerationNotifications was rendered in BOTH /arcade/room/page.tsx AND PageWithNav
- Both had separate useRoomData hooks, creating two socket listeners
- When join-request-submitted event fired, BOTH instances showed a toast
- Clicking "Approve" only closed one toast, leaving the other visible
**Fix:**
- Removed ModerationNotifications from room page entirely
- PageWithNav (inside MemoryPairsGame) already handles all moderation events
- Now only ONE instance listens and renders, so approvals properly dismiss
**Files Changed:**
- Removed ModerationNotifications import from room page
- Removed all 4 instances of <ModerationNotifications /> from room page
- Removed moderationEvent and clearModerationEvent from useRoomData destructuring
- Added comment explaining PageWithNav handles notifications
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Issue:**
Toast notification was persisting after successful approval because
showJoinRequestToast state was never reset to false.
**Fix:**
- Add else clause to useEffect that resets toast state when event cleared
- Reset both showJoinRequestToast and requestError when event changes type
- Ensures toast properly dismisses after approval/denial
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add room access mode check in invite POST endpoint
- Block invitation creation if room is retired (403 status)
- Clear error message: "Cannot send invitations to retired rooms"
- Check happens before host validation to catch retired rooms early
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Socket handler update:**
- Capture reason field from kicked-from-room socket event
**Modal UI improvements:**
- Detect when reason contains "retired"
- Show 🏁 emoji instead of ⚠️ for retired rooms
- Title changes from "Kicked from Room" to "Room Retired"
- Message explains room owner retired the room and access is closed
- Clarify only room owner can access retired rooms
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Join endpoint changes:**
- Only room creator can access retired rooms
- All other users blocked with 410 status and clear message
**Settings endpoint changes:**
- When room set to 'retired', all non-owner members immediately expelled
- Expelled members removed from database
- Each expulsion recorded in member history
- Socket notifications sent to all expelled members (kicked-from-room event)
- Owner notified of member expulsions via member-left event
**Member expulsion flow:**
- Similar pattern to ban ejection
- Expelled members see "kicked from room" modal with reason "Room has been retired"
- All expelled members logged in history with 'left' action
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add requestError state to track approval/deny errors
- Toast dismisses only on successful approval/deny
- On error, toast remains visible and displays error message inline
- Parse API error response to show meaningful error messages
- User can retry approval/deny action after error
- Replace generic alert() with styled error message within toast
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add 'join-request' type to ModerationEvent interface
- Add socket listener for 'join-request-submitted' event in useRoomData
- Update join request POST endpoint to broadcast socket event to room members
- Add prominent toast notification with inline approve/deny buttons in ModerationNotifications
- Toast appears immediately when host receives join request (not buried in settings)
- Approve/deny actions handled directly from toast with API calls
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update locked room terminology and implementation:
- Change description from "No members" to "No new members"
- Allow existing members to continue using locked rooms
- Only block new members from joining locked rooms
- Update join API to check membership before rejecting
This clarifies that "locked" means no NEW members, but existing members
can continue to participate in the room.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove premature check that blocked access to restricted rooms. Now:
- Frontend no longer blocks restricted room access upfront
- Backend API checks for pending invitation
- Users with valid invitations can join successfully
- Users without invitations get appropriate error message
This fixes the issue where users with pending invitations couldn't join
restricted rooms via the join link.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The socket wasn't receiving join-request-approved events because it hadn't
joined the user-specific channel. Now:
- Fetch viewer ID from /api/viewer endpoint
- Emit 'join-user-channel' with userId on socket connect
- Socket joins `user:${userId}` room to receive moderation events
- Approval notifications now trigger automatic room join
This completes the real-time approval notification flow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove polling interval that checked every 5 seconds for approval status.
The socket.io listener provides real-time notifications, making polling
unnecessary and wasteful.
Now relies solely on socket.io for instant approval notifications, which:
- Reduces network traffic
- Simplifies code
- Provides faster response time
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When users request to join an approval-only room, they now receive real-time
notifications when their request is approved:
- Add socket.io-client listener for 'join-request-approved' events
- Implement polling fallback (every 5 seconds) to check approval status
- Automatically join room when approval is detected via socket or polling
- Apply to both share link page and JoinRoomModal
This completes the approval flow - users no longer need to reload the page
to see if their join request was approved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Updated the ModerationPanel Settings tab to use a visual button grid
for access mode selection, matching the CreateRoomModal UX.
Changes:
- Replaced <select> dropdown with 3x2 grid of buttons
- Each button shows emoji + label + description
- Visual feedback for selected state and hover
- Includes all 6 access modes: open, password, approval-only,
restricted, locked, retired
- Maintains same functionality with improved UX
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When users enter an approval-only room code in the JoinRoomModal, they now:
- See a prompt to send a join request
- After sending, see a "Waiting for Approval" screen
- Can close the modal and check back later
This matches the UX flow from the share link approval flow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously the endpoint only had a GET handler, causing a 405 error
when users tried to request approval for approval-only rooms.
Now users can POST to create join requests with optional displayName.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
- Password errors now stay in password prompt UI instead of redirecting to error page
- Error message clears when user starts typing new password
- Users can now retry incorrect passwords without losing the join flow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Update placeholder text in room creation forms to show auto-generated format
- Make room.name nullable in database schema (migration 0008)
- Add accessMode field to RoomData interface
- Implement password prompt UI for password-protected rooms via share links
- Add password support to room browser join flow
- Remove autoFocus attribute for accessibility compliance
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The CreateRoomModal was trying to destructure createRoom from useRoomData(),
but that hook doesn't export it. Changed to use the proper useCreateRoom() hook.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Updated all test files to use accessMode instead of isLocked field
- Fixed room-manager tests to reflect new access control schema
- Installed bcryptjs dependency for password hashing
- All access mode TypeScript compilation errors resolved
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>
## [3.0.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v3.0.0) (2025-10-13)
### ⚠ BREAKING CHANGES
* Added DELETE /api/arcade/rooms/:roomId/invite endpoint for declining invitations
Authorization Error Handling:
- ModerationPanel: Parse and display API error messages (kick, ban, unban, invite, data loading)
- PendingInvitations: Parse and display API error messages (decline, fetch)
- All moderation actions now show specific auth errors like "Only the host can kick users"
New Endpoint:
- DELETE /api/arcade/rooms/:roomId/invite: Allow users to decline their pending invitations
* Validates invitation exists and is pending
* Only invited user can decline their own invitation
* Returns proper error messages for auth failures
Bug Fix:
- Fixed invitations/pending/route.ts ban check query (removed reference to non-existent unbannedAt field)
- Ban records are deleted when unbanned, so any existing ban is active
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
### Features
* add API routes for moderation and invitations ([79a8518](79a8518557))
* add backend library functions for room moderation ([84f3c4b](84f3c4bcfd))
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add common UI components ([cd3115a](cd3115aa6d))
* add database schema for room moderation and invitations ([97d1604](97d16041df))
* add invitation system UI components ([fd3a2d1](fd3a2d1f76))
* add moderation panel with unban & invite feature ([a2d0169](a2d0169f80))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* add real-time socket updates for moderation events ([86ceba3](86ceba3df3))
* add room creation and join flow UI ([7f95032](7f95032253))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* integrate moderation system into arcade pages ([087652f](087652f9e7))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* improve authorization error handling and add missing decline invitation endpoint ([97669ad](97669ad084))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **moderation:** don't show pending invitation for users already in room ([fae5920](fae5920e2f))
* move invitations into nav and filter out current/banned rooms ([cfaf82b](cfaf82b2cc))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** close hamburger menu when nested dropdown closes and mouse not hovering ([7d65212](7d652126d0))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent hamburger menu from closing when toggling Style dropdown ([a898fbc](a898fbc187))
* **nav:** prevent style dropdown from closing hamburger menu ([560a052](560a05266e))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** properly prevent nested style dropdown from closing hamburger menu ([c5b6a82](c5b6a82ca4))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* **matching:** unify duplicate MemoryGrid components into shared implementation ([5f7067a](5f7067a106))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* remove old arcade guard system ([2e6469b](2e6469bed4))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* update core UI components ([55ccf09](55ccf097d9))
* update game pages for room-based multiplayer ([54846bd](54846bdc3f))
* update nav components for room-based system ([31ac958](31ac958d33))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
* **zIndex:** create centralized z-index layering system ([a204c83](a204c83afc))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
* add tests for room and moderation features ([063a8e5](063a8e52fe))
BREAKING CHANGE: Added DELETE /api/arcade/rooms/:roomId/invite endpoint for declining invitations
Authorization Error Handling:
- ModerationPanel: Parse and display API error messages (kick, ban, unban, invite, data loading)
- PendingInvitations: Parse and display API error messages (decline, fetch)
- All moderation actions now show specific auth errors like "Only the host can kick users"
New Endpoint:
- DELETE /api/arcade/rooms/:roomId/invite: Allow users to decline their pending invitations
* Validates invitation exists and is pending
* Only invited user can decline their own invitation
* Returns proper error messages for auth failures
Bug Fix:
- Fixed invitations/pending/route.ts ban check query (removed reference to non-existent unbannedAt field)
- Ban records are deleted when unbanned, so any existing ban is active
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-13)
### Features
* add API routes for moderation and invitations ([79a8518](79a8518557))
* add backend library functions for room moderation ([84f3c4b](84f3c4bcfd))
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add common UI components ([cd3115a](cd3115aa6d))
* add database schema for room moderation and invitations ([97d1604](97d16041df))
* add invitation system UI components ([fd3a2d1](fd3a2d1f76))
* add moderation panel with unban & invite feature ([a2d0169](a2d0169f80))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* add real-time socket updates for moderation events ([86ceba3](86ceba3df3))
* add room creation and join flow UI ([7f95032](7f95032253))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* integrate moderation system into arcade pages ([087652f](087652f9e7))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **moderation:** don't show pending invitation for users already in room ([fae5920](fae5920e2f))
* move invitations into nav and filter out current/banned rooms ([cfaf82b](cfaf82b2cc))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** close hamburger menu when nested dropdown closes and mouse not hovering ([7d65212](7d652126d0))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent hamburger menu from closing when toggling Style dropdown ([a898fbc](a898fbc187))
* **nav:** prevent style dropdown from closing hamburger menu ([560a052](560a05266e))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** properly prevent nested style dropdown from closing hamburger menu ([c5b6a82](c5b6a82ca4))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* **matching:** unify duplicate MemoryGrid components into shared implementation ([5f7067a](5f7067a106))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* remove old arcade guard system ([2e6469b](2e6469bed4))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* update core UI components ([55ccf09](55ccf097d9))
* update game pages for room-based multiplayer ([54846bd](54846bdc3f))
* update nav components for room-based system ([31ac958](31ac958d33))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
* **zIndex:** create centralized z-index layering system ([a204c83](a204c83afc))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
* add tests for room and moderation features ([063a8e5](063a8e52fe))
Improvements to invitation banner UX:
- Move PendingInvitations from arcade page into GameContextNav
- Now appears as part of the mini app nav (not underneath it)
- Filter out invitations for the room user is currently in
- Filter out invitations for rooms where user is banned
- Backend filters banned room invitations automatically
This resolves awkward situations where users see invitations for:
1. The room they're already in
2. Rooms they were kicked from or banned from
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-13)
### Features
* add API routes for moderation and invitations ([79a8518](79a8518557))
* add backend library functions for room moderation ([84f3c4b](84f3c4bcfd))
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add common UI components ([cd3115a](cd3115aa6d))
* add database schema for room moderation and invitations ([97d1604](97d16041df))
* add invitation system UI components ([fd3a2d1](fd3a2d1f76))
* add moderation panel with unban & invite feature ([a2d0169](a2d0169f80))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* add real-time socket updates for moderation events ([86ceba3](86ceba3df3))
* add room creation and join flow UI ([7f95032](7f95032253))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* integrate moderation system into arcade pages ([087652f](087652f9e7))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **moderation:** don't show pending invitation for users already in room ([fae5920](fae5920e2f))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** close hamburger menu when nested dropdown closes and mouse not hovering ([7d65212](7d652126d0))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent hamburger menu from closing when toggling Style dropdown ([a898fbc](a898fbc187))
* **nav:** prevent style dropdown from closing hamburger menu ([560a052](560a05266e))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** properly prevent nested style dropdown from closing hamburger menu ([c5b6a82](c5b6a82ca4))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* **matching:** unify duplicate MemoryGrid components into shared implementation ([5f7067a](5f7067a106))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* remove old arcade guard system ([2e6469b](2e6469bed4))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* update core UI components ([55ccf09](55ccf097d9))
* update game pages for room-based multiplayer ([54846bd](54846bdc3f))
* update nav components for room-based system ([31ac958](31ac958d33))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
* **zIndex:** create centralized z-index layering system ([a204c83](a204c83afc))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
* add tests for room and moderation features ([063a8e5](063a8e52fe))
Fixed logic error where users currently in the room would still show
a "Pending invitation" indicator in the History tab. The invitation
status should only be displayed for users who are:
- Not currently in the room
- Not banned
- Have a pending invitation
This prevents the confusing UI state where a user appears to be
both "In Room" and have a "Pending invitation" at the same time.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-13)
### Features
* add API routes for moderation and invitations ([79a8518](79a8518557))
* add backend library functions for room moderation ([84f3c4b](84f3c4bcfd))
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add common UI components ([cd3115a](cd3115aa6d))
* add database schema for room moderation and invitations ([97d1604](97d16041df))
* add invitation system UI components ([fd3a2d1](fd3a2d1f76))
* add moderation panel with unban & invite feature ([a2d0169](a2d0169f80))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* add real-time socket updates for moderation events ([86ceba3](86ceba3df3))
* add room creation and join flow UI ([7f95032](7f95032253))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* integrate moderation system into arcade pages ([087652f](087652f9e7))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** close hamburger menu when nested dropdown closes and mouse not hovering ([7d65212](7d652126d0))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent hamburger menu from closing when toggling Style dropdown ([a898fbc](a898fbc187))
* **nav:** prevent style dropdown from closing hamburger menu ([560a052](560a05266e))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** properly prevent nested style dropdown from closing hamburger menu ([c5b6a82](c5b6a82ca4))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* **matching:** unify duplicate MemoryGrid components into shared implementation ([5f7067a](5f7067a106))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* remove old arcade guard system ([2e6469b](2e6469bed4))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* update core UI components ([55ccf09](55ccf097d9))
* update game pages for room-based multiplayer ([54846bd](54846bdc3f))
* update nav components for room-based system ([31ac958](31ac958d33))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
* **zIndex:** create centralized z-index layering system ([a204c83](a204c83afc))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
* add tests for room and moderation features ([063a8e5](063a8e52fe))
Update core UI components for new room system:
- AbacusDisplayDropdown: Enhanced styling and accessibility
- AppNavBar: Integration with room info and moderation
- PageWithNav: Room-aware page wrapper
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update game implementations to support new room system:
- Arcade matching game with room integration
- Local/Room memory pairs providers
- Complement race game modes
- Memory quiz game
- GameModeContext with room awareness
- MemoryGrid component updates
Games now properly integrate with room-based multiplayer,
moderation, and real-time updates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update navigation components to work with new room system:
- AddPlayerButton: Enhanced with multiplayer room integration
- ActivePlayersList: Update for room-based players
- GameContextNav: Add room info and moderation access
- GameControlButtons: Room-aware controls
- NetworkPlayerIndicator: Real-time room status
- PlayerTooltip: Enhanced player information
- Tests: Add comprehensive tests for AddPlayerButton
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove deprecated arcade guard hooks and components:
- useArcadeGuard.ts
- useArcadeRedirect.ts
- ArcadeGuardedPage.tsx
- Related tests
These have been replaced by the new room-based system with
proper moderation, invitations, and real-time updates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Update arcade pages to use new moderation features:
- /arcade: Add PendingInvitations and PlayOnlineTab with room management
- /arcade/room: Add ModerationNotifications to all render paths
- RoomInfo: Add moderation panel with focus capability for reported players
- join API: Enhanced with better error handling
Users can now:
- See and respond to invitations from the arcade lobby
- Receive real-time moderation notifications in rooms
- Access full moderation panel as room hosts
- Click on report toasts to view reported players
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive moderation UI:
- ModerationPanel: Full-featured moderation interface for room hosts
- Members tab: View active members, reports, kick/ban actions
- Bans tab: View and manage banned users with inline unban confirmation
- History tab: View all historical members with status tracking
- Real-time updates when members join/leave
- Unban & Invite feature: Banned users can be unbanned and auto-invited
from the History tab with inline confirmation
- ModerationNotifications: Toast/modal notifications for:
- Kicked from room
- Banned from room
- Player reported (clickable to open moderation panel)
- Room invitation received (with accept & join)
- ReportPlayerModal: Form for reporting player misconduct
Key features:
- Real-time member status updates across all tabs
- Focus/highlight system for reported players
- Auto-invite on unban workflow
- Inline confirmations for destructive actions
- Accessible UI with Radix primitives
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add invitation management UI:
- PendingInvitations: Display pending room invitations with accept/decline
- InvitePlayersTab: Tab for inviting players to rooms
- RoomShareButtons: Share room via link or code
Includes real-time invitation updates, auto-unban celebration UI,
and seamless accept & join flow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive room management UI:
- CreateRoomModal: Modal for creating new multiplayer rooms
- JoinRoomModal: Modal for joining rooms via code
- JoinRoomInput: Reusable input component for room codes
- PlayOnlineTab: Tab component for arcade lobby
- RecentRoomsList: List of user's recent rooms
- /join/[code] page: Direct join link page
- E2E test for join flow
Includes shareable room links, clipboard integration,
and user-friendly error handling.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add reusable components:
- Modal: Accessible modal component using Radix UI Dialog primitive
- CopyButton: Copy-to-clipboard button with visual feedback
- useClipboard: Hook for clipboard operations with success/error states
These components are used throughout the moderation and room
management UI for consistent user experience.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhance socket server and useRoomData hook to support:
- User-specific channels for personal notifications (kicks, bans, invitations)
- Socket connections maintained when authenticated (not just in rooms)
- Real-time moderation event handling (kicked, banned, reported, invited)
- ModerationEvent types and handlers in useRoomData
This enables users to receive invitations and moderation notifications
anywhere in the app, including the arcade lobby, without page refresh.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add REST API endpoints for:
- POST/DELETE /ban: Ban/unban users (with auto-invite on unban)
- POST /kick: Remove users from room temporarily
- POST /report: Submit player misconduct reports
- GET /reports: Retrieve pending reports for room hosts
- GET /history: Get all historical room members with statuses
- POST /invite: Send room invitations
- GET /invitations/pending: Get user's pending invitations
All endpoints include proper authentication, validation, and
real-time socket notifications for affected users.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement core moderation functionality:
- room-moderation.ts: Ban/unban users, kick, submit/manage reports
- room-invitations.ts: Create and manage room invitations
- room-member-history.ts: Track historical room membership
- room-membership.ts: Enhanced to work with new moderation system
These functions provide the business logic layer for all
moderation features including auto-invite on unban.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add comprehensive database schema to support:
- Room bans: Track banned users with reasons and timestamps
- Room reports: Allow players to report others for misconduct
- Room invitations: Send and track room invitations
- Room member history: Track all users who have ever been in a room
This foundational schema enables the complete moderation system
including banning, kicking, reporting, and invitation features.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-12)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** close hamburger menu when nested dropdown closes and mouse not hovering ([7d65212](7d652126d0))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent hamburger menu from closing when toggling Style dropdown ([a898fbc](a898fbc187))
* **nav:** prevent style dropdown from closing hamburger menu ([560a052](560a05266e))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** properly prevent nested style dropdown from closing hamburger menu ([c5b6a82](c5b6a82ca4))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* **matching:** unify duplicate MemoryGrid components into shared implementation ([5f7067a](5f7067a106))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
* **zIndex:** create centralized z-index layering system ([a204c83](a204c83afc))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Simplified nested dropdown state management to allow hamburger menu to stay
open when clicking the Style button to close it. The hamburger now naturally
stays open as long as the mouse is hovering, and closes when the mouse
actually leaves the menu area.
Changes:
- Removed forced close logic when nested dropdown closes
- Removed unused isCurrentlyHovering ref
- Let the existing hover/click state management handle menu visibility naturally
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-12)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** close hamburger menu when nested dropdown closes and mouse not hovering ([7d65212](7d652126d0))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent style dropdown from closing hamburger menu ([560a052](560a05266e))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** properly prevent nested style dropdown from closing hamburger menu ([c5b6a82](c5b6a82ca4))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* **matching:** unify duplicate MemoryGrid components into shared implementation ([5f7067a](5f7067a106))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
* **zIndex:** create centralized z-index layering system ([a204c83](a204c83afc))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Fixed issue where hamburger menu would stay open after:
1. Hover over hamburger (opens)
2. Click style dropdown (opens)
3. Click outside to close style dropdown
4. Hamburger stays open until clicked
Solution:
- Track hover state with useRef to know real-time mouse position
- When nested dropdown closes, check if mouse is still hovering
- If not hovering, close hamburger immediately
Also cleaned up debug console.log statements now that the issue is resolved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace magic z-index numbers with named constants in a centralized
system to prevent conflicts and make the layering hierarchy clear.
Created src/constants/zIndex.ts with:
- Logical layers (BASE, NAV, DROPDOWN, MODAL, etc.)
- Special game navigation layer (HAMBURGER_MENU, HAMBURGER_NESTED_DROPDOWN)
- Helper function for accessing nested values
Updated components:
- AppNavBar: Use Z_INDEX.GAME_NAV.HAMBURGER_MENU (was 9999)
- AppNavBar: Use Z_INDEX.NAV_BAR (was 100)
- AbacusDisplayDropdown: Use Z_INDEX.GAME_NAV.HAMBURGER_NESTED_DROPDOWN (was 10000)
Benefits:
- No more magic numbers
- Clear hierarchy of what appears on top
- Easy to adjust entire layers
- Self-documenting code
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The issue was Radix UI's onInteractOutside behavior, not event propagation.
When clicking the nested Style dropdown, Radix detected it as an "outside"
interaction and closed the parent hamburger menu.
Solution: Add onInteractOutside handler to hamburger menu Content that
checks if the interaction is within a nested Radix popup (role="dialog"
or data-radix-popper-content-wrapper) and prevents the close.
Reverted previous stopPropagation approach as it was addressing the wrong
layer of the problem.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Stop click event propagation from the nested AbacusDisplayDropdown
to prevent it from closing the parent hamburger menu.
Added stopPropagation to:
- Style dropdown trigger button
- Style dropdown content wrapper
This allows the nested dropdown to function independently without
affecting the parent menu state.
🤖 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>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-12)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** make MemoryGrid generic to support different card types ([dcda826](dcda826b9a))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** replace mismatch banner with card shake animation ([804096f](804096fd8a))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* **matching:** unify duplicate MemoryGrid components into shared implementation ([5f7067a](5f7067a106))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Fix TypeScript error where GameCard type was incompatible with MemoryCard
by making the shared MemoryGrid component generic over the card type.
This allows each route to use its own card type definition while still
sharing the grid implementation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Consolidate two nearly-identical MemoryGrid components (arcade vs games)
into a single shared component with optional multiplayer features.
Changes:
- Create src/components/matching/ for shared matching game components
- Extract HoverAvatar into standalone component for reusability
- Create unified MemoryGrid with enableMultiplayerPresence prop
- Update arcade GamePhase to use shared grid with presence features
- Update games GamePhase to use shared grid without presence features
- Remove duplicate MemoryGrid files from both routes
Benefits:
- Bug fixes only need to be applied once
- Features won't diverge over time
- Reduced testing surface area
- 420+ lines of duplicate code eliminated
The shared MemoryGrid uses a render prop pattern for GameCard to allow
each route to use its own card implementation while sharing grid logic.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Remove the obtrusive "Not a match! Try again." banner overlay that
appeared over cards when incorrect pairs were selected. Replace with
a kinetic shake animation applied directly to the mismatched cards
before they flip back over.
Changes:
- Remove banner overlay from both arcade and games MemoryGrid components
- Add shouldShake state to identify mismatched cards
- Apply cardShake animation (horizontal wiggle + rotation) to cards
- Update animation keyframes from banner shake to card shake
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add delay to hamburger menu hover to prevent premature closing ([95cd72e](95cd72e9bf))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Added 150ms delay when mouse leaves the hamburger button or menu to allow
smooth transition between them without the menu closing. This fixes the issue
where the menu would immediately close when moving the mouse from the button
to the dropdown content.
Implementation:
- useRef to track timeout
- handleMouseEnter: cancels any pending close timeout
- handleMouseLeave: sets 150ms delay before closing
- Applied to both button and menu content
- Cleanup effect to clear timeout on unmount
The menu now stays open while hovering either the button or the dropdown,
providing smooth UX for both hover and click interactions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** center game context with hamburger menu for utilities ([a35a7d5](a35a7d56df))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Complete redesign of minimal navigation for game pages:
**Layout Changes:**
- Game context (room info + players) now centered horizontally on viewport
- Hamburger menu (☰) in top-left with all utility items
- Responsive: game context shrinks to fit narrow viewports
**Hamburger Menu Features:**
- Opens on hover (instant access) OR click (traditional)
- Stays open while hovering menu or button
- Contains three organized sections:
1. Navigation: Home, Create, Guide, Games
2. Controls: Fullscreen toggle, Exit Arcade
3. Abacus Style: Dropdown integrated inline
**Benefits:**
- Clean, uncluttered interface focused on game/players
- Utilities accessible but out of the way
- Discoverable affordance (visible hamburger icon)
- Works on mobile (click) and desktop (hover)
- Maintains fullscreen badge when active
Removed old top-right utility panes and CompactNavLink component.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** enable tooltips for local players during gameplay ([5499700](54997007b8))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* **nav:** remove play arrow badge from turn indicators ([80cfc10](80cfc10f78))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Removed `pointerEvents: canModifyPlayers ? 'auto' : 'none'` from the active
players container that was blocking tooltips during gameplay.
During gameplay (canModifyPlayers=false), this was preventing hover events
from reaching the PlayerTooltip component, so tooltips wouldn't show for
local players. Network players didn't have this restriction, so their
tooltips worked fine.
The pointer-events restriction is not needed because:
- Interactive buttons (configure/remove) only show during setup (shouldEmphasize)
- Avatar click handlers already respect the shouldEmphasize flag
- Tooltips should always be accessible for player information
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the ▶ arrow badge from both local and network player turn indicators.
Turn indication is now shown through:
- Border ring with player color (pulsing)
- "Your turn" / "Their turn" text label below avatar
- Size increase and animation (local players only)
- Opacity dimming of non-current players
The arrow badge was unnecessary visual clutter with the text labels providing
clear indication of whose turn it is.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* **matching:** integrate game type into nav, remove redundant header ([389df29](389df29dc3))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Removed redundant in-game header and integrated all game info into the nav bar:
**Changes:**
- MemoryPairsGame now sets navTitle/navEmoji based on gameType:
- Complement Pairs (🤝) for complement-pairs mode
- Abacus Match (🧮) for abacus-numeral mode
- Removed redundant game header from GamePhase that showed:
- Game type emoji and name (now in nav)
- Player count "⚔️ 3Ps" (visible via player avatars in nav)
- New Game button (already in nav dropdown menu)
- Cleaned up unused imports and variables
**Result:**
- No duplicate information displayed
- Cleaner game interface with more space for gameplay
- All game/room info consolidated in the persistent nav bar
Applied to both /games/matching and /arcade/matching versions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove blue gradient background from network players ([2881aff](2881affecc))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Removed the decorative blue/purple/pink gradient "network frame border" that
was showing around remote player avatars. This background was not requested
and is unnecessary with the current turn indicator system.
Also cleaned up unused hover state tracking that was only used for the frame.
Network players now display cleanly with just their emoji and the network
badge (📡), showing the turn indicator border only when it's their turn.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** improve readability of turn label text ([bbd1da0](bbd1da02b5))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Made turn labels much more readable with white outline and bolder styling:
- Increased font weight from bold (700) to 900
- Increased font size from 11px to 12px
- Added white text outline using 4-directional text shadows
- Enhanced drop shadow for better depth
The white outline creates strong contrast against the player's theme color,
making "Your turn" and "Their turn" text clearly readable over any avatar.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** add z-index to turn labels to prevent avatar overlap ([7c294da](7c294dafff))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Added position: relative and zIndex: 10 to turn label text to ensure it
renders above the animated avatar emojis. The floating/scaling animations
were causing the avatars to obscure the text below them.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** prevent turn label text from being obscured ([c4b00dd](c4b00dd679))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Changed parent containers to use flex-end alignment and increased bottom
padding to prevent the turn label text from being clipped or hidden:
- Changed alignItems from 'center' to 'flex-end' for both network and local
player containers
- Increased bottom padding to accommodate the text labels
- Network player container: 6px 12px → 6px 12px 12px 12px
- Active player container: varies by emphasis state, added 4-6px bottom
This ensures "Your turn" and "Their turn" text is fully visible below avatars.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** add turn label text under current player avatars ([52a66d5](52a66d5f68))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Players now see clear text labels under avatars when it's someone's turn:
- "Your turn" under local players (in player's color)
- "Their turn" under network/remote players (in player's color)
Labels are:
- Small uppercase text (11px, bold)
- Colored with player's theme color
- Only shown during active gameplay (hasGameState)
- Only appear under the current player's avatar
This provides explicit confirmation of whose turn it is, complementing
the visual indicators (border ring, arrow badge, size/animation changes).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove animation/enlargement from network player turn indicator ([53079ed](53079ede13))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
When it's a network player's turn, the local player is waiting, not acting.
Network players now show turn indicators without animated/enlarged behavior:
- Border ring and arrow badge still show whose turn it is
- Avatar stays at normal size (56px, no enlargement)
- No floating/bobbing animation
- Opacity still changes to emphasize current player
This makes it clear which remote player has the turn while signaling that
no action is required from the current session.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** add turn indicators to network players ([623314b](623314bd38))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Network players now display the same turn indicator features as local players:
- Size increase and float animation for current player
- Border ring with player color
- Turn arrow badge (▶) in top-left
- Score badge in bottom-right
- Streak badge (🔥) in top-right when streak >= 2
- Opacity dimming for non-current players during game
This ensures all players (local and remote) have identical turn indicator
treatment, making it clear whose turn it is from any player's perspective.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add prominent turn indicator arrow badge ([f574558](f574558dff))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Add a pulsing arrow badge (▶) on the top-left of the current player's
avatar to make it crystal clear whose turn it is, even when viewing
from another player's perspective.
The badge:
- Circular with player's color gradient
- White border for contrast
- Pulsing animation to draw attention
- Shows right-pointing arrow to indicate active turn
- Positioned at top-left corner
This addresses the issue where non-current players couldn't easily
tell whose turn it was - they'd see themselves dimmed but no clear
indicator of who had the turn.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** apply turn indicators to arcade version too ([e6f96a8](e6f96a8b99))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
The previous commits only updated the /games/ version but not the
/arcade/ version. This commit applies the same changes to the arcade
version so turn indicators work properly there.
Changes:
- Pass game state (currentPlayer, scores, streaks) from arcade
MemoryPairsGame to PageWithNav
- Remove PlayerStatusBar import and usage from arcade GamePhase
Now both versions use nav avatars as turn indicators.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** only apply turn indicator when game is active ([cb4c061](cb4c061d11))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Fix avatars being dimmed when no game is running. Only apply turn
indicator styles (opacity, size, border, animations) when currentPlayerId
is defined (game is actually playing).
Without this check, all players were dimmed to 0.65 opacity when the
game wasn't running because isCurrentPlayer was false for everyone.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **matching:** use nav avatars as turn indicators ([7263828](7263828ed4))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Replace the in-game PlayerStatusBar with turn indicators integrated
directly into the nav bar avatars. This creates a cleaner, more
unified UI with less visual clutter while making turn state always
visible at the top of the screen.
Changes:
- Pass game state (currentPlayer, scores, streaks) through PageWithNav
to GameContextNav to ActivePlayersList
- Add turn indicator styling to avatars:
- Current player: larger (70px vs 56px), colored border ring with glow
- Other players: dimmed opacity (0.65)
- Floating animation on current player
- Add score badge (bottom-right) showing pairs matched
- Add streak badge (top-right) with fire emoji, color-coded by level:
- Green: 2+ streak (great)
- Orange: 3+ streak (epic)
- Purple: 5+ streak (legendary)
- Remove PlayerStatusBar from GamePhase since nav now shows all info
- Add CSS animations: avatarFloat, borderPulse, streakPulse
Benefits:
- Less visual clutter in game area
- Turn state always visible in persistent nav
- More space for the actual game
- Consistent with nav-as-control-center design
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **matching:** use UUID instead of numeric index for scores ([5036cb0](5036cb00b6))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Fix critical bug where PlayerStatusBar was looking up scores and
consecutiveMatches by numeric index (1, 2, 3...) instead of by
player UUID. This caused all scores and streaks to show as 0 in
multiplayer games.
The game state stores scores keyed by UUID strings, not numeric
indices, so we need to use player.id for the lookup.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **nav:** add pulsing indicator for offline network players ([64fb30e](64fb30e7ec))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Add smooth opacity pulsing animation to the network badge (📡) when
network players are offline. The badge smoothly fades from full
opacity to 30% opacity and back over 2 seconds, providing a clear
visual indicator of disconnection status.
When online, the badge remains at full opacity with no animation.
This subtle indicator helps users quickly identify connection issues
without being distracting.
Changes:
- Add isOnline property to NetworkPlayer interface
- Apply offlinePulse animation when isOnline is false
- Default to online if isOnline is not specified
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **nav:** combine room info and network players in single pane ([d5473ab](d5473ab66a))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Merge room info and network players into one bordered container for
better visual cohesion. This creates a clearer hierarchy with room
info + network players on the left, and the user's own players on
the right, maintaining vertical alignment.
Also remove the confusing pulsing green "online" indicator from
network players - the gradient border frame is sufficient to
distinguish them from local players.
Changes:
- Combine RoomInfo and NetworkPlayerIndicator in shared bordered pane
- Add subtle vertical divider between room info and network players
- Remove animated pulse indicator
- Simplify network player visual design
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* **nav:** remove opacity reduction from local players ([5215af8](5215af801f))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Remove the opacity: 0.6 style that was making local players appear
greyed out during network games. This was creating an inconsistent
appearance where network players looked vibrant but local players
looked faded.
The pointer-events property already prevents interaction when players
can't be modified, so the visual opacity hint was redundant and
confusing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* **nav:** improve text contrast in room info pane ([3e691cb](3e691cb06d))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Change text colors from white to dark gray for better readability
against the light background. The previous white-on-light-purple
was nearly unreadable.
- Game name: dark gray (rgba(17, 24, 39, 0.9))
- Room name: dark gray (rgba(17, 24, 39, 0.65))
- Dropdown indicator: dark gray (rgba(17, 24, 39, 0.5))
- Mode text: keeps color-coded text for visual distinction
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* **nav:** unify room dropdown with join code and game menu ([f7b83f8](f7b83f8c14))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Combine the room code tooltip and game menu dropdown into a single
unified dropdown triggered from the room info pane. This creates a
more cohesive UX with a single interaction point for both viewing
the join code and accessing game controls.
Key changes:
- Convert RoomInfo from Tooltip to DropdownMenu component
- Add game name to room pane with dropdown indicator
- Improve text contrast (white instead of purple)
- Include join code section + game menu items in one dropdown
- Make GameTitleMenu conditional (only show when not in room)
The unified dropdown provides:
- Click-to-copy join code button
- Setup, New Game, and Quit to Arcade menu items
- Clear visual hierarchy and better accessibility
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* stack game title dropdown ABOVE room pane, not inside it ([7bc815f](7bc815fd7d))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Corrects the layout to properly separate title from room details:
**Before (incorrect):**
- Game name was inside the room pane, no dropdown
**After (correct):**
┌──────────────┐
│ 🧩 Memory │ ← Title with dropdown (Setup/New Game/Quit)
│ Pairs ▼ │
└──────────────┘
┌──────────────┐
│ ⚔️ Battle │ ← Room pane (hover for join code)
│ Auto-gen │
│ Room │
└──────────────┘
[player avatars]
**Implementation:**
- GameTitleMenu always shown with dropdown functionality
- When in room: flexDirection: 'column' stacks title above room pane
- When not in room: flexDirection: 'row' shows title + mode horizontally
- RoomInfo back to 2-line: mode + room name
- 4px gap between stacked elements
Result: Title dropdown is separate and accessible, room pane shows
session details and serves as hover target for join code.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* move game name into room pane, remove player count ([eeb8d52](eeb8d52d03))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Major restructure of navigation when in a room:
**Before:**
- Separate title on left: "Memory Pairs ▼"
- Small room pill on right with mode and room name
**After:**
- Single comprehensive pane with 3-line stack:
1. 🧩 Memory Pairs (12px, white, game name)
2. ⚔️ Battle (11px, mode color, mode)
3. Auto-generated Room (10px, light purple, room name)
**Changes:**
- Removed redundant player count (visible in avatars to the right)
- Game name now at top of room pane instead of separate title
- Entire pane is hover target for join code tooltip
- When NOT in room: shows original title + mode layout
- Progressive text sizing: 12px → 11px → 10px for hierarchy
**Benefits:**
- All session info in one cohesive, hoverable pane
- No duplicate game name on screen
- Clearer visual hierarchy with game name most prominent
- More space for player avatars on right
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* add player count to stacked room info ([540f6b7](540f6b76d0))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Adds player count as third line in the combined mode/room pill:
**3-line vertical stack:**
1. Mode indicator: ⚔️ Battle (12px, bold, mode color)
2. Room name: Auto-generated Room (11px, semibold, light purple)
3. Player count: 2 players (9px, medium, lighter purple)
Styling:
- Player count uses smaller text (9px vs 11px)
- Lighter opacity (0.6 vs 0.8) for visual hierarchy
- Proper singular/plural handling
- lineHeight: 1 for tight spacing
Result: Compact info pill with all relevant room details in one place,
properly hierarchical with decreasing emphasis from mode → name → count.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* combine mode and room into single unified pane ([7a6f2ac](7a6f2ac6eb))
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Merged GameModeIndicator and RoomInfo into one cohesive pane:
**Before:**
- Two separate pills: [Battle] [Auto-generated Room]
- Only room pill was hover target for join code
**After:**
- Single unified pane with vertical stack:
```
┌─────────────────────┐
│ ⚔️ Battle │ <- hover for join code
│ Auto-generated Room │
└─────────────────────┘
```
**Implementation:**
- RoomInfo now accepts mode props (gameMode, modeColor, modeEmoji, modeLabel)
- Displays mode indicator on top, room name below
- Entire pane is tooltip trigger for join code
- Uses mode color for border and gradient background
- When not in room, shows standalone GameModeIndicator
**Benefits:**
- Single hover target (entire pane) for join code tooltip
- Clearer visual grouping of related session info
- More compact and cohesive design
- Color-coded by game mode
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* pixel-perfect alignment across all nav elements ([fa78a2c](fa78a2c001))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Got into the pixels! Fixed alignment issues throughout navigation:
**lineHeight: 1 everywhere**
- Title: added lineHeight: 1 to remove extra vertical space
- GameModeIndicator: lineHeight: 1 on container and text spans
- RoomInfo: lineHeight: 1 on pill and text
- Player avatars: lineHeight: 1 already set
- Dropdown arrow: lineHeight: 1 with flex centering
**Display fixes**
- Changed all pills from flex → inline-flex for tighter bounds
- Added display: flex, alignItems: center to title for perfect centering
- Added flexShrink: 0 to AddPlayerButton to prevent compression
**Player avatar alignment**
- Added display: flex, alignItems: center, justifyContent: center
- Ensures emoji glyphs are perfectly centered
- Removed redundant fontSize conditionals (always 56px)
**Container alignment**
- All elements now share common vertical center
- No extra space from default line-heights
- Pills are tight inline elements with no overflow
Result: Every element is pixel-perfect aligned with no extra vertical
space from line-heights, properly centered, and sharing a common baseline.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* implement Option A - balanced alignment and visual hierarchy ([6ad7170](6ad71702f9))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Major improvements to navigation alignment and proportions:
**Title:**
- Increased from 18px to 22px for better visual prominence
- Now properly anchors the left side
**Room Info (dramatically simplified):**
- Removed 🎮 emoji (redundant)
- Removed 👥 player count (redundant - visible in player avatars)
- Reduced padding: 4px 10px → 3px 8px
- Reduced border: 2px → 1px
- Reduced font size: 12px → 11px
- Lighter background: 0.2 → 0.15 opacity
- Result: Tiny subtle tag instead of dominant banner
**Spacing & Alignment:**
- Tightened gap in mode/room stack: 4px → 3px
- Reduced gap between title and stack: 16px → 12px
- Increased gap to players: 16px → 20px
- Everything vertically centered with 56px player avatars
**Visual Hierarchy (fixed):**
1. Players (largest, most prominent)
2. Title (22px, clear focal point)
3. Mode indicator (medium pill)
4. Room info (tiny subtle tag)
Result: Clean, balanced layout with proper visual weight distribution.
The title is now prominent, mode/room are compact metadata, and players
dominate the right side.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* stack mode indicator above room info ([d1aa567](d1aa567c1e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Creates cleaner vertical hierarchy in the navigation center section.
**Before:**
[Title▼] [Battle] [Room] [Players]
**After:**
[Title▼] [Battle] [Players]
[Room]
Changes:
- Mode and Room now in flexDirection: 'column' with 4px gap
- Aligned to flex-end to keep right-aligned
- Creates more vertical breathing room
- Clearer visual grouping of related info
Result: Better visual hierarchy with mode indicator prominently on top
and room details nested below.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* move game controls into title dropdown menu ([e39a031](e39a0313cb))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Replaces separate control buttons with elegant dropdown on game title,
dramatically cleaning up the navigation layout.
**New GameTitleMenu component:**
- Radix dropdown with subtle affordances (small ▼ indicator)
- Obvious but not dominating: hover background + rotating arrow
- Dark translucent menu with color-coded hover states:
- Purple for Setup ⚙️
- Blue for New Game 🎮
- Orange for Quit 🏟️ (separated by divider)
- Shows only when controls are available (!canModifyPlayers)
**Layout improvements:**
- Collapsed from 2-row left column to single row
- Now: [Title▼] [spacer] [Mode] [Room] | [Players]
- Players remain at full height, even more prominent
- Much cleaner, more space-efficient design
Result: Game controls are discoverable but hidden until needed, letting
the player avatars truly dominate the navigation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* restructure nav so player avatars truly span both rows ([e0fd793](e0fd793812))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Changed from 2x2 grid to split layout where players have nothing above/below:
**Before (2x2 grid):**
```
Row 1: [Title] [Mode + Room]
Row 2: [Controls] [Network + Players]
```
Players were in row 2, with content above them.
**After (split layout):**
```
Left column (2 rows):
- Row 1: Title | Mode + Room
- Row 2: Control buttons
Right side (spanning full height):
- Network players
- Your players
```
Now the 56px avatars are vertically centered with nothing above or below them,
truly justifying their two-row height. They span the full vertical space while
the left side contains the stacked title/controls.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* comprehensive navigation polish - prominence, contrast, cohesion ([1ba58b9](1ba58b9547))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Major improvements across all navigation components for better visual hierarchy
and user experience:
**RoomInfo**
- Redesigned with Radix tooltip for join code (hover to reveal)
- Much better contrast: purple pill with bright text (rgba(196, 181, 253, 1))
- Click-to-copy in elegant tooltip with visual feedback
- Auto-closes after successful copy with "Share this code with friends!" message
**Player Avatars (ActivePlayersList & NetworkPlayerIndicator)**
- Increased size from 20px to 56px - now span both rows for prominence
- Enhanced drop shadows (0 6px 12px) for depth
- Larger, more polished action buttons (configure/remove):
- 24-26px buttons with 3px borders and gradient backgrounds
- Better hover effects with color glows
- Network players: larger frame borders, badges, and pulse indicators
**AddPlayerButton**
- Increased from 48px to 56px to match player avatars
- Enhanced with gradient backgrounds and stronger shadows
- Better hover states with 1.08 scale and green glow
**GameContextNav Layout**
- Added minHeight: 64px to row 2 for avatar accommodation
- Network players now in distinct purple-gradient container
- Your players container gets enhanced polish even when not emphasized
- Improved spacing and gaps throughout (12px between player groups)
Result: Navigation now has clear visual hierarchy with player avatars as the
dominant focal point, excellent contrast on all text, and cohesive translucent
design language throughout.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* redesign GameControlButtons with translucent aesthetic ([5720c7d](5720c7dca3))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Unifies control buttons with the refined translucent design system used
throughout the navigation (matching GameModeIndicator and RoomInfo).
Changes:
- Replace solid gradient backgrounds with translucent color-coded pills
- Add subtle borders matching the mode indicator style (2px solid with opacity)
- Reduce padding (4px 10px) and font size (12px) for lighter visual weight
- Color-code each button: gray (Setup), blue (New Game), orange (Quit)
- Add hover effects with gradient translucent backgrounds
- Reduce gap between buttons (6px) for tighter grouping
Result: Navigation components now share a cohesive, polished design language
with consistent translucency, borders, and color patterns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
* redesign room info as compact inline badge with click-to-copy ([6b3a440](6b3a440369))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* compact room info display with join code in tooltip ([a6b1610](a6b1610993))
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* restructure GameContextNav to 2x2 grid layout ([9c9270f](9c9270f931))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Reorganizes navigation into clearer visual hierarchy:
- Row 1: Title (left) | Mode indicator + Room info (right)
- Row 2: Control buttons (left) | Network players + Your players (right)
Changes:
- Separate fullscreen mode into early return for clarity
- Remove unused state management (isTransitioning, layoutMode, containerWidth)
- Use space-between justification for horizontal alignment
- Group network players with local players (separated from control buttons)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace large card-style room display with compact single-line badge showing room name, join code, and player count. Add click-to-copy functionality for join code with visual feedback.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Replace large room info card with minimal inline badge showing just room name and player count. Move join code to a Radix UI tooltip that appears on hover, making it easy to share while keeping the nav bar compact.
Display: 🎮 Auto-generated Room (2)
Tooltip: Shows "Join Code" with code prominently displayed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-11)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
* improve arcade nav player grouping and add room join code display ([8e9980d](8e9980dc82))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Reorder navigation to group all player indicators together by moving game control buttons before network players, creating clearer visual separation. Add room join code display as a second line under room name for easier sharing.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-10)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* populate session activePlayers from room members on join ([2d00939](2d00939f1b))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
**Problem:**
When users joined rooms, the arcade session was created with empty
activePlayers array, causing the game to start in single-player mode
even though multiple users had joined the room.
**Root Cause:**
Initial session creation in `join-arcade-session` handler set
`activePlayers: []` without fetching the actual room members' players.
**Solution:**
1. **Session Creation**: When creating initial session, fetch all room
members' active players using `getRoomPlayerIds()` and populate
`activePlayers` field.
2. **Dynamic Updates**: When members join room (`join-room` event) or
toggle players (`players-updated` event), update session's
`activePlayers` dynamically using new `updateSessionActivePlayers()`
function.
3. **Protection**: Only update `activePlayers` if game is in 'setup'
phase - prevents disrupting games in progress.
**Key Changes:**
- `socket-server.ts`: Import `getRoomPlayerIds`, use it to populate
activePlayers on session creation, update session when room membership
changes
- `session-manager.ts`: Add `updateSessionActivePlayers()` function to
safely update activePlayers during setup phase
- Test fixes: Updated test files to match new schema (roomId PRIMARY KEY)
**Testing:**
- Session correctly populated with all room members' players on creation
- New members' players added to session when they join (if in setup)
- Player toggle updates session activePlayers in real-time
- Games in progress protected from activePlayers modifications
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-10)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* prevent duplicate arcade sessions per room ([4cc3de5](4cc3de5f43))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
**Root Cause:**
Database schema had `user_id` as PRIMARY KEY for `arcade_sessions`,
allowing multiple sessions for the same `roomId`. When two users joined
the same room, each created their own session, causing them to see
completely different games instead of sharing one session.
**Database Changes:**
- Changed PRIMARY KEY from `user_id` to `room_id`
- Now enforces one session per room at database level
- Updated all queries to use `room_id` as primary lookup key
**Code Changes:**
- Updated `createArcadeSession()` to check for existing room session
- Added error handling for race conditions (UNIQUE constraint failures)
- Modified `applyGameMove()`, `deleteArcadeSession()`, and `updateSessionActivity()` to use `room_id`
- Cleaned up orphaned sessions and duplicates before migration
**Migration:**
- Generated migration: `drizzle/0005_jazzy_mimic.sql`
- Cleaned up 58 orphaned sessions (NULL room_id)
- Removed duplicate sessions for same room (kept highest version)
- Migration successfully applied to dev database
**Testing Required:**
- Verify two users in same room now share the same game state
- Confirm session updates broadcast correctly to all room members
- Test that PRIMARY KEY constraint prevents duplicate creation
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-10)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* create arcade sessions on room join to enable config changes ([c29501f](c29501f666))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Fixes "No active session found" error when adjusting game settings
before starting a game in arcade rooms.
**Problem:**
- Sessions were only created on START_GAME move
- SET_CONFIG moves require an active session in setup phase
- Users couldn't adjust settings until after starting game
**Solution:**
- Create session in setup phase when user joins room (if none exists)
- Initialize with room's game config from database
- Allows SET_CONFIG moves before game starts
**Changes:**
- socket-server.ts:72-100 - Auto-create session on join-arcade-session
- RoomMemoryPairsProvider.tsx:4 - Remove unused import
- nas-deployment/docker-compose.yaml:15 - Fix DB volume mount path
**Related:**
- Also fixes database persistence by correcting volume mount from
./data:/app/data to ./data:/app/apps/web/data
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-10)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
### Bug Fixes
* clear hover state in CLEAR_MISMATCH for clean turn transitions ([43f7c92](43f7c92f6d))
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
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>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-10)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
### Bug Fixes
* clear hover state on turn changes and game transitions ([6fd425c](6fd425ce85))
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Clear playerHovers state in three scenarios to prevent stale hover avatars:
1. **Turn switching (mismatch)**: When a player misses a match and turn
switches to next player, clear the previous player's hover state so
their avatar doesn't show on the last card they hovered from their
previous turn.
2. **Starting new game**: Clear all hover state when starting fresh game
to ensure clean initial state.
3. **Returning to setup**: Clear all hover state when going back to setup
phase to prevent stale hovers from previous game sessions.
This fixes the issue where players would see their opponent's (or their
own) hover avatar stuck on a card at the start of a turn, even though
no hovering has occurred yet in the current turn.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-10)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
### Bug Fixes
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* hide hover avatar for current user's own player ([dba42b5](dba42b5925))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
Only show hover avatars for remote players (opponents), not the current
user's own player. This prevents the distracting behavior where users
would see a floating avatar following their own mouse cursor.
The filter now checks:
- playerMetadata[playerId].userId !== viewerId (remote player check)
- playerId === state.currentPlayer (only show for active turn)
This maintains the "presence" feature for opponents while keeping the
current user's view clean.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## [2.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.17.2...v2.18.0) (2025-10-10)
### Features
* add centralized player ownership utilities ([1019a48](1019a487f8))
### Bug Fixes
* correct TypeScript build configuration to prevent .js pollution in src/ ([2b7ff23](2b7ff237cc))
* exclude dist from TypeScript compilation and add missing type import ([b7f1d5a](b7f1d5a569))
* prevent database imports from being bundled into client code ([bda5bc6](bda5bc6c0e))
* remove build artifacts from source control ([29f5adc](29f5adcfbc))
* remove rootDir from tsconfig.server.json ([431e4a6](431e4a61de))
* update Dockerfile to copy dist/ instead of socket-server.js and src/ ([e56517b](e56517b129))
### Code Refactoring
* re-export ownership utilities from player-manager ([e85f800](e85f800c3e))
* use centralized ownership in RoomMemoryPairsProvider ([6c66bb2](6c66bb27b7))
* use centralized player ownership in session-manager ([a362e5e](a362e5e34a))
### Documentation
* add plan for centralizing player ownership logic ([d3ff89a](d3ff89a0ee))
### Tests
* add comprehensive integration tests for player ownership ([76a6390](76a63901c4))
- Add "dist" to tsconfig.server.json exclude to prevent TypeScript from
seeing compiled .js files as input files (was causing TS5055 errors)
- Fix schema path glob: "src/db/schema.ts" -> "src/db/schema/**/*.ts"
to properly include all schema files in the directory
- Add missing PlayerOwnershipMap type import in player-ownership.ts
to fix TS2304 compilation error
These fixes resolve the dev server compilation issues and ensure clean
builds without dist/ pollution.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-10 12:39:54 -05:00
570 changed files with 100045 additions and 84434 deletions
@@ -16,6 +16,7 @@ Following `docs/terminology-user-player-room.md`:
- **PLAYER ROSTER** - All PLAYERS belonging to a USER (can have many)
- **ACTIVE PLAYERS** - PLAYERS where `isActive = true` - these are the ones that actually participate in games
- **ROOM MEMBER** - A USER's participation in a multiplayer room (tracked in `room_members` table)
- **SPECTATOR** - A room member who watches another player's game without participating (see Spectator Mode section)
**Important:** A USER can have many PLAYERS in their roster, but only the ACTIVE PLAYERS (where `isActive = true`) participate in games. This enables "hot-potato" style local multiplayer where multiple people share the same device. This is LOCAL play (not networked), even though multiple PLAYERS participate.
@@ -27,25 +28,46 @@ In arcade sessions:
## Critical Architectural Requirements
### 1. Mode Isolation (MUST ENFORCE)
### 1. Game Synchronization Modes
**Local Play** (`/arcade/[game-name]`)
The arcade system supports three synchronization patterns:
#### Local Play (No Network Sync)
**Route**: Custom route or dedicated local page
**Use Case**: Practice, offline play, or games that should never be visible to others
- MUST NOT sync game state across the network
- MUST NOT use room data, even if the USER is currently a member of an active room
- MUST create isolated, per-USER game sessions
- MUST pass `roomId: undefined` to `useArcadeSession`
- Game state lives only in the current browser tab/session
- CAN have multiple ACTIVE PLAYERS from the same USER (local multiplayer / hot-potato)
- State is NOT shared across the network, only within the browser session
**Room-Based Play** (`/arcade/room`)
#### Room-Based with Spectator Mode (RECOMMENDED PATTERN)
**Route**: `/arcade/room` (or use room context anywhere)
**Use Case**: Most arcade games - enables spectating even for single-player games
-MUST sync game state across all room members via network
-MUST use the USER's current active room
-MUST coordinate moves via server WebSocket
-SYNCS game state across all room members via network
-Uses the USER's current active room (`roomId: roomData?.id`)
-Coordinates moves via server WebSocket
- Game state is shared across all ACTIVE PLAYERS from all USERS in the room
-When a PLAYER makes a move, all room members see it in real-time
-CAN ALSO have multiple ACTIVE PLAYERS per USER (networked + local multiplayer combined)
-**Non-playing room members become SPECTATORS** (see Spectator Mode section)
-When a PLAYER makes a move, all room members see it in real-time (players + spectators)
- CAN have multiple ACTIVE PLAYERS per USER (networked + local multiplayer combined)
**✅ This is the PREFERRED pattern** - even for single-player games like Card Sorting, because:
- Enables spectator mode automatically
- Creates social experience ("watch me solve this!")
- No extra code needed
- Works seamlessly with multiplayer games too
#### Pure Multiplayer (Room-Only)
**Route**: `/arcade/room` with validation
**Use Case**: Games that REQUIRE multiple players (e.g., competitive battles)
- Same as Room-Based with Spectator Mode
- Plus: Validates minimum player count before starting
- Plus: May prevent game start if `activePlayers.length < minPlayers`
### 2. Room ID Usage Rules
@@ -258,6 +280,327 @@ sendMove({
- Mode: Room-based play (roomId: "room_xyz")
- 5 total active PLAYERS across 2 devices, all synced over network
5.**Single-player game with spectators (Card Sorting):**
- USER A: "guest_abc"
- Active PLAYERS: ["player_001"]
- Playing Card Sorting Challenge
- USER B: "guest_def"
- Active PLAYERS: [] (none selected)
- Spectating USER A's game
- USER C: "guest_ghi"
- Active PLAYERS: ["player_005"]
- Spectating USER A's game (could play after USER A finishes)
- Mode: Room-based play (roomId: "room_xyz")
- All room members see USER A's card placements in real-time
- Spectators cannot interact with the game state
## Spectator Mode
### Overview
Spectator mode is automatically enabled when using room-based sync (`roomId: roomData?.id`). Any room member who is not actively playing becomes a spectator and can watch the game in real-time.
**Key Benefits**:
- Creates social/collaborative experience even for single-player games
- "Watch me solve this!" engagement
- Learning by observation
- Cheering/coaching opportunity
- No extra implementation needed
### How It Works
1.**Automatic Role Assignment**:
- Room members with active PLAYERs in the game → **Players**
- Room members without active PLAYERs in the game → **Spectators**
2.**State Synchronization**:
- All game state updates broadcast to entire room via `game:${roomId}` socket room
- Spectators receive same state updates as players
- Spectators see game in real-time as it happens
3.**Interaction Control**:
- Players can make moves (send move actions)
- Spectators can only observe (no move actions permitted)
- Server validates PLAYER ownership before accepting moves
**Update**: 2025-10-18 - Spectator mode recognized as intentional feature
## Executive Summary
The Card Sorting Challenge game was audited against the Arcade Architecture requirements documented in `.claude/ARCADE_ARCHITECTURE.md`. The initial audit identified the room-based sync pattern as a potential issue, but this was later recognized as an **intentional spectator mode feature**.
**Initial Assessment**: The provider **ALWAYS** calls `useRoomData()` and **ALWAYS** passes `roomId: roomData?.id` to `useArcadeSession`. This was initially flagged as a mode isolation violation.
**Issue:** TypeScript reports that `AbacusReact`, `useAbacusConfig`, and other exports do not exist from the `@soroban/abacus-react` package, even though:
- The package builds successfully
- The exports are correctly defined in `dist/index.d.ts`
- The imports work at runtime
- 20+ files across the codebase use these same imports without issue
**Impact:** `npm run type-check` will report errors for any files importing from `@soroban/abacus-react`.
**Workaround:** This is a known pre-existing issue. When running pre-commit checks, TypeScript errors related to `@soroban/abacus-react` imports can be ignored. Focus on:
- New TypeScript errors in your changed files (excluding @soroban/abacus-react imports)
- Format checks
- Lint checks
**Status:** Known issue, does not block development or deployment.
## Game Settings Persistence
When working on arcade room game settings, refer to:
**Status**: ✅ RESOLVED - State Adapter Solution Implemented
---
## What Went Wrong
I used the **correct modular game pattern** (useArcadeSession) but **threw away all the existing beautiful UI components** and created a simple quiz UI from scratch!
### The Correct Pattern (Used by ALL Modular Games)
The Complement Race Migration Plan Phase 4 mentioned `useSocketSync` and preserving the reducer, but that was **aspirational/theoretical**. In reality:
-`useSocketSync` doesn't exist in the codebase
- ALL modular games use `useArcadeSession`
- Matching game was migrated FROM reducer TO useArcadeSession
- The pattern is consistent across all games
**The migration plan was correct about preserving the UI, but wrong about the provider pattern.**
---
## What I Actually Did (Wrong)
✅ **CORRECT**:
- Created `Validator.ts` (~700 lines of server-side game logic)
- Created `types.ts` with proper TypeScript types
- Registered in `validators.ts` and `game-registry.ts`
- Fixed TypeScript issues (index signatures)
- Fixed test files (emoji fields)
- Disabled debug logging
❌ **COMPLETELY WRONG**:
- Created `Provider.tsx` using Pattern A (useArcadeSession)
- Threw away existing reducer with 30+ action types
- ⭐ **Proper integration** - Follows all patterns correctly
- ⭐ **Type safety** - Zero TypeScript errors
**Weaknesses**:
- ❌ **Missing multiplayer visuals** - Can't see other players
- ❌ **No AI opponents** - Can't test solo
- ❌ **Minimal lobby** - Auto-starts instead of ready check
- ❌ **No tests** - Untested code
### Is Multiplayer Working?
**Backend**: ✅ YES - All server logic functional
**Frontend**: ❌ NO - UI shows single-player only
**Can you play multiplayer?** Technically yes, but you won't see other players on screen. It's like racing blindfolded - your opponent's moves are tracked, but you can't see them.
### What Would Make This Complete?
**Minimum Viable Multiplayer** (8-10 hours of work):
The architecture is sound, the hard parts (validator, state management) are done correctly. What remains is "just" UI work to make multiplayer visible to players. The fact that we chose the state adapter pattern means this UI work won't require changing any existing game logic - just rendering multiple players instead of one.
**Verdict**: **Ship-ready for single-player, needs visual work for multiplayer** 🚀
**Mission:** Fill the gap in the USA school system by providing a complete, self-directed abacus curriculum that trains students from beginner to mastery using the Japanese kyu/dan ranking system.
**Target Users:**
- Primary: Elementary school students (ages 6-12)
- Secondary: Middle school students and adult learners
- Teachers/Parents: Dashboard for monitoring progress
**Core Experience Principles:**
1.**Integrated Learning Loop:** Tutorial → Practice → Play → Assessment → Progress
2.**Self-Directed:** Simple enough for kids to fire up and start learning independently
3.**Gamified Progression:** Games reinforce lessons, feel like play but teach skills
4.**Physical + Virtual:** Support both physical abacus and AbacusReact component
5.**Mastery-Based:** Students advance through clear skill levels with certification
---
## Current State Assessment
### ✅ What We Have (Well-Built)
**1. Interactive Abacus Component (AbacusReact)**
- Highly polished, production-ready
- Excellent pedagogical features (bead highlighting, direction arrows, tooltips)
- Multiple color schemes and accessibility options
- game-skill-mappings.ts (which games teach which skills)
```
---
## Next Immediate Steps
### Week 1: Database Schema Design
- [ ] Design complete schema for Phase 1
- [ ] Write migration scripts
- [ ] Document schema decisions
- [ ] Review with stakeholders
### Week 2-3: Content Planning
- [ ] Write detailed 10 Kyu curriculum outline
- [ ] Write detailed 9 Kyu curriculum outline
- [ ] Define all skills for 10-9 Kyu
- [ ] Map skills to existing games
### Week 4-5: Tutorial Content Creation
- [ ] Write 5 tutorials for 10 Kyu
- [ ] Write 3 tutorials for 9 Kyu
- [ ] Create interactive steps with highlighting
- [ ] Add kid-friendly explanations
### Week 6-7: Assessment System Build
- [ ] Build assessment component UI
- [ ] Implement grading engine
- [ ] Create placement test (20 problems)
- [ ] Create 10 Kyu certification test (30 problems)
- [ ] Create 9 Kyu certification test (40 problems)
### Week 8-9: Practice System
- [ ] Build practice session component
- [ ] Implement problem generator for each skill
- [ ] Add immediate feedback system
- [ ] Create hint system
### Week 10-11: Student Dashboard
- [ ] Design dashboard UI (kid-friendly)
- [ ] Build progress visualization
- [ ] Implement "next recommended activity" logic
- [ ] Add achievement display
### Week 12: Integration & Testing
- [ ] Connect all pieces: tutorials → practice → games → assessment
- [ ] Test complete user flow
- [ ] User testing with kids
- [ ] Iterate based on feedback
---
## Questions to Resolve
1.**Certification Validity:** Should kyu certifications expire? (Traditional abacus schools: no expiration)
2.**Retake Policy:** How many times can student retake certification test? (Suggest: unlimited, but must wait 24 hours)
3.**Grading Standards:** Strict adherence to Japanese standards or adjust for USA context?
4.**Physical Abacus:** Should we require physical abacus for certain levels? (Recommend: optional but encouraged)
5.**Age Restrictions:** Any minimum age? (Suggest: 6+ with parent/teacher supervision)
6.**Teacher Accounts:** Free for teachers? (Recommend: yes, free for teachers)
7.**Pricing Model:** Free tier + premium? School licensing? (TBD)
8.**Content Licensing:** Will curriculum be open source or proprietary? (Recommend: proprietary but allow teacher customization)
9.**Accessibility:** WCAG compliance level? (Recommend: AA minimum)
10.**Data Privacy:** COPPA compliance for users under 13? (Required: yes, must be compliant)
---
## Conclusion
This roadmap provides a clear path from current state (scattered features) to target state (complete educational platform). The phased approach allows incremental delivery while maintaining focus on core learning experience.
**Estimated Timeline:**
- Phase 1 (10-9 Kyu MVP): 3 months
- Phase 2 (8-5 Kyu): 5 months
- Phase 3 (4-1 Kyu): 6 months
- Phase 4 (Dan levels): 3 months
- Phase 5 (Ecosystem): Ongoing
**Total to Complete Platform:** ~17 months for core curriculum, then continuous improvement
**Priority:** Start with Phase 1 to prove the concept, get student feedback, and validate the learning loop before building the full system.
Game settings in room mode persist across game switches using a normalized database schema. Settings for each game are stored in a dedicated `room_game_configs` table with one row per game per room.
## Database Schema
Settings are stored in the `room_game_configs` table:
**Migration:** Create `room_game_configs` table (equivalent to drizzle migration 0011)
## Context
This migration was applied manually using sqlite3 CLI instead of through drizzle-kit's migration system, because the interactive prompt from `drizzle-kit push` cannot be automated in the deployment pipeline.
1.**Use `as const`**: TypeScript needs the array marked as `const` so the token strings are treated as literal types, not generic strings. The `token()` function expects the `Token` literal type.
2.**Use inline styles**: When using `token()`, apply colors via the `style` prop, not through the `css()` function:
The tutorial system is a sophisticated interactive learning platform for teaching soroban abacus concepts. It features step-by-step guidance, bead highlighting, pedagogical decomposition, and progress tracking.
Successfully implemented **all 3 critical architectural improvements** identified in the audit. The modular game system is now **truly modular** - new games can be added without touching database schemas, API endpoints, helper switch statements, or manual type definitions.
**Phase 1**: Eliminated database schema coupling
**Phase 2**: Moved config validation to game definitions
**Phase 3**: Implemented type inference from game definitions
**Grade**: **A** (Up from B- after improvements)
---
## What Was Fixed
### 1. ✅ Database Schema Coupling (CRITICAL)
**Problem**: Schemas used hardcoded enums, requiring migration for each new game.
**Solution**: Accept any string, validate at runtime against validator registry.
// Auto-derived RoomGameConfig (was 5 manual entries, now automatic!)
exporttypeRoomGameConfig={
[KinkeyofGameConfigByName]?:GameConfigByName[K]
}
```
**Files Modified**: 2 files
**Commits**:
-`271b8ec3 - refactor(arcade): implement Phase 3 - infer config types from game definitions`
-`4c15c13f - docs(arcade): update README with Phase 3 type inference architecture`
**Note**: Default config constants (e.g., `DEFAULT_MATH_SPRINT_CONFIG`) still manually defined. This small duplication is necessary for server-side code that can't import full game definitions with React components.
---
## Future Work (Optional)
### Phase 4: Extract Config-Only Exports
**Optional improvement**: Create separate `config.ts` files in each game directory that export just config and validation (no React dependencies). This would allow importing default configs directly without duplication.
---
## Testing
### Manual Testing
- ✅ Math Sprint works end-to-end
- ✅ Number Guesser works end-to-end
- ✅ Room settings API accepts math-sprint
- ✅ Config validation rejects invalid configs
- ✅ TypeScript compilation succeeds
### Test Coverage Needed
- [ ] Unit tests for `isValidGameName()`
- [ ] Unit tests for game `validateConfig()` functions
- [ ] Integration test: Add new game without touching infrastructure
- [ ] E2E test: Verify runtime validation works
---
## Lessons Learned
### What Worked Well
1.**Incremental Approach** - Fixed one issue at a time
2.**Backward Compatibility** - Legacy games still work
3.**Runtime Validation** - Flexible and extensible
4.**Clear Commit Messages** - Easy to track changes
- Add comprehensive test suite for validation and type inference
- Migrate legacy games (matching, memory-quiz) to new system
The architecture is now **production-ready** and can scale to dozens of games without becoming unmaintainable. Each game is truly self-contained, with all its logic, validation, and types defined in one place.
---
## Quick Reference: Adding a New Game
1. Create game directory with required files (types, Validator, Provider, components, index)
2. Add validation function (`validateConfig`) in index.ts and pass to `defineGame()`
3. Register validator in `validators.ts` (1 line)
4. Register game in `game-registry.ts` (1 line)
5. Add type inference to `game-configs.ts`:
```typescript
import type { myGame } from '@/arcade-games/my-game'
export type MyGameConfig = InferGameConfig<typeof myGame>
```
6. Add to `GameConfigByName` (1 line - type is auto-inferred!)
7. Add defaults to `game-configs.ts` (3-5 lines)
**That's it!** No database schemas, API endpoints, helper switch statements, or manual interface definitions.
**Total**: 3 files to update, ~20 lines of boilerplate
**Context**: After implementing Number Guesser (turn-based) and starting Math Sprint (free-for-all)
**Goal**: Assess if the system is truly modular or if there's too much boilerplate
---
## Executive Summary
**Status**: ⚠️ **Good Foundation, But Boilerplate Issues**
The unified validator registry successfully solved the dual registration problem. However, implementing a second game revealed **significant boilerplate** and **database schema coupling** that violate the modular architecture goals.
**Grade**: **B-** (Down from B+ after implementation testing)
The modular game system **now meets its stated intentions** after implementing the unified validator registry. The critical dual registration issue has been resolved.
**Original Issue**: Client-side implementation (SDK, registry, game definitions) was well-designed, but server-side validation used a hard-coded legacy system, breaking the core premise of modularity.
**Resolution**: Created unified isomorphic validator registry (`src/lib/arcade/validators.ts`) that serves both client and server needs, with auto-derived GameName type.
**Verdict**: ✅ **Production Ready** - System is now truly modular with single registration point
---
## Intention vs. Reality
### Stated Intentions
> "A modular, plugin-based architecture for building multiplayer arcade games"
>
> **Goals:**
> 1. **Modularity**: Each game is self-contained and independently deployable
> 2. Games register themselves with a central registry
> 3. No need to modify core infrastructure when adding games
### Current Reality
✅ **Client-Side**: Fully modular, games use SDK and register themselves
8. 🟢 **Plugin system** - True drop-in games with discovery
9. 🟢 **Deprecate old validation system** - Once all games migrated
---
## Conclusion
The modular game system has a **solid foundation** but is **not truly modular** due to server-side technical debt. The client-side implementation is excellent, but the server still uses a legacy hard-coded validation system.
**Status**: Needs significant refactoring before claiming "modular architecture"
**Path Forward**: Implement unified validator registry (Solution 1), then incrementally migrate old games.
**Risk**: If we add more games before fixing this, technical debt will compound.
---
*This audit was conducted by reviewing:*
-`src/lib/arcade/game-registry.ts`
-`src/lib/arcade/validation/index.ts`
-`src/lib/arcade/session-manager.ts`
-`src/socket-server.ts`
-`src/lib/arcade/game-sdk/`
-`src/arcade-games/number-guesser/`
- Documentation in `docs/` and `src/arcade-games/README.md`
# Matching Pairs Battle - Migration to Modular Game System
**Status**: Planning Phase
**Target Version**: v4.2.0
**Created**: 2025-01-16
**Game Name**: `matching`
---
## Executive Summary
This document outlines the migration plan for **Matching Pairs Battle** (aka Memory Pairs Challenge) from the legacy dual-location architecture to the modern modular game system using the Game SDK.
**Key Complexity Factors**:
- **Dual Location**: Game exists in BOTH `/src/app/arcade/matching/` AND `/src/app/games/matching/`
- **Partial Migration**: RoomMemoryPairsProvider already uses `useArcadeSession` but not in modular format
- **Turn-Based Multiplayer**: More complex than memory-quiz (requires turn validation, player ownership)
└── MemoryQuizGameValidator.ts # Server validator (✅ exists!)
```
### Important Notes
**⚠️ Local Mode Deprecated**: This migration only supports room mode. All games must be played in a room (even solo play is a single-player room). No local/offline mode code should be included.
returnNextResponse.json({error:'Failed to download asset'},{status: 500})
}
}
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.