Compare commits

..

15 Commits

Author SHA1 Message Date
semantic-release-bot
7418adb959 chore(abacus-react): release v2.5.0 [skip ci]
# [2.5.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.4.0...abacus-react-v2.5.0) (2025-11-03)

### Features

* **abacus-react:** add core utility functions for state management ([e65541c](e65541c100))
* **abacus-react:** add layout and educational props ([35bbcec](35bbcecb9e))
* **abacus-react:** add pre-defined theme presets ([cf1f950](cf1f950c7c))
* **abacus-react:** add React hooks for abacus calculations ([de038d2](de038d2afc))
* **abacus-react:** export new utilities, hooks, and themes ([ce4e44d](ce4e44d630))
2025-11-03 23:18:22 +00:00
Thomas Hallock
7228bbc2eb refactor(web): import utility functions from abacus-react
Re-export core utility functions from @soroban/abacus-react instead of
duplicating implementations:

- beadDiff.ts: Re-exports calculateBeadDiff, calculateBeadDiffFromValues,
  areStatesEqual, etc. Keeps app-specific calculateMultiStepBeadDiffs
  and validateBeadDiff.

- abacusInstructionGenerator.ts: Re-exports numberToAbacusState,
  calculateBeadChanges, and related types. Keeps app-specific
  tutorial generation logic.

- TutorialPlayer.tsx: Import calculateBeadDiffFromValues from abacus-react

- TutorialEditor.tsx: Import calculateBeadDiffFromValues from abacus-react

Eliminates ~200 lines of duplicate utility function implementations.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
ff1d60a233 refactor(web): use compact prop for inline mini-abacus
Replace manual customStyles with compact={true} in AbacusTarget.tsx.

Before: 7 props + customStyles override
After: compact={true} automatically handles frame visibility

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
9f7f001d74 refactor(web): use ABACUS_THEMES instead of manual style definitions
Replace manual theme style definitions with ABACUS_THEMES presets:
- MyAbacus.tsx: Use ABACUS_THEMES.light and ABACUS_THEMES.trophy
- HeroAbacus.tsx: Use ABACUS_THEMES.light
- LevelSliderDisplay.tsx: Use ABACUS_THEMES.dark

Eliminates ~60 lines of duplicate theme style code.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
35d8734a3a docs(abacus-react): update documentation for new features
Update README.md with:
- Theme presets usage examples
- Compact mode examples
- Column highlighting and labels
- New hooks (useAbacusDiff, useAbacusState)
- Utility functions documentation
- Updated TypeScript imports

Add ENHANCEMENT_PLAN.md:
- Documents the analysis of apps/web customizations
- Implementation roadmap with completion status
- Impact metrics

Add INTEGRATION_SUMMARY.md:
- Summary of integrated features
- Code deduplication metrics (~260-300 lines eliminated)
- Files modified in both packages

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
6a1cec06a7 docs(abacus-react): add Storybook stories for new features
Add comprehensive Storybook stories demonstrating:
- All 6 theme presets (light, dark, trophy, translucent, solid, traditional)
- Compact mode for inline displays
- Frame visibility control
- Column highlighting and labels
- useAbacusDiff and useAbacusState hooks
- Utility functions
- Combined feature showcases

Stories are organized under "AbacusReact/Themes & Utilities".

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
ce4e44d630 feat(abacus-react): export new utilities, hooks, and themes
Update index.ts to export:
- ABACUS_THEMES and AbacusThemeName
- Utility functions (numberToAbacusState, calculateBeadDiff, etc.)
- React hooks (useAbacusDiff, useAbacusState)
- New types (BeadState, AbacusState, BeadDiffResult, etc.)

All new features are now available to consumers of the package.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
35bbcecb9e feat(abacus-react): add layout and educational props
Add new props to AbacusConfig:
- frameVisible: Show/hide column posts and reckoning bar
- compact: Compact layout for inline displays (implies frameVisible=false)
- highlightColumns: Highlight specific columns by index
- columnLabels: Optional educational labels for columns

These props simplify common use cases:
- Inline mini-abacus displays now use just compact={true}
- Column highlighting for tutorials without custom overlays
- Frame visibility control for different contexts

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
cf1f950c7c feat(abacus-react): add pre-defined theme presets
Add ABACUS_THEMES with 6 pre-defined themes:
- light: Solid white frame (light backgrounds)
- dark: Translucent white with glow (dark backgrounds)
- trophy: Golden frame (achievements/rewards)
- translucent: Nearly invisible (inline/minimal UI)
- solid: Black frame (high contrast/educational)
- traditional: Brown wooden (traditional soroban aesthetic)

Eliminates ~60 lines of duplicate theme style definitions in apps/web.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
de038d2afc feat(abacus-react): add React hooks for abacus calculations
Add AbacusHooks.ts with memoized hooks:
- useAbacusDiff: Calculate bead differences for tutorials
- useAbacusState: Convert numbers to abacus state

These hooks provide performant, memoized access to core utility
functions for use in React components.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
Thomas Hallock
e65541c100 feat(abacus-react): add core utility functions for state management
Add AbacusUtils.ts with comprehensive utility functions:
- numberToAbacusState: Convert numbers to bead positions
- abacusStateToNumber: Convert bead positions to numbers
- calculateBeadChanges: Find bead differences between states
- calculateBeadDiff: Full diff with order and directions
- calculateBeadDiffFromValues: Convenience wrapper
- validateAbacusValue: Validate number ranges
- areStatesEqual: Compare abacus states

These utilities eliminate ~200 lines of duplicate code in apps/web.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:17:17 -06:00
semantic-release-bot
f4ec0689ff chore(release): 4.68.0 [skip ci]
## [4.68.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.67.1...v4.68.0) (2025-11-03)

### Features

