# [2.15.0](https://github.com/antialias/soroban-abacus-flashcards/compare/abacus-react-v2.14.0...abacus-react-v2.15.0) (2025-12-08) ### Bug Fixes * **404:** reset easter egg config on page reload/close ([d6f1c13](d6f1c13317)) * account for SVG preserveAspectRatio in coordinate transforms ([e4e0925](e4e09256c2)) * add 'auto' to RuleMode type to prevent undefined display values ([a8636ca](a8636ca6a2)) * add comprehensive dark mode support to Smart Difficulty controls ([a65feb7](a65feb7344)) * add cooldown after quick-escape to prevent precision mode re-activation ([e885ae7](e885ae7ef4)) * add currentStepEstimate to required fields in JSON schema ([0d66c54](0d66c54991)) * add custom cursor when pointer lock is active ([5d6d6b4](5d6d6b4ddc)) * add missing openDeploymentInfo prop to MinimalNav ([30879d8](30879d8959)) * add missing scaleX and scaleY number conversions ([ae6cc5e](ae6cc5e326)) * add missing selectedContinent to default config and fix ts-expect-error directives ([07e9224](07e92240e8)) * add mode descriptions and remove double borders ([6f2f6d4](6f2f6d444c)) * add quotes around unquoted keys when parsing customCrops.ts ([0add49c](0add49c599)) * add seed and prngAlgorithm fields to all Zod schema versions (V1-V4) ([1782f42](1782f427f1)) * add shuffling to progressive difficulty mode & UI improvements ([38e9982](38e9982c3d)) * add zoom to selected continent and improve click detection ([6651979](6651979ea0)) * align share persistence with user session logic ([72c72fc](72c72fc218)) * calculate total problems correctly in preview API ([25dfb71](25dfb71b3e)) * cancel previous give-up animation when new give-up starts ([c01cb7f](c01cb7f384)) * cap zoom when releasing pointer lock (escape key) ([2331f10](2331f1038c)) * center crosshairs and re-enable pointer lock after escape ([814bf94](814bf949f2)) * change zoom capping to create pause effect instead of slow easing ([f2ca9d1](f2ca9d1ebe)) * combine fast easing with smooth precision mode transition ([cab1fbf](cab1fbff95)) * configure Next.js to transpile [@svg-maps](https://github.com/svg-maps) ES modules ([ebf2b66](ebf2b66910)) * consolidate worksheet validation constants and increase MAX_PAGES to 100 ([0f3ec36](0f3ec369bf)) * correct GPT-5 API parameters and surface actual grading errors ([2d33f35](2d33f35c4d)) * **create:** use inline styles for dynamic gradient backgrounds ([ed25b32](ed25b323e8)) * DevCropTool key quoting and magnifier label positioning ([2e4f22a](2e4f22a522)) * don't show labels for excluded/filtered regions ([a83fbb1](a83fbb1070)) * eagerly load map caches in browser and use Suspense pattern ([db6be73](db6be73a1c)) * eliminate cursor dampening lag when changing direction ([fc08b77](fc08b775db)) * enable page virtualization in worksheet creator ([b675f6c](b675f6c96e)) * enable vertical scrolling in layout controls ([a1b31f4](a1b31f454a)) * enable virtualization for worksheet preview by limiting SSR to 3 pages ([f409e3c](f409e3c2ed)) * ensure entire region path is clickable with pointerEvents: all ([eb94191](eb94191b2e)) * export worksheet schema tables from index ([6a16674](6a1667404f)) * hide abacus on /arcade and /arcade-rooms routes ([77033f0](77033f0b22)) * implement manual click detection using precise cursor position ([156f63f](156f63faaf)) * improve dark mode contrast in OrientationPanel dropdown ([f9e2343](f9e2343ffb)) * improve dark mode for orientation and page buttons ([fe9b9f9](fe9b9f9ffa)) * improve draggable button constraints to avoid action button overlap ([00b0fb2](00b0fb297b)) * improve magnifier zoom calculation for multi-piece regions ([cb4114f](cb4114f344)) * improve magnifier zoom smoothness and debug panel ([639e662](639e662d76)) * improve socket server error messages for better debugging ([5d1ea7d](5d1ea7db2d)) * improve text contrast for selected dropdown items in dark mode ([8d03452](8d0345287f)) * improve zoom easing to threshold by deferring capping ([b355a3f](b355a3fc8f)) * increase sampling density for tiny region detection and show all detected regions ([2c9f760](2c9f760ae9)) * increase super zoom multiplier from 2.5x to 5.0x for Gibraltar ([138e6c0](138e6c071b)) * increase z-index of pointer lock prompt overlay ([5388441](5388441ebb)) * **know-your-world:** actually change magnifier element dimensions to 1/3 ([31a06d6](31a06d6fef)) * **know-your-world:** allow space in name confirmation input ([285b128](285b128bb8)) * **know-your-world:** center setup settings panel horizontally ([5f69fab](5f69fab859)) * **know-your-world:** correctly identify local player for cursor sharing ([7aafe8c](7aafe8c92e)) * **know-your-world:** enable hot/cold only for current player in turn mode ([f5ce53e](f5ce53efc0)) * **know-your-world:** fix celebration timer restart and mobile magnifier dismissal bugs ([0558132](055813205a)) * **know-your-world:** fix hot/cold visual feedback delay ([a6352ec](a6352ec624)) * **know-your-world:** fix pointer lock escape for all edges and add smooth release animation ([a7fa858](a7fa858a29)) * **know-your-world:** fix server/client filter mismatch for USA map ([98e74ba](98e74bae3a)) * **know-your-world:** fix TypeScript build errors ([f622bfa](f622bfab54)) * **know-your-world:** guard against undefined state during session init ([ea8965b](ea8965bc95)) * **know-your-world:** improve crosshair UX and fix mobile Select button ([0584863](0584863bdd)) * **know-your-world:** improve mobile layout for setup screen ([81b44a6](81b44a6422)) * **know-your-world:** improve mobile magnifier positioning and sizing ([9a254e2](9a254e2933)) * **know-your-world:** improve mobile magnifier touch controls ([aee5f21](aee5f21ecc)) * **know-your-world:** make game settings visible in right panel ([11d2d56](11d2d5693c)) * **know-your-world:** move Start button to top-right settings panel ([cc51de3](cc51de35e3)) * **know-your-world:** normalize accented letters for keyboard input ([b27856e](b27856e9fc)) * **know-your-world:** prevent hint bubble closing when toggling settings ([a67c11a](a67c11ae04)) * **know-your-world:** raise auto-zoom thresholds for tiny regions ([17c113e](17c113e68b)) * **know-your-world:** reduce magnifier size to 1/3 of pane dimensions ([61a438d](61a438dd31)) * **know-your-world:** remove confidence gate from hot/cold visual emoji ([7f6b9dd](7f6b9dd558)) * **know-your-world:** remove hovered region label from setup screen ([5bb2288](5bb228883d)) * **know-your-world:** Remove redundant preventDefault calls in touch handlers ([021a75f](021a75f583)) * **know-your-world:** remove tips section from setup screen ([c9e9190](c9e9190937)) * **know-your-world:** replace react-spring with CSS animation for crosshair rotation ([af5e7b5](af5e7b59dc)) * **know-your-world:** restore no-music celebration sounds ([f6d1295](f6d1295c6f)) * **know-your-world:** restore Start button character ([7013a7b](7013a7b068)) * **know-your-world:** stabilize mobile magnifier 1:1 touch tracking ([ab30add](ab30adda25)) * **know-your-world:** suppress hot/cold hints during takeover and give-up ([9f6b425](9f6b425daf)) * **know-your-world:** use getBBox() for consistent takeover positioning ([f8acc4a](f8acc4aa6a)) * **know-your-world:** use localPlayerId for cursor updates in all modes ([5e8c37b](5e8c37b68e)) * **know-your-world:** use shared MAX_ZOOM constant for mobile magnifier ([e4c35e9](e4c35e9425)) * **know-your-world:** use spring-for-speed pattern for smooth crosshair rotation ([b7fe2af](b7fe2af369)) * **know-your-world:** use viewport-based maxHeight for right panel ([a4f9db6](a4f9db6d3f)) * lazy-load map data in know-your-world validator ([07c25a2](07c25a2296)) * lower quick-escape threshold to 15px/frame for easier triggering ([97b214d](97b214da12)) * make map-renderer fill parent container for fit-crop-with-fill ([18b1476](18b14766b2)) * make placeholder pages match actual page dimensions ([4003c5c](4003c5ceb7)) * make useArcadeSocket work without ArcadeErrorProvider ([01740af](01740afcb7)) * merge duplicate style attributes on magnifier SVG ([3eda493](3eda493051)) * move pointer lock management to MapRenderer ([0ed4d13](0ed4d13db6)) * page indicator not tracking scroll when showing all pages ([3d157e3](3d157e32ed)) * page indicator stuck on page 1 due to stale closure ([952ebc7](952ebc7756)) * pass correct parameter for borrow boxes in subtraction ([00d892a](00d892a05c)) * position debug panel opposite from magnifier ([aa80a73](aa80a73664)) * position shared worksheet banner below app nav ([fb3412c](fb3412c9a5)) * **practice:** add 80px top padding to account for app nav height ([a50b268](a50b268d35)) * **practice:** allow vertical overflow for help overlays ([1ddf9fc](1ddf9fc94f)) * **practice:** prevent decomposition math from wrapping ([52ea3f1](52ea3f10fa)) * **practice:** remove overflow clipping to allow help overlays ([e9b123a](e9b123a7b3)) * **practice:** remove redundant 'already at target' message ([e9ccfb9](e9ccfb9186)) * **practice:** use explicit padding to prevent shorthand override ([4c00d92](4c00d92ccb)) * preserve saved seed on page reload ([64ce64b](64ce64bd35)) * preserve seed and prngAlgorithm in config migrations ([b18c412](b18c412736)) * preserve user's scaffolding settings when changing skills ([1eb04ce](1eb04ce0c4)) * prevent duplicate API calls in React StrictMode ([0d59676](0d59676b38)) * prevent modal closure when clicking tabs in AllSkillsModal ([4746e1f](4746e1f8fe)) * prevent regrouping problems in no-regrouping skills and enable progressive difficulty toggle ([59712e1](59712e1021)) * prevent skill name wrapping in mini cards with single-line ellipsis ([a463d08](a463d088d7)) * prevent zoom jump on precision mode activation by resetting spring target ([33d9f15](33d9f15897)) * prevent zoom jump when activating precision mode ([9cb3c89](9cb3c898ec)) * properly apply dark mode hover states in dropdown ([34553ce](34553cebf7)) * properly cycle through problem sets when exceeding unique problem space ([55d4920](55d4920167)) * properly zoom to selected continent in game phases ([e900e44](e900e4465b)) * reduce font size for mini skill card titles to prevent wrapping ([833b481](833b481ebb)) * refactor worksheet config persistence to blacklist approach + Storybook stories ([5b6db58](5b6db588a2)) * regenerate lockfile to remove big.js dependencies ([05fc5cf](05fc5cfe49)) * remove all scaffolding from final mastery skills ([d7bec42](d7bec423e0)) * remove background rect from main map SVG ([7d3c5c3](7d3c5c304b)) * remove duplicate containerRect declaration ([1a690e0](1a690e00b0)) * remove magnifierSpring.zoom from effect dependencies ([5eb2eed](5eb2eeda32)) * remove mispositioned background rect from magnifier SVG ([5815cbe](5815cbee15)) * remove pages from visible set when they leave viewport ([9757449](9757449e21)) * remove redundant 'Teens minus singles' subtraction skill ([e156e87](e156e870df)) * remove regex lookbehind for Safari compatibility ([4d77f1f](4d77f1ffd3)) * remove unused velocity tracking and fix TypeScript errors ([0195a6d](0195a6dc6d)) * replace deprecated path() with curve() in borrow arrows ([47d149c](47d149ca17)) * replace ES module imports with JSON data files ([fb735be](fb735be014)) * resolve auto zoom freeze and stuck zoom issues ([0aee60d](0aee60d8d1)) * respect borrow boxes display setting regardless of actual borrowing ([1aef0f2](1aef0f292f)) * respect operator-specific scaffolding in mastery+mixed mode ([a6472a2](a6472a231b)) * respect user's layout options (problemNumbers/cellBorders) in mastery mode ([e708add](e708add9f2)) * responsive page indicator and settings summary improvements ([93ddc28](93ddc28a3a)) * resume zoom animation immediately when precision mode activates ([7ba7e03](7ba7e03661)) * resume zoom animation immediately when precision mode activates ([7c1f2e5](7c1f2e54c9)) * resume zoom animation when target drops below threshold ([e73b59d](e73b59d510)) * scaffolding changes now apply in mastery+mixed mode ([510f052](510f052978)) * **server:** lazy-load game validators to avoid ES module errors ([a88bd58](a88bd5844c)) * show hot/cold button on iPad with mouse attached ([1333818](1333818bae)) * skip pointer lock request on unsupported devices (iPad) ([d6eb997](d6eb997445)) * stabilize mini skill card height and fix preview updates ([4a52943](4a5294353e)) * take all measurements inside animation callback for label sync ([2191e07](2191e0732b)) * track both SVG units and screen pixels for zoom and dampening ([d72f309](d72f309bad)) * transform screen coordinates to SVG space for isPointInFill() ([4c933be](4c933be48a)) * transmit hovered region ID with network cursor to avoid hit-testing discrepancies ([6c3f860](6c3f860efc)) * **tutorial:** expose activeGroupTargetColumn state to context ([69f759a](69f759a178)) * update AbacusQRCode for qrcode.react v4 compatibility ([0f0c3c6](0f0c3c65e8)) * update operator-specific display rules in mastery+mixed mode ([4174b6d](4174b6d2e7)) * upgrade to Node.js 20 to resolve ES module import issues ([192de5c](192de5c6b5)) * use ± symbol for mixed operator icon consistently ([2695b50](2695b50abe)) * use actual SVG path geometry for region detection instead of bounding boxes ([e255ce2](e255ce2c6f)) * use animated spring for magnifier label positioning ([94d1cdf](94d1cdfcb5)) * use ASCII characters for operator icons to support dark mode ([3bd5c00](3bd5c00d21)) * use correct Unicode minus sign (−) for subtraction operator checks ([0dd9e45](0dd9e45952)) * use dynamic ES module imports for [@svg-maps](https://github.com/svg-maps) packages in know-your-world ([ab94fd3](ab94fd350f)) * use LAN IP instead of localhost for QR code camera uploads ([00b400a](00b400ae8a)) * use screen pixels for zoom, abandon SVG path parsing ([912dc38](912dc385b3)) * use subtle gray highlights for dropdown in dark mode ([8d6170a](8d6170a8c7)) * use SVG viewBox units instead of screen pixels for zoom ([0dcaabb](0dcaabb8a5)) * use white text for selected dropdown items in dark mode ([e1a7375](e1a73758d6)) * **worksheets:** add backward compatibility for displayRules in SmartModeControls ([b956e2d](b956e2d605)) * **worksheets:** add borrowNotation and borrowingHints to DisplayRules interfaces ([3b908ac](3b908ac453)) * **worksheets:** add borrowNotation and borrowingHints to validation fallback ([3f700af](3f700af643)) * **worksheets:** add mastery mode to Zod schema validation ([003f1d1](003f1d11cc)) * **worksheets:** correct Typst array membership syntax for ten-frames rendering ([14b3594](14b359462f)) * **worksheets:** enable borrowNotation and borrowingHints in smart difficulty mode ([8020ee8](8020ee835e)) * **worksheets:** prevent infinite loop when problem space is empty ([02463df](02463df8e5)) * **worksheets:** render operators last for proper layering ([cdd0de7](cdd0de797f)) * **worksheets:** sync preview and download problem generation ([822ef78](822ef78e58)) * **worksheets:** ten-frames not rendering in mastery mode ([b36df3a](b36df3a40c)) * **worksheets:** validation function was converting mastery mode to manual ([4ad687d](4ad687df73)) ### Features * **abacus-react:** add defaultValue prop for uncontrolled mode ([3ce12c5](3ce12c59fc)) * add 'auto' option for scaffolding to defer to mastery progression ([a945a62](a945a620c4)) * add adaptive zoom magnifier for Know Your World map ([1e8846c](1e8846cdb1)) * add AI-powered worksheet grading with GPT-5 vision ([6e95732](6e9573288f)) * add auto scaffolding mode with visual feedback and override notices ([b62db5a](b62db5a323)) * add auto-submit on correct answer + Newton poem blog post ([2f7cb03](2f7cb03c3f)) * add comprehensive error handling for arcade games ([e8c5256](e8c52561a2)) * add continent filtering to Know Your World game ([7bb03b8](7bb03b8409)) * add custom error boundaries with navigation ([73cc418](73cc4185c3)) * add database schema for custom skills and skill customizations ([906fa63](906fa63f24)) * add debug bounding boxes to magnifier view ([9c7d2fa](9c7d2fab5f)) * add debug indicator for custom crop region (dev only) ([9c89aad](9c89aadb17)) * add detailed zoom decision debug panel ([cb57f15](cb57f1585a)) * add dev-only crop tool for custom map region cropping ([855e5df](855e5df2c0)) * add download and share buttons to shared worksheet viewer ([9b8947a](9b8947a198)) * add dynamic layout preview component for orientation selection ([8df62d6](8df62d6a45)) * add dynamic operator icon to tab navigation ([b6ff995](b6ff995a8c)) * add exponential zoom scaling for sub-pixel regions ([101213b](101213ba1c)) * add fancy QR codes with abacus logo throughout app ([ebcabf9](ebcabf9bb9)) * add give up with zoom animation for Know Your World ([94cff43](94cff4374f)) * add gold scrim overlay and improve precision mode messaging ([4b20d07](4b20d0753f)) * add interactive world map continent selector ([245005c](245005c8ec)) * add Know Your World geography quiz game ([25e24a7](25e24a7cbc)) * add mobile drawer and detailed summary for shared worksheets ([0a35e70](0a35e70e28)) * add ngrok tunnel to dev server for HTTPS testing ([ab2bfde](ab2bfde9c2)) * add per-country coloring and individual region clicks to continent selector ([2e9f409](2e9f409f26)) * add per-page worksheet generation API ([6398fbe](6398fbead9)) * add Pointer Lock API for precision mode to prevent edge issues ([4d5953d](4d5953d034)) * add precision controls for tiny regions in Know Your World ([3bf127f](3bf127f344)) * add precision mode system with pixel grid visualization ([53e9041](53e90414a3)) * add prev/next navigation buttons to mixed mode mini skill panes ([498df2c](498df2ca5a)) * add problem space validation to warn about duplicate risk ([0b8c180](0b8c1803ff)) * add responsive mobile drawer with draggable settings button ([fc1d7fc](fc1d7fcbd6)) * add responsive page button layout with dynamic dropdown ([3f33cd1](3f33cd1924)) * add shared worksheet viewer with open-in-editor functionality ([4b8b3ee](4b8b3ee532)) * add single-page worksheet preview API endpoint ([10e97db](10e97db78a)) * add skill configuration system with interactive 2D difficulty plot ([7fbc743](7fbc743c4c)), closes [#9333](https://github.com/antialias/soroban-abacus-flashcards/issues/9333) [#10b981](https://github.com/antialias/soroban-abacus-flashcards/issues/10b981) * add smooth fade-in animation for 404 message text changes ([e88380a](e88380a48d)) * add split-action share button with copy shortcut ([085d200](085d200da4)) * add themed backgrounds and enhanced styling to 404 page ([dd14062](dd14062112)) * add visible grab tab to worksheet panel resize handle ([288e6ed](288e6ed878)) * add visual debugging for zoom importance scoring ([e60a2c0](e60a2c09c0)) * add visual grab tab to resize handle with rounded corners ([6e55d5a](6e55d5add7)) * add visual warnings to page selector buttons ([5a87799](5a8779969c)) * add worksheet generation core logic and helpers ([163517d](163517db7d)) * add worksheet sharing infrastructure with database persistence ([7b4c7c3](7b4c7c3fb6)) * add worksheet studio with comprehensive features ([d5672bd](d5672bdddf)) * apply skill-specific scaffolding and fix mini card heights ([ee90182](ee90182ff2)) * **blog:** add subtraction and multi-digit worksheet blog posts ([dd9587f](dd9587f8cd)) * calculate zoom based on region under cursor, target 15% area ([6736336](6736336317)) * convert operator selection to checkboxes with required validation ([c997c4a](c997c4a7ba)) * create unified difficulty interface with 2-tab selector ([0b7382f](0b7382f1b6)) * enable production source maps for easier debugging ([d992e98](d992e98d77)) * enhance scaffolding tab with live preview and resolved display rules ([9a5a0d4](9a5a0d4e3c)) * **help-system:** add focus areas for skills needing reinforcement ([871390d](871390d8e1)) * **help-system:** add schema for progressive help and feedback loop ([41c4603](41c46038d8)) * **help-system:** add usePracticeHelp hook and skill extraction ([0b1ad1f](0b1ad1f896)) * **help-system:** integrate PracticeHelpPanel into ActiveSession ([373ec34](373ec34e46)) * hide easter egg hint until first discovery ([c2c7153](c2c71531ae)) * implement binary search for optimal zoom level ([1a54f09](1a54f09814)) * implement fit-crop-with-fill for custom map crops ([b6569ed](b6569ed4e1)) * implement lazy loading for worksheet preview with cursor pagination ([8b3d019](8b3d019652)) * implement lazy loading for worksheet preview with cursor pagination ([2a7d67d](2a7d67db58)) * improve shared worksheet viewer UX and multi-page support ([1c10a82](1c10a82c78)) * improve tab navigation layout and add pages to layout button ([926a029](926a029ff8)) * improve worksheet preview placeholder with cartoonish grid layout ([57fb99a](57fb99af63)) * increase max super zoom to 120x for ultra-tiny regions ([9b782be](9b782beabf)) * increase max zoom to 1000x with detailed debug logging ([a6be05f](a6be05f8c1)) * **know-your-world:** add 'H' keyboard shortcut for hint ([cdc9451](cdc94514d9)) * **know-your-world:** add adaptive hint cycling for struggling users ([5440250](54402501e5)) * **know-your-world:** add celebration animations for found regions ([3b9d6b0](3b9d6b0fdf)) * **know-your-world:** add device capability hooks and improve mobile support ([c502a4f](c502a4fa92)) * **know-your-world:** add drill-down map selector and improve setup UI ([a6f8dbe](a6f8dbe474)) * **know-your-world:** add filter tabs for size, importance, and population ([6c3c0ac](6c3c0ac70e)) * **know-your-world:** add fire tracer animation for learning mode takeover ([1e6153e](1e6153ee8b)) * **know-your-world:** add hint system, pointer lock buttons, and mobile magnifier support ([55e480c](55e480c03b)) * **know-your-world:** add hints for Europe and Africa regions ([46e5c6b](46e5c6b99b)) * **know-your-world:** add hot/cold audio feedback for cursor proximity ([69813e9](69813e92a2)) * **know-your-world:** add hot/cold debug panel and production debug mode ([493313a](493313a3bb)) * **know-your-world:** add hot/cold feedback for mobile magnifier ([824325b](824325b843)) * **know-your-world:** add interaction state machine foundation ([e4d6748](e4d6748d70)) * **know-your-world:** add Learning mode and fix hints before name unlock ([fc87808](fc87808b40)) * **know-your-world:** add learning mode takeover animation and fix give-up sequence ([3fd8472](3fd8472e68)) * **know-your-world:** add map zoom preview, remove study time feature ([57dd61b](57dd61b994)) * **know-your-world:** add missing island hints and revise all hints ([8b13b5a](8b13b5a455)) * **know-your-world:** add mobile cursor sharing and fix multi-device coop mode ([2ce5e18](2ce5e180b7)) * **know-your-world:** add mobile virtual keyboard and space-skipping ([5318d0d](5318d0dd89)) * **know-your-world:** add multiplayer cursor sharing and fix map viewport ([c3b94be](c3b94bea3d)) * **know-your-world:** add puzzle piece fly-to-map animation for learning mode ([7c49652](7c496525e9)) * **know-your-world:** add range thermometer for region size selection ([c7c4e7c](c7c4e7cef3)) * **know-your-world:** add region shape silhouette to learning takeover ([ebe07e3](ebe07e358f)) * **know-your-world:** add session-based give-up voting and fix cursor emojis ([bb2d6fc](bb2d6fc7d8)) * **know-your-world:** add speech announcements and compass-style crosshairs ([e0b762e](e0b762e3ee)) * **know-your-world:** add speech synthesis for hints with auto-hint/auto-speak ([cd841ff](cd841ff7dc)) * **know-your-world:** add Strudel-based music system ([7dab07b](7dab07b3a7)) * **know-your-world:** add SVG path geometry helpers for future use ([ea141f0](ea141f04f6)) * **know-your-world:** add turn-based restrictions for letter typing ([45730bb](45730bb4db)) * **know-your-world:** align guidance UI with assistance levels ([7e7a8dc](7e7a8dc1e8)) * **know-your-world:** auto-enable hot/cold for learning mode ([dcc32c2](dcc32c288f)) * **know-your-world:** enhance hint audio and region name display ([e6f58bf](e6f58bfd93)) * **know-your-world:** fix magnifier outline aspect ratio and add visual debug toggle ([ac915f2](ac915f2065)) * **know-your-world:** full-screen layout with squish-through pointer lock escape ([1729418](1729418dc5)) * **know-your-world:** implement empirical scale measurement for 1:1 magnifier tracking ([39886e8](39886e859c)) * **know-your-world:** improve magnifier UX and hide abacus on games ([fa1514d](fa1514d351)) * **know-your-world:** improve mobile magnifier controls and animations ([4449fb1](4449fb19b4)) * **know-your-world:** improve mobile magnifier with adaptive zoom and select button ([60cf98e](60cf98e77a)) * **know-your-world:** improve region size filter layout ([558d369](558d369ba0)) * **know-your-world:** improve setup screen UX ([dc4d621](dc4d62195b)) * **know-your-world:** improve takeover UI and fix celebration sound bug ([a8c6b84](a8c6b84855)) * **know-your-world:** live crop updates and safe zone improvements ([3f4691e](3f4691e8a3)) * **know-your-world:** make magnifier lazy - only move when cursor obscured ([ac82564](ac82564eac)) * **know-your-world:** make magnifier size responsive to aspect ratio ([5920cb4](5920cb4dc3)) * **know-your-world:** match setup phase map positioning with gameplay ([b030558](b0305581f9)) * **know-your-world:** move region size filters inside map preview ([81301ab](81301ab148)) * **know-your-world:** move start button below settings controls ([a05c4ca](a05c4ca5bf)) * **know-your-world:** Phase 2 - integrate useMagnifierZoom hook ([8ce878d](8ce878d03e)) * **know-your-world:** responsive setup + travel-themed start button ([02762fa](02762fad81)) * **know-your-world:** separate region filtering from assistance level ([9499e4e](9499e4e8b5)) * **know-your-world:** show magnifier on mobile drag gesture ([a02a710](a02a7108e9)) * **know-your-world:** speak country names in user's locale ([426a1e6](426a1e6868)) * **know-your-world:** sync letter confirmation across multiplayer sessions ([655660f](655660f7cf)) * **know-your-world:** unified region selector with inline list on desktop ([d329d80](d329d80399)) * **know-your-world:** unify setup and gameplay UI positions ([141a506](141a506739)) * **know-your-world:** unify setup UI positions with gameplay ([c1a0485](c1a0485b1d)) * **know-your-world:** wire interaction state machine to MapRenderer ([7e55953](7e55953eee)) * make 404 page abacus hero-sized and responsive ([41de252](41de25238f)) * make resize handle grab tab fully draggable with rounded corners ([be40f70](be40f70bc6)) * make scaffolding and preview collapsible ([804fb1a](804fb1a2f6)) * make super zoom threshold configurable and increase to 3px ([d7ce474](d7ce474a51)) * make zoom transitions 4x slower for smoother experience ([ca752bd](ca752bd0aa)) * move difficulty parameters into Smart mode ([4b66758](4b667587f8)) * move layout controls to OrientationPanel with toggles ([995966f](995966ffbc)) * operator-specific scaffolding for mixed mastery mode ([4d7d000](4d7d000046)) * optimize problem generation and add duplicate warning system ([11c46c1](11c46c1b44)) * pause zoom animation at precision mode threshold ([c4989b3](c4989b3ab0)) * pause/resume zoom animation at precision mode threshold ([bdf59e5](bdf59e571d)) * pause/resume zoom animation at precision mode threshold ([4687820](4687820d8a)) * persist seed and prngAlgorithm for exact problem reproducibility ([8cb2209](8cb2209d84)) * **practice:** add dark mode support and fix doubled answer digits ([026993c](026993cb05)) * **practice:** add progressive help overlay with proper positioning ([9a4ab82](9a4ab8296e)) * **practice:** add session HUD with tape-deck controls and PageWithNav ([b19c6d0](b19c6d0eca)) * **practice:** add smooth problem transition animation ([b12112e](b12112e8da)) * **practice:** add student onboarding and offline sync features ([b52f054](b52f0547af)) * **practice:** add three-part daily practice session system ([5855438](585543809a)) * **practice:** improve help UX with coach hints and simplified UI ([19169ad](19169ad9fe)) * **practice:** integrate progressive help with decomposition display ([804d937](804d937dd9)) * redesign shared worksheet viewer with read-only studio and proper error handling ([23dccc0](23dccc0ef3)) * remove all easter egg hints from 404 page ([1756182](17561829ef)) * remove redundant navigation buttons from 404 page ([e5262e5](e5262e5007)) * show magnifier only when current target region needs magnification ([996c973](996c973774)) * show magnifier only when target region needs it ([c6997ac](c6997ac9a7)) * show visual feedback for auto-resolved scaffolding values ([fbe776a](fbe776ac09)) * smooth cursor dampening transitions with react-spring ([66544dc](66544dc7dd)) * **thermometer:** add "only" buttons to quickly select single category ([623f882](623f882075)) * **worksheets:** add 3x scale effect to thrown dice ([920a855](920a855eb5)) * **worksheets:** add draggable dice easter egg with physics ([b8e66df](b8e66dfc17)) * **worksheets:** add duplicate risk warnings to page selector UI ([1d8dceb](1d8dceb55b)) * **worksheets:** add foundational steps to progression path ([7e6f99b](7e6f99b78c)) * **worksheets:** add QR codes with share codes for easy worksheet sharing ([a0e73d9](a0e73d971b)) * **worksheets:** add shuffle button with animated dice icon ([f97efb5](f97efb5c94)) * **worksheets:** add viewport edge ricochet to dice physics ([c6db7dc](c6db7dcfa2)) * **worksheets:** enhance dice throw physics for natural feel ([047a960](047a960567)) * **worksheets:** restore mastery progression UI with 3-way mode selector ([26a0885](26a08859d7)) * **worksheets:** smooth dice rotation settle to final face ([d00c707](d00c70750e)) * **worksheets:** upgrade to 3D dice with random rotation animation ([3cd5e49](3cd5e4992b)) ### Performance Improvements * add spatial filtering to skip distant regions ([8cb4c88](8cb4c88bef)) * cache polygon conversions to fix performance regression ([348ce8f](348ce8f314)) * **know-your-world:** memoize state machine return value and remove debug logging ([d85b976](d85b976f8b)) * reduce retry limit from 3000 to 100 in problem generators ([08fef59](08fef59cc5)) ### Reverts * **know-your-world:** undo premature extractions, restore working state ([f0bf205](f0bf2050d3)) * remove ngrok and LAN IP detection ([0040b57](0040b57829))
@soroban/abacus-react
A comprehensive React component for rendering interactive Soroban (Japanese abacus) visualizations with advanced customization and tutorial capabilities.
Features
- 🎯 Interactive beads - Click to toggle or use directional gestures
- 🎨 Complete visual customization - Style every element individually
- 📱 Responsive scaling - Configurable scale factor for different sizes
- 🌈 Multiple color schemes - Monochrome, place-value, alternating, heaven-earth
- 🎭 Flexible shapes - Diamond, square, or circle beads
- ⚡ React Spring animations - Smooth bead movements and transitions
- 🔧 Developer-friendly - Comprehensive hooks and callback system
- 🎓 Tutorial system - Built-in overlay and guidance capabilities
- 🧩 Framework-free SVG - Complete control over rendering
- ✨ 3D Enhancement - Three levels of progressive 3D effects for immersive visuals
- 🚀 Server Component support - AbacusStatic works in React Server Components (Next.js App Router)
Installation
npm install @soroban/abacus-react
# or
pnpm add @soroban/abacus-react
# or
yarn add @soroban/abacus-react
Quick Start
Basic Usage
Simple abacus showing a number
<AbacusReact value={123} columns={3} showNumbers={true} scaleFactor={1.0} />
Interactive Mode
Clickable abacus with animations
<AbacusReact
value={456}
columns={3}
interactive={true}
animated={true}
showNumbers={true}
callbacks={{
onValueChange: (newValue) => console.log("New value:", newValue),
onBeadClick: (event) => console.log("Bead clicked:", event),
}}
/>
Custom Styling
Personalized colors and highlights
<AbacusReact
value={789}
columns={3}
colorScheme="place-value"
beadShape="circle"
customStyles={{
heavenBeads: { fill: "#ff6b35" },
earthBeads: { fill: "#3498db" },
numerals: { color: "#2c3e50", fontWeight: "bold" },
}}
highlightBeads={[{ columnIndex: 1, beadType: "heaven" }]}
/>
Theme Presets
Use pre-defined themes for quick styling:
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)
Static Display (Server Components)
For static, non-interactive displays that work in React Server Components:
// IMPORTANT: Use /static import path for RSC compatibility!
import { AbacusStatic } from "@soroban/abacus-react/static";
// ✅ Works in React Server Components - no "use client" needed!
// ✅ No JavaScript sent to client
// ✅ Perfect for SSG, SSR, and static previews
<AbacusStatic value={123} columns="auto" hideInactiveBeads compact />;
Import paths:
@soroban/abacus-react- Full package (client components with hooks/animations)@soroban/abacus-react/static- Server-compatible components only (no client code)
Guaranteed Visual Consistency:
Both AbacusStatic and AbacusReact share the same underlying layout engine. Same props = same exact SVG output. This ensures:
- Static previews match interactive versions pixel-perfect
- Server-rendered abaci look identical to client-rendered ones
- PDF generation produces accurate representations
- No visual discrepancies between environments
Architecture: How We Guarantee Consistency
The package uses a shared rendering architecture with dependency injection:
┌─────────────────────────────────────────────┐
│ Shared Utilities (AbacusUtils.ts) │
│ • calculateStandardDimensions() - Single │
│ source of truth for all layout dimensions│
│ • calculateBeadPosition() - Exact bead │
│ positioning using shared formulas │
└────────────┬────────────────────────────────┘
│
├──────────────────────────────────┐
↓ ↓
┌─────────────────┐ ┌─────────────────┐
│ AbacusStatic │ │ AbacusReact │
│ (Server/Static) │ │ (Interactive) │
└────────┬────────┘ └────────┬────────┘
│ │
└────────────┬───────────────────┘
↓
┌────────────────────────┐
│ AbacusSVGRenderer │
│ • Pure SVG structure │
│ • Dependency injection │
│ • Bead component prop │
└────────────────────────┘
↓
┌───────────────┴───────────────┐
↓ ↓
┌──────────────┐ ┌──────────────────┐
│ AbacusStatic │ │ AbacusAnimated │
│ Bead │ │ Bead │
│ (Simple SVG) │ │ (react-spring) │
└──────────────┘ └──────────────────┘
Key Components:
calculateStandardDimensions()- Returns complete layout dimensions (bar position, bead sizes, gaps, etc.)calculateBeadPosition()- Calculates exact x,y coordinates for any beadAbacusSVGRenderer- Shared SVG rendering component that accepts a bead component via dependency injectionAbacusStaticBead- Simple SVG shapes for static display (no hooks, RSC-compatible)AbacusAnimatedBead- Client component with react-spring animations and gesture handling
This architecture eliminates code duplication (~560 lines removed in the refactor) while guaranteeing pixel-perfect consistency.
When to use AbacusStatic vs AbacusReact:
| Feature | AbacusStatic | AbacusReact |
|---|---|---|
| React Server Components | ✅ Yes | ❌ No (requires "use client") |
| Client-side JavaScript | ❌ None | ✅ Yes |
| User interaction | ❌ No | ✅ Click/drag beads |
| Animations | ❌ No | ✅ Smooth transitions |
| Sound effects | ❌ No | ✅ Optional sounds |
| 3D effects | ❌ No | ✅ Yes |
| Visual output | ✅ Identical | ✅ Identical |
| Bundle size | 📦 Minimal | 📦 Full-featured |
| Use cases | Preview cards, thumbnails, static pages, PDFs | Interactive tutorials, games, tools |
// Example: Server Component with static abacus cards
// app/flashcards/page.tsx
import { AbacusStatic } from "@soroban/abacus-react/static";
export default function FlashcardsPage() {
const numbers = [1, 5, 10, 25, 50, 100];
return (
<div className="grid grid-cols-3 gap-4">
{numbers.map((num) => (
<div key={num} className="card">
<AbacusStatic value={num} columns="auto" compact />
<p>{num}</p>
</div>
))}
</div>
);
}
Compact/Inline Display
Create mini abacus displays for inline use:
// 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 and column highlighting
<AbacusReact
value={42}
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: 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 === 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.
Subtle Mode
Light depth shadows and perspective for subtle dimensionality.
<AbacusReact
value={12345}
columns={5}
enhanced3d="subtle"
interactive
animated
/>
Realistic Mode
Material-based rendering with lighting effects and textures.
<AbacusReact
value={7890}
columns={4}
enhanced3d="realistic"
material3d={{
heavenBeads: "glossy", // 'glossy' | 'satin' | 'matte'
earthBeads: "satin",
lighting: "top-down", // 'top-down' | 'ambient' | 'dramatic'
woodGrain: true, // Add wood texture to frame
}}
interactive
animated
/>
Materials:
glossy- High shine with strong highlightssatin- Balanced shine (default)matte- Subtle shading, no shine
Lighting:
top-down- Balanced directional light from aboveambient- Soft light from all directionsdramatic- Strong directional light for high contrast
Delightful Mode
Maximum satisfaction with enhanced physics and interactive effects.
<AbacusReact
value={8642}
columns={4}
enhanced3d="delightful"
material3d={{
heavenBeads: "glossy",
earthBeads: "satin",
lighting: "dramatic",
woodGrain: true,
}}
physics3d={{
hoverParallax: true, // Beads lift on hover with Z-depth
}}
interactive
animated
soundEnabled
/>
Physics Options:
hoverParallax- Beads near mouse cursor lift up with depth perception
All 3D modes work with existing configurations and preserve exact geometry.
Core API
Basic Props
interface AbacusConfig {
// Display
value?: number; // 0-99999, number to display
columns?: number | "auto"; // Number of columns or auto-calculate
showNumbers?: boolean; // Show place value numbers
scaleFactor?: number; // 0.5 - 3.0, size multiplier
// Appearance
beadShape?: "diamond" | "square" | "circle";
colorScheme?: "monochrome" | "place-value" | "alternating" | "heaven-earth";
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
}
Event Callbacks
interface AbacusCallbacks {
onValueChange?: (newValue: number) => void;
onBeadClick?: (event: BeadClickEvent) => void;
onBeadHover?: (event: BeadClickEvent) => void;
onBeadLeave?: (event: BeadClickEvent) => void;
onColumnClick?: (columnIndex: number) => void;
onNumeralClick?: (columnIndex: number, value: number) => void;
onBeadRef?: (bead: BeadConfig, element: SVGElement | null) => void;
}
interface BeadClickEvent {
columnIndex: number; // 0, 1, 2...
beadType: "heaven" | "earth"; // Type of bead
position: number; // Position within type (0-3 for earth)
active: boolean; // Current state
value: number; // Numeric value (1 or 5)
bead: BeadConfig; // Full bead configuration
}
Advanced Customization
Granular Styling
Target any visual element with precise control:
const customStyles = {
// Global defaults
heavenBeads: { fill: "#ff6b35" },
earthBeads: { fill: "#3498db" },
activeBeads: { opacity: 1.0 },
inactiveBeads: { opacity: 0.3 },
// Column-specific overrides
columns: {
0: {
// Hundreds column
heavenBeads: { fill: "#e74c3c" },
earthBeads: { fill: "#2ecc71" },
},
},
// Individual bead targeting
beads: {
1: {
// Middle column
heaven: { fill: "#f39c12" },
earth: {
0: { fill: "#1abc9c" }, // First earth bead
3: { fill: "#e67e22" }, // Fourth earth bead
},
},
},
// UI elements
reckoningBar: { stroke: "#34495e", strokeWidth: 3 },
columnPosts: { stroke: "#7f8c8d" },
numerals: {
color: "#2c3e50",
fontSize: "14px",
fontFamily: "monospace",
},
};
<AbacusReact customStyles={customStyles} />;
Tutorial and Overlay System
Create interactive educational experiences:
const overlays = [
{
id: "welcome-tooltip",
type: "tooltip",
target: {
type: "bead",
columnIndex: 0,
beadType: "earth",
beadPosition: 0,
},
content: (
<div
style={{
background: "#333",
color: "white",
padding: "8px",
borderRadius: "4px",
}}
>
Click me to start!
</div>
),
offset: { x: 0, y: -30 },
},
];
<AbacusReact
overlays={overlays}
highlightBeads={[{ columnIndex: 0, beadType: "earth", position: 0 }]}
callbacks={{
onBeadClick: (event) => {
if (
event.columnIndex === 0 &&
event.beadType === "earth" &&
event.position === 0
) {
console.log("Tutorial step completed!");
}
},
}}
/>;
Bead Reference System
Access individual bead DOM elements for advanced positioning:
function AdvancedExample() {
const beadRefs = useRef(new Map<string, SVGElement>());
const handleBeadRef = (bead: BeadConfig, element: SVGElement | null) => {
const key = `${bead.columnIndex}-${bead.type}-${bead.position}`;
if (element) {
beadRefs.current.set(key, element);
// Now you can position tooltips, highlights, etc. precisely
const rect = element.getBoundingClientRect();
console.log(`Bead at column ${bead.columnIndex} is at:`, rect);
}
};
return (
<AbacusReact
callbacks={{ onBeadRef: handleBeadRef }}
// ... other props
/>
);
}
Hooks
useAbacusDiff
Calculate bead differences between values for tutorials and animations:
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 orderhighlights- Bead highlight data for stepBeadHighlights prophasChanges- Boolean indicating if any changes neededsummary- Human-readable description of changes (e.g., "add heaven bead in ones column")
useAbacusState
Convert numbers to abacus bead states:
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:
import { useAbacusDimensions } from "@soroban/abacus-react";
function MyComponent() {
const dimensions = useAbacusDimensions(3, 1.2); // 3 columns, 1.2x scale
return (
<div style={{ width: dimensions.width, height: dimensions.height }}>
<AbacusReact columns={3} scaleFactor={1.2} />
</div>
);
}
Utility Functions
Low-level functions for working with abacus states and calculations:
numberToAbacusState
Convert a number to bead positions:
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:
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:
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:
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:
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:
import { areStatesEqual, numberToAbacusState } from "@soroban/abacus-react";
const state1 = numberToAbacusState(123);
const state2 = numberToAbacusState(123);
const isEqual = areStatesEqual(state1, state2); // true
calculateStandardDimensions
⚡ Core Architecture Function - Calculate complete layout dimensions for consistent rendering.
This is the single source of truth for all layout dimensions, used internally by both AbacusStatic and AbacusReact to guarantee pixel-perfect consistency.
import { calculateStandardDimensions } from "@soroban/abacus-react";
const dimensions = calculateStandardDimensions({
columns: 3,
scaleFactor: 1.5,
showNumbers: true,
columnLabels: ["ones", "tens", "hundreds"],
});
// Returns complete layout info:
// {
// width, height, // SVG canvas size
// beadSize, // 12 * scaleFactor (standard bead size)
// rodSpacing, // 25 * scaleFactor (column spacing)
// rodWidth, // 3 * scaleFactor
// barThickness, // 2 * scaleFactor
// barY, // Reckoning bar Y position (30 * scaleFactor + labels)
// heavenY, earthY, // Inactive bead rest positions
// activeGap, // 1 * scaleFactor (gap to bar when active)
// inactiveGap, // 8 * scaleFactor (gap between active/inactive)
// adjacentSpacing, // 0.5 * scaleFactor (spacing between adjacent beads)
// padding, labelHeight, numbersHeight, totalColumns
// }
Why this matters: Same input parameters = same exact layout dimensions = pixel-perfect visual consistency across static and interactive displays.
calculateBeadPosition
⚡ Core Architecture Function - Calculate exact x,y coordinates for any bead.
Used internally by AbacusSVGRenderer to position all beads consistently in both static and interactive modes.
import {
calculateBeadPosition,
calculateStandardDimensions,
} from "@soroban/abacus-react";
const dimensions = calculateStandardDimensions({ columns: 3, scaleFactor: 1 });
const bead = {
type: "heaven",
active: true,
position: 0,
placeValue: 1, // tens column
};
const position = calculateBeadPosition(bead, dimensions);
// Returns: { x: 25, y: 29 } // exact pixel coordinates
Useful for custom rendering or positioning tooltips/overlays relative to specific beads.
Educational Use Cases
Interactive Math Lessons
function MathLesson() {
const [problem, setProblem] = useState({ a: 23, b: 45 });
const [step, setStep] = useState("show-first");
return (
<div>
<h3>
Add {problem.a} + {problem.b}
</h3>
<AbacusReact
value={step === "show-first" ? problem.a : 0}
interactive={step === "add-second"}
callbacks={{
onValueChange: (value) => {
if (value === problem.a + problem.b) {
celebrate();
}
},
}}
/>
</div>
);
}
Assessment Tools
function AbacusQuiz() {
const [answers, setAnswers] = useState([]);
const checkAnswer = (event: BeadClickEvent) => {
const isCorrect = validateBeadClick(event, expectedAnswer);
recordAnswer(event, isCorrect);
if (isCorrect) {
showSuccessFeedback();
} else {
showHint(event);
}
};
return (
<AbacusReact
interactive={true}
callbacks={{ onBeadClick: checkAnswer }}
customStyles={getAnswerHighlighting(answers)}
/>
);
}
TypeScript Support
Full TypeScript definitions included:
import {
// Components
AbacusReact,
// Hooks
useAbacusDiff,
useAbacusState,
useAbacusDimensions,
// Utility Functions
numberToAbacusState,
abacusStateToNumber,
calculateBeadDiff,
calculateBeadDiffFromValues,
validateAbacusValue,
areStatesEqual,
calculateStandardDimensions, // NEW: Shared layout calculator
calculateBeadPosition, // NEW: Bead position calculator
// Theme Presets
ABACUS_THEMES,
// Types
AbacusConfig,
BeadConfig,
BeadClickEvent,
AbacusCustomStyles,
AbacusOverlay,
AbacusCallbacks,
AbacusState,
BeadState,
BeadDiffResult,
BeadDiffOutput,
AbacusThemeName,
AbacusLayoutDimensions, // NEW: Complete layout dimensions type
BeadPositionConfig, // NEW: Bead config for position calculation
} from "@soroban/abacus-react";
// All interfaces fully typed for excellent developer experience
Contributing
Contributions welcome! Please see our contributing guidelines and feel free to submit issues or pull requests.
License
MIT License - see LICENSE file for details.