* **abacus:** add nativeAbacusNumbers setting to schema and UI ([79f7347](79f7347d48))
* add 3D printing support for abacus models ([dafdfdd](dafdfdd233))
* add comprehensive metadata, SEO, and make AbacusReact SSR-compatible ([0922ea1](0922ea10b7))
* add comprehensive Storybook coverage and migration guide ([7a4a37e](7a4a37ec6d))
* add game preview system with mock arcade environment ([25880cc](25880cc7e4))
* add per-player stats tracking system ([613301c](613301cd13))
* add Strategy & Tactics section to Rithmomachia guide ([81ead65](81ead65680))
* add unified trophy abacus with hero mode integration ([6620418](6620418a70))
* **arcade:** add ability to deactivate remote players without kicking user ([3628426](3628426a56))
* **arcade:** add native abacus numbers support to pressure gauge ([1d525c7](1d525c7b53))
* **arcade:** add Rithmomachia (Battle of Numbers) game ([2fc0a05](2fc0a05f7f))
* **arcade:** add yjs-demo collaborative game and Yjs persistence layer ([d568955](d568955d6a))
* **arcade:** auto-create room when user has none ([ff88c3a](ff88c3a1b8))
* **card-sorting:** add activity feed notifications for collaborative mode ([1461414](1461414ef4))
* **card-sorting:** add auto-submit countdown for perfect sequences ([780a716](780a7161bc))
* **card-sorting:** add bezier curves to connecting arrows ([4d8e873](4d8e873358))
* **card-sorting:** add CardPosition type and position syncing ([656f5a7](656f5a7838))
* **card-sorting:** add collapsible stats sidebar for spectators ([6527c26](6527c26a81))
* **card-sorting:** add game mode selector UI to setup phase ([d25b888](d25b888ffb))
* **card-sorting:** add GameMode type system for multiplayer support ([fd76533](fd765335ef))
* **card-sorting:** add green border to correctly positioned cards ([16fca86](16fca86b76)), closes [#22c55](https://github.com/antialias/soroban-abacus-flashcards/issues/22c55)
* **card-sorting:** add player emoji indicators on moving cards ([3a82099](3a82099757))
* **card-sorting:** add react-spring animations for real-time sync ([c367e0c](c367e0ceec))
* **card-sorting:** add smooth transition to drop shadow ([b0b93d0](b0b93d0175))
* **card-sorting:** add spectator mode UI enhancements ([ee7345d](ee7345d641)), closes [#6366f1](https://github.com/antialias/soroban-abacus-flashcards/issues/6366f1) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add team scoring UI for collaborative mode ([ed6f177](ed6f177914)), closes [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add updateCardPositions action to Provider ([f6ed4a2](f6ed4a27a2))
* **card-sorting:** auto-arrange prefix/suffix cards in corners ([4ba7f24](4ba7f24717))
* **card-sorting:** fade correctly positioned cards to 50% opacity ([7028cfc](7028cfc511))
* **card-sorting:** gentler spring animation for locked cards ([47189cb](47189cb6e7))
* **card-sorting:** implement continuous bezier curve paths ([2d93024](2d9302410f))
* **card-sorting:** improve card distribution for natural scattered look ([0b0503f](0b0503f035))
* **card-sorting:** make player emoji fill entire card background ([2e7a02c](2e7a02c9e4))
* **card-sorting:** optimize results screen for mobile ([d188789](d188789069))
* **card-sorting:** redesign setup screen with modern UI ([73cf967](73cf967492))
* **card-sorting:** scale correctly positioned cards to 50% ([222dc55](222dc555fa))
* **card-sorting:** shrink/fade cards in correct suffix as well ([8f6feec](8f6feec4f2))
* **card-sorting:** smooth spring transition from game table to results grid ([c5f39d5](c5f39d51eb))
* **card-sorting:** wrap prefix/suffix cards to multiple rows ([e3184dd](e3184dd0d4))
* complete 3D enhancement integration for all three proposals ([5ac55cc](5ac55cc149))
* **create-room:** replace hardcoded game grid with dynamic Radix Select dropdown ([83d0ba2](83d0ba26f5))
* dynamic day-of-month favicon using subprocess pattern ([4d0795a](4d0795a9df))
* dynamically crop favicon to active beads for maximum size ([5670322](567032296a))
* enable 3D enhancement on hero/open MyAbacus modes ([37e330f](37e330f26e))
* **games:** add autoplay and improve carousel layout ([9f51edf](9f51edfaa9))
* **games:** add horizontal scroll support to carousels ([a224abb](a224abb6f6))
* **games:** add rotating games hero carousel ([24231e6](24231e6b2e))
* **i18n:** add dynamic locale switching without page reload ([fe9bfea](fe9bfeabf9))
* **i18n:** add global language selector to navigation ([0506360](0506360117))
* **i18n:** add homepage translations for all supported languages ([8c9d35a](8c9d35a3b4))
* **i18n:** add Old High German (goh) language support ([b334a15](b334a15255))
* **i18n:** complete Old High German translations for all locales ([0b06a1c](0b06a1ce00))
* **i18n:** internationalize games page and tutorial content ([4253964](4253964af1))
* **i18n:** internationalize homepage with English translations ([40cff14](40cff143c7))
* **i18n:** migrate from react-i18next to next-intl ([9016b76](9016b76024))
* **i18n:** update games page hero section copy ([6333c60](6333c60352))
* install embla-carousel-autoplay for games carousel ([946e5d1](946e5d1910))
* install embla-carousel-react for player profile carousel ([642ae95](642ae95738))
* internationalize guide page with 6 languages ([e9c320b](e9c320bb10))
* internationalize tutorial player ([26d41cf](26d41cfd05))
* optimize card sorting for mobile displays ([b443ee9](b443ee9cdc))
* Redesign Rithmomachia setup page with dramatic medieval theme ([6ae4d13](6ae4d13dc7))
* **rithmomachia:** add 80% opacity to guide modal when not hovered ([4a78485](4a78485d2e))
* **rithmomachia:** add CaptureContext for capture dialog state management ([d7eb957](d7eb957a8d))
* **rithmomachia:** add ghost panel preview for guide docking ([c0d6526](c0d6526d30))
* **rithmomachia:** add guide docking with resizable panels ([f457f1a](f457f1a1c2))
* **rithmomachia:** add helper piece selection for mathematical captures ([cae3359](cae3359587))
* **rithmomachia:** add helpful error messages for failed captures ([b172440](b172440a41))
* **rithmomachia:** add initial board visual to guide Overview section ([d42bcff](d42bcff0d9))
* **rithmomachia:** Add interactive playing guide modal ([3121d82](3121d8240a))
* **rithmomachia:** add number bond visualization and helper placeholders ([82d8913](82d89131f0))
* **rithmomachia:** add ratio capture example to guide ([9150b0c](9150b0c678))
* **rithmomachia:** add standalone guide page route ([3fcc79f](3fcc79fe9e))
* **rithmomachia:** add useBoardLayout hook for centralized layout calculations ([27f1c98](27f1c989d5))
* **rithmomachia:** add usePieceSelection hook for selection state management ([275f401](275f401e3c))
* **rithmomachia:** add visual board examples to Capture section ([74bc3c0](74bc3c0dcf))
* **rithmomachia:** add visual board examples to Harmony section ([1d5f01c](1d5f01c966))
* **rithmomachia:** add visual winning example to Victory section ([b7fac78](b7fac78829))
* **rithmomachia:** auto-size tab labels with react-textfit ([9fd5406](9fd54067ce))
* **rithmomachia:** cycle through valid helpers with dynamic number tooltips ([4829e41](4829e41ea1))
* **rithmomachia:** enhance capture relation UI with smooth animations ([0a30801](0a308016e9))
* **rithmomachia:** enhance Harmony section with comprehensive content ([f555856](f5558563ea))
* **rithmomachia:** enhance Pieces section with visual examples and pyramid details ([55aff82](55aff829f4))
* **rithmomachia:** enhance Pyramid section with comprehensive details ([9fde1ef](9fde1ef9e7))
* **rithmomachia:** guide defaults to docked right on open ([11f674d](11f674d542))
* **rithmomachia:** improve guide pieces section layout ([a270bfc](a270bfc0cc))
* **rithmomachia:** improve guide UX and add persistence ([b314740](b314740697))
* **rithmomachia:** improve roster status notice UX ([e27df45](e27df45256))
* **rithmomachia:** integrate roster warning into game nav ([8a11594](8a11594203))
* **rithmomachia:** make guide modal ultra-responsive down to 150px width ([0474197](04741971b2))
* **rithmomachia:** recreate original guide modal header layout ([2489695](24896957d0))
* **rithmomachia:** show capture error on hover instead of click ([339b678](339b6780f6))
* **rithmomachia:** show pyramid face numbers on hover instead of selection ([b0c4523](b0c4523c0b))
* **rithmomachia:** show pyramid face numbers when selected ([5c186f3](5c186f3947))
* **rithmomachia:** show pyramid face numbers when selected with subtle animation ([5c2ddbe](5c2ddbef05))
* **rithmomachia:** show real preview layout when dragging guide to dock ([17d2460](17d2460a87))
* **rithmomachia:** simplify guide language for clarity ([85cb630](85cb630add))
* **rithmomachia:** skip helper selection UI and auto-select first valid helper ([be2a00e](be2a00e8b3))
* **rithmomachia:** Update harmony system to classical three-piece proportions ([08c9762](08c97620f5))
* **rithmomachia:** Update to traditional board setup with 25 pieces per side ([0769eaa](0769eaaa1d))
* **rithmomachia:** use actual piece SVGs in number bond with 2.5s rotation animation ([976a7de](976a7de949))
* **room-share:** add QR code button for easy mobile joining ([349290a](349290ac6a))
* show rithmomachia turn in nav ([7c89bfe](7c89bfef9c))
* switch to royal color theme with transparent background ([944ad65](944ad6574e)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#f59e0](https://github.com/antialias/soroban-abacus-flashcards/issues/f59e0) [#a855f7](https://github.com/antialias/soroban-abacus-flashcards/issues/a855f7) [#7e22](https://github.com/antialias/soroban-abacus-flashcards/issues/7e22)

### Bug Fixes

* adjust hero abacus position to avoid covering subtitle ([f03d341](f03d341314))
* **arcade:** add automatic retry for version conflict rejections ([fbcde25](fbcde2505f))
* **arcade:** allow deactivating players from users who left the room ([7c1c2d7](7c1c2d7beb))
* **arcade:** implement optimistic locking in session manager ([71fd66d](71fd66d96a))
* board rotation now properly fills height in portrait mode ([b5a96ea](b5a96eaeb1))
* **card-sorting:** add border radius to outer card container ([a922eba](a922eba73c))
* **card-sorting:** add debug logging for spring animations ([d42947e](d42947eb8d))
* **card-sorting:** add missing gameMode support after hard reset ([a832325](a832325deb))
* **card-sorting:** add missing useMemo import ([949d76d](949d76d844))
* **card-sorting:** add overflow hidden to clip rounded corners ([84c66fe](84c66feec6))
* **card-sorting:** adjust connecting paths for scaled cards ([829c741](829c741e55))
* **card-sorting:** adjust game board for spectator panels ([fc5cf12](fc5cf1216f))
* **card-sorting:** adjust viewport dimensions for spectator panels ([4dce16c](4dce16cca4))
* **card-sorting:** animate cards from game board to results grid ([17d45fe](17d45fe88c))
* **card-sorting:** correct suffix card detection in auto-arrange ([d02ab59](d02ab5922c))
* **card-sorting:** enable card scaling for spectators ([6b095c3](6b095c3383))
* **card-sorting:** enable New Game button during active gameplay ([f3f6eca](f3f6eca1db))
* **card-sorting:** end drag immediately when card becomes locked ([ae45298](ae45298ec4))
* **card-sorting:** filter local player from emoji overlays on dragged cards ([dc2d94a](dc2d94aaa5))
* **card-sorting:** fix results panel layout to not cover cards ([4b4fbfe](4b4fbfef32))
* **card-sorting:** hide activity notifications in spectator mode ([5cca279](5cca279687))
* **card-sorting:** keep arrow sequence numbers upright ([79c9469](79c94699fa))
* **card-sorting:** lock correctly positioned prefix/suffix cards ([170abed](170abed231))
* **card-sorting:** lock spring positions after initial animation completes ([275cc62](275cc62a52))
* **card-sorting:** New Game now restarts with same settings instantly ([f3687ed](f3687ed236))
* **card-sorting:** only shrink/fade cards in correct prefix ([51368c6](51368c6ec5))
* **card-sorting:** preserve card positions on pause/resume ([0d8af09](0d8af09517))
* **card-sorting:** preserve rotation when starting drag ([3364144](3364144fb6))
* **card-sorting:** prevent duplicate START_GAME moves on Play Again ([a0b14f8](a0b14f87e9))
* **card-sorting:** prevent ghost movements with proper optimistic updates ([bd014be](bd014bec4f))
* **card-sorting:** prevent infinite loop when all cards are correct ([34785f4](34785f466f))
* **card-sorting:** prevent infinite loop with tolerance-based position comparison ([627b873](627b873382))
* **card-sorting:** prevent position jump when clicking rotated cards ([564a00f](564a00f82b))
* **card-sorting:** prevent replaying own movements from server ([308168a](308168a7fb))
* **card-sorting:** prevent springs from reinitializing on window resize ([30953b8](30953b8c4a))
* **card-sorting:** prevent springs from resetting after animation ([8aff60c](8aff60ce3f))
* **card-sorting:** remove hasAnimatedRef logic causing backwards animation ([a44aa5a](a44aa5a4c2))
* **card-sorting:** remove remaining reveal numbers references ([15c53ea](15c53ea4eb))
* **card-sorting:** restore prefix/suffix card shrinking visual feedback ([f5fb4d7](f5fb4d7b76))
* **card-sorting:** show only active players in team members section ([fa9f1a5](fa9f1a568f))
* **card-sorting:** smooth scale animation while dragging cards ([0eefc33](0eefc332ac))
* **card-sorting:** stabilize inferred sequence for locked cards during drag ([b0cd194](b0cd194838))
* **card-sorting:** use empty deps array for useSprings to prevent recreation ([cee399e](cee399ed15))
* **card-sorting:** use ref to track initialized state and prevent re-animation ([f389afa](f389afa831))
* **card-sorting:** use same coordinate system for game board and results ([6972fdf](6972fdf110))
* **complement-race:** prevent delivery move thrashing in steam sprint mode ([e1258ee](e1258ee041))
* configure favicon metadata and improve bead visibility ([e1369fa](e1369fa275))
* copy entire packages/core and packages/templates ([0ccada0](0ccada0ca7))
* correct hero abacus scroll direction to flow with page content ([4232746](423274657c))
* correct Typst template path in Dockerfile ([4c518de](4c518decb7))
* delete existing user sessions before creating new ones ([0cced47](0cced47a0f))
* extract pure SVG content from AbacusReact renders ([b07f1c4](b07f1c4216))
* **games:** prevent horizontal page scroll from carousel overflow ([5a8c98f](5a8c98fc10))
* **games:** smooth scroll feel for carousel wheel navigation ([f80a73b](f80a73b35c))
* **games:** use specific transition properties for smooth carousel loop ([187271e](187271e515))
* **i18n:** eliminate FOUC by loading messages server-side ([4d4d930](4d4d930bd3))
* **i18n:** use useMessages() for tutorial translations ([95b0105](95b0105ca3))
* include column posts in favicon bounding box ([0b2f481](0b2f48106a))
* increase server update debounce to 2000ms for low bandwidth ([633ff12](633ff12750))
* Integrate threshold input into Point Victory card ([b29bbee](b29bbeefca))
* mark dynamic routes as force-dynamic to prevent static generation errors ([d7b35d9](d7b35d9544))
* **nav:** show full navigation on /games page ([d3fe6ac](d3fe6acbb0))
* **qr-button:** improve layout and z-index ([646a422](646a4228d0))
* **qr-button:** increase mini QR code size to 80px ([61ac737](61ac7378bd))
* **qr-button:** increase mini QR code to 84px ([3fae5ea](3fae5ea6fa))
* **qr-button:** make button square and increase QR size ([dc2d466](dc2d46663b))
* **qr-button:** match height of stacked buttons ([81f202d](81f202d215))
* reduce padding to minimize gap below last bead ([0e529be](0e529be789))
* remove distracting parallax and wobble 3D effects ([28a2d40](28a2d40996))
* remove wobble physics and enhance wood grain visibility ([5d97673](5d97673406))
* resolve z-index layering and hero abacus visibility issues ([ed9a050](ed9a050d64))
* rewrite 3D stories to use props instead of CSS wrappers ([26bdb11](26bdb11237))
* **rithmomachia:** add missing i18next dependencies ([91154d9](91154d9364))
* **rithmomachia:** add missing pyramid section keys to Japanese (ja.json) ([dae615e](dae615ee72))
* **rithmomachia:** adjust error dialog sizing to prevent text clipping ([cda1126](cda1126cb0))
* **rithmomachia:** adjust roster notice position to not overlap nav ([7093223](709322373a))
* **rithmomachia:** change undock icon to pop-out arrow ([2a91748](2a91748493))
* **rithmomachia:** correct board dimensions to 16x8 and restore original layout values ([cfac277](cfac277505))
* **rithmomachia:** Correct board setup to match reference image exactly ([618e563](618e56358d))
* **rithmomachia:** correct makeMove parameter types for capture handling ([aafb64f](aafb64f3e3))
* **rithmomachia:** fix guide modal resize drift by calculating from initial state ([1bcd99c](1bcd99c949))
* **rithmomachia:** fix harmony section translation structure for hi/ja/es ([14259a1](14259a19a9))
* **rithmomachia:** fix modal resizing zoom issue ([4fa20f4](4fa20f44cb))
* **rithmomachia:** Fix TypeScript errors in playing guide modal ([4834ece](4834ece98e))
* **rithmomachia:** handle pyramid pieces in hover error tooltip ([56f3164](56f3164155))
* **rithmomachia:** implement proper board cropping and highlighting in guide ([d0a8fcd](d0a8fcdea6))
* **rithmomachia:** improve guide modal tab navigation at narrow widths ([a673177](a673177bec))
* **rithmomachia:** reconnect player assignment UI and fix setup layout ([a1a0374](a1a0374fac))
* **rithmomachia:** render guide as docked in preview panel ([190f8cf](190f8cf302))
* **rithmomachia:** show actual values in tooltips for non-helper relations ([774c6b0](774c6b0ce7))
* **rithmomachia:** show guest-friendly message when they can't fix too many players ([54bfd2f](54bfd2fac8))
* **rithmomachia:** smooth guide dragging from docked state without jump ([8f4a79c](8f4a79c9b0))
* **rithmomachia:** validate move path before showing capture error on hover ([bd49964](bd49964186))
* **room-info:** hide Leave Room button when user is alone ([5927f61](5927f61c3c))
* separate horizontal and vertical bounding box logic ([83090df](83090df4df))
* tolerate OpenSCAD CGAL warnings if output file is created ([88993f3](88993f3662))
* use absolute positioning for hero abacus to eliminate scroll lag ([096104b](096104b094))
* use Debian base for deps stage to match runner for binary compatibility ([f8fe6e4](f8fe6e4a41))
* use default BOSL2 branch instead of non-existent v2.0.0 tag ([f4ffc5b](f4ffc5b027))
* use nested SVG viewBox for actual cropping, not just scaling ([440b492](440b492e85))
* various game improvements and UI enhancements ([b67cf61](b67cf610c5))

### Performance Improvements

* optimize Docker image size to reduce build failures ([9ca3106](9ca3106361))

### Code Refactoring

* **card-sorting:** remove reveal numbers feature ([ea5e3e8](ea5e3e838b))
* **card-sorting:** send complete card sequence instead of individual moves ([e4df843](e4df8432b9))
* **games:** implement carousel, fix victories bug, add conditional stats ([82c133f](82c133f742))
* **games:** move page title to nav bar ([712ee58](712ee58e59))
* **games:** remove redundant subtitle below nav ([ad5bb87](ad5bb87325))
* **games:** remove wheel scrolling, enable overflow visible carousel ([876513c](876513c9cc))
* reorganize Harmony and Victory guide sections ([fb629c4](fb629c44ea))
* restructure /create page into hub with sub-pages ([b91b23d](b91b23d95f))
* **rithmomachia:** extract board and capture components (phase 2+3) ([a0a867b](a0a867b271))
* **rithmomachia:** extract CaptureErrorDialog component (Phase 2 partial) ([f0a066d](f0a066d8f0))
* **rithmomachia:** extract constants and coordinate utilities (Phase 1) ([eace0ed](eace0ed529))
* **rithmomachia:** extract guide sections into separate files ([765525d](765525dc45))
* **rithmomachia:** extract hooks (phase 5) ([324a659](324a65992f))
* **rithmomachia:** extract phase components (phase 4) ([11364f6](11364f6394))
* **rithmomachia:** extract reusable components from SetupPhase ([3abc325](3abc325ea2))
* **rithmomachia:** make setup phase UI more compact ([e55f848](e55f848a26))
* **rithmomachia:** redesign error notification with modern UI ([dfeeb0e](dfeeb0e0db)), closes [#1e293](https://github.com/antialias/soroban-abacus-flashcards/issues/1e293) [#0f172](https://github.com/antialias/soroban-abacus-flashcards/issues/0f172) [#f1f5f9](https://github.com/antialias/soroban-abacus-flashcards/issues/f1f5f9)
* **rithmomachia:** simplify capture error dialog to one-liner ([82a5eb2](82a5eb2e4b))
* **rithmomachia:** Update board setup to authoritative CSV layout ([0471da5](0471da598d))
* **rithmomachia:** update capture components to use CaptureContext ([2ab6ab5](2ab6ab5799))
* **rithmomachia:** use useBoardLayout and usePieceSelection in BoardDisplay ([0ab7a1d](0ab7a1df32))
* use AbacusReact for dynamic Open Graph image ([9c20f12](9c20f12bac))

### Documentation

* add 3D enhancement documentation to README ([cc96802](cc96802df8))
* add database migration guide and playing guide modal spec ([5a29af7](5a29af78e2))
* add deployment verification guidelines to prevent false positives ([3d8da23](3d8da2348b))
* **card-sorting:** add comprehensive multiplayer plan ([008ccea](008ccead0f))
* **rithmomachia:** Add concise one-page playing guide ([e3c1f10](e3c1f10233))
* update workflow to require manual testing before commits ([0991796](0991796f1e))

### Styles

* **rithmomachia:** improve divider styling and make tabs responsive ([88ca35e](88ca35e044)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3)
* **rithmomachia:** improve pyramid face numbers visibility and contrast ([94e5e6a](94e5e6a268)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#b45309](https://github.com/antialias/soroban-abacus-flashcards/issues/b45309)
* **rithmomachia:** increase pyramid face numbers size and boldness ([7bf2d73](7bf2d730d3))

### Tests

* trigger compose-updater deployment test ([2b06aae](2b06aae394))
* verify compose-updater automatic deployment cycle ([af0552c](af0552ccd9))
2025-11-03 23:06:59 +00:00
Thomas Hallock
af0552ccd9 test: verify compose-updater automatic deployment cycle
Add test comment to trigger automatic deployment and verify that
compose-updater successfully updates the container without crashing
with the new separate project names configuration.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 17:04:05 -06:00
semantic-release-bot
90421cfc38 chore(release): 4.68.0 [skip ci]
## [4.68.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.67.1...v4.68.0) (2025-11-03)

### Features

* **abacus:** add nativeAbacusNumbers setting to schema and UI ([79f7347](79f7347d48))
* add 3D printing support for abacus models ([dafdfdd](dafdfdd233))
* add comprehensive metadata, SEO, and make AbacusReact SSR-compatible ([0922ea1](0922ea10b7))
* add comprehensive Storybook coverage and migration guide ([7a4a37e](7a4a37ec6d))
* add game preview system with mock arcade environment ([25880cc](25880cc7e4))
* add per-player stats tracking system ([613301c](613301cd13))
* add Strategy & Tactics section to Rithmomachia guide ([81ead65](81ead65680))
* add unified trophy abacus with hero mode integration ([6620418](6620418a70))
* **arcade:** add ability to deactivate remote players without kicking user ([3628426](3628426a56))
* **arcade:** add native abacus numbers support to pressure gauge ([1d525c7](1d525c7b53))
* **arcade:** add Rithmomachia (Battle of Numbers) game ([2fc0a05](2fc0a05f7f))
* **arcade:** add yjs-demo collaborative game and Yjs persistence layer ([d568955](d568955d6a))
* **arcade:** auto-create room when user has none ([ff88c3a](ff88c3a1b8))
* **card-sorting:** add activity feed notifications for collaborative mode ([1461414](1461414ef4))
* **card-sorting:** add auto-submit countdown for perfect sequences ([780a716](780a7161bc))
* **card-sorting:** add bezier curves to connecting arrows ([4d8e873](4d8e873358))
* **card-sorting:** add CardPosition type and position syncing ([656f5a7](656f5a7838))
* **card-sorting:** add collapsible stats sidebar for spectators ([6527c26](6527c26a81))
* **card-sorting:** add game mode selector UI to setup phase ([d25b888](d25b888ffb))
* **card-sorting:** add GameMode type system for multiplayer support ([fd76533](fd765335ef))
* **card-sorting:** add green border to correctly positioned cards ([16fca86](16fca86b76)), closes [#22c55](https://github.com/antialias/soroban-abacus-flashcards/issues/22c55)
* **card-sorting:** add player emoji indicators on moving cards ([3a82099](3a82099757))
* **card-sorting:** add react-spring animations for real-time sync ([c367e0c](c367e0ceec))
* **card-sorting:** add smooth transition to drop shadow ([b0b93d0](b0b93d0175))
* **card-sorting:** add spectator mode UI enhancements ([ee7345d](ee7345d641)), closes [#6366f1](https://github.com/antialias/soroban-abacus-flashcards/issues/6366f1) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add team scoring UI for collaborative mode ([ed6f177](ed6f177914)), closes [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add updateCardPositions action to Provider ([f6ed4a2](f6ed4a27a2))
* **card-sorting:** auto-arrange prefix/suffix cards in corners ([4ba7f24](4ba7f24717))
* **card-sorting:** fade correctly positioned cards to 50% opacity ([7028cfc](7028cfc511))
* **card-sorting:** gentler spring animation for locked cards ([47189cb](47189cb6e7))
* **card-sorting:** implement continuous bezier curve paths ([2d93024](2d9302410f))
* **card-sorting:** improve card distribution for natural scattered look ([0b0503f](0b0503f035))
* **card-sorting:** make player emoji fill entire card background ([2e7a02c](2e7a02c9e4))
* **card-sorting:** optimize results screen for mobile ([d188789](d188789069))
* **card-sorting:** redesign setup screen with modern UI ([73cf967](73cf967492))
* **card-sorting:** scale correctly positioned cards to 50% ([222dc55](222dc555fa))
* **card-sorting:** shrink/fade cards in correct suffix as well ([8f6feec](8f6feec4f2))
* **card-sorting:** smooth spring transition from game table to results grid ([c5f39d5](c5f39d51eb))
* **card-sorting:** wrap prefix/suffix cards to multiple rows ([e3184dd](e3184dd0d4))
* complete 3D enhancement integration for all three proposals ([5ac55cc](5ac55cc149))
* **create-room:** replace hardcoded game grid with dynamic Radix Select dropdown ([83d0ba2](83d0ba26f5))
* dynamic day-of-month favicon using subprocess pattern ([4d0795a](4d0795a9df))
* dynamically crop favicon to active beads for maximum size ([5670322](567032296a))
* enable 3D enhancement on hero/open MyAbacus modes ([37e330f](37e330f26e))
* **games:** add autoplay and improve carousel layout ([9f51edf](9f51edfaa9))
* **games:** add horizontal scroll support to carousels ([a224abb](a224abb6f6))
* **games:** add rotating games hero carousel ([24231e6](24231e6b2e))
* **i18n:** add dynamic locale switching without page reload ([fe9bfea](fe9bfeabf9))
* **i18n:** add global language selector to navigation ([0506360](0506360117))
* **i18n:** add homepage translations for all supported languages ([8c9d35a](8c9d35a3b4))
* **i18n:** add Old High German (goh) language support ([b334a15](b334a15255))
* **i18n:** complete Old High German translations for all locales ([0b06a1c](0b06a1ce00))
* **i18n:** internationalize games page and tutorial content ([4253964](4253964af1))
* **i18n:** internationalize homepage with English translations ([40cff14](40cff143c7))
* **i18n:** migrate from react-i18next to next-intl ([9016b76](9016b76024))
* **i18n:** update games page hero section copy ([6333c60](6333c60352))
* install embla-carousel-autoplay for games carousel ([946e5d1](946e5d1910))
* install embla-carousel-react for player profile carousel ([642ae95](642ae95738))
* internationalize guide page with 6 languages ([e9c320b](e9c320bb10))
* internationalize tutorial player ([26d41cf](26d41cfd05))
* optimize card sorting for mobile displays ([b443ee9](b443ee9cdc))
* Redesign Rithmomachia setup page with dramatic medieval theme ([6ae4d13](6ae4d13dc7))
* **rithmomachia:** add 80% opacity to guide modal when not hovered ([4a78485](4a78485d2e))
* **rithmomachia:** add CaptureContext for capture dialog state management ([d7eb957](d7eb957a8d))
* **rithmomachia:** add ghost panel preview for guide docking ([c0d6526](c0d6526d30))
* **rithmomachia:** add guide docking with resizable panels ([f457f1a](f457f1a1c2))
* **rithmomachia:** add helper piece selection for mathematical captures ([cae3359](cae3359587))
* **rithmomachia:** add helpful error messages for failed captures ([b172440](b172440a41))
* **rithmomachia:** add initial board visual to guide Overview section ([d42bcff](d42bcff0d9))
* **rithmomachia:** Add interactive playing guide modal ([3121d82](3121d8240a))
* **rithmomachia:** add number bond visualization and helper placeholders ([82d8913](82d89131f0))
* **rithmomachia:** add ratio capture example to guide ([9150b0c](9150b0c678))
* **rithmomachia:** add standalone guide page route ([3fcc79f](3fcc79fe9e))
* **rithmomachia:** add useBoardLayout hook for centralized layout calculations ([27f1c98](27f1c989d5))
* **rithmomachia:** add usePieceSelection hook for selection state management ([275f401](275f401e3c))
* **rithmomachia:** add visual board examples to Capture section ([74bc3c0](74bc3c0dcf))
* **rithmomachia:** add visual board examples to Harmony section ([1d5f01c](1d5f01c966))
* **rithmomachia:** add visual winning example to Victory section ([b7fac78](b7fac78829))
* **rithmomachia:** auto-size tab labels with react-textfit ([9fd5406](9fd54067ce))
* **rithmomachia:** cycle through valid helpers with dynamic number tooltips ([4829e41](4829e41ea1))
* **rithmomachia:** enhance capture relation UI with smooth animations ([0a30801](0a308016e9))
* **rithmomachia:** enhance Harmony section with comprehensive content ([f555856](f5558563ea))
* **rithmomachia:** enhance Pieces section with visual examples and pyramid details ([55aff82](55aff829f4))
* **rithmomachia:** enhance Pyramid section with comprehensive details ([9fde1ef](9fde1ef9e7))
* **rithmomachia:** guide defaults to docked right on open ([11f674d](11f674d542))
* **rithmomachia:** improve guide pieces section layout ([a270bfc](a270bfc0cc))
* **rithmomachia:** improve guide UX and add persistence ([b314740](b314740697))
* **rithmomachia:** improve roster status notice UX ([e27df45](e27df45256))
* **rithmomachia:** integrate roster warning into game nav ([8a11594](8a11594203))
* **rithmomachia:** make guide modal ultra-responsive down to 150px width ([0474197](04741971b2))
* **rithmomachia:** recreate original guide modal header layout ([2489695](24896957d0))
* **rithmomachia:** show capture error on hover instead of click ([339b678](339b6780f6))
* **rithmomachia:** show pyramid face numbers on hover instead of selection ([b0c4523](b0c4523c0b))
* **rithmomachia:** show pyramid face numbers when selected ([5c186f3](5c186f3947))
* **rithmomachia:** show pyramid face numbers when selected with subtle animation ([5c2ddbe](5c2ddbef05))
* **rithmomachia:** show real preview layout when dragging guide to dock ([17d2460](17d2460a87))
* **rithmomachia:** simplify guide language for clarity ([85cb630](85cb630add))
* **rithmomachia:** skip helper selection UI and auto-select first valid helper ([be2a00e](be2a00e8b3))
* **rithmomachia:** Update harmony system to classical three-piece proportions ([08c9762](08c97620f5))
* **rithmomachia:** Update to traditional board setup with 25 pieces per side ([0769eaa](0769eaaa1d))
* **rithmomachia:** use actual piece SVGs in number bond with 2.5s rotation animation ([976a7de](976a7de949))
* **room-share:** add QR code button for easy mobile joining ([349290a](349290ac6a))
* show rithmomachia turn in nav ([7c89bfe](7c89bfef9c))
* switch to royal color theme with transparent background ([944ad65](944ad6574e)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#f59e0](https://github.com/antialias/soroban-abacus-flashcards/issues/f59e0) [#a855f7](https://github.com/antialias/soroban-abacus-flashcards/issues/a855f7) [#7e22](https://github.com/antialias/soroban-abacus-flashcards/issues/7e22)

### Bug Fixes

* adjust hero abacus position to avoid covering subtitle ([f03d341](f03d341314))
* **arcade:** add automatic retry for version conflict rejections ([fbcde25](fbcde2505f))
* **arcade:** allow deactivating players from users who left the room ([7c1c2d7](7c1c2d7beb))
* **arcade:** implement optimistic locking in session manager ([71fd66d](71fd66d96a))
* board rotation now properly fills height in portrait mode ([b5a96ea](b5a96eaeb1))
* **card-sorting:** add border radius to outer card container ([a922eba](a922eba73c))
* **card-sorting:** add debug logging for spring animations ([d42947e](d42947eb8d))
* **card-sorting:** add missing gameMode support after hard reset ([a832325](a832325deb))
* **card-sorting:** add missing useMemo import ([949d76d](949d76d844))
* **card-sorting:** add overflow hidden to clip rounded corners ([84c66fe](84c66feec6))
* **card-sorting:** adjust connecting paths for scaled cards ([829c741](829c741e55))
* **card-sorting:** adjust game board for spectator panels ([fc5cf12](fc5cf1216f))
* **card-sorting:** adjust viewport dimensions for spectator panels ([4dce16c](4dce16cca4))
* **card-sorting:** animate cards from game board to results grid ([17d45fe](17d45fe88c))
* **card-sorting:** correct suffix card detection in auto-arrange ([d02ab59](d02ab5922c))
* **card-sorting:** enable card scaling for spectators ([6b095c3](6b095c3383))
* **card-sorting:** enable New Game button during active gameplay ([f3f6eca](f3f6eca1db))
* **card-sorting:** end drag immediately when card becomes locked ([ae45298](ae45298ec4))
* **card-sorting:** filter local player from emoji overlays on dragged cards ([dc2d94a](dc2d94aaa5))
* **card-sorting:** fix results panel layout to not cover cards ([4b4fbfe](4b4fbfef32))
* **card-sorting:** hide activity notifications in spectator mode ([5cca279](5cca279687))
* **card-sorting:** keep arrow sequence numbers upright ([79c9469](79c94699fa))
* **card-sorting:** lock correctly positioned prefix/suffix cards ([170abed](170abed231))
* **card-sorting:** lock spring positions after initial animation completes ([275cc62](275cc62a52))
* **card-sorting:** New Game now restarts with same settings instantly ([f3687ed](f3687ed236))
* **card-sorting:** only shrink/fade cards in correct prefix ([51368c6](51368c6ec5))
* **card-sorting:** preserve card positions on pause/resume ([0d8af09](0d8af09517))
* **card-sorting:** preserve rotation when starting drag ([3364144](3364144fb6))
* **card-sorting:** prevent duplicate START_GAME moves on Play Again ([a0b14f8](a0b14f87e9))
* **card-sorting:** prevent ghost movements with proper optimistic updates ([bd014be](bd014bec4f))
* **card-sorting:** prevent infinite loop when all cards are correct ([34785f4](34785f466f))
* **card-sorting:** prevent infinite loop with tolerance-based position comparison ([627b873](627b873382))
* **card-sorting:** prevent position jump when clicking rotated cards ([564a00f](564a00f82b))
* **card-sorting:** prevent replaying own movements from server ([308168a](308168a7fb))
* **card-sorting:** prevent springs from reinitializing on window resize ([30953b8](30953b8c4a))
* **card-sorting:** prevent springs from resetting after animation ([8aff60c](8aff60ce3f))
* **card-sorting:** remove hasAnimatedRef logic causing backwards animation ([a44aa5a](a44aa5a4c2))
* **card-sorting:** remove remaining reveal numbers references ([15c53ea](15c53ea4eb))
* **card-sorting:** restore prefix/suffix card shrinking visual feedback ([f5fb4d7](f5fb4d7b76))
* **card-sorting:** show only active players in team members section ([fa9f1a5](fa9f1a568f))
* **card-sorting:** smooth scale animation while dragging cards ([0eefc33](0eefc332ac))
* **card-sorting:** stabilize inferred sequence for locked cards during drag ([b0cd194](b0cd194838))
* **card-sorting:** use empty deps array for useSprings to prevent recreation ([cee399e](cee399ed15))
* **card-sorting:** use ref to track initialized state and prevent re-animation ([f389afa](f389afa831))
* **card-sorting:** use same coordinate system for game board and results ([6972fdf](6972fdf110))
* **complement-race:** prevent delivery move thrashing in steam sprint mode ([e1258ee](e1258ee041))
* configure favicon metadata and improve bead visibility ([e1369fa](e1369fa275))
* copy entire packages/core and packages/templates ([0ccada0](0ccada0ca7))
* correct hero abacus scroll direction to flow with page content ([4232746](423274657c))
* correct Typst template path in Dockerfile ([4c518de](4c518decb7))
* delete existing user sessions before creating new ones ([0cced47](0cced47a0f))
* extract pure SVG content from AbacusReact renders ([b07f1c4](b07f1c4216))
* **games:** prevent horizontal page scroll from carousel overflow ([5a8c98f](5a8c98fc10))
* **games:** smooth scroll feel for carousel wheel navigation ([f80a73b](f80a73b35c))
* **games:** use specific transition properties for smooth carousel loop ([187271e](187271e515))
* **i18n:** eliminate FOUC by loading messages server-side ([4d4d930](4d4d930bd3))
* **i18n:** use useMessages() for tutorial translations ([95b0105](95b0105ca3))
* include column posts in favicon bounding box ([0b2f481](0b2f48106a))
* increase server update debounce to 2000ms for low bandwidth ([633ff12](633ff12750))
* Integrate threshold input into Point Victory card ([b29bbee](b29bbeefca))
* mark dynamic routes as force-dynamic to prevent static generation errors ([d7b35d9](d7b35d9544))
* **nav:** show full navigation on /games page ([d3fe6ac](d3fe6acbb0))
* **qr-button:** improve layout and z-index ([646a422](646a4228d0))
* **qr-button:** increase mini QR code size to 80px ([61ac737](61ac7378bd))
* **qr-button:** increase mini QR code to 84px ([3fae5ea](3fae5ea6fa))
* **qr-button:** make button square and increase QR size ([dc2d466](dc2d46663b))
* **qr-button:** match height of stacked buttons ([81f202d](81f202d215))
* reduce padding to minimize gap below last bead ([0e529be](0e529be789))
* remove distracting parallax and wobble 3D effects ([28a2d40](28a2d40996))
* remove wobble physics and enhance wood grain visibility ([5d97673](5d97673406))
* resolve z-index layering and hero abacus visibility issues ([ed9a050](ed9a050d64))
* rewrite 3D stories to use props instead of CSS wrappers ([26bdb11](26bdb11237))
* **rithmomachia:** add missing i18next dependencies ([91154d9](91154d9364))
* **rithmomachia:** add missing pyramid section keys to Japanese (ja.json) ([dae615e](dae615ee72))
* **rithmomachia:** adjust error dialog sizing to prevent text clipping ([cda1126](cda1126cb0))
* **rithmomachia:** adjust roster notice position to not overlap nav ([7093223](709322373a))
* **rithmomachia:** change undock icon to pop-out arrow ([2a91748](2a91748493))
* **rithmomachia:** correct board dimensions to 16x8 and restore original layout values ([cfac277](cfac277505))
* **rithmomachia:** Correct board setup to match reference image exactly ([618e563](618e56358d))
* **rithmomachia:** correct makeMove parameter types for capture handling ([aafb64f](aafb64f3e3))
* **rithmomachia:** fix guide modal resize drift by calculating from initial state ([1bcd99c](1bcd99c949))
* **rithmomachia:** fix harmony section translation structure for hi/ja/es ([14259a1](14259a19a9))
* **rithmomachia:** fix modal resizing zoom issue ([4fa20f4](4fa20f44cb))
* **rithmomachia:** Fix TypeScript errors in playing guide modal ([4834ece](4834ece98e))
* **rithmomachia:** handle pyramid pieces in hover error tooltip ([56f3164](56f3164155))
* **rithmomachia:** implement proper board cropping and highlighting in guide ([d0a8fcd](d0a8fcdea6))
* **rithmomachia:** improve guide modal tab navigation at narrow widths ([a673177](a673177bec))
* **rithmomachia:** reconnect player assignment UI and fix setup layout ([a1a0374](a1a0374fac))
* **rithmomachia:** render guide as docked in preview panel ([190f8cf](190f8cf302))
* **rithmomachia:** show actual values in tooltips for non-helper relations ([774c6b0](774c6b0ce7))
* **rithmomachia:** show guest-friendly message when they can't fix too many players ([54bfd2f](54bfd2fac8))
* **rithmomachia:** smooth guide dragging from docked state without jump ([8f4a79c](8f4a79c9b0))
* **rithmomachia:** validate move path before showing capture error on hover ([bd49964](bd49964186))
* **room-info:** hide Leave Room button when user is alone ([5927f61](5927f61c3c))
* separate horizontal and vertical bounding box logic ([83090df](83090df4df))
* tolerate OpenSCAD CGAL warnings if output file is created ([88993f3](88993f3662))
* use absolute positioning for hero abacus to eliminate scroll lag ([096104b](096104b094))
* use Debian base for deps stage to match runner for binary compatibility ([f8fe6e4](f8fe6e4a41))
* use default BOSL2 branch instead of non-existent v2.0.0 tag ([f4ffc5b](f4ffc5b027))
* use nested SVG viewBox for actual cropping, not just scaling ([440b492](440b492e85))
* various game improvements and UI enhancements ([b67cf61](b67cf610c5))

### Performance Improvements

* optimize Docker image size to reduce build failures ([9ca3106](9ca3106361))

### Code Refactoring

* **card-sorting:** remove reveal numbers feature ([ea5e3e8](ea5e3e838b))
* **card-sorting:** send complete card sequence instead of individual moves ([e4df843](e4df8432b9))
* **games:** implement carousel, fix victories bug, add conditional stats ([82c133f](82c133f742))
* **games:** move page title to nav bar ([712ee58](712ee58e59))
* **games:** remove redundant subtitle below nav ([ad5bb87](ad5bb87325))
* **games:** remove wheel scrolling, enable overflow visible carousel ([876513c](876513c9cc))
* reorganize Harmony and Victory guide sections ([fb629c4](fb629c44ea))
* restructure /create page into hub with sub-pages ([b91b23d](b91b23d95f))
* **rithmomachia:** extract board and capture components (phase 2+3) ([a0a867b](a0a867b271))
* **rithmomachia:** extract CaptureErrorDialog component (Phase 2 partial) ([f0a066d](f0a066d8f0))
* **rithmomachia:** extract constants and coordinate utilities (Phase 1) ([eace0ed](eace0ed529))
* **rithmomachia:** extract guide sections into separate files ([765525d](765525dc45))
* **rithmomachia:** extract hooks (phase 5) ([324a659](324a65992f))
* **rithmomachia:** extract phase components (phase 4) ([11364f6](11364f6394))
* **rithmomachia:** extract reusable components from SetupPhase ([3abc325](3abc325ea2))
* **rithmomachia:** make setup phase UI more compact ([e55f848](e55f848a26))
* **rithmomachia:** redesign error notification with modern UI ([dfeeb0e](dfeeb0e0db)), closes [#1e293](https://github.com/antialias/soroban-abacus-flashcards/issues/1e293) [#0f172](https://github.com/antialias/soroban-abacus-flashcards/issues/0f172) [#f1f5f9](https://github.com/antialias/soroban-abacus-flashcards/issues/f1f5f9)
* **rithmomachia:** simplify capture error dialog to one-liner ([82a5eb2](82a5eb2e4b))
* **rithmomachia:** Update board setup to authoritative CSV layout ([0471da5](0471da598d))
* **rithmomachia:** update capture components to use CaptureContext ([2ab6ab5](2ab6ab5799))
* **rithmomachia:** use useBoardLayout and usePieceSelection in BoardDisplay ([0ab7a1d](0ab7a1df32))
* use AbacusReact for dynamic Open Graph image ([9c20f12](9c20f12bac))

### Documentation

* add 3D enhancement documentation to README ([cc96802](cc96802df8))
* add database migration guide and playing guide modal spec ([5a29af7](5a29af78e2))
* add deployment verification guidelines to prevent false positives ([3d8da23](3d8da2348b))
* **card-sorting:** add comprehensive multiplayer plan ([008ccea](008ccead0f))
* **rithmomachia:** Add concise one-page playing guide ([e3c1f10](e3c1f10233))
* update workflow to require manual testing before commits ([0991796](0991796f1e))

### Styles

* **rithmomachia:** improve divider styling and make tabs responsive ([88ca35e](88ca35e044)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3)
* **rithmomachia:** improve pyramid face numbers visibility and contrast ([94e5e6a](94e5e6a268)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#b45309](https://github.com/antialias/soroban-abacus-flashcards/issues/b45309)
* **rithmomachia:** increase pyramid face numbers size and boldness ([7bf2d73](7bf2d730d3))

### Tests

* trigger compose-updater deployment test ([2b06aae](2b06aae394))
2025-11-03 22:34:58 +00:00
Thomas Hallock
2b06aae394 test: trigger compose-updater deployment test
Testing that compose-updater successfully updates without crashing itself
2025-11-03 16:32:13 -06:00
21 changed files with 2707 additions and 387 deletions

View File

@@ -1,6 +1,543 @@
## [4.68.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.67.1...v4.68.0) (2025-11-03)
### Features
* **abacus:** add nativeAbacusNumbers setting to schema and UI ([79f7347](https://github.com/antialias/soroban-abacus-flashcards/commit/79f7347d4800646378470a7f9aca8e7f2fd5573c))
* add 3D printing support for abacus models ([dafdfdd](https://github.com/antialias/soroban-abacus-flashcards/commit/dafdfdd233b53464b9825a8a9b5f2e6206fc54cb))
* add comprehensive metadata, SEO, and make AbacusReact SSR-compatible ([0922ea1](https://github.com/antialias/soroban-abacus-flashcards/commit/0922ea10b77e7d16b8c414c596d23cb11e20c1cc))
* add comprehensive Storybook coverage and migration guide ([7a4a37e](https://github.com/antialias/soroban-abacus-flashcards/commit/7a4a37ec6d0171782778e18122da782f069e0556))
* add game preview system with mock arcade environment ([25880cc](https://github.com/antialias/soroban-abacus-flashcards/commit/25880cc7e463f98a5a23c812c1ffd43734d3fe1f))
* add per-player stats tracking system ([613301c](https://github.com/antialias/soroban-abacus-flashcards/commit/613301cd137ad6f712571a0be45c708ce391fc8f))
* add Strategy & Tactics section to Rithmomachia guide ([81ead65](https://github.com/antialias/soroban-abacus-flashcards/commit/81ead65680892efa4d0ab07e7f0ef77eb1bc1405))
* add unified trophy abacus with hero mode integration ([6620418](https://github.com/antialias/soroban-abacus-flashcards/commit/6620418a704dcca810b511a5f394084521104e6b))
* **arcade:** add ability to deactivate remote players without kicking user ([3628426](https://github.com/antialias/soroban-abacus-flashcards/commit/3628426a567d7e0273be75cce64632ae04b7d5eb))
* **arcade:** add native abacus numbers support to pressure gauge ([1d525c7](https://github.com/antialias/soroban-abacus-flashcards/commit/1d525c7b5320984a1582b8ab7eae57895c728428))
* **arcade:** add Rithmomachia (Battle of Numbers) game ([2fc0a05](https://github.com/antialias/soroban-abacus-flashcards/commit/2fc0a05f7f557cee55f7d31b585499dd04e68ff9))
* **arcade:** add yjs-demo collaborative game and Yjs persistence layer ([d568955](https://github.com/antialias/soroban-abacus-flashcards/commit/d568955d6abf389e6ab7c6979e33122a65917a46))
* **arcade:** auto-create room when user has none ([ff88c3a](https://github.com/antialias/soroban-abacus-flashcards/commit/ff88c3a1b81703a87a1d57eeb5cc139da7d9df04))
* **card-sorting:** add activity feed notifications for collaborative mode ([1461414](https://github.com/antialias/soroban-abacus-flashcards/commit/1461414ef4d0b213af241213447c91eed1abe5fb))
* **card-sorting:** add auto-submit countdown for perfect sequences ([780a716](https://github.com/antialias/soroban-abacus-flashcards/commit/780a7161bc05c2ca6597d7d8d89f01afd33d9f4d))
* **card-sorting:** add bezier curves to connecting arrows ([4d8e873](https://github.com/antialias/soroban-abacus-flashcards/commit/4d8e873358271fe3fd50b228aea8277e20aa5966))
* **card-sorting:** add CardPosition type and position syncing ([656f5a7](https://github.com/antialias/soroban-abacus-flashcards/commit/656f5a7838ed6003c214ec484d4c37072270fa8d))
* **card-sorting:** add collapsible stats sidebar for spectators ([6527c26](https://github.com/antialias/soroban-abacus-flashcards/commit/6527c26a8166b23f074e85eb335a15800c1947a2))
* **card-sorting:** add game mode selector UI to setup phase ([d25b888](https://github.com/antialias/soroban-abacus-flashcards/commit/d25b888ffb3915d2d482442ab708ba3e159af512))
* **card-sorting:** add GameMode type system for multiplayer support ([fd76533](https://github.com/antialias/soroban-abacus-flashcards/commit/fd765335efbc91366c596c7789b92882cd3379d9))
* **card-sorting:** add green border to correctly positioned cards ([16fca86](https://github.com/antialias/soroban-abacus-flashcards/commit/16fca86b7687115f1cf565c533a512e92946e3a8)), closes [#22c55](https://github.com/antialias/soroban-abacus-flashcards/issues/22c55)
* **card-sorting:** add player emoji indicators on moving cards ([3a82099](https://github.com/antialias/soroban-abacus-flashcards/commit/3a8209975728cdcf914c43ba08339454a9e2457f))
* **card-sorting:** add react-spring animations for real-time sync ([c367e0c](https://github.com/antialias/soroban-abacus-flashcards/commit/c367e0ceece41d8e7c2bc8aebe3239ff6053a115))
* **card-sorting:** add smooth transition to drop shadow ([b0b93d0](https://github.com/antialias/soroban-abacus-flashcards/commit/b0b93d0175c8a1c8958d6ba346d969c234fdd6ff))
* **card-sorting:** add spectator mode UI enhancements ([ee7345d](https://github.com/antialias/soroban-abacus-flashcards/commit/ee7345d641e0ee72915afb9cdbd6d284b7e238bd)), closes [#6366f1](https://github.com/antialias/soroban-abacus-flashcards/issues/6366f1) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add team scoring UI for collaborative mode ([ed6f177](https://github.com/antialias/soroban-abacus-flashcards/commit/ed6f1779141d0bc9dff2d532a3dfc638015936b5)), closes [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add updateCardPositions action to Provider ([f6ed4a2](https://github.com/antialias/soroban-abacus-flashcards/commit/f6ed4a27a26d8bfa495ba5f580a446286b9674a0))
* **card-sorting:** auto-arrange prefix/suffix cards in corners ([4ba7f24](https://github.com/antialias/soroban-abacus-flashcards/commit/4ba7f247175d93e4d339e2be7bbdb2e009992232))
* **card-sorting:** fade correctly positioned cards to 50% opacity ([7028cfc](https://github.com/antialias/soroban-abacus-flashcards/commit/7028cfc51164e9219479e6040b03c29239aa7edb))
* **card-sorting:** gentler spring animation for locked cards ([47189cb](https://github.com/antialias/soroban-abacus-flashcards/commit/47189cb6e79ed2915f5ddcc9cb3626540dfb07f3))
* **card-sorting:** implement continuous bezier curve paths ([2d93024](https://github.com/antialias/soroban-abacus-flashcards/commit/2d9302410f5e98145a435b00df3ae5fcf3f4c0b5))
* **card-sorting:** improve card distribution for natural scattered look ([0b0503f](https://github.com/antialias/soroban-abacus-flashcards/commit/0b0503f0354a4a82fe6b9bfe827729e8e5a9e329))
* **card-sorting:** make player emoji fill entire card background ([2e7a02c](https://github.com/antialias/soroban-abacus-flashcards/commit/2e7a02c9e4ab84e821d58661d6e7a326f7882afb))
* **card-sorting:** optimize results screen for mobile ([d188789](https://github.com/antialias/soroban-abacus-flashcards/commit/d188789069b4c350ce3cc0d221bd4a43dab528e0))
* **card-sorting:** redesign setup screen with modern UI ([73cf967](https://github.com/antialias/soroban-abacus-flashcards/commit/73cf96749234c480482f62392245b38c1fd5f0a0))
* **card-sorting:** scale correctly positioned cards to 50% ([222dc55](https://github.com/antialias/soroban-abacus-flashcards/commit/222dc555fa5068e2594dcc074e33f70320f5742c))
* **card-sorting:** shrink/fade cards in correct suffix as well ([8f6feec](https://github.com/antialias/soroban-abacus-flashcards/commit/8f6feec4f21d0af0d1c98daf5017eddd91d3d578))
* **card-sorting:** smooth spring transition from game table to results grid ([c5f39d5](https://github.com/antialias/soroban-abacus-flashcards/commit/c5f39d51eb45ec816f32151dc7f9d7c06360474b))
* **card-sorting:** wrap prefix/suffix cards to multiple rows ([e3184dd](https://github.com/antialias/soroban-abacus-flashcards/commit/e3184dd0d444e5dc204731f5b396d5c553cf7d11))
* complete 3D enhancement integration for all three proposals ([5ac55cc](https://github.com/antialias/soroban-abacus-flashcards/commit/5ac55cc14980b778f9be32f0833f8760aa16b631))
* **create-room:** replace hardcoded game grid with dynamic Radix Select dropdown ([83d0ba2](https://github.com/antialias/soroban-abacus-flashcards/commit/83d0ba26f5eeec3e189d279710d5bbcf13e82f29))
* dynamic day-of-month favicon using subprocess pattern ([4d0795a](https://github.com/antialias/soroban-abacus-flashcards/commit/4d0795a9df74fcb085af821eafb923bdcb5f0b0c))
* dynamically crop favicon to active beads for maximum size ([5670322](https://github.com/antialias/soroban-abacus-flashcards/commit/567032296aecaad13408bdc17d108ec7c57fb4a8))
* enable 3D enhancement on hero/open MyAbacus modes ([37e330f](https://github.com/antialias/soroban-abacus-flashcards/commit/37e330f26e5398c2358599361cd417b4aeefac7d))
* **games:** add autoplay and improve carousel layout ([9f51edf](https://github.com/antialias/soroban-abacus-flashcards/commit/9f51edfaa95c14f55a30a6eceafb9099eeed437f))
* **games:** add horizontal scroll support to carousels ([a224abb](https://github.com/antialias/soroban-abacus-flashcards/commit/a224abb6f660e1aa31ab04f5590b003fae072af9))
* **games:** add rotating games hero carousel ([24231e6](https://github.com/antialias/soroban-abacus-flashcards/commit/24231e6b2ebbdcae066344df54e7e80e7d221128))
* **i18n:** add dynamic locale switching without page reload ([fe9bfea](https://github.com/antialias/soroban-abacus-flashcards/commit/fe9bfeabf9ee66923501b18e1b69f2d666d0817d))
* **i18n:** add global language selector to navigation ([0506360](https://github.com/antialias/soroban-abacus-flashcards/commit/0506360117807665e8f5a6fcd8f1178339f6e65c))
* **i18n:** add homepage translations for all supported languages ([8c9d35a](https://github.com/antialias/soroban-abacus-flashcards/commit/8c9d35a3b43dd29664f5afb1bd96c4e584d9ec75))
* **i18n:** add Old High German (goh) language support ([b334a15](https://github.com/antialias/soroban-abacus-flashcards/commit/b334a15255ed9fa29beb43de66da0288691390c6))
* **i18n:** complete Old High German translations for all locales ([0b06a1c](https://github.com/antialias/soroban-abacus-flashcards/commit/0b06a1ce005d92e7ae9c225aba40d240e965753d))
* **i18n:** internationalize games page and tutorial content ([4253964](https://github.com/antialias/soroban-abacus-flashcards/commit/4253964af19f9aaa16f2394f41819223542fb519))
* **i18n:** internationalize homepage with English translations ([40cff14](https://github.com/antialias/soroban-abacus-flashcards/commit/40cff143c72e9228d7cce607cab64c4a6d067017))
* **i18n:** migrate from react-i18next to next-intl ([9016b76](https://github.com/antialias/soroban-abacus-flashcards/commit/9016b760247a20271255839e4dd7e5b9a8353b9f))
* **i18n:** update games page hero section copy ([6333c60](https://github.com/antialias/soroban-abacus-flashcards/commit/6333c60352b920916afd81cc3b0229706a1519fa))
* install embla-carousel-autoplay for games carousel ([946e5d1](https://github.com/antialias/soroban-abacus-flashcards/commit/946e5d19107020992be8945f8fe7c41e4bc2a0e2))
* install embla-carousel-react for player profile carousel ([642ae95](https://github.com/antialias/soroban-abacus-flashcards/commit/642ae957383cfe1d6045f645bbe426fd80c56f35))
* internationalize guide page with 6 languages ([e9c320b](https://github.com/antialias/soroban-abacus-flashcards/commit/e9c320bb1032e94c3852b9459236409da4669c09))
* internationalize tutorial player ([26d41cf](https://github.com/antialias/soroban-abacus-flashcards/commit/26d41cfd058bfdf5b61ee6e20cfc61cbecb32f45))
* optimize card sorting for mobile displays ([b443ee9](https://github.com/antialias/soroban-abacus-flashcards/commit/b443ee9cdcd9fcb7674845d8c92f7c338ad98dea))
* Redesign Rithmomachia setup page with dramatic medieval theme ([6ae4d13](https://github.com/antialias/soroban-abacus-flashcards/commit/6ae4d13dc784a87f85206c6ff6d005e5b23b678c))
* **rithmomachia:** add 80% opacity to guide modal when not hovered ([4a78485](https://github.com/antialias/soroban-abacus-flashcards/commit/4a78485d2e20f2cbf36cc898a1beafa8eb48bfbf))
* **rithmomachia:** add CaptureContext for capture dialog state management ([d7eb957](https://github.com/antialias/soroban-abacus-flashcards/commit/d7eb957a8dabbcac35e166a83dd679a628e19baa))
* **rithmomachia:** add ghost panel preview for guide docking ([c0d6526](https://github.com/antialias/soroban-abacus-flashcards/commit/c0d6526d30aca8deaeda2b7c2e27eb37af8b577c))
* **rithmomachia:** add guide docking with resizable panels ([f457f1a](https://github.com/antialias/soroban-abacus-flashcards/commit/f457f1a1c22b6cb7fff23a7701474322cf423dd9))
* **rithmomachia:** add helper piece selection for mathematical captures ([cae3359](https://github.com/antialias/soroban-abacus-flashcards/commit/cae335958751c27684bfb10c8e2e526b460954ed))
* **rithmomachia:** add helpful error messages for failed captures ([b172440](https://github.com/antialias/soroban-abacus-flashcards/commit/b172440a41e958ced98903bb8f4c2e4b423e1356))
* **rithmomachia:** add initial board visual to guide Overview section ([d42bcff](https://github.com/antialias/soroban-abacus-flashcards/commit/d42bcff0d922895549c1c12f8e02a3ae6d53425a))
* **rithmomachia:** Add interactive playing guide modal ([3121d82](https://github.com/antialias/soroban-abacus-flashcards/commit/3121d8240a567817f5f205a4ef4a788fcf451f71))
* **rithmomachia:** add number bond visualization and helper placeholders ([82d8913](https://github.com/antialias/soroban-abacus-flashcards/commit/82d89131f00517f162ec496397cb390f9ecfc52e))
* **rithmomachia:** add ratio capture example to guide ([9150b0c](https://github.com/antialias/soroban-abacus-flashcards/commit/9150b0c678ce7104fe984ee0fc93748b43a245f4))
* **rithmomachia:** add standalone guide page route ([3fcc79f](https://github.com/antialias/soroban-abacus-flashcards/commit/3fcc79fe9eae11d4bd3a724c1b1f7d086e7cae81))
* **rithmomachia:** add useBoardLayout hook for centralized layout calculations ([27f1c98](https://github.com/antialias/soroban-abacus-flashcards/commit/27f1c989d59a19844b90a5148ae27fb97161da2d))
* **rithmomachia:** add usePieceSelection hook for selection state management ([275f401](https://github.com/antialias/soroban-abacus-flashcards/commit/275f401e3c25b75fec4700a8c2d4be6e33f0afe9))
* **rithmomachia:** add visual board examples to Capture section ([74bc3c0](https://github.com/antialias/soroban-abacus-flashcards/commit/74bc3c0dcf8d1ee7084e88a04861a85f9b623809))
* **rithmomachia:** add visual board examples to Harmony section ([1d5f01c](https://github.com/antialias/soroban-abacus-flashcards/commit/1d5f01c966cf1eec9a9c19ee37f1cad93c89df40))
* **rithmomachia:** add visual winning example to Victory section ([b7fac78](https://github.com/antialias/soroban-abacus-flashcards/commit/b7fac788292e00c6060a47fdbcca89a7e7fee35c))
* **rithmomachia:** auto-size tab labels with react-textfit ([9fd5406](https://github.com/antialias/soroban-abacus-flashcards/commit/9fd54067ce257e028b02f4784568ff3f2bbb32ca))
* **rithmomachia:** cycle through valid helpers with dynamic number tooltips ([4829e41](https://github.com/antialias/soroban-abacus-flashcards/commit/4829e41ea13fae2edec10837e65e505929445782))
* **rithmomachia:** enhance capture relation UI with smooth animations ([0a30801](https://github.com/antialias/soroban-abacus-flashcards/commit/0a308016e9d6a926c52dbfc5623b60b169d16d03))
* **rithmomachia:** enhance Harmony section with comprehensive content ([f555856](https://github.com/antialias/soroban-abacus-flashcards/commit/f5558563ea93ef7428aa220c2e15e3f02711420f))
* **rithmomachia:** enhance Pieces section with visual examples and pyramid details ([55aff82](https://github.com/antialias/soroban-abacus-flashcards/commit/55aff829f4c284e8cfe6d471c0821575928b93bc))
* **rithmomachia:** enhance Pyramid section with comprehensive details ([9fde1ef](https://github.com/antialias/soroban-abacus-flashcards/commit/9fde1ef9e703e26b2450128155b53fdf2d2e1fe5))
* **rithmomachia:** guide defaults to docked right on open ([11f674d](https://github.com/antialias/soroban-abacus-flashcards/commit/11f674d542ea5e4e88bd60ff1068451805d9766e))
* **rithmomachia:** improve guide pieces section layout ([a270bfc](https://github.com/antialias/soroban-abacus-flashcards/commit/a270bfc0cc4a3b6b54ba43a5af14a227cc7d29f9))
* **rithmomachia:** improve guide UX and add persistence ([b314740](https://github.com/antialias/soroban-abacus-flashcards/commit/b31474069734350a7059cd7c73255a7e11b78eb9))
* **rithmomachia:** improve roster status notice UX ([e27df45](https://github.com/antialias/soroban-abacus-flashcards/commit/e27df45256147f958ca215f9dd1f4e133e8cf06c))
* **rithmomachia:** integrate roster warning into game nav ([8a11594](https://github.com/antialias/soroban-abacus-flashcards/commit/8a11594203fb91faee6cbc4cb74367164ecd6d85))
* **rithmomachia:** make guide modal ultra-responsive down to 150px width ([0474197](https://github.com/antialias/soroban-abacus-flashcards/commit/04741971b296976f4476ecd949e84066fc549010))
* **rithmomachia:** recreate original guide modal header layout ([2489695](https://github.com/antialias/soroban-abacus-flashcards/commit/24896957d0817758c5f64c0e3473e6a0a343af67))
* **rithmomachia:** show capture error on hover instead of click ([339b678](https://github.com/antialias/soroban-abacus-flashcards/commit/339b6780f657ace5bfe1611c4ef64bb0c2c31587))
* **rithmomachia:** show pyramid face numbers on hover instead of selection ([b0c4523](https://github.com/antialias/soroban-abacus-flashcards/commit/b0c4523c0b4669c96a50b2812ba6cb2faa3f9a22))
* **rithmomachia:** show pyramid face numbers when selected ([5c186f3](https://github.com/antialias/soroban-abacus-flashcards/commit/5c186f3947cc38f1f5db5de3e68e590b90c2d092))
* **rithmomachia:** show pyramid face numbers when selected with subtle animation ([5c2ddbe](https://github.com/antialias/soroban-abacus-flashcards/commit/5c2ddbef05d7f4195d21b084cb1c0c4193ee3c9c))
* **rithmomachia:** show real preview layout when dragging guide to dock ([17d2460](https://github.com/antialias/soroban-abacus-flashcards/commit/17d2460a8769a21d33fabc5f909cf5b939712d36))
* **rithmomachia:** simplify guide language for clarity ([85cb630](https://github.com/antialias/soroban-abacus-flashcards/commit/85cb630add395a6693ecbbe9c8fc6aaf8c47be29))
* **rithmomachia:** skip helper selection UI and auto-select first valid helper ([be2a00e](https://github.com/antialias/soroban-abacus-flashcards/commit/be2a00e8b366b5606525309b4c7813f5c35c7f7c))
* **rithmomachia:** Update harmony system to classical three-piece proportions ([08c9762](https://github.com/antialias/soroban-abacus-flashcards/commit/08c97620f5e694b8526c448c44d265e6dd1fe1eb))
* **rithmomachia:** Update to traditional board setup with 25 pieces per side ([0769eaa](https://github.com/antialias/soroban-abacus-flashcards/commit/0769eaaa1dc238b901e3a7cfe0486e6122d5eda9))
* **rithmomachia:** use actual piece SVGs in number bond with 2.5s rotation animation ([976a7de](https://github.com/antialias/soroban-abacus-flashcards/commit/976a7de949c22842f4b6da3ced990f502a1c2733))
* **room-share:** add QR code button for easy mobile joining ([349290a](https://github.com/antialias/soroban-abacus-flashcards/commit/349290ac6a411651686b64d2e6b540083d2df1d9))
* show rithmomachia turn in nav ([7c89bfe](https://github.com/antialias/soroban-abacus-flashcards/commit/7c89bfef9c60db0e2c46e920500dcc1fbe90d3df))
* switch to royal color theme with transparent background ([944ad65](https://github.com/antialias/soroban-abacus-flashcards/commit/944ad6574e01a67ce1fdbb1f2452fe632c78ce43)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#f59e0](https://github.com/antialias/soroban-abacus-flashcards/issues/f59e0) [#a855f7](https://github.com/antialias/soroban-abacus-flashcards/issues/a855f7) [#7e22](https://github.com/antialias/soroban-abacus-flashcards/issues/7e22)
### Bug Fixes
* adjust hero abacus position to avoid covering subtitle ([f03d341](https://github.com/antialias/soroban-abacus-flashcards/commit/f03d3413145cc7ddfba93728ecdec7eabea9ada6))
* **arcade:** add automatic retry for version conflict rejections ([fbcde25](https://github.com/antialias/soroban-abacus-flashcards/commit/fbcde2505f7ff2bf3426f3458e480c4548314ba4))
* **arcade:** allow deactivating players from users who left the room ([7c1c2d7](https://github.com/antialias/soroban-abacus-flashcards/commit/7c1c2d7bebbb9a1acb274d17dd43b6ee5d196f44))
* **arcade:** implement optimistic locking in session manager ([71fd66d](https://github.com/antialias/soroban-abacus-flashcards/commit/71fd66d96a3b03650c90f59f6e516aae7dddc345))
* board rotation now properly fills height in portrait mode ([b5a96ea](https://github.com/antialias/soroban-abacus-flashcards/commit/b5a96eaeb1e29c20304142a7a0adf62f1cef570f))
* **card-sorting:** add border radius to outer card container ([a922eba](https://github.com/antialias/soroban-abacus-flashcards/commit/a922eba73c4656ee941ce4dfb1dc57a62f076570))
* **card-sorting:** add debug logging for spring animations ([d42947e](https://github.com/antialias/soroban-abacus-flashcards/commit/d42947eb8d5d3d8298f5d3b3d1644891c268dbb6))
* **card-sorting:** add missing gameMode support after hard reset ([a832325](https://github.com/antialias/soroban-abacus-flashcards/commit/a832325debde289d6928c5e6f9c24311c5e079ad))
* **card-sorting:** add missing useMemo import ([949d76d](https://github.com/antialias/soroban-abacus-flashcards/commit/949d76d844c786ada8a6373e4abb7f498f6befb9))
* **card-sorting:** add overflow hidden to clip rounded corners ([84c66fe](https://github.com/antialias/soroban-abacus-flashcards/commit/84c66feec6b4112b015e1afd95bf33b24b5f6a4f))
* **card-sorting:** adjust connecting paths for scaled cards ([829c741](https://github.com/antialias/soroban-abacus-flashcards/commit/829c741e554d1490dd7a5bbc17f2a32f7195dc07))
* **card-sorting:** adjust game board for spectator panels ([fc5cf12](https://github.com/antialias/soroban-abacus-flashcards/commit/fc5cf1216fe03edfb7e44afda01192f4b97b4f4e))
* **card-sorting:** adjust viewport dimensions for spectator panels ([4dce16c](https://github.com/antialias/soroban-abacus-flashcards/commit/4dce16cca46c965199b7e09f8b34bfa221efac33))
* **card-sorting:** animate cards from game board to results grid ([17d45fe](https://github.com/antialias/soroban-abacus-flashcards/commit/17d45fe88cd9773f5e550f6ee5a7f0c82cca2023))
* **card-sorting:** correct suffix card detection in auto-arrange ([d02ab59](https://github.com/antialias/soroban-abacus-flashcards/commit/d02ab5922c416042d525f54097a6975ae1541586))
* **card-sorting:** enable card scaling for spectators ([6b095c3](https://github.com/antialias/soroban-abacus-flashcards/commit/6b095c33830341c46139bc847ddaab3db632265e))
* **card-sorting:** enable New Game button during active gameplay ([f3f6eca](https://github.com/antialias/soroban-abacus-flashcards/commit/f3f6eca1db30df9e1e34cc4e77a069a6a3954f3d))
* **card-sorting:** end drag immediately when card becomes locked ([ae45298](https://github.com/antialias/soroban-abacus-flashcards/commit/ae45298ec48efb29587c0a1c1a7986a72821f3ef))
* **card-sorting:** filter local player from emoji overlays on dragged cards ([dc2d94a](https://github.com/antialias/soroban-abacus-flashcards/commit/dc2d94aaa58531ed4f9047e2ca92724d9264643d))
* **card-sorting:** fix results panel layout to not cover cards ([4b4fbfe](https://github.com/antialias/soroban-abacus-flashcards/commit/4b4fbfef322ecda06020ad52d4b1788267112460))
* **card-sorting:** hide activity notifications in spectator mode ([5cca279](https://github.com/antialias/soroban-abacus-flashcards/commit/5cca279687d8973d25bd9a411a55b632d1c82f63))
* **card-sorting:** keep arrow sequence numbers upright ([79c9469](https://github.com/antialias/soroban-abacus-flashcards/commit/79c94699fa1cc2a2886e3ab1addc5fcd975602f5))
* **card-sorting:** lock correctly positioned prefix/suffix cards ([170abed](https://github.com/antialias/soroban-abacus-flashcards/commit/170abed2318432f309de40692f6092bb4c4a1a45))
* **card-sorting:** lock spring positions after initial animation completes ([275cc62](https://github.com/antialias/soroban-abacus-flashcards/commit/275cc62a523d9e849f2162001141b6d75ae0925e))
* **card-sorting:** New Game now restarts with same settings instantly ([f3687ed](https://github.com/antialias/soroban-abacus-flashcards/commit/f3687ed236eff4ebe61699ec02909024c7086fb5))
* **card-sorting:** only shrink/fade cards in correct prefix ([51368c6](https://github.com/antialias/soroban-abacus-flashcards/commit/51368c6ec59d5447ce2875c5e1181dec97fd509d))
* **card-sorting:** preserve card positions on pause/resume ([0d8af09](https://github.com/antialias/soroban-abacus-flashcards/commit/0d8af09517534f1e1cf1f57160391d465a279d76))
* **card-sorting:** preserve rotation when starting drag ([3364144](https://github.com/antialias/soroban-abacus-flashcards/commit/3364144fb6212934b6ad6d63ac6e7b78b436b258))
* **card-sorting:** prevent duplicate START_GAME moves on Play Again ([a0b14f8](https://github.com/antialias/soroban-abacus-flashcards/commit/a0b14f87e9c5b32fcbb685da4e70c563f70ed91a))
* **card-sorting:** prevent ghost movements with proper optimistic updates ([bd014be](https://github.com/antialias/soroban-abacus-flashcards/commit/bd014bec4ffa12bcd8f4a4e84ff51203c90c1f1d))
* **card-sorting:** prevent infinite loop when all cards are correct ([34785f4](https://github.com/antialias/soroban-abacus-flashcards/commit/34785f466faaa6b9f2958df786af88561fa80b06))
* **card-sorting:** prevent infinite loop with tolerance-based position comparison ([627b873](https://github.com/antialias/soroban-abacus-flashcards/commit/627b873382eaa76ad16477280d10451cf2951e1a))
* **card-sorting:** prevent position jump when clicking rotated cards ([564a00f](https://github.com/antialias/soroban-abacus-flashcards/commit/564a00f82b6ca6aa8a2c0586ca49fc42d44991a8))
* **card-sorting:** prevent replaying own movements from server ([308168a](https://github.com/antialias/soroban-abacus-flashcards/commit/308168a7fb51013b0851e98b161ba1a1a3e39fbb))
* **card-sorting:** prevent springs from reinitializing on window resize ([30953b8](https://github.com/antialias/soroban-abacus-flashcards/commit/30953b8c4a3cf147f980455818f9ce8eea07837c))
* **card-sorting:** prevent springs from resetting after animation ([8aff60c](https://github.com/antialias/soroban-abacus-flashcards/commit/8aff60ce3f8d302ce5c1bde7cb773e63064c36b7))
* **card-sorting:** remove hasAnimatedRef logic causing backwards animation ([a44aa5a](https://github.com/antialias/soroban-abacus-flashcards/commit/a44aa5a4c2d84cab7cf0bbf87485bb61548fdeb2))
* **card-sorting:** remove remaining reveal numbers references ([15c53ea](https://github.com/antialias/soroban-abacus-flashcards/commit/15c53ea4eb4abb824eb0360fb645b1f3e455578e))
* **card-sorting:** restore prefix/suffix card shrinking visual feedback ([f5fb4d7](https://github.com/antialias/soroban-abacus-flashcards/commit/f5fb4d7b76e25286bcdecd017894ff2d78b31963))
* **card-sorting:** show only active players in team members section ([fa9f1a5](https://github.com/antialias/soroban-abacus-flashcards/commit/fa9f1a568f3dff2f4e5e7d3e8841b951ef1b7d04))
* **card-sorting:** smooth scale animation while dragging cards ([0eefc33](https://github.com/antialias/soroban-abacus-flashcards/commit/0eefc332ac2724c54b477301a269915e895db94f))
* **card-sorting:** stabilize inferred sequence for locked cards during drag ([b0cd194](https://github.com/antialias/soroban-abacus-flashcards/commit/b0cd194838705bb7bbf21ac9e318eaba491097b2))
* **card-sorting:** use empty deps array for useSprings to prevent recreation ([cee399e](https://github.com/antialias/soroban-abacus-flashcards/commit/cee399ed1513d32d0fff51a6f63898aa861605e1))
* **card-sorting:** use ref to track initialized state and prevent re-animation ([f389afa](https://github.com/antialias/soroban-abacus-flashcards/commit/f389afa831935e896a626f526cfee378e340a64b))
* **card-sorting:** use same coordinate system for game board and results ([6972fdf](https://github.com/antialias/soroban-abacus-flashcards/commit/6972fdf1105b6e854494efe1c4c587e6b6ff32a9))
* **complement-race:** prevent delivery move thrashing in steam sprint mode ([e1258ee](https://github.com/antialias/soroban-abacus-flashcards/commit/e1258ee0416010909774694c0b25306b6f30329c))
* configure favicon metadata and improve bead visibility ([e1369fa](https://github.com/antialias/soroban-abacus-flashcards/commit/e1369fa2754cd61745a2950e6cb767d6b08db38f))
* copy entire packages/core and packages/templates ([0ccada0](https://github.com/antialias/soroban-abacus-flashcards/commit/0ccada0ca783e635f9ae08f33a69c392018ee342))
* correct hero abacus scroll direction to flow with page content ([4232746](https://github.com/antialias/soroban-abacus-flashcards/commit/423274657c9698bba28f7246fbf48d8508d97ef9))
* correct Typst template path in Dockerfile ([4c518de](https://github.com/antialias/soroban-abacus-flashcards/commit/4c518decb7fcc0b519d07680cbfd01c94c23dd41))
* delete existing user sessions before creating new ones ([0cced47](https://github.com/antialias/soroban-abacus-flashcards/commit/0cced47a0f414a04371bdb253fc5a43e4d9557be))
* extract pure SVG content from AbacusReact renders ([b07f1c4](https://github.com/antialias/soroban-abacus-flashcards/commit/b07f1c421616bcfd1f949f9a42ce1b03df418945))
* **games:** prevent horizontal page scroll from carousel overflow ([5a8c98f](https://github.com/antialias/soroban-abacus-flashcards/commit/5a8c98fc10704e459690308a84dc7ee2bfa0ef6c))
* **games:** smooth scroll feel for carousel wheel navigation ([f80a73b](https://github.com/antialias/soroban-abacus-flashcards/commit/f80a73b35c324959bfd7141ebf086cb47d3c0ebc))
* **games:** use specific transition properties for smooth carousel loop ([187271e](https://github.com/antialias/soroban-abacus-flashcards/commit/187271e51527ee0129f71d77be1bd24072b963c4))
* **i18n:** eliminate FOUC by loading messages server-side ([4d4d930](https://github.com/antialias/soroban-abacus-flashcards/commit/4d4d930bd307ce5a405fc5751af6682a9f221f1f))
* **i18n:** use useMessages() for tutorial translations ([95b0105](https://github.com/antialias/soroban-abacus-flashcards/commit/95b0105ca3b28c5adfa843e8d77a8b27d9e7ade4))
* include column posts in favicon bounding box ([0b2f481](https://github.com/antialias/soroban-abacus-flashcards/commit/0b2f48106a939307b728c86fe2ea1be1e0247ea8))
* increase server update debounce to 2000ms for low bandwidth ([633ff12](https://github.com/antialias/soroban-abacus-flashcards/commit/633ff127500c893a215491afa0e6ff814ad553bf))
* Integrate threshold input into Point Victory card ([b29bbee](https://github.com/antialias/soroban-abacus-flashcards/commit/b29bbeefcad92be42f7a3ca27ac126db4232ab26))
* mark dynamic routes as force-dynamic to prevent static generation errors ([d7b35d9](https://github.com/antialias/soroban-abacus-flashcards/commit/d7b35d954421fd7577cd2c26247666e5953b647d))
* **nav:** show full navigation on /games page ([d3fe6ac](https://github.com/antialias/soroban-abacus-flashcards/commit/d3fe6acbb0390e1df71869a4095e5ee6021e06b1))
* **qr-button:** improve layout and z-index ([646a422](https://github.com/antialias/soroban-abacus-flashcards/commit/646a4228d0573796b1a429e31bc037411024c0ff))
* **qr-button:** increase mini QR code size to 80px ([61ac737](https://github.com/antialias/soroban-abacus-flashcards/commit/61ac7378bdb01132b26bfc265a057c095ea41606))
* **qr-button:** increase mini QR code to 84px ([3fae5ea](https://github.com/antialias/soroban-abacus-flashcards/commit/3fae5ea6fa9ebd0f8fe8c9140a027be7f6a041aa))
* **qr-button:** make button square and increase QR size ([dc2d466](https://github.com/antialias/soroban-abacus-flashcards/commit/dc2d46663b8e0ec94a1508a57c4f8c2d8ba03506))
* **qr-button:** match height of stacked buttons ([81f202d](https://github.com/antialias/soroban-abacus-flashcards/commit/81f202d21556aa430402fda814519adbc8883831))
* reduce padding to minimize gap below last bead ([0e529be](https://github.com/antialias/soroban-abacus-flashcards/commit/0e529be789caf16e73f3e2ee77f52e243841aef4))
* remove distracting parallax and wobble 3D effects ([28a2d40](https://github.com/antialias/soroban-abacus-flashcards/commit/28a2d40996256700bf19cd80130b26e24441949f))
* remove wobble physics and enhance wood grain visibility ([5d97673](https://github.com/antialias/soroban-abacus-flashcards/commit/5d976734062eb3d943bfdfdd125473c56b533759))
* resolve z-index layering and hero abacus visibility issues ([ed9a050](https://github.com/antialias/soroban-abacus-flashcards/commit/ed9a050d64db905e1328008f25dc0014e9a81999))
* rewrite 3D stories to use props instead of CSS wrappers ([26bdb11](https://github.com/antialias/soroban-abacus-flashcards/commit/26bdb112370cece08634e3d693d15336111fc70f))
* **rithmomachia:** add missing i18next dependencies ([91154d9](https://github.com/antialias/soroban-abacus-flashcards/commit/91154d93647e59f7e5f96d1db5624a7ec9b1b9ff))
* **rithmomachia:** add missing pyramid section keys to Japanese (ja.json) ([dae615e](https://github.com/antialias/soroban-abacus-flashcards/commit/dae615ee72a7ec7d0b235a22c61ebc4af0d8eadb))
* **rithmomachia:** adjust error dialog sizing to prevent text clipping ([cda1126](https://github.com/antialias/soroban-abacus-flashcards/commit/cda1126cb0eab6840df89f3a8778d72410298093))
* **rithmomachia:** adjust roster notice position to not overlap nav ([7093223](https://github.com/antialias/soroban-abacus-flashcards/commit/709322373a91c8174d21052d184fa84dd8bda326))
* **rithmomachia:** change undock icon to pop-out arrow ([2a91748](https://github.com/antialias/soroban-abacus-flashcards/commit/2a917484938bc269cf16acb501d4d26584405e0f))
* **rithmomachia:** correct board dimensions to 16x8 and restore original layout values ([cfac277](https://github.com/antialias/soroban-abacus-flashcards/commit/cfac27750526fb1f6a7e4314a96aab3b92e08e44))
* **rithmomachia:** Correct board setup to match reference image exactly ([618e563](https://github.com/antialias/soroban-abacus-flashcards/commit/618e56358deb66cba968472f39b8d4e28b4dd211))
* **rithmomachia:** correct makeMove parameter types for capture handling ([aafb64f](https://github.com/antialias/soroban-abacus-flashcards/commit/aafb64f3e337c6cf925766fe179b91f66c4a040b))
* **rithmomachia:** fix guide modal resize drift by calculating from initial state ([1bcd99c](https://github.com/antialias/soroban-abacus-flashcards/commit/1bcd99c949e4d2b4fb1c0813debd50176fa58cb9))
* **rithmomachia:** fix harmony section translation structure for hi/ja/es ([14259a1](https://github.com/antialias/soroban-abacus-flashcards/commit/14259a19a9817d0947467faa004d5f43118f8d8d))
* **rithmomachia:** fix modal resizing zoom issue ([4fa20f4](https://github.com/antialias/soroban-abacus-flashcards/commit/4fa20f44cb9758f29d1f1512232be0fdc0b53b3d))
* **rithmomachia:** Fix TypeScript errors in playing guide modal ([4834ece](https://github.com/antialias/soroban-abacus-flashcards/commit/4834ece98e86f2fb00511bb876a5c32c289df0e0))
* **rithmomachia:** handle pyramid pieces in hover error tooltip ([56f3164](https://github.com/antialias/soroban-abacus-flashcards/commit/56f3164155beb94ceec2838bed9fc74fd75524db))
* **rithmomachia:** implement proper board cropping and highlighting in guide ([d0a8fcd](https://github.com/antialias/soroban-abacus-flashcards/commit/d0a8fcdea6aa4fdacfee33e183c92923634ee2b7))
* **rithmomachia:** improve guide modal tab navigation at narrow widths ([a673177](https://github.com/antialias/soroban-abacus-flashcards/commit/a673177bec1c709463ce0f266848f473a79f4ef0))
* **rithmomachia:** reconnect player assignment UI and fix setup layout ([a1a0374](https://github.com/antialias/soroban-abacus-flashcards/commit/a1a0374fac5dce676df5890663b75531589ed93a))
* **rithmomachia:** render guide as docked in preview panel ([190f8cf](https://github.com/antialias/soroban-abacus-flashcards/commit/190f8cf302aa966f029d05931811e217c67bfe39))
* **rithmomachia:** show actual values in tooltips for non-helper relations ([774c6b0](https://github.com/antialias/soroban-abacus-flashcards/commit/774c6b0ce712b1a77bb684457da9831e6ec91138))
* **rithmomachia:** show guest-friendly message when they can't fix too many players ([54bfd2f](https://github.com/antialias/soroban-abacus-flashcards/commit/54bfd2fac86be3597d40c67a1235e4c4ed8e2709))
* **rithmomachia:** smooth guide dragging from docked state without jump ([8f4a79c](https://github.com/antialias/soroban-abacus-flashcards/commit/8f4a79c9b0cad55336584fdc8e67409015d3a8ae))
* **rithmomachia:** validate move path before showing capture error on hover ([bd49964](https://github.com/antialias/soroban-abacus-flashcards/commit/bd49964186a0daa1639ae849b128a76081643daf))
* **room-info:** hide Leave Room button when user is alone ([5927f61](https://github.com/antialias/soroban-abacus-flashcards/commit/5927f61c3c34ba583ee45c8cee48a116c1c03071))
* separate horizontal and vertical bounding box logic ([83090df](https://github.com/antialias/soroban-abacus-flashcards/commit/83090df4dfad1d1d5cfa6c278c241526cacc7972))
* tolerate OpenSCAD CGAL warnings if output file is created ([88993f3](https://github.com/antialias/soroban-abacus-flashcards/commit/88993f36629206a7bdcf9aa9d5641f1580b64de5))
* use absolute positioning for hero abacus to eliminate scroll lag ([096104b](https://github.com/antialias/soroban-abacus-flashcards/commit/096104b094b45aa584f2b9d47a440a8c14d82fc0))
* use Debian base for deps stage to match runner for binary compatibility ([f8fe6e4](https://github.com/antialias/soroban-abacus-flashcards/commit/f8fe6e4a415f8655626af567129d0cda61b82e15))
* use default BOSL2 branch instead of non-existent v2.0.0 tag ([f4ffc5b](https://github.com/antialias/soroban-abacus-flashcards/commit/f4ffc5b0277535358bea7588309a1a4afd1983a1))
* use nested SVG viewBox for actual cropping, not just scaling ([440b492](https://github.com/antialias/soroban-abacus-flashcards/commit/440b492e85beff1612697346b6c5cfc8461e83da))
* various game improvements and UI enhancements ([b67cf61](https://github.com/antialias/soroban-abacus-flashcards/commit/b67cf610c570d54744553cd8f6694243fa50bee1))
### Performance Improvements
* optimize Docker image size to reduce build failures ([9ca3106](https://github.com/antialias/soroban-abacus-flashcards/commit/9ca310636183f4970db925ce8fa368e23645eb02))
### Code Refactoring
* **card-sorting:** remove reveal numbers feature ([ea5e3e8](https://github.com/antialias/soroban-abacus-flashcards/commit/ea5e3e838bd6a5b8b38469a70aa92a0e9baba769))
* **card-sorting:** send complete card sequence instead of individual moves ([e4df843](https://github.com/antialias/soroban-abacus-flashcards/commit/e4df8432b9c4a2055d47833d56b6e9fcf325ca94))
* **games:** implement carousel, fix victories bug, add conditional stats ([82c133f](https://github.com/antialias/soroban-abacus-flashcards/commit/82c133f742f3f5c40b723c18d1997b518f25b320))
* **games:** move page title to nav bar ([712ee58](https://github.com/antialias/soroban-abacus-flashcards/commit/712ee58e5956e5bbdb13d5a5fb367020c87c8c9a))
* **games:** remove redundant subtitle below nav ([ad5bb87](https://github.com/antialias/soroban-abacus-flashcards/commit/ad5bb87325a44825f0cd85b38eb0e5f0eea7a695))
* **games:** remove wheel scrolling, enable overflow visible carousel ([876513c](https://github.com/antialias/soroban-abacus-flashcards/commit/876513c9cc6323c20845ae8f1a3a5478d449f9e4))
* reorganize Harmony and Victory guide sections ([fb629c4](https://github.com/antialias/soroban-abacus-flashcards/commit/fb629c44ea37a7b296561919a4980c10d14efed8))
* restructure /create page into hub with sub-pages ([b91b23d](https://github.com/antialias/soroban-abacus-flashcards/commit/b91b23d95ffaeeaa30dbc8579f4c30bae8829ee7))
* **rithmomachia:** extract board and capture components (phase 2+3) ([a0a867b](https://github.com/antialias/soroban-abacus-flashcards/commit/a0a867b27166a838ca7e0dcbd8f89fe1be812a80))
* **rithmomachia:** extract CaptureErrorDialog component (Phase 2 partial) ([f0a066d](https://github.com/antialias/soroban-abacus-flashcards/commit/f0a066d8f0a51d35e18f87a8436c0d05153c03b5))
* **rithmomachia:** extract constants and coordinate utilities (Phase 1) ([eace0ed](https://github.com/antialias/soroban-abacus-flashcards/commit/eace0ed52979b71870f77ee68f8568558f2aaecb))
* **rithmomachia:** extract guide sections into separate files ([765525d](https://github.com/antialias/soroban-abacus-flashcards/commit/765525dc451897f561f017e444aae892dc27177f))
* **rithmomachia:** extract hooks (phase 5) ([324a659](https://github.com/antialias/soroban-abacus-flashcards/commit/324a65992f97c295ea3968aaf54d266373f4c035))
* **rithmomachia:** extract phase components (phase 4) ([11364f6](https://github.com/antialias/soroban-abacus-flashcards/commit/11364f6394c15e49850e5cad2cbd32e1ea08a178))
* **rithmomachia:** extract reusable components from SetupPhase ([3abc325](https://github.com/antialias/soroban-abacus-flashcards/commit/3abc325ea27feee5c4cc59f02296ff218f342a81))
* **rithmomachia:** make setup phase UI more compact ([e55f848](https://github.com/antialias/soroban-abacus-flashcards/commit/e55f848a26092a2b4a5b09c3c255544ea9666f1b))
* **rithmomachia:** redesign error notification with modern UI ([dfeeb0e](https://github.com/antialias/soroban-abacus-flashcards/commit/dfeeb0e0db8b2c4a38198cf71cd918439d6c211b)), closes [#1e293](https://github.com/antialias/soroban-abacus-flashcards/issues/1e293) [#0f172](https://github.com/antialias/soroban-abacus-flashcards/issues/0f172) [#f1f5f9](https://github.com/antialias/soroban-abacus-flashcards/issues/f1f5f9)
* **rithmomachia:** simplify capture error dialog to one-liner ([82a5eb2](https://github.com/antialias/soroban-abacus-flashcards/commit/82a5eb2e4bf74f42a183a15f1129e5ec84cc5231))
* **rithmomachia:** Update board setup to authoritative CSV layout ([0471da5](https://github.com/antialias/soroban-abacus-flashcards/commit/0471da598d8d591b3f9d63f467cb35f999924c13))
* **rithmomachia:** update capture components to use CaptureContext ([2ab6ab5](https://github.com/antialias/soroban-abacus-flashcards/commit/2ab6ab57995a6d7d9c66b9fba8de945507209661))
* **rithmomachia:** use useBoardLayout and usePieceSelection in BoardDisplay ([0ab7a1d](https://github.com/antialias/soroban-abacus-flashcards/commit/0ab7a1df327d7258228af9851762555583a20d61))
* use AbacusReact for dynamic Open Graph image ([9c20f12](https://github.com/antialias/soroban-abacus-flashcards/commit/9c20f12bacff4fe7f8bd7a87032afbed9711e94b))
### Documentation
* add 3D enhancement documentation to README ([cc96802](https://github.com/antialias/soroban-abacus-flashcards/commit/cc96802df87c805c946ee59af509663ba570e75b))
* add database migration guide and playing guide modal spec ([5a29af7](https://github.com/antialias/soroban-abacus-flashcards/commit/5a29af78e27e897ab35273611b79c4b669304f71))
* add deployment verification guidelines to prevent false positives ([3d8da23](https://github.com/antialias/soroban-abacus-flashcards/commit/3d8da2348b4e8a227e963791d15dc6718eac5af1))
* **card-sorting:** add comprehensive multiplayer plan ([008ccea](https://github.com/antialias/soroban-abacus-flashcards/commit/008ccead0f9c634fe52fd156e6f9a04d6cdd7744))
* **rithmomachia:** Add concise one-page playing guide ([e3c1f10](https://github.com/antialias/soroban-abacus-flashcards/commit/e3c1f10233cc0924ff96a643c7c4c1f1278de3e3))
* update workflow to require manual testing before commits ([0991796](https://github.com/antialias/soroban-abacus-flashcards/commit/0991796f1eccef345f10205e675e4c33d1a62b17))
### Styles
* **rithmomachia:** improve divider styling and make tabs responsive ([88ca35e](https://github.com/antialias/soroban-abacus-flashcards/commit/88ca35e0440157ff9349e8d3d2d3cc844f18ffea)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3)
* **rithmomachia:** improve pyramid face numbers visibility and contrast ([94e5e6a](https://github.com/antialias/soroban-abacus-flashcards/commit/94e5e6a268b387380b88b192737bd55578b98bc7)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#b45309](https://github.com/antialias/soroban-abacus-flashcards/issues/b45309)
* **rithmomachia:** increase pyramid face numbers size and boldness ([7bf2d73](https://github.com/antialias/soroban-abacus-flashcards/commit/7bf2d730d370e562486b229f4d209099ff8c4463))
### Tests
* trigger compose-updater deployment test ([2b06aae](https://github.com/antialias/soroban-abacus-flashcards/commit/2b06aae39474cc80d501c47c9685fa99e7120c48))
* verify compose-updater automatic deployment cycle ([af0552c](https://github.com/antialias/soroban-abacus-flashcards/commit/af0552ccd98f7b5a62d6e4074b7d87b3716af698))
## [4.68.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.67.1...v4.68.0) (2025-11-03)
### Features
* **abacus:** add nativeAbacusNumbers setting to schema and UI ([79f7347](https://github.com/antialias/soroban-abacus-flashcards/commit/79f7347d4800646378470a7f9aca8e7f2fd5573c))
* add 3D printing support for abacus models ([dafdfdd](https://github.com/antialias/soroban-abacus-flashcards/commit/dafdfdd233b53464b9825a8a9b5f2e6206fc54cb))
* add comprehensive metadata, SEO, and make AbacusReact SSR-compatible ([0922ea1](https://github.com/antialias/soroban-abacus-flashcards/commit/0922ea10b77e7d16b8c414c596d23cb11e20c1cc))
* add comprehensive Storybook coverage and migration guide ([7a4a37e](https://github.com/antialias/soroban-abacus-flashcards/commit/7a4a37ec6d0171782778e18122da782f069e0556))
* add game preview system with mock arcade environment ([25880cc](https://github.com/antialias/soroban-abacus-flashcards/commit/25880cc7e463f98a5a23c812c1ffd43734d3fe1f))
* add per-player stats tracking system ([613301c](https://github.com/antialias/soroban-abacus-flashcards/commit/613301cd137ad6f712571a0be45c708ce391fc8f))
* add Strategy & Tactics section to Rithmomachia guide ([81ead65](https://github.com/antialias/soroban-abacus-flashcards/commit/81ead65680892efa4d0ab07e7f0ef77eb1bc1405))
* add unified trophy abacus with hero mode integration ([6620418](https://github.com/antialias/soroban-abacus-flashcards/commit/6620418a704dcca810b511a5f394084521104e6b))
* **arcade:** add ability to deactivate remote players without kicking user ([3628426](https://github.com/antialias/soroban-abacus-flashcards/commit/3628426a567d7e0273be75cce64632ae04b7d5eb))
* **arcade:** add native abacus numbers support to pressure gauge ([1d525c7](https://github.com/antialias/soroban-abacus-flashcards/commit/1d525c7b5320984a1582b8ab7eae57895c728428))
* **arcade:** add Rithmomachia (Battle of Numbers) game ([2fc0a05](https://github.com/antialias/soroban-abacus-flashcards/commit/2fc0a05f7f557cee55f7d31b585499dd04e68ff9))
* **arcade:** add yjs-demo collaborative game and Yjs persistence layer ([d568955](https://github.com/antialias/soroban-abacus-flashcards/commit/d568955d6abf389e6ab7c6979e33122a65917a46))
* **arcade:** auto-create room when user has none ([ff88c3a](https://github.com/antialias/soroban-abacus-flashcards/commit/ff88c3a1b81703a87a1d57eeb5cc139da7d9df04))
* **card-sorting:** add activity feed notifications for collaborative mode ([1461414](https://github.com/antialias/soroban-abacus-flashcards/commit/1461414ef4d0b213af241213447c91eed1abe5fb))
* **card-sorting:** add auto-submit countdown for perfect sequences ([780a716](https://github.com/antialias/soroban-abacus-flashcards/commit/780a7161bc05c2ca6597d7d8d89f01afd33d9f4d))
* **card-sorting:** add bezier curves to connecting arrows ([4d8e873](https://github.com/antialias/soroban-abacus-flashcards/commit/4d8e873358271fe3fd50b228aea8277e20aa5966))
* **card-sorting:** add CardPosition type and position syncing ([656f5a7](https://github.com/antialias/soroban-abacus-flashcards/commit/656f5a7838ed6003c214ec484d4c37072270fa8d))
* **card-sorting:** add collapsible stats sidebar for spectators ([6527c26](https://github.com/antialias/soroban-abacus-flashcards/commit/6527c26a8166b23f074e85eb335a15800c1947a2))
* **card-sorting:** add game mode selector UI to setup phase ([d25b888](https://github.com/antialias/soroban-abacus-flashcards/commit/d25b888ffb3915d2d482442ab708ba3e159af512))
* **card-sorting:** add GameMode type system for multiplayer support ([fd76533](https://github.com/antialias/soroban-abacus-flashcards/commit/fd765335efbc91366c596c7789b92882cd3379d9))
* **card-sorting:** add green border to correctly positioned cards ([16fca86](https://github.com/antialias/soroban-abacus-flashcards/commit/16fca86b7687115f1cf565c533a512e92946e3a8)), closes [#22c55](https://github.com/antialias/soroban-abacus-flashcards/issues/22c55)
* **card-sorting:** add player emoji indicators on moving cards ([3a82099](https://github.com/antialias/soroban-abacus-flashcards/commit/3a8209975728cdcf914c43ba08339454a9e2457f))
* **card-sorting:** add react-spring animations for real-time sync ([c367e0c](https://github.com/antialias/soroban-abacus-flashcards/commit/c367e0ceece41d8e7c2bc8aebe3239ff6053a115))
* **card-sorting:** add smooth transition to drop shadow ([b0b93d0](https://github.com/antialias/soroban-abacus-flashcards/commit/b0b93d0175c8a1c8958d6ba346d969c234fdd6ff))
* **card-sorting:** add spectator mode UI enhancements ([ee7345d](https://github.com/antialias/soroban-abacus-flashcards/commit/ee7345d641e0ee72915afb9cdbd6d284b7e238bd)), closes [#6366f1](https://github.com/antialias/soroban-abacus-flashcards/issues/6366f1) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add team scoring UI for collaborative mode ([ed6f177](https://github.com/antialias/soroban-abacus-flashcards/commit/ed6f1779141d0bc9dff2d532a3dfc638015936b5)), closes [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#8b5cf6](https://github.com/antialias/soroban-abacus-flashcards/issues/8b5cf6)
* **card-sorting:** add updateCardPositions action to Provider ([f6ed4a2](https://github.com/antialias/soroban-abacus-flashcards/commit/f6ed4a27a26d8bfa495ba5f580a446286b9674a0))
* **card-sorting:** auto-arrange prefix/suffix cards in corners ([4ba7f24](https://github.com/antialias/soroban-abacus-flashcards/commit/4ba7f247175d93e4d339e2be7bbdb2e009992232))
* **card-sorting:** fade correctly positioned cards to 50% opacity ([7028cfc](https://github.com/antialias/soroban-abacus-flashcards/commit/7028cfc51164e9219479e6040b03c29239aa7edb))
* **card-sorting:** gentler spring animation for locked cards ([47189cb](https://github.com/antialias/soroban-abacus-flashcards/commit/47189cb6e79ed2915f5ddcc9cb3626540dfb07f3))
* **card-sorting:** implement continuous bezier curve paths ([2d93024](https://github.com/antialias/soroban-abacus-flashcards/commit/2d9302410f5e98145a435b00df3ae5fcf3f4c0b5))
* **card-sorting:** improve card distribution for natural scattered look ([0b0503f](https://github.com/antialias/soroban-abacus-flashcards/commit/0b0503f0354a4a82fe6b9bfe827729e8e5a9e329))
* **card-sorting:** make player emoji fill entire card background ([2e7a02c](https://github.com/antialias/soroban-abacus-flashcards/commit/2e7a02c9e4ab84e821d58661d6e7a326f7882afb))
* **card-sorting:** optimize results screen for mobile ([d188789](https://github.com/antialias/soroban-abacus-flashcards/commit/d188789069b4c350ce3cc0d221bd4a43dab528e0))
* **card-sorting:** redesign setup screen with modern UI ([73cf967](https://github.com/antialias/soroban-abacus-flashcards/commit/73cf96749234c480482f62392245b38c1fd5f0a0))
* **card-sorting:** scale correctly positioned cards to 50% ([222dc55](https://github.com/antialias/soroban-abacus-flashcards/commit/222dc555fa5068e2594dcc074e33f70320f5742c))
* **card-sorting:** shrink/fade cards in correct suffix as well ([8f6feec](https://github.com/antialias/soroban-abacus-flashcards/commit/8f6feec4f21d0af0d1c98daf5017eddd91d3d578))
* **card-sorting:** smooth spring transition from game table to results grid ([c5f39d5](https://github.com/antialias/soroban-abacus-flashcards/commit/c5f39d51eb45ec816f32151dc7f9d7c06360474b))
* **card-sorting:** wrap prefix/suffix cards to multiple rows ([e3184dd](https://github.com/antialias/soroban-abacus-flashcards/commit/e3184dd0d444e5dc204731f5b396d5c553cf7d11))
* complete 3D enhancement integration for all three proposals ([5ac55cc](https://github.com/antialias/soroban-abacus-flashcards/commit/5ac55cc14980b778f9be32f0833f8760aa16b631))
* **create-room:** replace hardcoded game grid with dynamic Radix Select dropdown ([83d0ba2](https://github.com/antialias/soroban-abacus-flashcards/commit/83d0ba26f5eeec3e189d279710d5bbcf13e82f29))
* dynamic day-of-month favicon using subprocess pattern ([4d0795a](https://github.com/antialias/soroban-abacus-flashcards/commit/4d0795a9df74fcb085af821eafb923bdcb5f0b0c))
* dynamically crop favicon to active beads for maximum size ([5670322](https://github.com/antialias/soroban-abacus-flashcards/commit/567032296aecaad13408bdc17d108ec7c57fb4a8))
* enable 3D enhancement on hero/open MyAbacus modes ([37e330f](https://github.com/antialias/soroban-abacus-flashcards/commit/37e330f26e5398c2358599361cd417b4aeefac7d))
* **games:** add autoplay and improve carousel layout ([9f51edf](https://github.com/antialias/soroban-abacus-flashcards/commit/9f51edfaa95c14f55a30a6eceafb9099eeed437f))
* **games:** add horizontal scroll support to carousels ([a224abb](https://github.com/antialias/soroban-abacus-flashcards/commit/a224abb6f660e1aa31ab04f5590b003fae072af9))
* **games:** add rotating games hero carousel ([24231e6](https://github.com/antialias/soroban-abacus-flashcards/commit/24231e6b2ebbdcae066344df54e7e80e7d221128))
* **i18n:** add dynamic locale switching without page reload ([fe9bfea](https://github.com/antialias/soroban-abacus-flashcards/commit/fe9bfeabf9ee66923501b18e1b69f2d666d0817d))
* **i18n:** add global language selector to navigation ([0506360](https://github.com/antialias/soroban-abacus-flashcards/commit/0506360117807665e8f5a6fcd8f1178339f6e65c))
* **i18n:** add homepage translations for all supported languages ([8c9d35a](https://github.com/antialias/soroban-abacus-flashcards/commit/8c9d35a3b43dd29664f5afb1bd96c4e584d9ec75))
* **i18n:** add Old High German (goh) language support ([b334a15](https://github.com/antialias/soroban-abacus-flashcards/commit/b334a15255ed9fa29beb43de66da0288691390c6))
* **i18n:** complete Old High German translations for all locales ([0b06a1c](https://github.com/antialias/soroban-abacus-flashcards/commit/0b06a1ce005d92e7ae9c225aba40d240e965753d))
* **i18n:** internationalize games page and tutorial content ([4253964](https://github.com/antialias/soroban-abacus-flashcards/commit/4253964af19f9aaa16f2394f41819223542fb519))
* **i18n:** internationalize homepage with English translations ([40cff14](https://github.com/antialias/soroban-abacus-flashcards/commit/40cff143c72e9228d7cce607cab64c4a6d067017))
* **i18n:** migrate from react-i18next to next-intl ([9016b76](https://github.com/antialias/soroban-abacus-flashcards/commit/9016b760247a20271255839e4dd7e5b9a8353b9f))
* **i18n:** update games page hero section copy ([6333c60](https://github.com/antialias/soroban-abacus-flashcards/commit/6333c60352b920916afd81cc3b0229706a1519fa))
* install embla-carousel-autoplay for games carousel ([946e5d1](https://github.com/antialias/soroban-abacus-flashcards/commit/946e5d19107020992be8945f8fe7c41e4bc2a0e2))
* install embla-carousel-react for player profile carousel ([642ae95](https://github.com/antialias/soroban-abacus-flashcards/commit/642ae957383cfe1d6045f645bbe426fd80c56f35))
* internationalize guide page with 6 languages ([e9c320b](https://github.com/antialias/soroban-abacus-flashcards/commit/e9c320bb1032e94c3852b9459236409da4669c09))
* internationalize tutorial player ([26d41cf](https://github.com/antialias/soroban-abacus-flashcards/commit/26d41cfd058bfdf5b61ee6e20cfc61cbecb32f45))
* optimize card sorting for mobile displays ([b443ee9](https://github.com/antialias/soroban-abacus-flashcards/commit/b443ee9cdcd9fcb7674845d8c92f7c338ad98dea))
* Redesign Rithmomachia setup page with dramatic medieval theme ([6ae4d13](https://github.com/antialias/soroban-abacus-flashcards/commit/6ae4d13dc784a87f85206c6ff6d005e5b23b678c))
* **rithmomachia:** add 80% opacity to guide modal when not hovered ([4a78485](https://github.com/antialias/soroban-abacus-flashcards/commit/4a78485d2e20f2cbf36cc898a1beafa8eb48bfbf))
* **rithmomachia:** add CaptureContext for capture dialog state management ([d7eb957](https://github.com/antialias/soroban-abacus-flashcards/commit/d7eb957a8dabbcac35e166a83dd679a628e19baa))
* **rithmomachia:** add ghost panel preview for guide docking ([c0d6526](https://github.com/antialias/soroban-abacus-flashcards/commit/c0d6526d30aca8deaeda2b7c2e27eb37af8b577c))
* **rithmomachia:** add guide docking with resizable panels ([f457f1a](https://github.com/antialias/soroban-abacus-flashcards/commit/f457f1a1c22b6cb7fff23a7701474322cf423dd9))
* **rithmomachia:** add helper piece selection for mathematical captures ([cae3359](https://github.com/antialias/soroban-abacus-flashcards/commit/cae335958751c27684bfb10c8e2e526b460954ed))
* **rithmomachia:** add helpful error messages for failed captures ([b172440](https://github.com/antialias/soroban-abacus-flashcards/commit/b172440a41e958ced98903bb8f4c2e4b423e1356))
* **rithmomachia:** add initial board visual to guide Overview section ([d42bcff](https://github.com/antialias/soroban-abacus-flashcards/commit/d42bcff0d922895549c1c12f8e02a3ae6d53425a))
* **rithmomachia:** Add interactive playing guide modal ([3121d82](https://github.com/antialias/soroban-abacus-flashcards/commit/3121d8240a567817f5f205a4ef4a788fcf451f71))
* **rithmomachia:** add number bond visualization and helper placeholders ([82d8913](https://github.com/antialias/soroban-abacus-flashcards/commit/82d89131f00517f162ec496397cb390f9ecfc52e))
* **rithmomachia:** add ratio capture example to guide ([9150b0c](https://github.com/antialias/soroban-abacus-flashcards/commit/9150b0c678ce7104fe984ee0fc93748b43a245f4))
* **rithmomachia:** add standalone guide page route ([3fcc79f](https://github.com/antialias/soroban-abacus-flashcards/commit/3fcc79fe9eae11d4bd3a724c1b1f7d086e7cae81))
* **rithmomachia:** add useBoardLayout hook for centralized layout calculations ([27f1c98](https://github.com/antialias/soroban-abacus-flashcards/commit/27f1c989d59a19844b90a5148ae27fb97161da2d))
* **rithmomachia:** add usePieceSelection hook for selection state management ([275f401](https://github.com/antialias/soroban-abacus-flashcards/commit/275f401e3c25b75fec4700a8c2d4be6e33f0afe9))
* **rithmomachia:** add visual board examples to Capture section ([74bc3c0](https://github.com/antialias/soroban-abacus-flashcards/commit/74bc3c0dcf8d1ee7084e88a04861a85f9b623809))
* **rithmomachia:** add visual board examples to Harmony section ([1d5f01c](https://github.com/antialias/soroban-abacus-flashcards/commit/1d5f01c966cf1eec9a9c19ee37f1cad93c89df40))
* **rithmomachia:** add visual winning example to Victory section ([b7fac78](https://github.com/antialias/soroban-abacus-flashcards/commit/b7fac788292e00c6060a47fdbcca89a7e7fee35c))
* **rithmomachia:** auto-size tab labels with react-textfit ([9fd5406](https://github.com/antialias/soroban-abacus-flashcards/commit/9fd54067ce257e028b02f4784568ff3f2bbb32ca))
* **rithmomachia:** cycle through valid helpers with dynamic number tooltips ([4829e41](https://github.com/antialias/soroban-abacus-flashcards/commit/4829e41ea13fae2edec10837e65e505929445782))
* **rithmomachia:** enhance capture relation UI with smooth animations ([0a30801](https://github.com/antialias/soroban-abacus-flashcards/commit/0a308016e9d6a926c52dbfc5623b60b169d16d03))
* **rithmomachia:** enhance Harmony section with comprehensive content ([f555856](https://github.com/antialias/soroban-abacus-flashcards/commit/f5558563ea93ef7428aa220c2e15e3f02711420f))
* **rithmomachia:** enhance Pieces section with visual examples and pyramid details ([55aff82](https://github.com/antialias/soroban-abacus-flashcards/commit/55aff829f4c284e8cfe6d471c0821575928b93bc))
* **rithmomachia:** enhance Pyramid section with comprehensive details ([9fde1ef](https://github.com/antialias/soroban-abacus-flashcards/commit/9fde1ef9e703e26b2450128155b53fdf2d2e1fe5))
* **rithmomachia:** guide defaults to docked right on open ([11f674d](https://github.com/antialias/soroban-abacus-flashcards/commit/11f674d542ea5e4e88bd60ff1068451805d9766e))
* **rithmomachia:** improve guide pieces section layout ([a270bfc](https://github.com/antialias/soroban-abacus-flashcards/commit/a270bfc0cc4a3b6b54ba43a5af14a227cc7d29f9))
* **rithmomachia:** improve guide UX and add persistence ([b314740](https://github.com/antialias/soroban-abacus-flashcards/commit/b31474069734350a7059cd7c73255a7e11b78eb9))
* **rithmomachia:** improve roster status notice UX ([e27df45](https://github.com/antialias/soroban-abacus-flashcards/commit/e27df45256147f958ca215f9dd1f4e133e8cf06c))
* **rithmomachia:** integrate roster warning into game nav ([8a11594](https://github.com/antialias/soroban-abacus-flashcards/commit/8a11594203fb91faee6cbc4cb74367164ecd6d85))
* **rithmomachia:** make guide modal ultra-responsive down to 150px width ([0474197](https://github.com/antialias/soroban-abacus-flashcards/commit/04741971b296976f4476ecd949e84066fc549010))
* **rithmomachia:** recreate original guide modal header layout ([2489695](https://github.com/antialias/soroban-abacus-flashcards/commit/24896957d0817758c5f64c0e3473e6a0a343af67))
* **rithmomachia:** show capture error on hover instead of click ([339b678](https://github.com/antialias/soroban-abacus-flashcards/commit/339b6780f657ace5bfe1611c4ef64bb0c2c31587))
* **rithmomachia:** show pyramid face numbers on hover instead of selection ([b0c4523](https://github.com/antialias/soroban-abacus-flashcards/commit/b0c4523c0b4669c96a50b2812ba6cb2faa3f9a22))
* **rithmomachia:** show pyramid face numbers when selected ([5c186f3](https://github.com/antialias/soroban-abacus-flashcards/commit/5c186f3947cc38f1f5db5de3e68e590b90c2d092))
* **rithmomachia:** show pyramid face numbers when selected with subtle animation ([5c2ddbe](https://github.com/antialias/soroban-abacus-flashcards/commit/5c2ddbef05d7f4195d21b084cb1c0c4193ee3c9c))
* **rithmomachia:** show real preview layout when dragging guide to dock ([17d2460](https://github.com/antialias/soroban-abacus-flashcards/commit/17d2460a8769a21d33fabc5f909cf5b939712d36))
* **rithmomachia:** simplify guide language for clarity ([85cb630](https://github.com/antialias/soroban-abacus-flashcards/commit/85cb630add395a6693ecbbe9c8fc6aaf8c47be29))
* **rithmomachia:** skip helper selection UI and auto-select first valid helper ([be2a00e](https://github.com/antialias/soroban-abacus-flashcards/commit/be2a00e8b366b5606525309b4c7813f5c35c7f7c))
* **rithmomachia:** Update harmony system to classical three-piece proportions ([08c9762](https://github.com/antialias/soroban-abacus-flashcards/commit/08c97620f5e694b8526c448c44d265e6dd1fe1eb))
* **rithmomachia:** Update to traditional board setup with 25 pieces per side ([0769eaa](https://github.com/antialias/soroban-abacus-flashcards/commit/0769eaaa1dc238b901e3a7cfe0486e6122d5eda9))
* **rithmomachia:** use actual piece SVGs in number bond with 2.5s rotation animation ([976a7de](https://github.com/antialias/soroban-abacus-flashcards/commit/976a7de949c22842f4b6da3ced990f502a1c2733))
* **room-share:** add QR code button for easy mobile joining ([349290a](https://github.com/antialias/soroban-abacus-flashcards/commit/349290ac6a411651686b64d2e6b540083d2df1d9))
* show rithmomachia turn in nav ([7c89bfe](https://github.com/antialias/soroban-abacus-flashcards/commit/7c89bfef9c60db0e2c46e920500dcc1fbe90d3df))
* switch to royal color theme with transparent background ([944ad65](https://github.com/antialias/soroban-abacus-flashcards/commit/944ad6574e01a67ce1fdbb1f2452fe632c78ce43)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#f59e0](https://github.com/antialias/soroban-abacus-flashcards/issues/f59e0) [#a855f7](https://github.com/antialias/soroban-abacus-flashcards/issues/a855f7) [#7e22](https://github.com/antialias/soroban-abacus-flashcards/issues/7e22)
### Bug Fixes
* adjust hero abacus position to avoid covering subtitle ([f03d341](https://github.com/antialias/soroban-abacus-flashcards/commit/f03d3413145cc7ddfba93728ecdec7eabea9ada6))
* **arcade:** add automatic retry for version conflict rejections ([fbcde25](https://github.com/antialias/soroban-abacus-flashcards/commit/fbcde2505f7ff2bf3426f3458e480c4548314ba4))
* **arcade:** allow deactivating players from users who left the room ([7c1c2d7](https://github.com/antialias/soroban-abacus-flashcards/commit/7c1c2d7bebbb9a1acb274d17dd43b6ee5d196f44))
* **arcade:** implement optimistic locking in session manager ([71fd66d](https://github.com/antialias/soroban-abacus-flashcards/commit/71fd66d96a3b03650c90f59f6e516aae7dddc345))
* board rotation now properly fills height in portrait mode ([b5a96ea](https://github.com/antialias/soroban-abacus-flashcards/commit/b5a96eaeb1e29c20304142a7a0adf62f1cef570f))
* **card-sorting:** add border radius to outer card container ([a922eba](https://github.com/antialias/soroban-abacus-flashcards/commit/a922eba73c4656ee941ce4dfb1dc57a62f076570))
* **card-sorting:** add debug logging for spring animations ([d42947e](https://github.com/antialias/soroban-abacus-flashcards/commit/d42947eb8d5d3d8298f5d3b3d1644891c268dbb6))
* **card-sorting:** add missing gameMode support after hard reset ([a832325](https://github.com/antialias/soroban-abacus-flashcards/commit/a832325debde289d6928c5e6f9c24311c5e079ad))
* **card-sorting:** add missing useMemo import ([949d76d](https://github.com/antialias/soroban-abacus-flashcards/commit/949d76d844c786ada8a6373e4abb7f498f6befb9))
* **card-sorting:** add overflow hidden to clip rounded corners ([84c66fe](https://github.com/antialias/soroban-abacus-flashcards/commit/84c66feec6b4112b015e1afd95bf33b24b5f6a4f))
* **card-sorting:** adjust connecting paths for scaled cards ([829c741](https://github.com/antialias/soroban-abacus-flashcards/commit/829c741e554d1490dd7a5bbc17f2a32f7195dc07))
* **card-sorting:** adjust game board for spectator panels ([fc5cf12](https://github.com/antialias/soroban-abacus-flashcards/commit/fc5cf1216fe03edfb7e44afda01192f4b97b4f4e))
* **card-sorting:** adjust viewport dimensions for spectator panels ([4dce16c](https://github.com/antialias/soroban-abacus-flashcards/commit/4dce16cca46c965199b7e09f8b34bfa221efac33))
* **card-sorting:** animate cards from game board to results grid ([17d45fe](https://github.com/antialias/soroban-abacus-flashcards/commit/17d45fe88cd9773f5e550f6ee5a7f0c82cca2023))
* **card-sorting:** correct suffix card detection in auto-arrange ([d02ab59](https://github.com/antialias/soroban-abacus-flashcards/commit/d02ab5922c416042d525f54097a6975ae1541586))
* **card-sorting:** enable card scaling for spectators ([6b095c3](https://github.com/antialias/soroban-abacus-flashcards/commit/6b095c33830341c46139bc847ddaab3db632265e))
* **card-sorting:** enable New Game button during active gameplay ([f3f6eca](https://github.com/antialias/soroban-abacus-flashcards/commit/f3f6eca1db30df9e1e34cc4e77a069a6a3954f3d))
* **card-sorting:** end drag immediately when card becomes locked ([ae45298](https://github.com/antialias/soroban-abacus-flashcards/commit/ae45298ec48efb29587c0a1c1a7986a72821f3ef))
* **card-sorting:** filter local player from emoji overlays on dragged cards ([dc2d94a](https://github.com/antialias/soroban-abacus-flashcards/commit/dc2d94aaa58531ed4f9047e2ca92724d9264643d))
* **card-sorting:** fix results panel layout to not cover cards ([4b4fbfe](https://github.com/antialias/soroban-abacus-flashcards/commit/4b4fbfef322ecda06020ad52d4b1788267112460))
* **card-sorting:** hide activity notifications in spectator mode ([5cca279](https://github.com/antialias/soroban-abacus-flashcards/commit/5cca279687d8973d25bd9a411a55b632d1c82f63))
* **card-sorting:** keep arrow sequence numbers upright ([79c9469](https://github.com/antialias/soroban-abacus-flashcards/commit/79c94699fa1cc2a2886e3ab1addc5fcd975602f5))
* **card-sorting:** lock correctly positioned prefix/suffix cards ([170abed](https://github.com/antialias/soroban-abacus-flashcards/commit/170abed2318432f309de40692f6092bb4c4a1a45))
* **card-sorting:** lock spring positions after initial animation completes ([275cc62](https://github.com/antialias/soroban-abacus-flashcards/commit/275cc62a523d9e849f2162001141b6d75ae0925e))
* **card-sorting:** New Game now restarts with same settings instantly ([f3687ed](https://github.com/antialias/soroban-abacus-flashcards/commit/f3687ed236eff4ebe61699ec02909024c7086fb5))
* **card-sorting:** only shrink/fade cards in correct prefix ([51368c6](https://github.com/antialias/soroban-abacus-flashcards/commit/51368c6ec59d5447ce2875c5e1181dec97fd509d))
* **card-sorting:** preserve card positions on pause/resume ([0d8af09](https://github.com/antialias/soroban-abacus-flashcards/commit/0d8af09517534f1e1cf1f57160391d465a279d76))
* **card-sorting:** preserve rotation when starting drag ([3364144](https://github.com/antialias/soroban-abacus-flashcards/commit/3364144fb6212934b6ad6d63ac6e7b78b436b258))
* **card-sorting:** prevent duplicate START_GAME moves on Play Again ([a0b14f8](https://github.com/antialias/soroban-abacus-flashcards/commit/a0b14f87e9c5b32fcbb685da4e70c563f70ed91a))
* **card-sorting:** prevent ghost movements with proper optimistic updates ([bd014be](https://github.com/antialias/soroban-abacus-flashcards/commit/bd014bec4ffa12bcd8f4a4e84ff51203c90c1f1d))
* **card-sorting:** prevent infinite loop when all cards are correct ([34785f4](https://github.com/antialias/soroban-abacus-flashcards/commit/34785f466faaa6b9f2958df786af88561fa80b06))
* **card-sorting:** prevent infinite loop with tolerance-based position comparison ([627b873](https://github.com/antialias/soroban-abacus-flashcards/commit/627b873382eaa76ad16477280d10451cf2951e1a))
* **card-sorting:** prevent position jump when clicking rotated cards ([564a00f](https://github.com/antialias/soroban-abacus-flashcards/commit/564a00f82b6ca6aa8a2c0586ca49fc42d44991a8))
* **card-sorting:** prevent replaying own movements from server ([308168a](https://github.com/antialias/soroban-abacus-flashcards/commit/308168a7fb51013b0851e98b161ba1a1a3e39fbb))
* **card-sorting:** prevent springs from reinitializing on window resize ([30953b8](https://github.com/antialias/soroban-abacus-flashcards/commit/30953b8c4a3cf147f980455818f9ce8eea07837c))
* **card-sorting:** prevent springs from resetting after animation ([8aff60c](https://github.com/antialias/soroban-abacus-flashcards/commit/8aff60ce3f8d302ce5c1bde7cb773e63064c36b7))
* **card-sorting:** remove hasAnimatedRef logic causing backwards animation ([a44aa5a](https://github.com/antialias/soroban-abacus-flashcards/commit/a44aa5a4c2d84cab7cf0bbf87485bb61548fdeb2))
* **card-sorting:** remove remaining reveal numbers references ([15c53ea](https://github.com/antialias/soroban-abacus-flashcards/commit/15c53ea4eb4abb824eb0360fb645b1f3e455578e))
* **card-sorting:** restore prefix/suffix card shrinking visual feedback ([f5fb4d7](https://github.com/antialias/soroban-abacus-flashcards/commit/f5fb4d7b76e25286bcdecd017894ff2d78b31963))
* **card-sorting:** show only active players in team members section ([fa9f1a5](https://github.com/antialias/soroban-abacus-flashcards/commit/fa9f1a568f3dff2f4e5e7d3e8841b951ef1b7d04))
* **card-sorting:** smooth scale animation while dragging cards ([0eefc33](https://github.com/antialias/soroban-abacus-flashcards/commit/0eefc332ac2724c54b477301a269915e895db94f))
* **card-sorting:** stabilize inferred sequence for locked cards during drag ([b0cd194](https://github.com/antialias/soroban-abacus-flashcards/commit/b0cd194838705bb7bbf21ac9e318eaba491097b2))
* **card-sorting:** use empty deps array for useSprings to prevent recreation ([cee399e](https://github.com/antialias/soroban-abacus-flashcards/commit/cee399ed1513d32d0fff51a6f63898aa861605e1))
* **card-sorting:** use ref to track initialized state and prevent re-animation ([f389afa](https://github.com/antialias/soroban-abacus-flashcards/commit/f389afa831935e896a626f526cfee378e340a64b))
* **card-sorting:** use same coordinate system for game board and results ([6972fdf](https://github.com/antialias/soroban-abacus-flashcards/commit/6972fdf1105b6e854494efe1c4c587e6b6ff32a9))
* **complement-race:** prevent delivery move thrashing in steam sprint mode ([e1258ee](https://github.com/antialias/soroban-abacus-flashcards/commit/e1258ee0416010909774694c0b25306b6f30329c))
* configure favicon metadata and improve bead visibility ([e1369fa](https://github.com/antialias/soroban-abacus-flashcards/commit/e1369fa2754cd61745a2950e6cb767d6b08db38f))
* copy entire packages/core and packages/templates ([0ccada0](https://github.com/antialias/soroban-abacus-flashcards/commit/0ccada0ca783e635f9ae08f33a69c392018ee342))
* correct hero abacus scroll direction to flow with page content ([4232746](https://github.com/antialias/soroban-abacus-flashcards/commit/423274657c9698bba28f7246fbf48d8508d97ef9))
* correct Typst template path in Dockerfile ([4c518de](https://github.com/antialias/soroban-abacus-flashcards/commit/4c518decb7fcc0b519d07680cbfd01c94c23dd41))
* delete existing user sessions before creating new ones ([0cced47](https://github.com/antialias/soroban-abacus-flashcards/commit/0cced47a0f414a04371bdb253fc5a43e4d9557be))
* extract pure SVG content from AbacusReact renders ([b07f1c4](https://github.com/antialias/soroban-abacus-flashcards/commit/b07f1c421616bcfd1f949f9a42ce1b03df418945))
* **games:** prevent horizontal page scroll from carousel overflow ([5a8c98f](https://github.com/antialias/soroban-abacus-flashcards/commit/5a8c98fc10704e459690308a84dc7ee2bfa0ef6c))
* **games:** smooth scroll feel for carousel wheel navigation ([f80a73b](https://github.com/antialias/soroban-abacus-flashcards/commit/f80a73b35c324959bfd7141ebf086cb47d3c0ebc))
* **games:** use specific transition properties for smooth carousel loop ([187271e](https://github.com/antialias/soroban-abacus-flashcards/commit/187271e51527ee0129f71d77be1bd24072b963c4))
* **i18n:** eliminate FOUC by loading messages server-side ([4d4d930](https://github.com/antialias/soroban-abacus-flashcards/commit/4d4d930bd307ce5a405fc5751af6682a9f221f1f))
* **i18n:** use useMessages() for tutorial translations ([95b0105](https://github.com/antialias/soroban-abacus-flashcards/commit/95b0105ca3b28c5adfa843e8d77a8b27d9e7ade4))
* include column posts in favicon bounding box ([0b2f481](https://github.com/antialias/soroban-abacus-flashcards/commit/0b2f48106a939307b728c86fe2ea1be1e0247ea8))
* increase server update debounce to 2000ms for low bandwidth ([633ff12](https://github.com/antialias/soroban-abacus-flashcards/commit/633ff127500c893a215491afa0e6ff814ad553bf))
* Integrate threshold input into Point Victory card ([b29bbee](https://github.com/antialias/soroban-abacus-flashcards/commit/b29bbeefcad92be42f7a3ca27ac126db4232ab26))
* mark dynamic routes as force-dynamic to prevent static generation errors ([d7b35d9](https://github.com/antialias/soroban-abacus-flashcards/commit/d7b35d954421fd7577cd2c26247666e5953b647d))
* **nav:** show full navigation on /games page ([d3fe6ac](https://github.com/antialias/soroban-abacus-flashcards/commit/d3fe6acbb0390e1df71869a4095e5ee6021e06b1))
* **qr-button:** improve layout and z-index ([646a422](https://github.com/antialias/soroban-abacus-flashcards/commit/646a4228d0573796b1a429e31bc037411024c0ff))
* **qr-button:** increase mini QR code size to 80px ([61ac737](https://github.com/antialias/soroban-abacus-flashcards/commit/61ac7378bdb01132b26bfc265a057c095ea41606))
* **qr-button:** increase mini QR code to 84px ([3fae5ea](https://github.com/antialias/soroban-abacus-flashcards/commit/3fae5ea6fa9ebd0f8fe8c9140a027be7f6a041aa))
* **qr-button:** make button square and increase QR size ([dc2d466](https://github.com/antialias/soroban-abacus-flashcards/commit/dc2d46663b8e0ec94a1508a57c4f8c2d8ba03506))
* **qr-button:** match height of stacked buttons ([81f202d](https://github.com/antialias/soroban-abacus-flashcards/commit/81f202d21556aa430402fda814519adbc8883831))
* reduce padding to minimize gap below last bead ([0e529be](https://github.com/antialias/soroban-abacus-flashcards/commit/0e529be789caf16e73f3e2ee77f52e243841aef4))
* remove distracting parallax and wobble 3D effects ([28a2d40](https://github.com/antialias/soroban-abacus-flashcards/commit/28a2d40996256700bf19cd80130b26e24441949f))
* remove wobble physics and enhance wood grain visibility ([5d97673](https://github.com/antialias/soroban-abacus-flashcards/commit/5d976734062eb3d943bfdfdd125473c56b533759))
* resolve z-index layering and hero abacus visibility issues ([ed9a050](https://github.com/antialias/soroban-abacus-flashcards/commit/ed9a050d64db905e1328008f25dc0014e9a81999))
* rewrite 3D stories to use props instead of CSS wrappers ([26bdb11](https://github.com/antialias/soroban-abacus-flashcards/commit/26bdb112370cece08634e3d693d15336111fc70f))
* **rithmomachia:** add missing i18next dependencies ([91154d9](https://github.com/antialias/soroban-abacus-flashcards/commit/91154d93647e59f7e5f96d1db5624a7ec9b1b9ff))
* **rithmomachia:** add missing pyramid section keys to Japanese (ja.json) ([dae615e](https://github.com/antialias/soroban-abacus-flashcards/commit/dae615ee72a7ec7d0b235a22c61ebc4af0d8eadb))
* **rithmomachia:** adjust error dialog sizing to prevent text clipping ([cda1126](https://github.com/antialias/soroban-abacus-flashcards/commit/cda1126cb0eab6840df89f3a8778d72410298093))
* **rithmomachia:** adjust roster notice position to not overlap nav ([7093223](https://github.com/antialias/soroban-abacus-flashcards/commit/709322373a91c8174d21052d184fa84dd8bda326))
* **rithmomachia:** change undock icon to pop-out arrow ([2a91748](https://github.com/antialias/soroban-abacus-flashcards/commit/2a917484938bc269cf16acb501d4d26584405e0f))
* **rithmomachia:** correct board dimensions to 16x8 and restore original layout values ([cfac277](https://github.com/antialias/soroban-abacus-flashcards/commit/cfac27750526fb1f6a7e4314a96aab3b92e08e44))
* **rithmomachia:** Correct board setup to match reference image exactly ([618e563](https://github.com/antialias/soroban-abacus-flashcards/commit/618e56358deb66cba968472f39b8d4e28b4dd211))
* **rithmomachia:** correct makeMove parameter types for capture handling ([aafb64f](https://github.com/antialias/soroban-abacus-flashcards/commit/aafb64f3e337c6cf925766fe179b91f66c4a040b))
* **rithmomachia:** fix guide modal resize drift by calculating from initial state ([1bcd99c](https://github.com/antialias/soroban-abacus-flashcards/commit/1bcd99c949e4d2b4fb1c0813debd50176fa58cb9))
* **rithmomachia:** fix harmony section translation structure for hi/ja/es ([14259a1](https://github.com/antialias/soroban-abacus-flashcards/commit/14259a19a9817d0947467faa004d5f43118f8d8d))
* **rithmomachia:** fix modal resizing zoom issue ([4fa20f4](https://github.com/antialias/soroban-abacus-flashcards/commit/4fa20f44cb9758f29d1f1512232be0fdc0b53b3d))
* **rithmomachia:** Fix TypeScript errors in playing guide modal ([4834ece](https://github.com/antialias/soroban-abacus-flashcards/commit/4834ece98e86f2fb00511bb876a5c32c289df0e0))
* **rithmomachia:** handle pyramid pieces in hover error tooltip ([56f3164](https://github.com/antialias/soroban-abacus-flashcards/commit/56f3164155beb94ceec2838bed9fc74fd75524db))
* **rithmomachia:** implement proper board cropping and highlighting in guide ([d0a8fcd](https://github.com/antialias/soroban-abacus-flashcards/commit/d0a8fcdea6aa4fdacfee33e183c92923634ee2b7))
* **rithmomachia:** improve guide modal tab navigation at narrow widths ([a673177](https://github.com/antialias/soroban-abacus-flashcards/commit/a673177bec1c709463ce0f266848f473a79f4ef0))
* **rithmomachia:** reconnect player assignment UI and fix setup layout ([a1a0374](https://github.com/antialias/soroban-abacus-flashcards/commit/a1a0374fac5dce676df5890663b75531589ed93a))
* **rithmomachia:** render guide as docked in preview panel ([190f8cf](https://github.com/antialias/soroban-abacus-flashcards/commit/190f8cf302aa966f029d05931811e217c67bfe39))
* **rithmomachia:** show actual values in tooltips for non-helper relations ([774c6b0](https://github.com/antialias/soroban-abacus-flashcards/commit/774c6b0ce712b1a77bb684457da9831e6ec91138))
* **rithmomachia:** show guest-friendly message when they can't fix too many players ([54bfd2f](https://github.com/antialias/soroban-abacus-flashcards/commit/54bfd2fac86be3597d40c67a1235e4c4ed8e2709))
* **rithmomachia:** smooth guide dragging from docked state without jump ([8f4a79c](https://github.com/antialias/soroban-abacus-flashcards/commit/8f4a79c9b0cad55336584fdc8e67409015d3a8ae))
* **rithmomachia:** validate move path before showing capture error on hover ([bd49964](https://github.com/antialias/soroban-abacus-flashcards/commit/bd49964186a0daa1639ae849b128a76081643daf))
* **room-info:** hide Leave Room button when user is alone ([5927f61](https://github.com/antialias/soroban-abacus-flashcards/commit/5927f61c3c34ba583ee45c8cee48a116c1c03071))
* separate horizontal and vertical bounding box logic ([83090df](https://github.com/antialias/soroban-abacus-flashcards/commit/83090df4dfad1d1d5cfa6c278c241526cacc7972))
* tolerate OpenSCAD CGAL warnings if output file is created ([88993f3](https://github.com/antialias/soroban-abacus-flashcards/commit/88993f36629206a7bdcf9aa9d5641f1580b64de5))
* use absolute positioning for hero abacus to eliminate scroll lag ([096104b](https://github.com/antialias/soroban-abacus-flashcards/commit/096104b094b45aa584f2b9d47a440a8c14d82fc0))
* use Debian base for deps stage to match runner for binary compatibility ([f8fe6e4](https://github.com/antialias/soroban-abacus-flashcards/commit/f8fe6e4a415f8655626af567129d0cda61b82e15))
* use default BOSL2 branch instead of non-existent v2.0.0 tag ([f4ffc5b](https://github.com/antialias/soroban-abacus-flashcards/commit/f4ffc5b0277535358bea7588309a1a4afd1983a1))
* use nested SVG viewBox for actual cropping, not just scaling ([440b492](https://github.com/antialias/soroban-abacus-flashcards/commit/440b492e85beff1612697346b6c5cfc8461e83da))
* various game improvements and UI enhancements ([b67cf61](https://github.com/antialias/soroban-abacus-flashcards/commit/b67cf610c570d54744553cd8f6694243fa50bee1))
### Performance Improvements
* optimize Docker image size to reduce build failures ([9ca3106](https://github.com/antialias/soroban-abacus-flashcards/commit/9ca310636183f4970db925ce8fa368e23645eb02))
### Code Refactoring
* **card-sorting:** remove reveal numbers feature ([ea5e3e8](https://github.com/antialias/soroban-abacus-flashcards/commit/ea5e3e838bd6a5b8b38469a70aa92a0e9baba769))
* **card-sorting:** send complete card sequence instead of individual moves ([e4df843](https://github.com/antialias/soroban-abacus-flashcards/commit/e4df8432b9c4a2055d47833d56b6e9fcf325ca94))
* **games:** implement carousel, fix victories bug, add conditional stats ([82c133f](https://github.com/antialias/soroban-abacus-flashcards/commit/82c133f742f3f5c40b723c18d1997b518f25b320))
* **games:** move page title to nav bar ([712ee58](https://github.com/antialias/soroban-abacus-flashcards/commit/712ee58e5956e5bbdb13d5a5fb367020c87c8c9a))
* **games:** remove redundant subtitle below nav ([ad5bb87](https://github.com/antialias/soroban-abacus-flashcards/commit/ad5bb87325a44825f0cd85b38eb0e5f0eea7a695))
* **games:** remove wheel scrolling, enable overflow visible carousel ([876513c](https://github.com/antialias/soroban-abacus-flashcards/commit/876513c9cc6323c20845ae8f1a3a5478d449f9e4))
* reorganize Harmony and Victory guide sections ([fb629c4](https://github.com/antialias/soroban-abacus-flashcards/commit/fb629c44ea37a7b296561919a4980c10d14efed8))
* restructure /create page into hub with sub-pages ([b91b23d](https://github.com/antialias/soroban-abacus-flashcards/commit/b91b23d95ffaeeaa30dbc8579f4c30bae8829ee7))
* **rithmomachia:** extract board and capture components (phase 2+3) ([a0a867b](https://github.com/antialias/soroban-abacus-flashcards/commit/a0a867b27166a838ca7e0dcbd8f89fe1be812a80))
* **rithmomachia:** extract CaptureErrorDialog component (Phase 2 partial) ([f0a066d](https://github.com/antialias/soroban-abacus-flashcards/commit/f0a066d8f0a51d35e18f87a8436c0d05153c03b5))
* **rithmomachia:** extract constants and coordinate utilities (Phase 1) ([eace0ed](https://github.com/antialias/soroban-abacus-flashcards/commit/eace0ed52979b71870f77ee68f8568558f2aaecb))
* **rithmomachia:** extract guide sections into separate files ([765525d](https://github.com/antialias/soroban-abacus-flashcards/commit/765525dc451897f561f017e444aae892dc27177f))
* **rithmomachia:** extract hooks (phase 5) ([324a659](https://github.com/antialias/soroban-abacus-flashcards/commit/324a65992f97c295ea3968aaf54d266373f4c035))
* **rithmomachia:** extract phase components (phase 4) ([11364f6](https://github.com/antialias/soroban-abacus-flashcards/commit/11364f6394c15e49850e5cad2cbd32e1ea08a178))
* **rithmomachia:** extract reusable components from SetupPhase ([3abc325](https://github.com/antialias/soroban-abacus-flashcards/commit/3abc325ea27feee5c4cc59f02296ff218f342a81))
* **rithmomachia:** make setup phase UI more compact ([e55f848](https://github.com/antialias/soroban-abacus-flashcards/commit/e55f848a26092a2b4a5b09c3c255544ea9666f1b))
* **rithmomachia:** redesign error notification with modern UI ([dfeeb0e](https://github.com/antialias/soroban-abacus-flashcards/commit/dfeeb0e0db8b2c4a38198cf71cd918439d6c211b)), closes [#1e293](https://github.com/antialias/soroban-abacus-flashcards/issues/1e293) [#0f172](https://github.com/antialias/soroban-abacus-flashcards/issues/0f172) [#f1f5f9](https://github.com/antialias/soroban-abacus-flashcards/issues/f1f5f9)
* **rithmomachia:** simplify capture error dialog to one-liner ([82a5eb2](https://github.com/antialias/soroban-abacus-flashcards/commit/82a5eb2e4bf74f42a183a15f1129e5ec84cc5231))
* **rithmomachia:** Update board setup to authoritative CSV layout ([0471da5](https://github.com/antialias/soroban-abacus-flashcards/commit/0471da598d8d591b3f9d63f467cb35f999924c13))
* **rithmomachia:** update capture components to use CaptureContext ([2ab6ab5](https://github.com/antialias/soroban-abacus-flashcards/commit/2ab6ab57995a6d7d9c66b9fba8de945507209661))
* **rithmomachia:** use useBoardLayout and usePieceSelection in BoardDisplay ([0ab7a1d](https://github.com/antialias/soroban-abacus-flashcards/commit/0ab7a1df327d7258228af9851762555583a20d61))
* use AbacusReact for dynamic Open Graph image ([9c20f12](https://github.com/antialias/soroban-abacus-flashcards/commit/9c20f12bacff4fe7f8bd7a87032afbed9711e94b))
### Documentation
* add 3D enhancement documentation to README ([cc96802](https://github.com/antialias/soroban-abacus-flashcards/commit/cc96802df87c805c946ee59af509663ba570e75b))
* add database migration guide and playing guide modal spec ([5a29af7](https://github.com/antialias/soroban-abacus-flashcards/commit/5a29af78e27e897ab35273611b79c4b669304f71))
* add deployment verification guidelines to prevent false positives ([3d8da23](https://github.com/antialias/soroban-abacus-flashcards/commit/3d8da2348b4e8a227e963791d15dc6718eac5af1))
* **card-sorting:** add comprehensive multiplayer plan ([008ccea](https://github.com/antialias/soroban-abacus-flashcards/commit/008ccead0f9c634fe52fd156e6f9a04d6cdd7744))
* **rithmomachia:** Add concise one-page playing guide ([e3c1f10](https://github.com/antialias/soroban-abacus-flashcards/commit/e3c1f10233cc0924ff96a643c7c4c1f1278de3e3))
* update workflow to require manual testing before commits ([0991796](https://github.com/antialias/soroban-abacus-flashcards/commit/0991796f1eccef345f10205e675e4c33d1a62b17))
### Styles
* **rithmomachia:** improve divider styling and make tabs responsive ([88ca35e](https://github.com/antialias/soroban-abacus-flashcards/commit/88ca35e0440157ff9349e8d3d2d3cc844f18ffea)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3)
* **rithmomachia:** improve pyramid face numbers visibility and contrast ([94e5e6a](https://github.com/antialias/soroban-abacus-flashcards/commit/94e5e6a268b387380b88b192737bd55578b98bc7)), closes [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24) [#b45309](https://github.com/antialias/soroban-abacus-flashcards/issues/b45309)
* **rithmomachia:** increase pyramid face numbers size and boldness ([7bf2d73](https://github.com/antialias/soroban-abacus-flashcards/commit/7bf2d730d370e562486b229f4d209099ff8c4463))
### Tests
* trigger compose-updater deployment test ([2b06aae](https://github.com/antialias/soroban-abacus-flashcards/commit/2b06aae39474cc80d501c47c9685fa99e7120c48))
## [4.68.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.67.1...v4.68.0) (2025-11-03)
### Features
* **abacus:** add nativeAbacusNumbers setting to schema and UI ([79f7347](https://github.com/antialias/soroban-abacus-flashcards/commit/79f7347d4800646378470a7f9aca8e7f2fd5573c))

1
apps/web/README.md Normal file
View File

@@ -0,0 +1 @@
# Test deployment - Mon Nov 3 16:31:57 CST 2025

View File

@@ -23,13 +23,11 @@ export function AbacusTarget({ number }: AbacusTargetProps) {
<AbacusReact
value={number}
columns={1}
compact={true}
interactive={false}
showNumbers={false}
hideInactiveBeads={true}
scaleFactor={0.72}
customStyles={{
columnPosts: { opacity: 0 },
}}
/>
</div>
)

View File

@@ -22,6 +22,8 @@ import { getAllGames, getGame, hasGame } from '@/lib/arcade/game-registry'
*
* Note: ModerationNotifications is handled by PageWithNav inside each game component,
* so we don't need to render it here.
*
* Test: Verifying compose-updater automatic deployment cycle
*/
export default function RoomPage() {
const router = useRouter()

View File

@@ -1,7 +1,7 @@
'use client'
import { useEffect, useRef } from 'react'
import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react'
import { AbacusReact, useAbacusConfig, ABACUS_THEMES } from '@soroban/abacus-react'
import { css } from '../../styled-system/css'
import { useHomeHero } from '../contexts/HomeHeroContext'
@@ -17,19 +17,8 @@ export function HeroAbacus() {
const appConfig = useAbacusConfig()
const heroRef = useRef<HTMLDivElement>(null)
// Styling for structural elements (solid, no translucency)
const structuralStyles = {
columnPosts: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 2,
},
reckoningBar: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 3,
},
}
// Use theme preset from abacus-react instead of manual definition
const structuralStyles = ABACUS_THEMES.light
// Detect when hero scrolls out of view
useEffect(() => {

View File

@@ -3,7 +3,7 @@
import { useState, useEffect } from 'react'
import { useSpring, useTransition, animated } from '@react-spring/web'
import * as Slider from '@radix-ui/react-slider'
import { AbacusReact, StandaloneBead } from '@soroban/abacus-react'
import { AbacusReact, StandaloneBead, ABACUS_THEMES } from '@soroban/abacus-react'
import { css } from '../../styled-system/css'
import { stack } from '../../styled-system/patterns'
import { kyuLevelDetails } from '@/data/kyuLevelDetails'
@@ -260,19 +260,8 @@ function parseKyuDetails(rawText: string) {
return sections
}
// Dark theme styles matching the homepage
const darkStyles = {
columnPosts: {
fill: 'rgba(255, 255, 255, 0.3)',
stroke: 'rgba(255, 255, 255, 0.2)',
strokeWidth: 2,
},
reckoningBar: {
fill: 'rgba(255, 255, 255, 0.4)',
stroke: 'rgba(255, 255, 255, 0.25)',
strokeWidth: 3,
},
}
// Use dark theme preset from abacus-react instead of manual definition
const darkStyles = ABACUS_THEMES.dark
interface LevelSliderDisplayProps {
initialIndex?: number

View File

@@ -2,7 +2,7 @@
import { useContext, useEffect, useState } from 'react'
import { usePathname } from 'next/navigation'
import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react'
import { AbacusReact, useAbacusConfig, ABACUS_THEMES } from '@soroban/abacus-react'
import { css } from '../../styled-system/css'
import { useMyAbacus } from '@/contexts/MyAbacusContext'
import { HomeHeroContext } from '@/contexts/HomeHeroContext'
@@ -56,33 +56,9 @@ export function MyAbacus() {
}
}, [isOpen])
// Hero mode styles - white structural (from original HeroAbacus)
const structuralStyles = {
columnPosts: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 2,
},
reckoningBar: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 3,
},
}
// Trophy abacus styles - golden/premium look
const trophyStyles = {
columnPosts: {
fill: '#fbbf24',
stroke: '#f59e0b',
strokeWidth: 3,
},
reckoningBar: {
fill: '#fbbf24',
stroke: '#f59e0b',
strokeWidth: 4,
},
}
// Use theme presets from abacus-react instead of manual definitions
const structuralStyles = ABACUS_THEMES.light
const trophyStyles = ABACUS_THEMES.trophy
return (
<>

View File

@@ -2,6 +2,7 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import Resizable from 'react-resizable-layout'
import { calculateBeadDiffFromValues } from '@soroban/abacus-react'
import { css } from '../../../styled-system/css'
import { hstack, stack, vstack } from '../../../styled-system/patterns'
import {
@@ -13,7 +14,6 @@ import {
type TutorialValidation,
} from '../../types/tutorial'
import { generateAbacusInstructions } from '../../utils/abacusInstructionGenerator'
import { calculateBeadDiffFromValues } from '../../utils/beadDiff'
import { generateSingleProblem } from '../../utils/problemGenerator'
import {
createBasicAllowedConfiguration,

View File

@@ -6,6 +6,7 @@ import {
AbacusReact,
type StepBeadHighlight,
useAbacusDisplay,
calculateBeadDiffFromValues,
} from '@soroban/abacus-react'
import { useTranslations } from 'next-intl'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
@@ -18,7 +19,6 @@ import type {
TutorialStep,
UIState,
} from '../../types/tutorial'
import { calculateBeadDiffFromValues } from '../../utils/beadDiff'
import { generateUnifiedInstructionSequence } from '../../utils/unifiedStepGenerator'
import { CoachBar } from './CoachBar/CoachBar'
import { DecompositionWithReasons } from './DecompositionWithReasons'

View File

@@ -1,29 +1,30 @@
// Automatic instruction generator for abacus tutorial steps
import type { ValidPlaceValues } from '@soroban/abacus-react'
// Re-exports core types and functions from abacus-react
export interface BeadState {
heavenActive: boolean
earthActive: number // 0-4
}
export type { ValidPlaceValues } from '@soroban/abacus-react'
export {
type BeadState,
type AbacusState,
type PlaceValueBasedBead as BeadHighlight,
numberToAbacusState,
calculateBeadChanges,
} from '@soroban/abacus-react'
export interface AbacusState {
[placeValue: number]: BeadState
}
import type { ValidPlaceValues, PlaceValueBasedBead } from '@soroban/abacus-react'
import { numberToAbacusState, calculateBeadChanges } from '@soroban/abacus-react'
export interface BeadHighlight {
placeValue: ValidPlaceValues
beadType: 'heaven' | 'earth'
position?: number
}
// Type alias for internal use
type BeadHighlight = PlaceValueBasedBead
export interface StepBeadHighlight extends BeadHighlight {
// App-specific extension for step-based tutorial highlighting
export interface StepBeadHighlight extends PlaceValueBasedBead {
stepIndex: number // Which instruction step this bead belongs to
direction: 'up' | 'down' | 'activate' | 'deactivate' // Movement direction
order?: number // Order within the step (for multiple beads per step)
}
export interface GeneratedInstruction {
highlightBeads: BeadHighlight[]
highlightBeads: PlaceValueBasedBead[]
expectedAction: 'add' | 'remove' | 'multi-step'
actionDescription: string
multiStepInstructions?: string[]
@@ -40,68 +41,7 @@ export interface GeneratedInstruction {
}
}
// Convert a number to abacus state representation
export function numberToAbacusState(value: number, maxPlaces: number = 5): AbacusState {
const state: AbacusState = {}
for (let place = 0; place < maxPlaces; place++) {
const placeValueNum = 10 ** place
const digit = Math.floor(value / placeValueNum) % 10
state[place] = {
heavenActive: digit >= 5,
earthActive: digit >= 5 ? digit - 5 : digit,
}
}
return state
}
// Calculate the difference between two abacus states
export function calculateBeadChanges(
startState: AbacusState,
targetState: AbacusState
): {
additions: BeadHighlight[]
removals: BeadHighlight[]
placeValue: number
} {
const additions: BeadHighlight[] = []
const removals: BeadHighlight[] = []
let mainPlaceValue = 0
for (const placeStr in targetState) {
const place = parseInt(placeStr, 10) as ValidPlaceValues
const start = startState[place] || { heavenActive: false, earthActive: 0 }
const target = targetState[place]
// Check heaven bead changes
if (!start.heavenActive && target.heavenActive) {
additions.push({ placeValue: place, beadType: 'heaven' })
mainPlaceValue = place
} else if (start.heavenActive && !target.heavenActive) {
removals.push({ placeValue: place, beadType: 'heaven' })
mainPlaceValue = place
}
// Check earth bead changes
if (target.earthActive > start.earthActive) {
// Adding earth beads
for (let pos = start.earthActive; pos < target.earthActive; pos++) {
additions.push({ placeValue: place, beadType: 'earth', position: pos })
mainPlaceValue = place
}
} else if (target.earthActive < start.earthActive) {
// Removing earth beads
for (let pos = start.earthActive - 1; pos >= target.earthActive; pos--) {
removals.push({ placeValue: place, beadType: 'earth', position: pos })
mainPlaceValue = place
}
}
}
return { additions, removals, placeValue: mainPlaceValue }
}
// Note: numberToAbacusState and calculateBeadChanges are now re-exported from @soroban/abacus-react above
// Generate proper complement breakdown using simple bead movements
function generateProperComplementDescription(

View File

@@ -1,107 +1,25 @@
// Dynamic bead diff algorithm for calculating transitions between abacus states
// Provides arrows, highlights, and movement directions for tutorial UI
// Re-export core bead diff functionality from abacus-react
// App-specific extensions for multi-step tutorials and validation
import type { ValidPlaceValues } from '@soroban/abacus-react'
import {
export {
type BeadDiffResult,
type BeadDiffOutput,
calculateBeadDiff,
calculateBeadDiffFromValues,
areStatesEqual,
type AbacusState,
type BeadHighlight,
calculateBeadChanges,
numberToAbacusState,
} from './abacusInstructionGenerator'
type BeadState,
} from '@soroban/abacus-react'
export interface BeadDiffResult {
placeValue: ValidPlaceValues
beadType: 'heaven' | 'earth'
position?: number
direction: 'activate' | 'deactivate'
order: number // Order of operations for animations
}
export interface BeadDiffOutput {
changes: BeadDiffResult[]
highlights: BeadHighlight[]
hasChanges: boolean
summary: string
}
/**
* THE BEAD DIFF ALGORITHM
*
* Takes current and desired abacus states and returns exactly which beads
* need to move with arrows and highlights for the tutorial UI.
*
* This is the core "diff" function that keeps tutorial highlights in sync.
*/
export function calculateBeadDiff(fromState: AbacusState, toState: AbacusState): BeadDiffOutput {
const { additions, removals } = calculateBeadChanges(fromState, toState)
const changes: BeadDiffResult[] = []
const highlights: BeadHighlight[] = []
let order = 0
// Process removals first (pedagogical order: clear before adding)
removals.forEach((removal) => {
changes.push({
placeValue: removal.placeValue,
beadType: removal.beadType,
position: removal.position,
direction: 'deactivate',
order: order++,
})
highlights.push({
placeValue: removal.placeValue,
beadType: removal.beadType,
position: removal.position,
})
})
// Process additions second (pedagogical order: add after clearing)
additions.forEach((addition) => {
changes.push({
placeValue: addition.placeValue,
beadType: addition.beadType,
position: addition.position,
direction: 'activate',
order: order++,
})
highlights.push({
placeValue: addition.placeValue,
beadType: addition.beadType,
position: addition.position,
})
})
// Generate summary
const summary = generateDiffSummary(changes)
return {
changes,
highlights,
hasChanges: changes.length > 0,
summary,
}
}
/**
* Calculate bead diff from numeric values
* Convenience function for when you have numbers instead of states
*/
export function calculateBeadDiffFromValues(
fromValue: number,
toValue: number,
maxPlaces: number = 5
): BeadDiffOutput {
const fromState = numberToAbacusState(fromValue, maxPlaces)
const toState = numberToAbacusState(toValue, maxPlaces)
return calculateBeadDiff(fromState, toState)
}
import type { BeadDiffOutput, BeadDiffResult, AbacusState } from '@soroban/abacus-react'
import { calculateBeadDiffFromValues } from '@soroban/abacus-react'
/**
* Calculate step-by-step bead diffs for multi-step operations
* This is used for tutorial multi-step instructions where we want to show
* the progression through intermediate states
*
* APP-SPECIFIC FUNCTION - not in core abacus-react
*/
export function calculateMultiStepBeadDiffs(
startValue: number,
@@ -133,126 +51,10 @@ export function calculateMultiStepBeadDiffs(
return stepDiffs
}
/**
* Generate a human-readable summary of what the diff does
* Respects pedagogical order: removals first, then additions
*/
function generateDiffSummary(changes: BeadDiffResult[]): string {
if (changes.length === 0) {
return 'No changes needed'
}
// Sort by order to respect pedagogical sequence
const sortedChanges = [...changes].sort((a, b) => a.order - b.order)
const deactivations = sortedChanges.filter((c) => c.direction === 'deactivate')
const activations = sortedChanges.filter((c) => c.direction === 'activate')
const parts: string[] = []
// Process deactivations first (pedagogical order)
if (deactivations.length > 0) {
const deactivationsByPlace = groupByPlace(deactivations)
Object.entries(deactivationsByPlace).forEach(([place, beads]) => {
const placeName = getPlaceName(parseInt(place, 10))
const heavenBeads = beads.filter((b) => b.beadType === 'heaven')
const earthBeads = beads.filter((b) => b.beadType === 'earth')
if (heavenBeads.length > 0) {
parts.push(`remove heaven bead in ${placeName}`)
}
if (earthBeads.length > 0) {
const count = earthBeads.length
parts.push(`remove ${count} earth bead${count > 1 ? 's' : ''} in ${placeName}`)
}
})
}
// Process activations second (pedagogical order)
if (activations.length > 0) {
const activationsByPlace = groupByPlace(activations)
Object.entries(activationsByPlace).forEach(([place, beads]) => {
const placeName = getPlaceName(parseInt(place, 10))
const heavenBeads = beads.filter((b) => b.beadType === 'heaven')
const earthBeads = beads.filter((b) => b.beadType === 'earth')
if (heavenBeads.length > 0) {
parts.push(`add heaven bead in ${placeName}`)
}
if (earthBeads.length > 0) {
const count = earthBeads.length
parts.push(`add ${count} earth bead${count > 1 ? 's' : ''} in ${placeName}`)
}
})
}
return parts.join(', then ')
}
/**
* Group bead changes by place value
*/
function groupByPlace(changes: BeadDiffResult[]): {
[place: string]: BeadDiffResult[]
} {
return changes.reduce(
(groups, change) => {
const place = change.placeValue.toString()
if (!groups[place]) {
groups[place] = []
}
groups[place].push(change)
return groups
},
{} as { [place: string]: BeadDiffResult[] }
)
}
/**
* Get human-readable place name
*/
function getPlaceName(place: number): string {
switch (place) {
case 0:
return 'ones column'
case 1:
return 'tens column'
case 2:
return 'hundreds column'
case 3:
return 'thousands column'
default:
return `place ${place} column`
}
}
/**
* Check if two abacus states are equal
*/
export function areStatesEqual(state1: AbacusState, state2: AbacusState): boolean {
const places1 = Object.keys(state1)
.map((k) => parseInt(k, 10))
.sort()
const places2 = Object.keys(state2)
.map((k) => parseInt(k, 10))
.sort()
if (places1.length !== places2.length) return false
for (const place of places1) {
const bead1 = state1[place]
const bead2 = state2[place]
if (!bead2) return false
if (bead1.heavenActive !== bead2.heavenActive) return false
if (bead1.earthActive !== bead2.earthActive) return false
}
return true
}
/**
* Validate that a bead diff is feasible (no impossible bead states)
*
* APP-SPECIFIC FUNCTION - not in core abacus-react
*/
export function validateBeadDiff(diff: BeadDiffOutput): {
isValid: boolean
@@ -282,3 +84,20 @@ export function validateBeadDiff(diff: BeadDiffOutput): {
errors,
}
}
// Helper function for validation
function groupByPlace(changes: BeadDiffResult[]): {
[place: string]: BeadDiffResult[]
} {
return changes.reduce(
(groups, change) => {
const place = change.placeValue.toString()
if (!groups[place]) {
groups[place] = []
}
groups[place].push(change)
return groups
},
{} as { [place: string]: BeadDiffResult[] }
)
}

View File

@@ -1,3 +1,14 @@
# [2.5.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.4.0...abacus-react-v2.5.0) (2025-11-03)
### Features
* **abacus-react:** add core utility functions for state management ([e65541c](https://github.com/antialias/soroban-abacus-flashcards/commit/e65541c100e590a51448750c6d5178ed4f3e8eeb))
* **abacus-react:** add layout and educational props ([35bbcec](https://github.com/antialias/soroban-abacus-flashcards/commit/35bbcecb9e36f1ef5917a5a629f5e78f1f490e9c))
* **abacus-react:** add pre-defined theme presets ([cf1f950](https://github.com/antialias/soroban-abacus-flashcards/commit/cf1f950c7c5fb9ee1f0de673235d6f037be3b9d6))
* **abacus-react:** add React hooks for abacus calculations ([de038d2](https://github.com/antialias/soroban-abacus-flashcards/commit/de038d2afc26c36c1490d5ea45dace0ab812c5cc))
* **abacus-react:** export new utilities, hooks, and themes ([ce4e44d](https://github.com/antialias/soroban-abacus-flashcards/commit/ce4e44d6302746053ad40dc61bab57ef3a0a9f31))
# [2.4.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.3.0...abacus-react-v2.4.0) (2025-11-03)

View File

@@ -0,0 +1,402 @@
# Abacus-React Feature Enhancement Plan
## Executive Summary
The web application has developed numerous custom patterns and workarounds for styling, layout, and interactions with the abacus component. These patterns reveal gaps in the abacus-react API that, if addressed, would significantly improve developer experience and reduce code duplication across the application.
## Priority 1: Critical Features (High Impact, High Frequency)
### 1. **Inline "Mini Abacus" Component**
**Location**: `apps/web/src/app/arcade/complement-race/components/AbacusTarget.tsx`
**Current Implementation**:
```tsx
<AbacusReact
value={number}
columns={1}
interactive={false}
showNumbers={false}
hideInactiveBeads={true}
scaleFactor={0.72}
customStyles={{
columnPosts: { opacity: 0 },
}}
/>
```
**Problem**: Creating an inline mini-abacus for displaying single digits requires multiple props and style overrides. This pattern appears throughout game UIs.
**Proposed Solution**: Add a `variant` prop with preset configurations:
```tsx
// Native API proposal
<AbacusReact
value={7}
variant="inline-digit"
// Automatically sets: columns=1, hideInactiveBeads, transparent frame, optimal scaleFactor
/>
// Or more granular:
<AbacusReact
value={7}
compact={true} // Removes frame, optimizes spacing
frameVisible={false} // Hide posts and bar
/>
```
**Benefits**:
- Single prop instead of 5+
- Consistent inline abacus appearance across the app
- Better semantic intent
---
### 2. **Theme-Aware Styling Presets**
**Locations**:
- `MyAbacus.tsx` (lines 60-85) - structural & trophy styles
- `HeroAbacus.tsx` (lines 20-32) - structural styles
- `LevelSliderDisplay.tsx` (lines 263-275) - dark theme styles
**Current Pattern**: Every component defines custom style objects for structural elements:
```tsx
const structuralStyles = {
columnPosts: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 2,
},
reckoningBar: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 3,
},
}
```
**Problem**: Manual style object creation for common themes is repetitive and error-prone.
**Proposed Solution**: Add theme presets to abacus-react:
```tsx
// Native API proposal
<AbacusReact
value={123}
theme="dark" // or "light", "translucent", "solid", "trophy"
/>
// Or expose theme constants
import { ABACUS_THEMES } from '@soroban/abacus-react'
<AbacusReact customStyles={ABACUS_THEMES.dark} />
```
**Benefits**:
- Eliminates ~30 lines of style definitions per component
- Ensures visual consistency
- Makes theme switching trivial
---
### 3. **Scaling Containers & Responsive Layouts**
**Locations**:
- `HeroAbacus.tsx` (lines 133-138) - manual scale transforms
- `MyAbacus.tsx` (lines 214-218) - responsive scale values
- `LevelSliderDisplay.tsx` (lines 370-379) - dynamic scale calculation
**Current Pattern**: Components manually wrap abacus in transform containers:
```tsx
<div style={{
transform: 'scale(3.5)',
transformOrigin: 'center center'
}}>
<AbacusReact value={1234} columns={4} />
</div>
```
**Problem**: Manual transform handling requires extra DOM nesting, breaks click boundaries, and makes centering complex.
**Proposed Solution**: Enhanced `scaleFactor` with responsive breakpoints:
```tsx
// Native API proposal
<AbacusReact
value={1234}
scaleFactor={{ base: 2.5, md: 3.5, lg: 4.5 }} // Responsive
scaleOrigin="center" // Handle transform origin
scaleContainer={true} // Apply correct boundaries for interaction
/>
```
**Benefits**:
- Eliminates wrapper divs
- Proper click/hover boundaries
- Built-in responsive scaling
---
## Priority 2: Developer Experience Improvements
### 4. **Bead Diff Calculation System**
**Location**: `apps/web/src/utils/beadDiff.ts` (285 lines) + `abacusInstructionGenerator.ts` (400+ lines)
**Current Implementation**: Complex utilities to calculate which beads need to move between states:
```tsx
// Current external pattern
import { calculateBeadDiffFromValues } from '@/utils/beadDiff'
const diff = calculateBeadDiffFromValues(fromValue, toValue)
const stepBeadHighlights = diff.changes.map(change => ({
placeValue: change.placeValue,
beadType: change.beadType,
direction: change.direction,
// ...
}))
```
**Problem**: Tutorial/game developers need to calculate bead movements manually. This core logic belongs in abacus-react.
**Proposed Solution**: Add a diff calculation hook:
```tsx
// Native API proposal
import { useAbacusDiff } from '@soroban/abacus-react'
function Tutorial() {
const diff = useAbacusDiff(startValue, targetValue)
return (
<AbacusReact
value={currentValue}
stepBeadHighlights={diff.highlights} // Generated by hook
// diff also includes: instructions, order, validation
/>
)
}
```
**Benefits**:
- Centralizes "diff" algorithm
- Eliminates ~500 lines of application code
- Better tested and maintained
---
### 5. **Tutorial/Step Context Provider**
**Location**: `apps/web/src/components/tutorial/TutorialContext.tsx`
**Current Pattern**: Apps need to implement complex state management for multi-step tutorial flows with reducer patterns, event tracking, and error handling.
**Problem**: Tutorial infrastructure is duplicated across components. The logic for tracking progress through abacus instruction steps is tightly coupled to application code.
**Proposed Solution**: Add optional tutorial/stepper context to abacus-react:
```tsx
// Native API proposal
import { AbacusReact, AbacusTutorial } from '@soroban/abacus-react'
<AbacusTutorial
steps={[
{ from: 0, to: 5, instruction: "Add 5" },
{ from: 5, to: 15, instruction: "Add 10" },
]}
onStepComplete={(step) => { /* analytics */ }}
onComplete={() => { /* celebration */ }}
>
<AbacusReact />
</AbacusTutorial>
```
**Benefits**:
- Reusable tutorial infrastructure
- Built-in progress tracking and validation
- Could power educational features across projects
---
## Priority 3: Nice-to-Have Enhancements
### 6. **Animation Speed Configuration**
**Location**: `LevelSliderDisplay.tsx` (lines 306-345)
**Current Pattern**: Applications control animation speed by rapidly changing the value prop:
```tsx
const intervalMs = 500 - danProgress * 490 // 500ms down to 10ms
setInterval(() => {
setAnimatedDigits(prev => {
// Rapidly change digits to simulate calculation
})
}, intervalMs)
```
**Problem**: "Rapid calculation" animation requires external interval management.
**Proposed Solution**: Add animation speed prop:
```tsx
// Native API proposal
<AbacusReact
value={calculatingValue}
animationSpeed="fast" // or "normal", "slow", or ms number
autoAnimate={true} // Animate value prop changes automatically
/>
```
**Benefits**:
- Smoother animations with internal management
- Consistent timing across the app
---
### 7. **Draggable/Positionable Abacus Cards**
**Location**: `InteractiveFlashcards.tsx`
**Current Pattern**: Complex drag-and-drop implementation wrapped around each AbacusReact instance with pointer capture, offset tracking, and rotation.
**Problem**: Making abacus instances draggable requires significant boilerplate.
**Proposed Solution**: This is probably too specific to remain external. However, a ref-based API to get bounding boxes would help:
```tsx
// Possible improvement
const abacusRef = useAbacusRef()
<AbacusReact ref={abacusRef} />
// abacusRef.current.getBoundingBox() for drag calculations
```
---
### 8. **Column Highlighting for Multi-Step Problems**
**Location**: Tutorial system extensively
**Current Pattern**: Manual column highlighting based on place values with custom overlay positioning logic.
**Problem**: Highlighting specific columns (e.g., "the tens column") requires external overlay management.
**Proposed Solution**: Add native column highlighting:
```tsx
// Native API proposal
<AbacusReact
value={123}
highlightColumns={[1]} // Highlight tens column
columnLabels={["ones", "tens", "hundreds"]} // Optional labels
/>
```
---
## Priority 4: Documentation & Exports
### 9. **Utility Functions & Types**
**Current State**: Apps re-implement utilities for working with abacus states:
- `numberToAbacusState()` - convert numbers to bead states
- `calculateBeadChanges()` - diff algorithm
- `ValidPlaceValues` type - imported but limited
**Proposed Solution**: Export more utilities from abacus-react:
```tsx
// Expanded exports
export {
// Utilities
numberToAbacusState,
abacusStateToNumber,
calculateBeadDiff,
validateAbacusValue,
// Types
AbacusState,
BeadState,
PlaceValue,
// Hooks
useAbacusDiff,
useAbacusValidation,
useAbacusState,
}
```
---
## Implementation Roadmap
### Phase 1 (Immediate) ✅ COMPLETED
1. ✅ Add `frameVisible={false}` prop
2. ✅ Add `compact` prop/variant
3. ✅ Export theme presets (ABACUS_THEMES constant)
### Phase 2 (Short-term) ✅ COMPLETED
4. ⏸️ Enhanced `scaleFactor` with responsive object support (DEFERRED - too complex, low priority)
5. ✅ Export utility functions (numberToAbacusState, calculateBeadDiff, etc.)
### Phase 3 (Medium-term) ✅ COMPLETED
6. ✅ Add `useAbacusDiff` hook
7. ✅ Add native column highlighting with `highlightColumns` and `columnLabels` props
### Phase 4 (Long-term - Future)
8. 📋 Consider tutorial context provider (needs more research)
9. 📋 Animation speed controls
## Completed Features Summary
### New Props
- `frameVisible?: boolean` - Show/hide column posts and reckoning bar
- `compact?: boolean` - Compact layout for inline display (implies frameVisible=false)
- `highlightColumns?: number[]` - Highlight specific columns by index
- `columnLabels?: string[]` - Optional labels for columns
### New Exports
- `ABACUS_THEMES` - Pre-defined theme presets (light, dark, trophy, translucent, solid, traditional)
- `AbacusThemeName` type - TypeScript type for theme names
### New Utility Functions
- `numberToAbacusState(value, maxPlaces)` - Convert number to bead positions
- `abacusStateToNumber(state)` - Convert bead positions to number
- `calculateBeadChanges(startState, targetState)` - Calculate bead differences
- `calculateBeadDiff(fromState, toState)` - Full diff with order and directions
- `calculateBeadDiffFromValues(from, to, maxPlaces)` - Convenience wrapper
- `validateAbacusValue(value, maxPlaces)` - Validate number ranges
- `areStatesEqual(state1, state2)` - Compare states
### New Hooks
- `useAbacusDiff(fromValue, toValue, maxPlaces)` - Calculate bead differences for tutorials
- `useAbacusState(value, maxPlaces)` - Convert number to abacus state (memoized)
### New Types
- `BeadState` - Bead state in a single column
- `AbacusState` - Complete abacus state
- `BeadDiffResult` - Single bead movement result
- `BeadDiffOutput` - Complete diff output
- `PlaceValueBasedBead` - Internal place-value based bead type
---
## Metrics & Impact
**Code Reduction Estimate**:
- Eliminates ~800-1000 lines of repetitive application code
- Reduces component complexity by ~40% for tutorial/game components
**Developer Experience**:
- Faster onboarding for new features using abacus
- More consistent UX across application
- Better TypeScript support and autocomplete
**Maintenance**:
- Centralized logic easier to test and debug
- Single source of truth for abacus behavior
- Easier to add new features (e.g., sound effects for different themes)
---
## Questions for Discussion
1. Should we split these into separate packages (e.g., `@soroban/abacus-tutorial`)?
2. Which theme presets should be included by default?
3. Should responsive scaling use CSS media queries or JS breakpoints?
4. How much tutorial logic belongs in the core library vs. app code?

View File

@@ -0,0 +1,162 @@
# Integration Summary
## ✅ Completed: Apps/Web Integration with Abacus-React Enhancements
### Features Implemented & Integrated
#### 1. **Theme Presets (ABACUS_THEMES)**
**Status:** ✅ Fully integrated
**Files Updated:**
- `apps/web/src/components/MyAbacus.tsx` - Now uses `ABACUS_THEMES.light` and `ABACUS_THEMES.trophy`
- `apps/web/src/components/HeroAbacus.tsx` - Now uses `ABACUS_THEMES.light`
- `apps/web/src/components/LevelSliderDisplay.tsx` - Now uses `ABACUS_THEMES.dark`
**Code Eliminated:** ~60 lines of duplicate theme style definitions
---
#### 2. **Compact Prop**
**Status:** ✅ Fully integrated
**Files Updated:**
- `apps/web/src/app/arcade/complement-race/components/AbacusTarget.tsx` - Now uses `compact={true}`
**Before:**
```tsx
<AbacusReact
value={number}
columns={1}
interactive={false}
showNumbers={false}
hideInactiveBeads={true}
scaleFactor={0.72}
customStyles={{ columnPosts: { opacity: 0 } }}
/>
```
**After:**
```tsx
<AbacusReact
value={number}
columns={1}
compact={true}
interactive={false}
showNumbers={false}
hideInactiveBeads={true}
scaleFactor={0.72}
/>
```
---
#### 3. **Utility Functions**
**Status:** ✅ Fully integrated
**Files Updated:**
- `apps/web/src/utils/beadDiff.ts` - Now re-exports from abacus-react
- `apps/web/src/utils/abacusInstructionGenerator.ts` - Now re-exports from abacus-react
- `apps/web/src/components/tutorial/TutorialPlayer.tsx` - Imports `calculateBeadDiffFromValues` from abacus-react
- `apps/web/src/components/tutorial/TutorialEditor.tsx` - Imports `calculateBeadDiffFromValues` from abacus-react
**Exports from abacus-react:**
- `numberToAbacusState()`
- `abacusStateToNumber()`
- `calculateBeadChanges()`
- `calculateBeadDiff()`
- `calculateBeadDiffFromValues()`
- `validateAbacusValue()`
- `areStatesEqual()`
**Code Eliminated:** ~200+ lines of duplicate utility implementations
---
#### 4. **React Hooks**
**Status:** ✅ Exported and ready to use
**Available Hooks:**
- `useAbacusDiff(fromValue, toValue, maxPlaces)` - Memoized bead diff calculation
- `useAbacusState(value, maxPlaces)` - Memoized state conversion
**Not yet used in app** (available for future tutorials)
---
#### 5. **Column Highlighting**
**Status:** ✅ Implemented, not yet used
**New Props:**
- `highlightColumns?: number[]` - Highlight specific columns
- `columnLabels?: string[]` - Add educational labels above columns
**Usage Example:**
```tsx
<AbacusReact
value={123}
highlightColumns={[1]}
columnLabels={['ones', 'tens', 'hundreds']}
/>
```
---
### Code Deduplication Summary
**Total Lines Eliminated:** ~260-300 lines
**Breakdown:**
- Theme style definitions: ~60 lines
- Utility function implementations: ~200 lines
- Custom styles for inline abacus: ~5-10 lines per usage
---
### Remaining Work (Optional Future Enhancements)
1. Use `highlightColumns` and `columnLabels` in tutorial components
2. Replace manual bead diff calculations with `useAbacusDiff` hook in interactive tutorials
3. Use `useAbacusState` for state inspection in debugging/development tools
4. Consider implementing `frameVisible` toggles in settings pages
---
### Files Modified
**packages/abacus-react:**
- `src/AbacusReact.tsx` - Added new props (frameVisible, compact, highlightColumns, columnLabels)
- `src/AbacusThemes.ts` - **NEW FILE** - 6 theme presets
- `src/AbacusUtils.ts` - **NEW FILE** - Core utility functions
- `src/AbacusHooks.ts` - **NEW FILE** - React hooks
- `src/index.ts` - Updated exports
- `src/AbacusReact.themes-and-utilities.stories.tsx` - **NEW FILE** - Storybook demos
- `README.md` - Updated with new features documentation
- `ENHANCEMENT_PLAN.md` - Updated with completion status
**apps/web:**
- `src/components/MyAbacus.tsx` - Using ABACUS_THEMES
- `src/components/HeroAbacus.tsx` - Using ABACUS_THEMES
- `src/components/LevelSliderDisplay.tsx` - Using ABACUS_THEMES
- `src/app/arcade/complement-race/components/AbacusTarget.tsx` - Using compact prop
- `src/components/tutorial/TutorialPlayer.tsx` - Importing from abacus-react
- `src/components/tutorial/TutorialEditor.tsx` - Importing from abacus-react
- `src/utils/beadDiff.ts` - Re-exports from abacus-react
- `src/utils/abacusInstructionGenerator.ts` - Re-exports from abacus-react
---
### Testing
✅ Build successful for packages/abacus-react
✅ TypeScript compilation passes for integrated files
✅ Runtime tests confirm functions work correctly
✅ Storybook stories demonstrate all new features
---
### Next Steps
1. Monitor app for any runtime issues with the new integrations
2. Consider using hooks in future tutorial implementations
3. Explore using column highlighting in educational content
4. Document best practices for theme usage in the app

View File

@@ -86,34 +86,98 @@ Personalized colors and highlights
/>
```
### Theme Presets
Use pre-defined themes for quick styling:
```tsx
import { AbacusReact, ABACUS_THEMES } from '@soroban/abacus-react';
// Available themes: 'light', 'dark', 'trophy', 'translucent', 'solid', 'traditional'
<AbacusReact
value={123}
columns={3}
customStyles={ABACUS_THEMES.dark}
/>
<AbacusReact
value={456}
columns={3}
customStyles={ABACUS_THEMES.trophy} // Golden frame for achievements
/>
<AbacusReact
value={789}
columns={3}
customStyles={ABACUS_THEMES.traditional} // Brown wooden appearance
/>
```
**Available Themes:**
- `light` - Solid white frame with subtle gray accents (best for light backgrounds)
- `dark` - Translucent white with subtle glow (best for dark backgrounds)
- `trophy` - Golden frame with warm tones (best for achievements/rewards)
- `translucent` - Nearly invisible frame (best for inline/minimal UI)
- `solid` - Black frame (best for high contrast/educational contexts)
- `traditional` - Brown wooden appearance (best for traditional soroban aesthetic)
### Compact/Inline Display
Create mini abacus displays for inline use:
```tsx
// Compact mode - automatically hides frame and optimizes spacing
<AbacusReact
value={7}
columns={1}
compact={true}
hideInactiveBeads={true}
scaleFactor={0.7}
/>
// Or manually control frame visibility
<AbacusReact
value={42}
columns={2}
frameVisible={false} // Hide column posts and reckoning bar
/>
```
### Tutorial System
Educational guidance with tooltips
Educational guidance with tooltips and column highlighting
<img src="https://raw.githubusercontent.com/antialias/soroban-abacus-flashcards/main/packages/abacus-react/examples/tutorial-mode.svg" alt="Tutorial System">
```tsx
<AbacusReact
value={42}
columns={2}
columns={3}
interactive={true}
// Highlight the tens column with a label
highlightColumns={[1]} // Highlight column index 1 (tens)
columnLabels={['ones', 'tens', 'hundreds']} // Add labels to columns
overlays={[{
id: 'tip',
type: 'tooltip',
target: { type: 'bead', columnIndex: 0, beadType: 'earth', beadPosition: 1 },
content: <div>Click this bead!</div>,
target: { type: 'bead', columnIndex: 1, beadType: 'earth', beadPosition: 1 },
content: <div>Click this bead in the tens column!</div>,
offset: { x: 0, y: -30 }
}]}
callbacks={{
onBeadClick: (event) => {
if (event.columnIndex === 0 && event.beadType === 'earth' && event.position === 1) {
console.log('Correct!');
if (event.columnIndex === 1 && event.beadType === 'earth' && event.position === 1) {
console.log('Correct! You clicked the tens column.');
}
}
}}
/>
```
**Column Highlighting:**
- `highlightColumns` - Array of column indices to highlight (e.g., `[0, 2]` highlights first and third columns)
- `columnLabels` - Optional labels displayed above each column (indexed left to right)
## 3D Enhancement
Make the abacus feel tangible and satisfying with three progressive levels of 3D effects.
@@ -209,10 +273,18 @@ interface AbacusConfig {
colorPalette?: 'default' | 'colorblind' | 'mnemonic' | 'grayscale' | 'nature';
hideInactiveBeads?: boolean; // Hide/show inactive beads
// Layout & Frame
frameVisible?: boolean; // Show/hide column posts and reckoning bar
compact?: boolean; // Compact layout (implies frameVisible=false)
// Interaction
interactive?: boolean; // Enable user interactions
animated?: boolean; // Enable animations
gestures?: boolean; // Enable drag gestures
// Tutorial Features
highlightColumns?: number[]; // Highlight specific columns by index
columnLabels?: string[]; // Optional labels for columns
}
```
@@ -359,6 +431,60 @@ function AdvancedExample() {
## Hooks
### useAbacusDiff
Calculate bead differences between values for tutorials and animations:
```tsx
import { useAbacusDiff } from '@soroban/abacus-react';
function Tutorial() {
const [currentValue, setCurrentValue] = useState(5);
const targetValue = 15;
// Get diff information: which beads need to move
const diff = useAbacusDiff(currentValue, targetValue);
return (
<div>
<p>{diff.summary}</p> {/* "add heaven bead in tens column, then..." */}
<AbacusReact
value={currentValue}
stepBeadHighlights={diff.highlights} // Highlight beads that need to change
interactive
onValueChange={setCurrentValue}
/>
<p>Changes needed: {diff.changes.length}</p>
</div>
);
}
```
**Returns:**
- `changes` - Array of bead movements with direction and order
- `highlights` - Bead highlight data for stepBeadHighlights prop
- `hasChanges` - Boolean indicating if any changes needed
- `summary` - Human-readable description of changes (e.g., "add heaven bead in ones column")
### useAbacusState
Convert numbers to abacus bead states:
```tsx
import { useAbacusState } from '@soroban/abacus-react';
function BeadAnalyzer() {
const value = 123;
const state = useAbacusState(value);
// Check bead positions
const onesHasHeaven = state[0].heavenActive; // false (3 < 5)
const tensEarthCount = state[1].earthActive; // 2 (20 = 2 tens)
return <div>Ones column heaven bead: {onesHasHeaven ? 'active' : 'inactive'}</div>;
}
```
### useAbacusDimensions
Get exact sizing information for layout planning:
@@ -377,6 +503,92 @@ function MyComponent() {
}
```
## Utility Functions
Low-level functions for working with abacus states and calculations:
### numberToAbacusState
Convert a number to bead positions:
```tsx
import { numberToAbacusState } from '@soroban/abacus-react';
const state = numberToAbacusState(123, 5); // 5 columns
// Returns: {
// 0: { heavenActive: false, earthActive: 3 }, // ones = 3
// 1: { heavenActive: false, earthActive: 2 }, // tens = 2
// 2: { heavenActive: true, earthActive: 0 }, // hundreds = 1
// ...
// }
```
### abacusStateToNumber
Convert bead positions back to a number:
```tsx
import { abacusStateToNumber } from '@soroban/abacus-react';
const state = {
0: { heavenActive: false, earthActive: 3 },
1: { heavenActive: false, earthActive: 2 },
2: { heavenActive: true, earthActive: 0 }
};
const value = abacusStateToNumber(state); // 123
```
### calculateBeadDiff
Calculate the exact bead movements needed between two states:
```tsx
import { calculateBeadDiff, numberToAbacusState } from '@soroban/abacus-react';
const fromState = numberToAbacusState(5);
const toState = numberToAbacusState(15);
const diff = calculateBeadDiff(fromState, toState);
console.log(diff.summary); // "add heaven bead in tens column"
console.log(diff.changes); // Detailed array of movements with order
```
### calculateBeadDiffFromValues
Convenience wrapper for calculating diff from numbers:
```tsx
import { calculateBeadDiffFromValues } from '@soroban/abacus-react';
const diff = calculateBeadDiffFromValues(42, 57);
// Equivalent to: calculateBeadDiff(numberToAbacusState(42), numberToAbacusState(57))
```
### validateAbacusValue
Check if a value is within the supported range:
```tsx
import { validateAbacusValue } from '@soroban/abacus-react';
const result = validateAbacusValue(123456, 5); // 5 columns max
console.log(result.isValid); // false
console.log(result.error); // "Value exceeds maximum for 5 columns (max: 99999)"
```
### areStatesEqual
Compare two abacus states:
```tsx
import { areStatesEqual, numberToAbacusState } from '@soroban/abacus-react';
const state1 = numberToAbacusState(123);
const state2 = numberToAbacusState(123);
const isEqual = areStatesEqual(state1, state2); // true
```
## Educational Use Cases
### Interactive Math Lessons
@@ -439,14 +651,37 @@ Full TypeScript definitions included:
```tsx
import {
// Components
AbacusReact,
// Hooks
useAbacusDiff,
useAbacusState,
useAbacusDimensions,
// Utility Functions
numberToAbacusState,
abacusStateToNumber,
calculateBeadDiff,
calculateBeadDiffFromValues,
validateAbacusValue,
areStatesEqual,
// Theme Presets
ABACUS_THEMES,
// Types
AbacusConfig,
BeadConfig,
BeadClickEvent,
AbacusCustomStyles,
AbacusOverlay,
AbacusCallbacks,
useAbacusDimensions
AbacusState,
BeadState,
BeadDiffResult,
BeadDiffOutput,
AbacusThemeName
} from '@soroban/abacus-react';
// All interfaces fully typed for excellent developer experience

View File

@@ -0,0 +1,71 @@
/**
* Utility hooks for working with abacus calculations and state
*/
import { useMemo } from 'react'
import {
calculateBeadDiffFromValues,
numberToAbacusState,
type BeadDiffOutput,
type AbacusState,
} from './AbacusUtils'
/**
* Hook to calculate bead differences between two values
* Useful for tutorials, animations, and highlighting which beads need to move
*
* @param fromValue - Starting value
* @param toValue - Target value
* @param maxPlaces - Maximum number of place values to consider (default: 5)
* @returns BeadDiffOutput with changes, highlights, and summary
*
* @example
* ```tsx
* function Tutorial() {
* const diff = useAbacusDiff(5, 15)
*
* return (
* <AbacusReact
* value={currentValue}
* stepBeadHighlights={diff.highlights}
* />
* )
* }
* ```
*/
export function useAbacusDiff(
fromValue: number | bigint,
toValue: number | bigint,
maxPlaces: number = 5
): BeadDiffOutput {
return useMemo(() => {
return calculateBeadDiffFromValues(fromValue, toValue, maxPlaces)
}, [fromValue, toValue, maxPlaces])
}
/**
* Hook to convert a number to abacus state
* Memoized for performance when used in components
*
* @param value - The number to convert
* @param maxPlaces - Maximum number of place values (default: 5)
* @returns AbacusState representing the bead positions
*
* @example
* ```tsx
* function MyComponent() {
* const state = useAbacusState(123)
*
* // Check if ones column has heaven bead active
* const onesHasHeaven = state[0].heavenActive
* }
* ```
*/
export function useAbacusState(
value: number | bigint,
maxPlaces: number = 5
): AbacusState {
return useMemo(() => {
return numberToAbacusState(value, maxPlaces)
}, [value, maxPlaces])
}

View File

@@ -0,0 +1,613 @@
/**
* Theme system, layout utilities, hooks, and helper functions
* Features: Theme presets, compact mode, column highlighting, hooks, utility functions
*/
import type { Meta, StoryObj } from '@storybook/react';
import React, { useState } from 'react';
import AbacusReact from './AbacusReact';
import {
ABACUS_THEMES,
AbacusThemeName,
useAbacusDiff,
useAbacusState,
numberToAbacusState,
abacusStateToNumber,
calculateBeadDiffFromValues,
validateAbacusValue,
areStatesEqual
} from './index';
const meta = {
title: 'AbacusReact/Themes & Utilities',
component: AbacusReact,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof AbacusReact>;
export default meta;
type Story = StoryObj<typeof meta>;
// ============================================================================
// THEME PRESETS
// ============================================================================
export const AllThemePresets: Story = {
render: () => {
const themes: AbacusThemeName[] = ['light', 'dark', 'trophy', 'translucent', 'solid', 'traditional'];
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '30px', padding: '20px' }}>
<h2>Theme Presets</h2>
<p>Pre-defined themes eliminate manual style object creation</p>
{themes.map((themeName) => (
<div key={themeName} style={{
background: themeName === 'dark' ? '#1a1a1a' : '#f5f5f5',
padding: '20px',
borderRadius: '8px'
}}>
<h3 style={{
marginTop: 0,
color: themeName === 'dark' ? 'white' : 'black',
textTransform: 'capitalize'
}}>
{themeName} Theme
</h3>
<AbacusReact
value={12345}
columns={5}
customStyles={ABACUS_THEMES[themeName]}
showNumbers={true}
/>
</div>
))}
</div>
);
},
};
export const LightTheme: Story = {
render: () => (
<div style={{ padding: '20px', background: '#f5f5f5' }}>
<h3>Light Theme - Best for light backgrounds</h3>
<AbacusReact
value={12345}
columns={5}
customStyles={ABACUS_THEMES.light}
showNumbers={true}
/>
</div>
),
};
export const DarkTheme: Story = {
render: () => (
<div style={{ padding: '20px', background: '#1a1a1a' }}>
<h3 style={{ color: 'white' }}>Dark Theme - Best for dark backgrounds</h3>
<AbacusReact
value={12345}
columns={5}
customStyles={ABACUS_THEMES.dark}
showNumbers={true}
/>
</div>
),
};
export const TrophyTheme: Story = {
render: () => (
<div style={{ padding: '20px', background: '#f0f0f0' }}>
<h3>Trophy Theme - Golden frame for achievements</h3>
<AbacusReact
value={9999}
columns={4}
customStyles={ABACUS_THEMES.trophy}
showNumbers={true}
/>
</div>
),
};
export const TraditionalTheme: Story = {
render: () => (
<div style={{ padding: '20px', background: '#f5f5f0' }}>
<h3>Traditional Theme - Brown wooden soroban aesthetic</h3>
<AbacusReact
value={8765}
columns={4}
customStyles={ABACUS_THEMES.traditional}
showNumbers={true}
/>
</div>
),
};
// ============================================================================
// COMPACT MODE & FRAME VISIBILITY
// ============================================================================
export const CompactMode: Story = {
render: () => (
<div style={{ padding: '20px' }}>
<h3>Compact Mode - Inline mini-abacus displays</h3>
<p>Perfect for inline number displays, badges, or game UI</p>
<div style={{ display: 'flex', gap: '20px', alignItems: 'center', marginTop: '20px' }}>
<span>Single digits: </span>
{[1, 2, 3, 4, 5, 6, 7, 8, 9].map(num => (
<AbacusReact
key={num}
value={num}
columns={1}
compact={true}
hideInactiveBeads={true}
scaleFactor={0.6}
/>
))}
</div>
<div style={{ marginTop: '30px' }}>
<h4>Two-digit compact displays:</h4>
<div style={{ display: 'flex', gap: '15px', alignItems: 'center' }}>
{[12, 34, 56, 78, 99].map(num => (
<AbacusReact
key={num}
value={num}
columns={2}
compact={true}
hideInactiveBeads={true}
scaleFactor={0.7}
/>
))}
</div>
</div>
</div>
),
};
export const FrameVisibilityControl: Story = {
render: () => {
const [frameVisible, setFrameVisible] = useState(true);
return (
<div style={{ padding: '20px' }}>
<h3>Frame Visibility Control</h3>
<p>Toggle column posts and reckoning bar on/off</p>
<div style={{ marginBottom: '20px' }}>
<label>
<input
type="checkbox"
checked={frameVisible}
onChange={(e) => setFrameVisible(e.target.checked)}
/>
{' '}Show Frame
</label>
</div>
<AbacusReact
value={12345}
columns={5}
frameVisible={frameVisible}
showNumbers={true}
/>
</div>
);
},
};
// ============================================================================
// COLUMN HIGHLIGHTING & LABELS
// ============================================================================
export const ColumnHighlighting: Story = {
render: () => (
<div style={{ padding: '20px' }}>
<h3>Column Highlighting</h3>
<p>Highlight specific columns for educational purposes</p>
<div style={{ marginBottom: '30px' }}>
<h4>Highlight ones column:</h4>
<AbacusReact
value={12345}
columns={5}
highlightColumns={[0]}
showNumbers={true}
/>
</div>
<div style={{ marginBottom: '30px' }}>
<h4>Highlight tens and hundreds:</h4>
<AbacusReact
value={12345}
columns={5}
highlightColumns={[1, 2]}
showNumbers={true}
/>
</div>
<div>
<h4>Highlight all columns:</h4>
<AbacusReact
value={12345}
columns={5}
highlightColumns={[0, 1, 2, 3, 4]}
showNumbers={true}
/>
</div>
</div>
),
};
export const ColumnLabels: Story = {
render: () => (
<div style={{ padding: '20px' }}>
<h3>Column Labels</h3>
<p>Add educational labels above columns</p>
<div style={{ marginBottom: '30px' }}>
<h4>Standard place value labels:</h4>
<AbacusReact
value={12345}
columns={5}
columnLabels={['ones', 'tens', 'hundreds', 'thousands', '10k']}
showNumbers={true}
/>
</div>
<div>
<h4>Custom labels:</h4>
<AbacusReact
value={789}
columns={3}
columnLabels={['1s', '10s', '100s']}
highlightColumns={[1]}
showNumbers={true}
/>
</div>
</div>
),
};
export const ColumnHighlightingWithLabels: Story = {
render: () => (
<div style={{ padding: '20px' }}>
<h3>Combined: Column Highlighting + Labels</h3>
<p>Perfect for tutorials showing which column to work with</p>
<AbacusReact
value={42}
columns={3}
highlightColumns={[1]}
columnLabels={['ones', 'tens', 'hundreds']}
showNumbers={true}
/>
<p style={{ marginTop: '20px', fontStyle: 'italic' }}>
"Add 10 to the tens column"
</p>
</div>
),
};
// ============================================================================
// HOOKS: useAbacusDiff
// ============================================================================
function AbacusDiffDemo() {
const [currentValue, setCurrentValue] = useState(5);
const targetValue = 23;
const diff = useAbacusDiff(currentValue, targetValue);
return (
<div style={{ padding: '20px' }}>
<h3>useAbacusDiff Hook</h3>
<p>Automatically calculate which beads need to move</p>
<div style={{ marginBottom: '20px' }}>
<p><strong>Current value:</strong> {currentValue}</p>
<p><strong>Target value:</strong> {targetValue}</p>
<p><strong>Instructions:</strong> {diff.summary}</p>
<p><strong>Changes needed:</strong> {diff.changes.length}</p>
</div>
<AbacusReact
value={currentValue}
columns={2}
stepBeadHighlights={diff.highlights}
showNumbers={true}
interactive={true}
onValueChange={setCurrentValue}
/>
<div style={{ marginTop: '20px' }}>
<button onClick={() => setCurrentValue(5)}>Reset to 5</button>
{' '}
<button onClick={() => setCurrentValue(targetValue)}>Jump to target (23)</button>
</div>
{diff.hasChanges ? (
<div style={{ marginTop: '20px', color: '#666' }}>
<p><strong>Detailed changes:</strong></p>
<pre style={{ fontSize: '12px' }}>
{JSON.stringify(diff.changes, null, 2)}
</pre>
</div>
) : (
<p style={{ marginTop: '20px', color: 'green', fontWeight: 'bold' }}>
Target reached!
</p>
)}
</div>
);
}
export const UseAbacusDiffHook: Story = {
render: () => <AbacusDiffDemo />,
};
// ============================================================================
// HOOKS: useAbacusState
// ============================================================================
function AbacusStateDemo() {
const [value, setValue] = useState(123);
const state = useAbacusState(value);
return (
<div style={{ padding: '20px' }}>
<h3>useAbacusState Hook</h3>
<p>Convert numbers to bead positions (memoized)</p>
<div style={{ marginBottom: '20px' }}>
<label>
Value:
<input
type="number"
value={value}
onChange={(e) => setValue(parseInt(e.target.value) || 0)}
style={{ marginLeft: '10px', width: '100px' }}
/>
</label>
</div>
<AbacusReact
value={value}
columns={3}
showNumbers={true}
/>
<div style={{ marginTop: '20px', fontSize: '14px' }}>
<p><strong>Bead State Analysis:</strong></p>
<table style={{ borderCollapse: 'collapse', marginTop: '10px' }}>
<thead>
<tr style={{ background: '#f0f0f0' }}>
<th style={{ border: '1px solid #ccc', padding: '8px' }}>Place</th>
<th style={{ border: '1px solid #ccc', padding: '8px' }}>Heaven Active?</th>
<th style={{ border: '1px solid #ccc', padding: '8px' }}>Earth Count</th>
<th style={{ border: '1px solid #ccc', padding: '8px' }}>Digit</th>
</tr>
</thead>
<tbody>
{[0, 1, 2].map(place => {
const placeState = state[place];
const digit = (placeState.heavenActive ? 5 : 0) + placeState.earthActive;
const placeName = ['Ones', 'Tens', 'Hundreds'][place];
return (
<tr key={place}>
<td style={{ border: '1px solid #ccc', padding: '8px' }}>{placeName}</td>
<td style={{ border: '1px solid #ccc', padding: '8px', textAlign: 'center' }}>
{placeState.heavenActive ? '✓' : '✗'}
</td>
<td style={{ border: '1px solid #ccc', padding: '8px', textAlign: 'center' }}>
{placeState.earthActive}
</td>
<td style={{ border: '1px solid #ccc', padding: '8px', textAlign: 'center' }}>
{digit}
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
);
}
export const UseAbacusStateHook: Story = {
render: () => <AbacusStateDemo />,
};
// ============================================================================
// UTILITY FUNCTIONS
// ============================================================================
function UtilityFunctionsDemo() {
const [inputValue, setInputValue] = useState(123);
const state = numberToAbacusState(inputValue, 5);
const backToNumber = abacusStateToNumber(state);
const fromValue = 42;
const toValue = 57;
const diff = calculateBeadDiffFromValues(fromValue, toValue);
const validation1 = validateAbacusValue(inputValue, 5);
const validation2 = validateAbacusValue(123456, 5);
const state1 = numberToAbacusState(100);
const state2 = numberToAbacusState(100);
const state3 = numberToAbacusState(200);
const areEqual1 = areStatesEqual(state1, state2);
const areEqual2 = areStatesEqual(state1, state3);
return (
<div style={{ padding: '20px' }}>
<h3>Utility Functions</h3>
<p>Low-level functions for working with abacus states</p>
<div style={{ marginBottom: '30px' }}>
<h4>numberToAbacusState & abacusStateToNumber:</h4>
<label>
Input value:
<input
type="number"
value={inputValue}
onChange={(e) => setInputValue(parseInt(e.target.value) || 0)}
style={{ marginLeft: '10px', width: '100px' }}
/>
</label>
<pre style={{ background: '#f5f5f5', padding: '10px', marginTop: '10px', fontSize: '12px' }}>
{`numberToAbacusState(${inputValue}, 5) = ${JSON.stringify(state, null, 2)}`}
</pre>
<pre style={{ background: '#f5f5f5', padding: '10px', fontSize: '12px' }}>
{`abacusStateToNumber(state) = ${backToNumber}`}
</pre>
</div>
<div style={{ marginBottom: '30px' }}>
<h4>calculateBeadDiffFromValues:</h4>
<p>From {fromValue} to {toValue}:</p>
<pre style={{ background: '#f5f5f5', padding: '10px', fontSize: '12px' }}>
{`Summary: ${diff.summary}\nChanges: ${diff.changes.length}`}
</pre>
</div>
<div style={{ marginBottom: '30px' }}>
<h4>validateAbacusValue:</h4>
<pre style={{ background: '#f5f5f5', padding: '10px', fontSize: '12px' }}>
{`validateAbacusValue(${inputValue}, 5):\n isValid: ${validation1.isValid}\n error: ${validation1.error || 'none'}`}
</pre>
<pre style={{ background: '#f5f5f5', padding: '10px', fontSize: '12px' }}>
{`validateAbacusValue(123456, 5):\n isValid: ${validation2.isValid}\n error: ${validation2.error || 'none'}`}
</pre>
</div>
<div>
<h4>areStatesEqual:</h4>
<pre style={{ background: '#f5f5f5', padding: '10px', fontSize: '12px' }}>
{`areStatesEqual(state(100), state(100)) = ${areEqual1}\nareStatesEqual(state(100), state(200)) = ${areEqual2}`}
</pre>
</div>
</div>
);
}
export const UtilityFunctions: Story = {
render: () => <UtilityFunctionsDemo />,
};
// ============================================================================
// COMBINED FEATURES
// ============================================================================
export const AllFeaturesShowcase: Story = {
render: () => {
const [value, setValue] = useState(42);
const targetValue = 75;
const diff = useAbacusDiff(value, targetValue);
return (
<div style={{ padding: '20px', maxWidth: '800px' }}>
<h2>All New Features Combined</h2>
<p>Theme preset + column highlighting + labels + diff hook</p>
<div style={{
background: '#1a1a1a',
padding: '30px',
borderRadius: '8px',
marginTop: '20px'
}}>
<h3 style={{ color: 'white', marginTop: 0 }}>
Tutorial: Add to reach {targetValue}
</h3>
<p style={{ color: '#ccc' }}>
<strong>Current:</strong> {value} <strong>Target:</strong> {targetValue}
</p>
<p style={{ color: '#fbbf24' }}>
<strong>Instructions:</strong> {diff.summary}
</p>
<AbacusReact
value={value}
columns={2}
customStyles={ABACUS_THEMES.dark}
highlightColumns={[0, 1]}
columnLabels={['ones', 'tens']}
stepBeadHighlights={diff.highlights}
showNumbers={true}
interactive={true}
onValueChange={setValue}
/>
<div style={{ marginTop: '20px' }}>
<button onClick={() => setValue(42)}>Reset</button>
{' '}
<button onClick={() => setValue(targetValue)}>Show Answer</button>
</div>
{!diff.hasChanges && (
<p style={{ color: '#4ade80', fontWeight: 'bold', marginTop: '20px' }}>
🎉 Perfect! You reached the target!
</p>
)}
</div>
</div>
);
},
};
export const CompactThemeComparison: Story = {
render: () => {
const themes: AbacusThemeName[] = ['light', 'dark', 'trophy', 'traditional'];
return (
<div style={{ padding: '20px' }}>
<h3>Compact Mode with Different Themes</h3>
<p>Inline displays work with all theme presets</p>
<div style={{ display: 'flex', flexDirection: 'column', gap: '20px', marginTop: '20px' }}>
{themes.map(theme => (
<div
key={theme}
style={{
background: theme === 'dark' ? '#1a1a1a' : '#f5f5f5',
padding: '15px',
borderRadius: '6px'
}}
>
<h4 style={{
margin: '0 0 15px 0',
color: theme === 'dark' ? 'white' : 'black',
textTransform: 'capitalize'
}}>
{theme}:
</h4>
<div style={{ display: 'flex', gap: '15px', alignItems: 'center' }}>
{[7, 42, 99].map(num => (
<div key={num}>
<AbacusReact
value={num}
columns={num < 10 ? 1 : 2}
compact={true}
hideInactiveBeads={true}
customStyles={ABACUS_THEMES[theme]}
scaleFactor={0.7}
/>
</div>
))}
</div>
</div>
))}
</div>
</div>
);
},
};

View File

@@ -270,6 +270,10 @@ export interface AbacusConfig {
soundEnabled?: boolean;
soundVolume?: number;
// Layout & Frame
frameVisible?: boolean; // Show/hide column posts and reckoning bar
compact?: boolean; // Compact layout for inline display (implies frameVisible=false, optimized spacing)
// 3D Enhancement
enhanced3d?: boolean | "subtle" | "realistic";
material3d?: Abacus3DMaterial;
@@ -281,6 +285,7 @@ export interface AbacusConfig {
// Tutorial and accessibility features
highlightColumns?: number[]; // Highlight specific columns (legacy - array indices)
columnLabels?: string[]; // Optional labels for columns (indexed by column index, left to right)
highlightBeads?: BeadHighlight[]; // Support both place-value and column-index based highlighting
stepBeadHighlights?: StepBeadHighlight[]; // Progressive step-based highlighting with directions
currentStep?: number; // Current step index for progressive highlighting
@@ -1581,6 +1586,9 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
showNumbers,
soundEnabled,
soundVolume,
// Layout & Frame props
frameVisible,
compact,
// 3D enhancement props
enhanced3d,
material3d,
@@ -1589,6 +1597,7 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
callbacks,
overlays = [],
highlightColumns = [],
columnLabels = [],
highlightBeads = [],
stepBeadHighlights = [],
currentStep = 0,
@@ -1609,6 +1618,9 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
}
// Use props if provided, otherwise fall back to context config
// Apply compact preset logic
const effectiveFrameVisible = compact ? false : (frameVisible ?? true);
const finalConfig = {
hideInactiveBeads: hideInactiveBeads ?? contextConfig.hideInactiveBeads,
beadShape: beadShape ?? contextConfig.beadShape,
@@ -1621,6 +1633,8 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
showNumbers: showNumbers ?? contextConfig.showNumbers,
soundEnabled: soundEnabled ?? contextConfig.soundEnabled,
soundVolume: soundVolume ?? contextConfig.soundVolume,
frameVisible: effectiveFrameVisible,
compact: compact ?? false,
};
// Calculate effective columns first, without depending on columnStates
const effectiveColumns = useMemo(() => {
@@ -2167,8 +2181,55 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
);
})}
{/* Column highlights - rendered behind everything for tutorial/educational purposes */}
{highlightColumns.map((colIndex) => {
if (colIndex < 0 || colIndex >= effectiveColumns) return null;
const x = colIndex * dimensions.rodSpacing + dimensions.rodSpacing / 2;
const highlightWidth = dimensions.rodSpacing * 0.9; // Slightly narrower than full column
const highlightHeight = dimensions.height;
return (
<rect
key={`column-highlight-${colIndex}`}
x={x - highlightWidth / 2}
y={0}
width={highlightWidth}
height={highlightHeight}
fill="rgba(59, 130, 246, 0.15)" // Light blue highlight
stroke="rgba(59, 130, 246, 0.4)" // Slightly darker blue border
strokeWidth={2}
rx={6}
style={{ pointerEvents: "none" }}
/>
);
})}
{/* Column labels - rendered above columns for tutorial/educational purposes */}
{columnLabels.map((label, colIndex) => {
if (!label || colIndex >= effectiveColumns) return null;
const x = colIndex * dimensions.rodSpacing + dimensions.rodSpacing / 2;
const labelY = -20; // Position above the abacus
return (
<text
key={`column-label-${colIndex}`}
x={x}
y={labelY}
textAnchor="middle"
fontSize="14"
fontWeight="600"
fill="rgba(0, 0, 0, 0.7)"
style={{ pointerEvents: "none", userSelect: "none" }}
>
{label}
</text>
);
})}
{/* Rods - positioned as rectangles like in Typst */}
{Array.from({ length: effectiveColumns }, (_, colIndex) => {
{finalConfig.frameVisible && Array.from({ length: effectiveColumns }, (_, colIndex) => {
const placeValue = effectiveColumns - 1 - colIndex;
const x =
colIndex * dimensions.rodSpacing + dimensions.rodSpacing / 2;
@@ -2229,32 +2290,36 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
})}
{/* Reckoning bar - spans from leftmost to rightmost bead */}
<rect
x={dimensions.rodSpacing / 2 - dimensions.beadSize / 2}
y={barY}
width={
(effectiveColumns - 1) * dimensions.rodSpacing + dimensions.beadSize
}
height={dimensions.barThickness}
fill={customStyles?.reckoningBar?.fill || "black"} // Typst default is black
stroke={customStyles?.reckoningBar?.stroke || "none"}
strokeWidth={customStyles?.reckoningBar?.strokeWidth ?? 0}
opacity={customStyles?.reckoningBar?.opacity ?? 1}
className="reckoning-bar"
/>
{/* Wood grain texture overlay for reckoning bar */}
{enhanced3d === 'realistic' && material3d?.woodGrain && (
<rect
x={dimensions.rodSpacing / 2 - dimensions.beadSize / 2}
y={barY}
width={
(effectiveColumns - 1) * dimensions.rodSpacing + dimensions.beadSize
}
height={dimensions.barThickness}
fill="url(#wood-grain-pattern)"
className="frame-wood"
style={{ pointerEvents: 'none' }}
/>
{finalConfig.frameVisible && (
<>
<rect
x={dimensions.rodSpacing / 2 - dimensions.beadSize / 2}
y={barY}
width={
(effectiveColumns - 1) * dimensions.rodSpacing + dimensions.beadSize
}
height={dimensions.barThickness}
fill={customStyles?.reckoningBar?.fill || "black"} // Typst default is black
stroke={customStyles?.reckoningBar?.stroke || "none"}
strokeWidth={customStyles?.reckoningBar?.strokeWidth ?? 0}
opacity={customStyles?.reckoningBar?.opacity ?? 1}
className="reckoning-bar"
/>
{/* Wood grain texture overlay for reckoning bar */}
{enhanced3d === 'realistic' && material3d?.woodGrain && (
<rect
x={dimensions.rodSpacing / 2 - dimensions.beadSize / 2}
y={barY}
width={
(effectiveColumns - 1) * dimensions.rodSpacing + dimensions.beadSize
}
height={dimensions.barThickness}
fill="url(#wood-grain-pattern)"
className="frame-wood"
style={{ pointerEvents: 'none' }}
/>
)}
</>
)}
{/* Beads */}

View File

@@ -0,0 +1,115 @@
/**
* Pre-defined theme presets for AbacusReact component
* These eliminate the need for manual style object creation
*/
import type { AbacusCustomStyles } from './AbacusReact'
export const ABACUS_THEMES = {
/**
* Light theme - solid white frame with subtle gray accents
* Best for: Clean, minimalist designs on light backgrounds
*/
light: {
columnPosts: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 2,
},
reckoningBar: {
fill: 'rgb(255, 255, 255)',
stroke: 'rgb(200, 200, 200)',
strokeWidth: 3,
},
} as AbacusCustomStyles,
/**
* Dark theme - translucent white with subtle glow
* Best for: Dark backgrounds, hero sections, dramatic presentations
*/
dark: {
columnPosts: {
fill: 'rgba(255, 255, 255, 0.3)',
stroke: 'rgba(255, 255, 255, 0.2)',
strokeWidth: 2,
},
reckoningBar: {
fill: 'rgba(255, 255, 255, 0.4)',
stroke: 'rgba(255, 255, 255, 0.25)',
strokeWidth: 3,
},
} as AbacusCustomStyles,
/**
* Trophy/Premium theme - golden frame with warm tones
* Best for: Achievements, rewards, celebration contexts
*/
trophy: {
columnPosts: {
fill: '#fbbf24',
stroke: '#f59e0b',
strokeWidth: 3,
},
reckoningBar: {
fill: '#fbbf24',
stroke: '#f59e0b',
strokeWidth: 4,
},
} as AbacusCustomStyles,
/**
* Translucent theme - subtle, nearly invisible frame
* Best for: Inline displays, minimal UI, focus on beads
*/
translucent: {
columnPosts: {
fill: 'rgba(0, 0, 0, 0.05)',
stroke: 'rgba(0, 0, 0, 0.1)',
strokeWidth: 1,
},
reckoningBar: {
fill: 'rgba(0, 0, 0, 0.1)',
stroke: 'none',
strokeWidth: 0,
},
} as AbacusCustomStyles,
/**
* Solid/High-contrast theme - black frame for maximum visibility
* Best for: Educational contexts, high visibility requirements
*/
solid: {
columnPosts: {
fill: 'rgb(0, 0, 0)',
stroke: 'rgb(0, 0, 0)',
strokeWidth: 2,
},
reckoningBar: {
fill: 'rgb(0, 0, 0)',
stroke: 'none',
strokeWidth: 0,
},
} as AbacusCustomStyles,
/**
* Traditional/Natural theme - brown wooden appearance
* Best for: Traditional soroban aesthetic, cultural contexts
*/
traditional: {
columnPosts: {
fill: '#8B5A2B',
stroke: '#654321',
strokeWidth: 2,
},
reckoningBar: {
fill: '#8B5A2B',
stroke: '#654321',
strokeWidth: 3,
},
} as AbacusCustomStyles,
} as const
/**
* Theme names type for TypeScript autocomplete
*/
export type AbacusThemeName = keyof typeof ABACUS_THEMES

View File

@@ -0,0 +1,358 @@
/**
* Utility functions for working with abacus states and calculations
* These help convert between numbers and bead positions, calculate diffs, etc.
*/
import type { ValidPlaceValues, BeadHighlight } from './AbacusReact'
/**
* Represents the state of beads in a single column
*/
export interface BeadState {
heavenActive: boolean
earthActive: number // 0-4
}
/**
* Represents the complete state of an abacus
* Key is the place value (0 = ones, 1 = tens, etc.)
*/
export interface AbacusState {
[placeValue: number]: BeadState
}
/**
* Convert a number to abacus state representation
* @param value - The number to convert
* @param maxPlaces - Maximum number of place values to include
* @returns AbacusState object representing the bead positions
*/
export function numberToAbacusState(value: number | bigint, maxPlaces: number = 5): AbacusState {
const state: AbacusState = {}
const valueNum = typeof value === 'bigint' ? Number(value) : value
for (let place = 0; place < maxPlaces; place++) {
const placeValueNum = 10 ** place
const digit = Math.floor(valueNum / placeValueNum) % 10
state[place] = {
heavenActive: digit >= 5,
earthActive: digit >= 5 ? digit - 5 : digit,
}
}
return state
}
/**
* Convert abacus state to a number
* @param state - The abacus state to convert
* @returns The numeric value represented by the abacus
*/
export function abacusStateToNumber(state: AbacusState): number {
let total = 0
for (const placeStr in state) {
const place = parseInt(placeStr, 10)
const beadState = state[place]
const digit = (beadState.heavenActive ? 5 : 0) + beadState.earthActive
total += digit * (10 ** place)
}
return total
}
/**
* Bead highlight with place value (internal type for calculations)
*/
export interface PlaceValueBasedBead {
placeValue: ValidPlaceValues
beadType: 'heaven' | 'earth'
position?: 0 | 1 | 2 | 3
}
/**
* Calculate which beads need to change between two abacus states
* @param startState - The starting abacus state
* @param targetState - The target abacus state
* @returns Object with arrays of bead additions and removals
*/
export function calculateBeadChanges(
startState: AbacusState,
targetState: AbacusState
): {
additions: PlaceValueBasedBead[]
removals: PlaceValueBasedBead[]
placeValue: number
} {
const additions: PlaceValueBasedBead[] = []
const removals: PlaceValueBasedBead[] = []
let mainPlaceValue = 0
for (const placeStr in targetState) {
const place = parseInt(placeStr, 10) as ValidPlaceValues
const start = startState[place] || { heavenActive: false, earthActive: 0 }
const target = targetState[place]
// Check heaven bead changes
if (!start.heavenActive && target.heavenActive) {
additions.push({ placeValue: place, beadType: 'heaven' })
mainPlaceValue = place
} else if (start.heavenActive && !target.heavenActive) {
removals.push({ placeValue: place, beadType: 'heaven' })
mainPlaceValue = place
}
// Check earth bead changes
if (target.earthActive > start.earthActive) {
// Adding earth beads
for (let pos = start.earthActive; pos < target.earthActive; pos++) {
additions.push({ placeValue: place, beadType: 'earth', position: pos as 0 | 1 | 2 | 3 })
mainPlaceValue = place
}
} else if (target.earthActive < start.earthActive) {
// Removing earth beads
for (let pos = start.earthActive - 1; pos >= target.earthActive; pos--) {
removals.push({ placeValue: place, beadType: 'earth', position: pos as 0 | 1 | 2 | 3 })
mainPlaceValue = place
}
}
}
return { additions, removals, placeValue: mainPlaceValue }
}
/**
* Result of a bead diff calculation
*/
export interface BeadDiffResult {
placeValue: ValidPlaceValues
beadType: 'heaven' | 'earth'
position?: number
direction: 'activate' | 'deactivate'
order: number // Order of operations for animations
}
/**
* Output of calculateBeadDiff function
*/
export interface BeadDiffOutput {
changes: BeadDiffResult[]
highlights: PlaceValueBasedBead[]
hasChanges: boolean
summary: string
}
/**
* Calculate the diff between two abacus states
* Returns exactly which beads need to move with directions and order
* @param fromState - Starting state
* @param toState - Target state
* @returns BeadDiffOutput with changes, highlights, and summary
*/
export function calculateBeadDiff(fromState: AbacusState, toState: AbacusState): BeadDiffOutput {
const { additions, removals } = calculateBeadChanges(fromState, toState)
const changes: BeadDiffResult[] = []
const highlights: PlaceValueBasedBead[] = []
let order = 0
// Process removals first (pedagogical order: clear before adding)
removals.forEach((removal) => {
changes.push({
placeValue: removal.placeValue,
beadType: removal.beadType,
position: removal.position,
direction: 'deactivate',
order: order++,
})
highlights.push({
placeValue: removal.placeValue,
beadType: removal.beadType,
position: removal.position,
})
})
// Process additions second (pedagogical order: add after clearing)
additions.forEach((addition) => {
changes.push({
placeValue: addition.placeValue,
beadType: addition.beadType,
position: addition.position,
direction: 'activate',
order: order++,
})
highlights.push({
placeValue: addition.placeValue,
beadType: addition.beadType,
position: addition.position,
})
})
// Generate summary
const summary = generateDiffSummary(changes)
return {
changes,
highlights,
hasChanges: changes.length > 0,
summary,
}
}
/**
* Calculate bead diff from numeric values
* Convenience function for when you have numbers instead of states
* @param fromValue - Starting numeric value
* @param toValue - Target numeric value
* @param maxPlaces - Maximum number of place values to consider
* @returns BeadDiffOutput
*/
export function calculateBeadDiffFromValues(
fromValue: number | bigint,
toValue: number | bigint,
maxPlaces: number = 5
): BeadDiffOutput {
const fromState = numberToAbacusState(fromValue, maxPlaces)
const toState = numberToAbacusState(toValue, maxPlaces)
return calculateBeadDiff(fromState, toState)
}
/**
* Validate that an abacus value is within the supported range
* @param value - The value to validate
* @param maxPlaces - Maximum number of place values supported
* @returns Object with isValid boolean and optional error message
*/
export function validateAbacusValue(
value: number | bigint,
maxPlaces: number = 5
): { isValid: boolean; error?: string } {
const valueNum = typeof value === 'bigint' ? Number(value) : value
if (valueNum < 0) {
return { isValid: false, error: 'Negative values are not supported' }
}
const maxValue = 10 ** maxPlaces - 1
if (valueNum > maxValue) {
return { isValid: false, error: `Value exceeds maximum for ${maxPlaces} columns (max: ${maxValue})` }
}
return { isValid: true }
}
/**
* Check if two abacus states are equal
* @param state1 - First state
* @param state2 - Second state
* @returns true if states are equal
*/
export function areStatesEqual(state1: AbacusState, state2: AbacusState): boolean {
const places1 = Object.keys(state1)
.map((k) => parseInt(k, 10))
.sort()
const places2 = Object.keys(state2)
.map((k) => parseInt(k, 10))
.sort()
if (places1.length !== places2.length) return false
for (const place of places1) {
const bead1 = state1[place]
const bead2 = state2[place]
if (!bead2) return false
if (bead1.heavenActive !== bead2.heavenActive) return false
if (bead1.earthActive !== bead2.earthActive) return false
}
return true
}
// Internal helper functions
function generateDiffSummary(changes: BeadDiffResult[]): string {
if (changes.length === 0) {
return 'No changes needed'
}
// Sort by order to respect pedagogical sequence
const sortedChanges = [...changes].sort((a, b) => a.order - b.order)
const deactivations = sortedChanges.filter((c) => c.direction === 'deactivate')
const activations = sortedChanges.filter((c) => c.direction === 'activate')
const parts: string[] = []
// Process deactivations first (pedagogical order)
if (deactivations.length > 0) {
const deactivationsByPlace = groupByPlace(deactivations)
Object.entries(deactivationsByPlace).forEach(([place, beads]) => {
const placeName = getPlaceName(parseInt(place, 10))
const heavenBeads = beads.filter((b) => b.beadType === 'heaven')
const earthBeads = beads.filter((b) => b.beadType === 'earth')
if (heavenBeads.length > 0) {
parts.push(`remove heaven bead in ${placeName}`)
}
if (earthBeads.length > 0) {
const count = earthBeads.length
parts.push(`remove ${count} earth bead${count > 1 ? 's' : ''} in ${placeName}`)
}
})
}
// Process activations second (pedagogical order)
if (activations.length > 0) {
const activationsByPlace = groupByPlace(activations)
Object.entries(activationsByPlace).forEach(([place, beads]) => {
const placeName = getPlaceName(parseInt(place, 10))
const heavenBeads = beads.filter((b) => b.beadType === 'heaven')
const earthBeads = beads.filter((b) => b.beadType === 'earth')
if (heavenBeads.length > 0) {
parts.push(`add heaven bead in ${placeName}`)
}
if (earthBeads.length > 0) {
const count = earthBeads.length
parts.push(`add ${count} earth bead${count > 1 ? 's' : ''} in ${placeName}`)
}
})
}
return parts.join(', then ')
}
function groupByPlace(changes: BeadDiffResult[]): {
[place: string]: BeadDiffResult[]
} {
return changes.reduce(
(groups, change) => {
const place = change.placeValue.toString()
if (!groups[place]) {
groups[place] = []
}
groups[place].push(change)
return groups
},
{} as { [place: string]: BeadDiffResult[] }
)
}
function getPlaceName(place: number): string {
switch (place) {
case 0:
return 'ones column'
case 1:
return 'tens column'
case 2:
return 'hundreds column'
case 3:
return 'thousands column'
default:
return `place ${place} column`
}
}

View File

@@ -1,5 +1,20 @@
export { default as AbacusReact } from "./AbacusReact";
export type { AbacusConfig, BeadConfig, AbacusDimensions } from "./AbacusReact";
export type {
AbacusConfig,
BeadConfig,
AbacusDimensions,
AbacusCustomStyles,
BeadStyle,
ColumnPostStyle,
ReckoningBarStyle,
NumeralStyle,
ValidPlaceValues,
BeadHighlight,
StepBeadHighlight,
BeadClickEvent,
AbacusCallbacks,
AbacusOverlay,
} from "./AbacusReact";
export {
useAbacusConfig,
@@ -17,3 +32,25 @@ export type {
export { StandaloneBead } from "./StandaloneBead";
export type { StandaloneBeadProps } from "./StandaloneBead";
export { ABACUS_THEMES } from "./AbacusThemes";
export type { AbacusThemeName } from "./AbacusThemes";
export {
numberToAbacusState,
abacusStateToNumber,
calculateBeadChanges,
calculateBeadDiff,
calculateBeadDiffFromValues,
validateAbacusValue,
areStatesEqual,
} from "./AbacusUtils";
export type {
BeadState,
AbacusState,
BeadDiffResult,
BeadDiffOutput,
PlaceValueBasedBead,
} from "./AbacusUtils";
export { useAbacusDiff, useAbacusState } from "./AbacusHooks";