Compare commits

...

59 Commits

Author SHA1 Message Date
semantic-release-bot
e37ee87ea3 chore(release): 4.24.1 [skip ci]
## [4.24.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.24.0...v4.24.1) (2025-10-20)

### Bug Fixes

* **homepage:** adjust mini abacus container height ([c4066d6](c4066d6879))
* **homepage:** fix MiniAbacus runtime error and improve sizing ([1fa0df8](1fa0df85f7))
* **homepage:** use correct AbacusReact API and fix clipping/styling issues ([1432afd](1432afd6e6))
* **homepage:** use direct conditionals for mini abacus padding ([38ef16a](38ef16a8f9))

### Styles

* **homepage:** add more padding around mini abacus ([c5103d0](c5103d049f))
* **homepage:** balance mini abacus padding horizontally and vertically ([2f0304e](2f0304eb81))
* **homepage:** increase mini abacus padding to '5' ([1da9ed1](1da9ed1ce6))
2025-10-20 11:11:51 +00:00
Thomas Hallock
38ef16a8f9 fix(homepage): use direct conditionals for mini abacus padding
Replace spread operator with direct conditional values for py and px
to ensure proper application with Panda CSS.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:10:44 -05:00
Thomas Hallock
2f0304eb81 style(homepage): balance mini abacus padding horizontally and vertically
Use py: '5' and px: '3' instead of uniform padding to create visually
equal spacing on all sides of the animated abacus.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:09:49 -05:00
Thomas Hallock
1da9ed1ce6 style(homepage): increase mini abacus padding to '5'
Double the padding from '3' to '5' for more generous vertical spacing
around the animated abacus.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:09:13 -05:00
Thomas Hallock
c5103d049f style(homepage): add more padding around mini abacus
Increase padding from '2' to '3' on the mini abacus icon container
to provide more vertical space between the abacus and the card border.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:08:52 -05:00
Thomas Hallock
c4066d6879 fix(homepage): adjust mini abacus container height
Set container height to 80px to prevent excess vertical space while
keeping the abacus fully visible and centered.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:07:41 -05:00
Thomas Hallock
1432afd6e6 fix(homepage): use correct AbacusReact API and fix clipping/styling issues
Use the correct pattern for AbacusReact:
- Call useAbacusConfig() without parameters for global config
- Pass individual props (value, columns, beadShape, styles) instead of config object
- No more timing hacks - the proper API doesn't have initialization issues

Fix display issues:
- Remove fixed height and overflow:hidden to prevent clipping
- Add dark theme styles for columnPosts and reckoningBar
- Reduce scale from 0.7 to 0.6 with proper transform origin

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:06:42 -05:00
Thomas Hallock
1fa0df85f7 fix(homepage): fix MiniAbacus runtime error and improve sizing
Fix "Cannot read properties of undefined (reading 'earthActive')" error by:
- Adding 500ms initialization delay before starting value cycling
- Starting with value 123 instead of 0 to show valid state immediately
- Splitting ready state management into separate useEffect

Also improve container sizing:
- Set explicit width (75px) and height (75px)
- Add transform scale(0.7) to fit better in card icon space
- Add overflow hidden to contain the component
- Increase cycle interval to 2.5s for smoother transitions

The error occurred because the AbacusReact component was trying to access
column states before they were fully initialized. The delay ensures proper
initialization before animation starts.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:06:42 -05:00
semantic-release-bot
8baeba6987 chore(release): 4.24.0 [skip ci]
## [4.24.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.23.0...v4.24.0) (2025-10-20)

### Features

* **homepage:** add animated mini abacus to "Read and set numbers" card ([e028e34](e028e342ad))
2025-10-20 11:01:48 +00:00
Thomas Hallock
e028e342ad feat(homepage): add animated mini abacus to "Read and set numbers" card
Replace static 🔢 emoji with a functional 3-column abacus that cycles through
random 3-digit numbers (0-999) every 2 seconds. Uses dark theme styling to
match the homepage aesthetic.

Changes:
- Create MiniAbacus component using AbacusReact
- Cycle through random numbers using useState/useEffect
- Constrain height to ~75px as specified
- Conditionally render in first skill card (i === 0)
- Use theme="dark" to match tutorial abacus styling

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 06:00:46 -05:00
semantic-release-bot
263237a152 chore(release): 4.23.0 [skip ci]
## [4.23.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.22.0...v4.23.0) (2025-10-20)

### Features

* **homepage:** add more visual embellishments to learning cards ([4ec1b95](4ec1b952f2))
2025-10-20 10:57:15 +00:00
Thomas Hallock
4ec1b952f2 feat(homepage): add more visual embellishments to learning cards
Significantly enhance "What You'll Learn" section with:
- Larger icons (3xl) with background containers
- Skill level badges (Foundation/Core/Advanced/Expert)
- More padding and spacing throughout
- Gradient backgrounds on cards
- Box shadows with depth
- Hover lift animations
- Longer, more detailed descriptions
- Example text styled as highlighted code badges
- Increased container width for better balance

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 05:56:02 -05:00
semantic-release-bot
aa9d389540 chore(release): 4.22.0 [skip ci]
## [4.22.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.21.1...v4.22.0) (2025-10-20)

### Features

* **homepage:** enhance "What You'll Learn" with visual cards ([d142342](d1423420e6))

### Bug Fixes

* **tutorial:** reduce tooltip z-index to scroll under nav bar ([47640f3](47640f3486))
2025-10-20 10:55:50 +00:00
Thomas Hallock
d1423420e6 feat(homepage): enhance "What You'll Learn" with visual cards
Replace simple checklist with rich visual cards for each learning objective.
Each card now includes:
- Large prominent icon
- Title and description
- Example/demo text
- Hover effects
- Card-based layout with subtle borders

Makes the section more engaging and less text-heavy, better balanced with
the tutorial demo on the left.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 05:54:44 -05:00
Thomas Hallock
47640f3486 fix(tutorial): reduce tooltip z-index to scroll under nav bar
Change tutorial tooltip z-index from 1000 to 50 so it scrolls under the
app nav bar (z-index 100) along with the abacus. This prevents the coaching
text from appearing layered above the nav while the abacus it points to is
hidden beneath it.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 05:54:44 -05:00
semantic-release-bot
cb7595c95b chore(release): 4.21.1 [skip ci]
## [4.21.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.21.0...v4.21.1) (2025-10-20)

### Code Refactoring

* **homepage:** rearrange tutorial demo layout side by side ([8b4ecee](8b4eceebfa))
2025-10-20 10:52:32 +00:00
Thomas Hallock
8b4eceebfa refactor(homepage): rearrange tutorial demo layout side by side
Restructure "Learn by Doing" section to display tutorial and "What You'll
Learn" side by side. Items now display vertically instead of in a 2x2 grid.

Changes:
- Tutorial positioned on left with flex: 1
- "What You'll Learn" section positioned on right
- Items arranged vertically using stack layout
- Increased container max-width from 900px to 1200px
- Responsive: stacks vertically on mobile, side-by-side on md+ screens

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 05:51:29 -05:00
semantic-release-bot
850fd33943 chore(release): 4.21.0 [skip ci]
## [4.21.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.7...v4.21.0) (2025-10-20)

### Features

* **tutorial:** add silentErrors prop to suppress error messages ([8835e1c](8835e1c57a))
2025-10-20 10:50:59 +00:00
Thomas Hallock
8835e1c57a feat(tutorial): add silentErrors prop to suppress error messages
Add silentErrors prop to TutorialPlayer to allow suppressing "That's not
the highlighted bead" error messages. Used on homepage to provide a less
intrusive demo experience.

Changes:
- Add silentErrors prop to TutorialPlayerProps interface
- Conditionally skip error dispatch when silentErrors is true
- Pass silentErrors={true} from homepage TutorialPlayer

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 05:49:43 -05:00
Thomas Hallock
b635ed1c2d chore(home): reduce abacus tutorial to single column
Changed abacusColumns from 2 to 1 for the homepage tutorial demo.
Since the "Friends of 5" (2+3) demonstration only needs the ones place,
a single column is more focused and less distracting.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 05:44:24 -05:00
semantic-release-bot
4e6cecfe27 chore(release): 4.20.7 [skip ci]
## [4.20.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.6...v4.20.7) (2025-10-20)

### Bug Fixes

* **home:** use Panda CSS token() for dynamic colors and center arrows properly ([d52ba63](d52ba6373a))
2025-10-20 02:37:39 +00:00
Thomas Hallock
d52ba6373a fix(home): use Panda CSS token() for dynamic colors and center arrows properly
**Changes:**
- Use Panda CSS token() function for dynamic color references instead of inline hex values
- Fix arrow positioning to be centered in gap between progression stages
- Document Panda CSS dynamic token usage pattern

**Color Token Fix:**
Panda CSS css() requires static values at build time. For dynamic token references,
use the token() function with inline styles:
- Import: token from styled-system/tokens
- Usage: style={{ color: token(stage.color) }}
- Token paths marked as const for TypeScript literal types

**Arrow Positioning Fix:**
Arrows between stages were positioned based on element width, not gap width.
Now properly centered using:
- left: 100% (position at right edge)
- marginLeft: 0.5rem (half of 1rem gap)
- transform: translate(-50%, -50%) (center on that point)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 21:36:30 -05:00
semantic-release-bot
002c2888ac chore(release): 4.20.6 [skip ci]
## [4.20.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.5...v4.20.6) (2025-10-20)

### Bug Fixes

* **homepage:** use inline styles for journey level colors ([5d85e89](5d85e898d6)), closes [#4ade80](https://github.com/antialias/soroban-abacus-flashcards/issues/4ade80) [#60a5](https://github.com/antialias/soroban-abacus-flashcards/issues/60a5) [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24)
2025-10-20 01:14:49 +00:00
Thomas Hallock
5d85e898d6 fix(homepage): use inline styles for journey level colors
Changed progression level text from Panda CSS tokens to inline hex colors:
- 10 Kyu: #4ade80 (green)
- 5 Kyu: #60a5fa (blue)
- 1 Kyu: #a78bfa (purple)
- Dan: #fbbf24 (gold)

This ensures all text displays with proper colors regardless of CSS loading.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 20:13:39 -05:00
semantic-release-bot
eed890dc81 chore(release): 4.20.5 [skip ci]
## [4.20.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.4...v4.20.5) (2025-10-20)

### Bug Fixes

* **docker:** include Panda CSS styled-system in production image ([57fabff](57fabffe60))
2025-10-20 01:12:42 +00:00
Thomas Hallock
57fabffe60 fix(docker): include Panda CSS styled-system in production image
The styled-system directory containing generated Panda CSS was being
created during build but not copied to the production image, causing
all Panda CSS classes to be undefined at runtime.

This fix copies the generated styled-system directory from the builder
stage to the production image, ensuring styles.css is available.

Fixes missing CSS definitions for classes like fs_xl, font_bold, text_blue.400

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 20:11:38 -05:00
semantic-release-bot
89fb670f93 chore(release): 4.20.4 [skip ci]
## [4.20.4](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.3...v4.20.4) (2025-10-19)

### Bug Fixes

* **homepage:** use inline styles for Your Journey text contrast ([8e51390](8e51390018)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3) [#d1d5](https://github.com/antialias/soroban-abacus-flashcards/issues/d1d5)
2025-10-19 19:07:08 +00:00
Thomas Hallock
8e51390018 fix(homepage): use inline styles for Your Journey text contrast
Switched from Panda CSS to direct inline styles with hex colors:
- Labels (Beginner/Intermediate/Advanced/Master): #e5e7eb (light gray)
- Subtitle: #e5e7eb (light gray)
- Arrows: #9ca3af (medium gray)
- Footer: #d1d5db (light gray)

This bypasses any CSS framework issues and applies colors directly.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 14:06:02 -05:00
semantic-release-bot
e7e54619ae chore(release): 4.20.3 [skip ci]
## [4.20.3](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.2...v4.20.3) (2025-10-19)

### Bug Fixes

* **homepage:** use explicit RGBA colors for Your Journey text ([9c51cc9](9c51cc94ee))
2025-10-19 18:58:39 +00:00
Thomas Hallock
9c51cc94ee fix(homepage): use explicit RGBA colors for Your Journey text
Switched from Panda CSS gray tokens to explicit RGBA values for better compatibility:
- Text: rgba(229, 231, 235, 1) - light gray for maximum readability
- Subtitle: rgba(209, 213, 219, 1) - slightly darker light gray
- Arrows: rgba(156, 163, 175, 1) - medium gray

This ensures proper rendering regardless of theme token configuration.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:57:30 -05:00
semantic-release-bot
df674426c5 chore(release): 4.20.2 [skip ci]
## [4.20.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.1...v4.20.2) (2025-10-19)

### Bug Fixes

* **homepage:** improve text contrast in Your Journey section ([24d1200](24d120004d))
* **tutorial:** resolve TypeScript errors in TutorialPlayer ([88f57ce](88f57ce6df))
2025-10-19 18:57:20 +00:00
Thomas Hallock
24d120004d fix(homepage): improve text contrast in Your Journey section
Changed gray text colors to lighter values for better readability on dark background:
- Subtitle text: gray.400 → gray.200
- Stage labels: gray.400 → gray.200
- Navigation arrows: gray.600 → gray.400
- Footer text: gray.500 → gray.300

This addresses readability concerns while maintaining visual hierarchy.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:56:04 -05:00
Thomas Hallock
88f57ce6df fix(tutorial): resolve TypeScript errors in TutorialPlayer
- Remove references to non-existent highlight.columnIndex property
- Remove references to removed currentStep.errorMessages property
- Use placeValue directly for highlight filtering and calculations
- Add generic error message for incorrect bead clicks

All changes maintain existing functionality while fixing type safety issues.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:54:56 -05:00
semantic-release-bot
3a5dc0f1c8 chore(release): 4.20.1 [skip ci]
## [4.20.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.0...v4.20.1) (2025-10-19)

### Bug Fixes

* **homepage:** correct positioning of progression arrows in Your Journey section ([3fff9ef](3fff9ef140))

### Code Refactoring

* **homepage:** move What You'll Learn above tutorial ([ca1c6d8](ca1c6d8602))
2025-10-19 18:51:24 +00:00
Thomas Hallock
3fff9ef140 fix(homepage): correct positioning of progression arrows in Your Journey section
Added position: 'relative' to parent containers to properly anchor the absolutely positioned arrow elements between progression levels. This ensures the arrows display correctly between stages.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:50:23 -05:00
Thomas Hallock
ca1c6d8602 refactor(homepage): move What You'll Learn above tutorial
Repositioned the learning objectives section to appear before the interactive tutorial for better visual hierarchy and user flow.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:49:21 -05:00
semantic-release-bot
e6bcf20807 chore(release): 4.20.0 [skip ci]
## [4.20.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.19.0...v4.20.0) (2025-10-19)

### Features

* **tutorial:** add hideTooltip prop and improve dark mode coaching bar ([1ee25b3](1ee25b3dd2))
2025-10-19 18:47:56 +00:00
Thomas Hallock
1ee25b3dd2 feat(tutorial): add hideTooltip prop and improve dark mode coaching bar
- Added hideTooltip prop to TutorialPlayer to optionally hide guidance panels
- Enhanced coaching bar text for dark mode (brighter yellow with glow effect)
- Applied hideTooltip to homepage tutorial for cleaner presentation
- Updated dark mode header background for better integration

These changes are specific to the homepage dark theme instance while preserving default behavior for all other uses of the tutorial system.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:46:54 -05:00
semantic-release-bot
468bdebe3a chore(release): 4.19.0 [skip ci]
## [4.19.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.18.1...v4.19.0) (2025-10-19)

### Features

* **tutorial:** add fill color support for dark mode column posts and reckoning bar ([2eb3ff3](2eb3ff3406))
2025-10-19 18:38:20 +00:00
Thomas Hallock
2eb3ff3406 feat(tutorial): add fill color support for dark mode column posts and reckoning bar
Added fill property to ColumnPostStyle and ReckoningBarStyle interfaces in abacus-react to enable high-contrast colors in dark mode. Updated TutorialPlayer to set fill colors for column posts (30% white) and reckoning bar (40% white) when in dark theme mode.

This improves visibility of the abacus frame elements in dark mode on the homepage tutorial.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:37:10 -05:00
semantic-release-bot
efbe99a9e2 chore(release): 4.18.1 [skip ci]
## [4.18.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.18.0...v4.18.1) (2025-10-19)

### Bug Fixes

* **tutorial:** use correct customStyles API for dark mode frame styling ([fdc882c](fdc882cb04))
2025-10-19 18:30:53 +00:00
Thomas Hallock
fdc882cb04 fix(tutorial): use correct customStyles API for dark mode frame styling
Fixed the dark mode styling to use the correct AbacusReact customStyles API:

Previous (incorrect):
- Used nested `frame` object that doesn't exist in the API
- `frame.column`, `frame.reckoningBar`, `frame.border`

Corrected (per AbacusReact.tsx interface):
- `columnPosts` - Global styling for all column dividers
- `reckoningBar` - Horizontal middle bar styling

Changes:
- Column dividers: rgba(255, 255, 255, 0.2) with 2px stroke
- Reckoning bar: rgba(255, 255, 255, 0.25) with 3px stroke

These properties are at the root level of customStyles, not nested
under a `frame` object. The styling will now properly apply to the
abacus frame elements in dark mode.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:29:45 -05:00
semantic-release-bot
a7778c648d chore(release): 4.18.0 [skip ci]
## [4.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.2...v4.18.0) (2025-10-19)

### Features

* **tutorial:** add dark mode styling for coaching bar and abacus frame ([7e2f580](7e2f580877))
2025-10-19 18:27:22 +00:00
Thomas Hallock
7e2f580877 feat(tutorial): add dark mode styling for coaching bar and abacus frame
Enhanced the dark mode theme support for the tutorial player:

Coaching Bar:
- Updated instruction text color to use yellow.300 for dark mode instead of
  hardcoded yellow.900
- Ensures coaching instructions are readable against dark backgrounds

Abacus Frame:
- Added custom frame styling for dark mode using customStyles prop
- Column dividers: rgba(255, 255, 255, 0.15) with 2px stroke
- Reckoning bar: rgba(255, 255, 255, 0.2) with 3px stroke
- Outer border: rgba(255, 255, 255, 0.15) with 2px stroke
- Provides subtle, elegant appearance that blends with dark theme

The frame styling is automatically applied when theme="dark" and does not
affect light mode or other tutorial instances.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:26:14 -05:00
semantic-release-bot
f18a89974a chore(release): 4.17.2 [skip ci]
## [4.17.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.1...v4.17.2) (2025-10-19)

### Bug Fixes

* **tutorial:** correct column index calculation for variable column counts ([bf1ced4](bf1ced43f8))
2025-10-19 18:25:43 +00:00
Thomas Hallock
bf1ced43f8 fix(tutorial): correct column index calculation for variable column counts
Fixed a critical bug where tooltip overlays were referencing invalid column
indices when using fewer than 5 columns. The issue occurred because column
index calculations assumed a 5-column layout (0-4), but when using
abacusColumns={2}, the valid indices should be 0-1.

Changes:
- Updated targetColumnIndex calculation to use (abacusColumns - 1) - placeValue
  instead of hardcoded 4 - placeValue
- Fixed hasActiveBeadsToLeft logic to use abacusColumns for padding and
  column index conversions
- All column index calculations now properly account for the actual number
  of columns

This resolves the "Cannot read properties of undefined (reading 'heavenActive')"
error that occurred when using fewer than 5 columns on the homepage tutorial.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:24:35 -05:00
semantic-release-bot
6435027147 chore(release): 4.17.1 [skip ci]
## [4.17.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.0...v4.17.1) (2025-10-19)

### Bug Fixes

* **tutorial:** filter bead highlights when using fewer columns ([4d906ec](4d906ec20e))
2025-10-19 18:17:38 +00:00
Thomas Hallock
4d906ec20e fix(tutorial): filter bead highlights when using fewer columns
Fix runtime error when abacusColumns < 5 by filtering all bead highlights
to only include columns that actually exist.

Changes:
- Filter highlightBeads prop to only include valid place values
- Filter stepBeadHighlights to only include valid place values
- Filter customStyles column highlights to only include valid columns
- Add abacusColumns to dependencies of relevant useMemo/useCallback

This prevents accessing undefined column states when rendering with
fewer than 5 columns (e.g., abacusColumns={2} for simple demos).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:16:24 -05:00
semantic-release-bot
ff7b711fe0 chore(release): 4.17.0 [skip ci]
## [4.17.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.16.0...v4.17.0) (2025-10-19)

### Features

* **tutorial:** add dark theme and column control props ([d42f9b2](d42f9b2d9a))

### Styles

* **homepage:** soften tutorial styling for dark theme cohesion ([faaefba](faaefbacff)), closes [#f9](https://github.com/antialias/soroban-abacus-flashcards/issues/f9)
2025-10-19 18:13:26 +00:00
Thomas Hallock
d42f9b2d9a feat(tutorial): add dark theme and column control props
Add `theme` and `abacusColumns` props to TutorialPlayer for better customization:
- theme: 'light' | 'dark' controls all color schemes
- abacusColumns: number controls abacus column count (default 5)

Updated homepage to use:
- abacusColumns={2} for simpler 2+3 demo
- theme="dark" for cohesive integration with dark page design
- Vertical layout with "What You'll Learn" below tutorial

Dark theme styling:
- Transparent dark backgrounds for all containers
- Muted text colors (gray.200-gray.400)
- Subtle borders and shadows
- Removed bright yellow/amber gradients

All changes maintain backward compatibility - defaults to light theme with 5 columns.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 13:12:18 -05:00
Thomas Hallock
faaefbacff style(homepage): soften tutorial styling for dark theme cohesion
Apply visual improvements to homepage tutorial demo only (not tutorial system):
- Hide close (X) button in CoachBar - not needed on homepage
- Soften white backgrounds to light gray (#f9fafb)
- Mute text colors (h2 to gray.800, p to gray.600)
- Add transparency to guidance box (reduced opacity)
- Improve spacing and padding throughout
- Soften all shadows (reduced opacity)
- Mute amber/slate text colors for better dark theme integration

All changes scoped to .homepage-tutorial-demo wrapper via CSS overrides.
Tutorial system remains unchanged.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:59:27 -05:00
semantic-release-bot
8d650c5c52 chore(release): 4.16.0 [skip ci]
## [4.16.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.15.1...v4.16.0) (2025-10-19)

### Features

* **tutorial:** add hideNavigation prop to TutorialPlayer ([79ea52a](79ea52af80))

### Styles

* **homepage:** update tutorial container to match dark theme ([6b017b0](6b017b0fe9))
2025-10-19 17:49:06 +00:00
Thomas Hallock
79ea52af80 feat(tutorial): add hideNavigation prop to TutorialPlayer
Add `hideNavigation` prop to TutorialPlayer component that hides
the header and footer navigation controls, allowing the tutorial
content to be embedded cleanly without navigation chrome.

Perfect for single-step tutorial demos like the homepage.

Changes:
- Add hideNavigation prop to TutorialPlayerProps
- Wrap header section in conditional rendering
- Wrap navigation footer in conditional rendering
- Update homepage to use hideNavigation={true}
- Adjust minHeight when navigation is hidden

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:47:54 -05:00
Thomas Hallock
6b017b0fe9 style(homepage): update tutorial container to match dark theme
Change tutorial container background from bright white to dark
semi-transparent black (rgba(0, 0, 0, 0.4)) with gray border to
match the homepage's dark aesthetic. Improves visual cohesion.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:43:02 -05:00
semantic-release-bot
8f8b1e80db chore(release): 4.15.1 [skip ci]
## [4.15.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.15.0...v4.15.1) (2025-10-19)

### Bug Fixes

* **tutorial:** resolve React hydration error in TutorialPlayer ([c883d9e](c883d9e4c1))

### Code Refactoring

* replace demo component with real TutorialPlayer system ([19b03bc](19b03bc77c))
2025-10-19 17:42:44 +00:00
Thomas Hallock
c883d9e4c1 fix(tutorial): resolve React hydration error in TutorialPlayer
Change <p> tag to <div> tag to fix HTML nesting violation. The
<p> tag was containing <DecompositionWithReasons> which renders
a <div>, causing a hydration error. In HTML, <p> cannot contain
block-level elements like <div>.

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

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:41:33 -05:00
Thomas Hallock
19b03bc77c refactor: replace demo component with real TutorialPlayer system
Replaced the simple FriendsOfFiveDemo component with the actual TutorialPlayer
from the existing tutorial system, showing the "Friends of 5" lesson (2+3=5).

Changes:
- Removed FriendsOfFiveDemo.tsx (redundant custom component)
- Updated homepage to use TutorialPlayer with filtered tutorial steps
- Added TUTORIAL_SYSTEM.md documentation explaining the full tutorial system
- Homepage now demonstrates the real learning system instead of a mock

The tutorial system includes:
- Step-by-step guidance with bead highlighting
- Real-time feedback and validation
- Multi-step instruction support
- Pedagogical decomposition
- Auto-advancement on correct completion
- Full tutorial editor at /tutorial-editor

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:37:49 -05:00
semantic-release-bot
be68af0d56 chore(release): 4.15.0 [skip ci]
## [4.15.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.14.6...v4.15.0) (2025-10-19)

### Features

* redesign homepage with educational vision and interactive demo ([2f09cb5](2f09cb5539))
2025-10-19 17:32:38 +00:00
Thomas Hallock
2f09cb5539 feat: redesign homepage with educational vision and interactive demo
Redesigned the homepage to communicate the platform's educational
mission: providing a structured, self-directed path to soroban fluency
for students and families.

Key changes:
- New hero section with "Learn → Practice → Play → Master" journey
- Interactive Friends of 5 demo embedded directly on homepage
- Clear sections: Learn by Doing, Available Now, For Kids & Families
- Progression visualization (10 Kyu → Dan levels)
- Honest development status badge
- Removed aspirational language, focused on what's ready now
- Added comprehensive education roadmap documentation

The homepage now serves as both a visual introduction and working
demonstration of the learning system.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-19 12:31:30 -05:00
12 changed files with 2405 additions and 498 deletions

View File

@@ -1,3 +1,200 @@
## [4.24.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.24.0...v4.24.1) (2025-10-20)
### Bug Fixes
* **homepage:** adjust mini abacus container height ([c4066d6](https://github.com/antialias/soroban-abacus-flashcards/commit/c4066d687925bbe7737ebfeefdada7365ff97c6c))
* **homepage:** fix MiniAbacus runtime error and improve sizing ([1fa0df8](https://github.com/antialias/soroban-abacus-flashcards/commit/1fa0df85f7d3988cbc61701d89476419ccf0a13c))
* **homepage:** use correct AbacusReact API and fix clipping/styling issues ([1432afd](https://github.com/antialias/soroban-abacus-flashcards/commit/1432afd6e6bd547bd0da76dbeea1c2b71244826f))
* **homepage:** use direct conditionals for mini abacus padding ([38ef16a](https://github.com/antialias/soroban-abacus-flashcards/commit/38ef16a8f91f8ab4ad0d717b0321e2002636fafb))
### Styles
* **homepage:** add more padding around mini abacus ([c5103d0](https://github.com/antialias/soroban-abacus-flashcards/commit/c5103d049f73a8f7ef26915edfbef9ea56d59094))
* **homepage:** balance mini abacus padding horizontally and vertically ([2f0304e](https://github.com/antialias/soroban-abacus-flashcards/commit/2f0304eb81cdf84c21b0554c9cd4bd5478896dd8))
* **homepage:** increase mini abacus padding to '5' ([1da9ed1](https://github.com/antialias/soroban-abacus-flashcards/commit/1da9ed1ce6995c605622fc2863f248e5e91ab9c3))
## [4.24.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.23.0...v4.24.0) (2025-10-20)
### Features
* **homepage:** add animated mini abacus to "Read and set numbers" card ([e028e34](https://github.com/antialias/soroban-abacus-flashcards/commit/e028e342ad4bc01491e05a4ba074628155926fd8))
## [4.23.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.22.0...v4.23.0) (2025-10-20)
### Features
* **homepage:** add more visual embellishments to learning cards ([4ec1b95](https://github.com/antialias/soroban-abacus-flashcards/commit/4ec1b952f202d50f6db287c41732ec65ca17c142))
## [4.22.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.21.1...v4.22.0) (2025-10-20)
### Features
* **homepage:** enhance "What You'll Learn" with visual cards ([d142342](https://github.com/antialias/soroban-abacus-flashcards/commit/d1423420e653b26b2f89d9d17ae5d597807d6979))
### Bug Fixes
* **tutorial:** reduce tooltip z-index to scroll under nav bar ([47640f3](https://github.com/antialias/soroban-abacus-flashcards/commit/47640f3486c6d4a7107d59bdcce043f76fabbb1d))
## [4.21.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.21.0...v4.21.1) (2025-10-20)
### Code Refactoring
* **homepage:** rearrange tutorial demo layout side by side ([8b4ecee](https://github.com/antialias/soroban-abacus-flashcards/commit/8b4eceebfaaaf07e38ea64c7fe015aec86ac754f))
## [4.21.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.7...v4.21.0) (2025-10-20)
### Features
* **tutorial:** add silentErrors prop to suppress error messages ([8835e1c](https://github.com/antialias/soroban-abacus-flashcards/commit/8835e1c57ab8adcecefe0db082360dd98fbfaac7))
## [4.20.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.6...v4.20.7) (2025-10-20)
### Bug Fixes
* **home:** use Panda CSS token() for dynamic colors and center arrows properly ([d52ba63](https://github.com/antialias/soroban-abacus-flashcards/commit/d52ba6373a4577655dc1e5f5ff4926af7f7d96c3))
## [4.20.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.5...v4.20.6) (2025-10-20)
### Bug Fixes
* **homepage:** use inline styles for journey level colors ([5d85e89](https://github.com/antialias/soroban-abacus-flashcards/commit/5d85e898d65d44d8d09bee952fad44b5a9c0cd20)), closes [#4ade80](https://github.com/antialias/soroban-abacus-flashcards/issues/4ade80) [#60a5](https://github.com/antialias/soroban-abacus-flashcards/issues/60a5) [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24)
## [4.20.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.4...v4.20.5) (2025-10-20)
### Bug Fixes
* **docker:** include Panda CSS styled-system in production image ([57fabff](https://github.com/antialias/soroban-abacus-flashcards/commit/57fabffe605d953b4a4d7e05032401cbf1ab2d14))
## [4.20.4](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.3...v4.20.4) (2025-10-19)
### Bug Fixes
* **homepage:** use inline styles for Your Journey text contrast ([8e51390](https://github.com/antialias/soroban-abacus-flashcards/commit/8e5139001818d7013e1b2654ac707f7429316d58)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3) [#d1d5](https://github.com/antialias/soroban-abacus-flashcards/issues/d1d5)
## [4.20.3](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.2...v4.20.3) (2025-10-19)
### Bug Fixes
* **homepage:** use explicit RGBA colors for Your Journey text ([9c51cc9](https://github.com/antialias/soroban-abacus-flashcards/commit/9c51cc94eec4efcab9c0b9d1190f5b79c0c7d365))
## [4.20.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.1...v4.20.2) (2025-10-19)
### Bug Fixes
* **homepage:** improve text contrast in Your Journey section ([24d1200](https://github.com/antialias/soroban-abacus-flashcards/commit/24d120004dccecc1ce2f08c1b73eec902868fb23))
* **tutorial:** resolve TypeScript errors in TutorialPlayer ([88f57ce](https://github.com/antialias/soroban-abacus-flashcards/commit/88f57ce6df125142d6ea7feec60c475926bd4929))
## [4.20.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.0...v4.20.1) (2025-10-19)
### Bug Fixes
* **homepage:** correct positioning of progression arrows in Your Journey section ([3fff9ef](https://github.com/antialias/soroban-abacus-flashcards/commit/3fff9ef140bf1f462042f8319ed6c5e2a376e4ba))
### Code Refactoring
* **homepage:** move What You'll Learn above tutorial ([ca1c6d8](https://github.com/antialias/soroban-abacus-flashcards/commit/ca1c6d86029c891e019a96ba161e49b08b5be1bf))
## [4.20.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.19.0...v4.20.0) (2025-10-19)
### Features
* **tutorial:** add hideTooltip prop and improve dark mode coaching bar ([1ee25b3](https://github.com/antialias/soroban-abacus-flashcards/commit/1ee25b3dd2f0ee9dd7ed571ba818b7ca5a247f85))
## [4.19.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.18.1...v4.19.0) (2025-10-19)
### Features
* **tutorial:** add fill color support for dark mode column posts and reckoning bar ([2eb3ff3](https://github.com/antialias/soroban-abacus-flashcards/commit/2eb3ff340613301df20bf14f5b461371a27d7f05))
## [4.18.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.18.0...v4.18.1) (2025-10-19)
### Bug Fixes
* **tutorial:** use correct customStyles API for dark mode frame styling ([fdc882c](https://github.com/antialias/soroban-abacus-flashcards/commit/fdc882cb046e3d8835fbca59841e9af5329bcc52))
## [4.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.2...v4.18.0) (2025-10-19)
### Features
* **tutorial:** add dark mode styling for coaching bar and abacus frame ([7e2f580](https://github.com/antialias/soroban-abacus-flashcards/commit/7e2f580877af9d21409f427778fa3569c950fcf5))
## [4.17.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.1...v4.17.2) (2025-10-19)
### Bug Fixes
* **tutorial:** correct column index calculation for variable column counts ([bf1ced4](https://github.com/antialias/soroban-abacus-flashcards/commit/bf1ced43f801938b05f01548eea5fe771de1b58f))
## [4.17.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.0...v4.17.1) (2025-10-19)
### Bug Fixes
* **tutorial:** filter bead highlights when using fewer columns ([4d906ec](https://github.com/antialias/soroban-abacus-flashcards/commit/4d906ec20e90a9b0b3838f9b8428e0c68992f381))
## [4.17.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.16.0...v4.17.0) (2025-10-19)
### Features
* **tutorial:** add dark theme and column control props ([d42f9b2](https://github.com/antialias/soroban-abacus-flashcards/commit/d42f9b2d9ad630826c55b753dc581c469e8f9083))
### Styles
* **homepage:** soften tutorial styling for dark theme cohesion ([faaefba](https://github.com/antialias/soroban-abacus-flashcards/commit/faaefbacff419b337aa0fac4a101d5106a18c77c)), closes [#f9](https://github.com/antialias/soroban-abacus-flashcards/issues/f9)
## [4.16.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.15.1...v4.16.0) (2025-10-19)
### Features
* **tutorial:** add hideNavigation prop to TutorialPlayer ([79ea52a](https://github.com/antialias/soroban-abacus-flashcards/commit/79ea52af80c8cbb482bbdd87f77caf32ada737ee))
### Styles
* **homepage:** update tutorial container to match dark theme ([6b017b0](https://github.com/antialias/soroban-abacus-flashcards/commit/6b017b0fe92d4277843d9fe2645c22366f219d76))
## [4.15.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.15.0...v4.15.1) (2025-10-19)
### Bug Fixes
* **tutorial:** resolve React hydration error in TutorialPlayer ([c883d9e](https://github.com/antialias/soroban-abacus-flashcards/commit/c883d9e4c1b3a2f52c9d41e3ddce7418399f2649))
### Code Refactoring
* replace demo component with real TutorialPlayer system ([19b03bc](https://github.com/antialias/soroban-abacus-flashcards/commit/19b03bc77c649cf51d7b9a3617417c6ec8229ac7))
## [4.15.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.14.6...v4.15.0) (2025-10-19)
### Features
* redesign homepage with educational vision and interactive demo ([2f09cb5](https://github.com/antialias/soroban-abacus-flashcards/commit/2f09cb5539f2bb0b8c77359c6f774c3742313e1e))
## [4.14.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.14.5...v4.14.6) (2025-10-19)

View File

@@ -59,6 +59,9 @@ RUN adduser --system --uid 1001 nextjs
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next ./apps/web/.next
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
# Copy Panda CSS generated styles
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/styled-system ./apps/web/styled-system
# Copy server files (compiled from TypeScript)
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/server.js ./apps/web/
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/dist ./apps/web/dist

View File

@@ -0,0 +1,935 @@
# Soroban Abacus Education Platform - Comprehensive Roadmap
## Vision Statement
**Mission:** Fill the gap in the USA school system by providing a complete, self-directed abacus curriculum that trains students from beginner to mastery using the Japanese kyu/dan ranking system.
**Target Users:**
- Primary: Elementary school students (ages 6-12)
- Secondary: Middle school students and adult learners
- Teachers/Parents: Dashboard for monitoring progress
**Core Experience Principles:**
1. **Integrated Learning Loop:** Tutorial → Practice → Play → Assessment → Progress
2. **Self-Directed:** Simple enough for kids to fire up and start learning independently
3. **Gamified Progression:** Games reinforce lessons, feel like play but teach skills
4. **Physical + Virtual:** Support both physical abacus and AbacusReact component
5. **Mastery-Based:** Students advance through clear skill levels with certification
---
## Current State Assessment
### ✅ What We Have (Well-Built)
**1. Interactive Abacus Component (AbacusReact)**
- Highly polished, production-ready
- Excellent pedagogical features (bead highlighting, direction arrows, tooltips)
- Multiple color schemes and accessibility options
- Interactive and display-only modes
- **Rating: 95% Complete**
**2. Game System (4 Games)**
- Memory Lightning (memorization skills)
- Matching Pairs Battle (pattern recognition, complements)
- Card Sorting (visual literacy, ordering)
- Complement Race (speed calculation, friends-of-5/10)
- Type-safe architecture with game SDK
- Multiplayer and spectator modes
- **Rating: 80% Complete** (games exist but need curriculum integration)
**3. Tutorial Infrastructure**
- Tutorial player with step-based guidance
- Tutorial editor for content creation
- Bead highlighting system for instruction
- Event tracking and progress monitoring
- **Rating: 70% Complete** (infrastructure exists but lacks content)
**4. Real-time Multiplayer**
- Socket.IO integration
- Room-based architecture
- State synchronization
- **Rating: 90% Complete**
**5. Flashcard Generator**
- PDF/PNG/SVG export
- Customizable layouts and themes
- **Rating: 100% Complete**
### ⚠️ What We Have (Partially Built)
**1. Progress Tracking**
- Basic user stats (games played, wins, accuracy)
- No skill-level tracking
- No tutorial completion tracking
- No assessment history
- **Rating: 30% Complete**
**2. Tutorial Content**
- One example tutorial (GuidedAdditionTutorial)
- Type system for tutorials defined
- No comprehensive curriculum
- **Rating: 15% Complete**
**3. Assessment System**
- Per-game scoring exists
- Achievement system exists
- No formal tests or certification
- No placement tests
- **Rating: 25% Complete**
### ❌ What We're Missing (Critical Gaps)
**1. Kyu/Dan Ranking System** - 0% Complete
**2. Structured Curriculum** - 5% Complete
**3. Adaptive Learning** - 0% Complete
**4. Student Dashboard** - 0% Complete
**5. Teacher/Parent Dashboard** - 0% Complete
**6. Formal Assessment/Testing** - 0% Complete
**7. Learning Path Sequencing** - 0% Complete
**8. Content Library** - 10% Complete
---
## Kyu/Dan Level System (Japanese Abacus Standard)
### Beginner Levels (Kyu)
**10 Kyu - "First Steps"**
- Age: 6-7 years
- Skills: Basic bead manipulation, numbers 1-10
- Curriculum: Recognize and set numbers on abacus, understand place value
- Assessment: Set numbers 1-99 correctly, basic addition single digits
- Games: Card Sorting (visual recognition), Memory Lightning (basic)
**9 Kyu - "Number Explorer"**
- Skills: Addition/subtraction with no carry (1-9)
- Curriculum: Friends of 5 concept introduction
- Assessment: 20 problems, 2-digit addition/subtraction, no carry, 80% accuracy
- Games: Complement Race (practice mode), Matching Pairs (numerals)
**8 Kyu - "Complement Apprentice"**
- Skills: Friends of 5 mastery, introduction to friends of 10
- Curriculum: All combinations that make 5, carry concepts
- Assessment: 30 problems including carries using friends of 5, 85% accuracy
- Games: Complement Race (friends-5 sprint), Matching Pairs (complement pairs)
**7 Kyu - "Addition Warrior"**
- Skills: Friends of 10 mastery, 2-digit addition/subtraction with carries
- Curriculum: All combinations that make 10, mixed complement strategies
- Assessment: 40 problems, 2-3 digit calculations, mixed operations, 85% accuracy
- Games: Complement Race (friends-10 sprint), All games at medium difficulty
**6 Kyu - "Speed Calculator"**
- Skills: Multi-digit addition/subtraction (3-4 digits), speed emphasis
- Curriculum: Chain calculations, mental imagery beginning
- Assessment: 50 problems, 3-4 digits, 3 minutes time limit, 90% accuracy
- Games: Complement Race (survival mode), Memory Lightning (medium)
**5 Kyu - "Multiplication Initiate"**
- Skills: Single-digit multiplication (1-5)
- Curriculum: Multiplication tables 1-5, abacus multiplication method
- Assessment: 30 multiplication problems, 40 add/subtract problems, 90% accuracy
- Games: All games at hard difficulty
**4 Kyu - "Multiplication Master"**
- Skills: Full multiplication tables (1-9), 2-digit × 1-digit
- Curriculum: All multiplication patterns, division introduction
- Assessment: 40 multiplication, 20 division, 40 add/subtract, 90% accuracy
**3 Kyu - "Division Explorer"**
- Skills: Division mastery (2-digit ÷ 1-digit), mixed operations
- Curriculum: Division algorithm, remainders, mixed problem solving
- Assessment: 100 mixed problems in 10 minutes, 92% accuracy
**2 Kyu - "Advanced Operator"**
- Skills: Multi-digit multiplication/division, decimals introduction
- Curriculum: 3-digit × 2-digit, decimals, percentages
- Assessment: 120 mixed problems including decimals, 10 minutes, 93% accuracy
**1 Kyu - "Pre-Mastery"**
- Skills: Decimal operations, fractions, complex multi-step problems
- Curriculum: Real-world applications, word problems
- Assessment: 150 mixed problems, 10 minutes, 95% accuracy
- Mental calculation ability without physical abacus
### Master Levels (Dan)
**1 Dan - "Shodan" (First Degree)**
- Skills: Mental imagery without abacus, complex calculations
- Assessment: 200 mixed problems, 10 minutes, 96% accuracy
- Mental arithmetic certification
**2 Dan - "Nidan"**
- Skills: Advanced mental calculation, speed competitions
- Assessment: 250 problems, 10 minutes, 97% accuracy
**3 Dan - "Sandan"**
- Skills: Championship-level speed and accuracy
- Assessment: 300 problems, 10 minutes, 98% accuracy
**4-10 Dan** - Expert/Master levels with increasing complexity
---
## Integrated Learning Experience Design
### The Core Loop (Per Skill/Concept)
```
1. ASSESS → Placement test determines current level
2. LEARN → Tutorial teaches new concept
3. PRACTICE → Guided exercises with immediate feedback
4. PLAY → Games reinforce the skill in fun context
5. TEST → Formal assessment for mastery certification
6. ADVANCE → Unlock next level, update progress
```
### Example: Teaching "Friends of 5"
**1. Assessment (Placement)**
- Quick quiz: "Can you add 3 + 4 using the abacus?"
- Result: Student struggles → Assign Friends of 5 tutorial
**2. Learn (Tutorial)**
- Interactive tutorial: "Friends of 5"
- Steps:
1. Show that 5 = 1+4, 2+3, 3+2, 4+1
2. Demonstrate on abacus: setting 3, adding 2 to make 5
3. Explain heaven bead (top) = 5, earth beads (bottom) = 1 each
4. Interactive: Student sets 3, adds 2 using heaven bead
5. Practice all combinations
**3. Practice (Structured Exercises)**
- 20 problems: Set number, add its friend
- Real-time feedback on bead movements
- Hints available: "Use the heaven bead!"
- Must achieve 90% accuracy to proceed
**4. Play (Game Reinforcement)**
- Complement Race: Friends-5 mode
- Matching Pairs: Match numbers that make 5
- Makes practice feel like play
**5. Test (Formal Assessment)**
- 30 problems mixing friends-5 with previous skills
- Timed: 5 minutes
- Must achieve 85% to certify skill
- Can retake after reviewing mistakes
**6. Advance (Progress Update)**
- Friends of 5 skill marked as "Mastered"
- Unlock: Friends of 10 tutorial
- Update skill matrix
- Award achievement badge
---
## Detailed Curriculum Structure
### Curriculum Database Schema
```typescript
// Skill taxonomy
enum SkillCategory {
NUMBER_SENSE = 'number-sense',
ADDITION = 'addition',
SUBTRACTION = 'subtraction',
MULTIPLICATION = 'multiplication',
DIVISION = 'division',
MENTAL_CALC = 'mental-calculation',
COMPLEMENTS = 'complements',
SPEED = 'speed',
ACCURACY = 'accuracy'
}
// Individual skill (atomic unit)
interface Skill {
id: string
name: string
category: SkillCategory
kyuLevel: number // Which kyu level this skill belongs to
prerequisiteSkills: string[] // Must master these first
description: string
estimatedPracticeTime: number // minutes
}
// Learning module (collection of related skills)
interface Module {
id: string
title: string
kyuLevel: number
description: string
skills: string[] // Skill IDs
estimatedCompletionTime: number // hours
sequence: number // Order within kyu level
}
// Tutorial (teaches one or more skills)
interface Tutorial {
id: string
skillIds: string[]
moduleId: string
type: 'interactive' | 'video' | 'reading'
content: TutorialStep[]
estimatedDuration: number
}
// Practice set (reinforces skills)
interface PracticeSet {
id: string
skillIds: string[]
problemCount: number
timeLimit?: number
passingAccuracy: number
difficulty: 'easy' | 'medium' | 'hard'
}
// Game mapping (which games teach which skills)
interface GameSkillMapping {
gameId: string
skillIds: string[]
difficulty: string
recommendedKyuRange: [number, number]
}
// Assessment (formal test)
interface Assessment {
id: string
type: 'placement' | 'skill-check' | 'kyu-certification'
kyuLevel?: number
skillIds: string[]
problemCount: number
timeLimit: number
passingAccuracy: number
}
```
### Sample Curriculum Map
**10 Kyu Module Sequence:**
1. **Module 1: "Introduction to Abacus" (Week 1)**
- Skill: Understand abacus structure
- Skill: Recognize place values (ones, tens, hundreds)
- Tutorial: "What is an Abacus?"
- Tutorial: "Parts of the Abacus"
- Practice: Set numbers 1-10
- Game: Card Sorting (visual recognition)
2. **Module 2: "Setting Numbers" (Week 2)**
- Skill: Set single-digit numbers (1-9)
- Skill: Set two-digit numbers (10-99)
- Tutorial: "Setting Numbers on Abacus"
- Practice: 50 number-setting exercises
- Game: Memory Lightning (set and remember)
3. **Module 3: "Basic Addition" (Week 3-4)**
- Skill: Add single digits without carry (1+1 through 4+4)
- Tutorial: "Simple Addition"
- Practice: 100 addition problems
- Game: Complement Race (practice mode)
- Assessment: 10 Kyu Certification Test
**9 Kyu Module Sequence:**
1. **Module 4: "Friends of 5 - Introduction" (Week 5)**
- Skill: Recognize pairs that make 5
- Skill: Add using heaven bead (5 bead)
- Tutorial: "Friends of 5 - Part 1"
- Practice: Pattern recognition exercises
- Game: Matching Pairs (complement mode)
2. **Module 5: "Friends of 5 - Application" (Week 6-7)**
- Skill: Add crossing 5 (e.g., 3+4, 2+5)
- Tutorial: "Friends of 5 - Part 2"
- Practice: 200 problems with friends of 5
- Game: Complement Race (friends-5 mode)
- Assessment: 9 Kyu Certification Test
... (Continue through all kyu levels)
---
## Implementation Roadmap
### Phase 1: Foundation (Months 1-3) - "MVP for 10-9 Kyu"
**Goal:** Students can learn and certify 10 Kyu and 9 Kyu levels
**Database Schema Updates:**
- [ ] Create `skills` table
- [ ] Create `modules` table
- [ ] Create `curriculum_tutorials` table (links tutorials to skills)
- [ ] Create `curriculum_practice_sets` table
- [ ] Create `curriculum_assessments` table
- [ ] Create `user_progress` table
- Fields: userId, skillId, status (not_started, in_progress, mastered), attempts, bestScore, lastAttemptAt
- [ ] Create `user_skill_history` table (track all practice attempts)
- [ ] Create `user_assessments` table (formal test results)
- [ ] Create `user_kyu_levels` table
- Fields: userId, currentKyu, currentDan, certifiedAt, expiresAt
- [ ] Extend `user_stats` table: add `currentKyuLevel`, `currentDanLevel`, `skillsMastered`
**Tutorial Content Creation:**
- [ ] 10 Kyu tutorials (5 tutorials):
1. Introduction to Abacus
2. Understanding Place Value
3. Setting Numbers 1-99
4. Basic Addition (single digit, no carry)
5. Basic Subtraction (single digit, no borrow)
- [ ] 9 Kyu tutorials (3 tutorials):
1. Friends of 5 - Concept
2. Friends of 5 - Addition
3. Friends of 5 - Subtraction
**Practice Sets:**
- [ ] Build practice set generator for each skill
- [ ] Implement immediate feedback system
- [ ] Add hint system for common mistakes
- [ ] Track accuracy and time per problem
**Assessment System:**
- [ ] Build placement test component (determines starting level)
- [ ] Build skill-check test component (practice test before certification)
- [ ] Build kyu certification test component (formal test)
- [ ] Implement grading engine
- [ ] Create detailed results/feedback page
- [ ] Allow test retakes with review of mistakes
**Game Integration:**
- [ ] Map existing games to skills
- Memory Lightning → Number recognition, memory
- Card Sorting → Visual pattern recognition, ordering
- Matching Pairs → Complements, pattern matching
- Complement Race → Friends-5, Friends-10, speed
- [ ] Add skill-based game recommendations
- [ ] Track game performance per skill
**Student Dashboard:**
- [ ] Create dashboard showing:
- Current kyu level
- Skills mastered / in progress / locked
- Next recommended activity
- Recent achievements
- Progress bar toward next kyu level
- [ ] Implement simple, kid-friendly UI
- [ ] Add celebratory animations for milestones
**Core User Flow:**
- [ ] Onboarding: Placement test → Assign kyu level
- [ ] Home: Dashboard shows next recommended activity
- [ ] Click "Start Learning" → Next tutorial
- [ ] Complete tutorial → Practice exercises
- [ ] Complete practice → Game suggestion
- [ ] Master all module skills → Unlock certification test
- [ ] Pass certification → Advance to next kyu level
- [ ] Celebration and badge award
**Deliverables:**
- Students can complete 10 Kyu and 9 Kyu
- ~8 tutorials
- ~10 skills defined
- Placement test + 2 certification tests
- Student dashboard
- Progress tracking fully functional
---
### Phase 2: Core Curriculum (Months 4-8) - "8 Kyu through 5 Kyu"
**Goal:** Complete beginner curriculum through multiplication introduction
**Content Creation:**
- [ ] 8 Kyu: Friends of 10 tutorials and practice (4 weeks)
- [ ] 7 Kyu: Mixed complements, 2-digit operations (4 weeks)
- [ ] 6 Kyu: Multi-digit, speed training (6 weeks)
- [ ] 5 Kyu: Multiplication introduction, tables 1-5 (8 weeks)
- Total: ~40 tutorials, ~30 skills
**Enhanced Features:**
- [ ] Adaptive difficulty in practice sets (adjusts based on performance)
- [ ] Spaced repetition system (review mastered skills periodically)
- [ ] Daily recommended practice (10-15 min sessions)
- [ ] Streaks and habit formation
- [ ] Peer comparison (anonymous, optional)
**New Games:**
- [ ] Multiplication tables game
- [ ] Speed drill game (flash calculation)
- [ ] Mental math game (visualization without physical abacus)
**Parent/Teacher Dashboard:**
- [ ] View student progress
- [ ] See time spent learning
- [ ] Review test results
- [ ] Assign specific modules or skills
- [ ] Generate progress reports
**Gamification Enhancements:**
- [ ] Achievement badges for milestones
- [ ] Experience points (XP) system
- [ ] Level-up animations
- [ ] Customizable avatars (unlocked via achievements)
- [ ] Virtual rewards (stickers, themes)
**Deliverables:**
- Complete 8-5 Kyu curriculum
- ~50 total tutorials (cumulative)
- ~40 total skills (cumulative)
- Parent/teacher dashboard
- 2-3 new games
- Enhanced gamification
---
### Phase 3: Advanced Skills (Months 9-14) - "4 Kyu through 1 Kyu"
**Goal:** Advanced operations, real-world applications, mental calculation
**Content Creation:**
- [ ] 4 Kyu: Full multiplication, division introduction (8 weeks)
- [ ] 3 Kyu: Division mastery, mixed operations (8 weeks)
- [ ] 2 Kyu: Decimals, percentages (10 weeks)
- [ ] 1 Kyu: Fractions, word problems, mental calculation (12 weeks)
- Total: ~60 additional tutorials, ~40 additional skills
**Mental Calculation Training:**
- [ ] Visualization exercises (see abacus in mind)
- [ ] Flash anzan (rapid mental calculation)
- [ ] Mental calculation games
- [ ] Transition from physical to mental abacus
**Real-World Applications:**
- [ ] Shopping math (money, change, discounts)
- [ ] Measurement conversions
- [ ] Time calculations
- [ ] Real-world word problems
**Competition Features:**
- [ ] Speed competitions (leaderboards)
- [ ] Accuracy challenges
- [ ] Weekly tournaments
- [ ] Regional/global rankings (optional)
**AI Tutor Assistant:**
- [ ] Smart hints during practice
- [ ] Personalized learning paths
- [ ] Concept explanations on demand
- [ ] Answer specific questions ("Why do I use friends of 5 here?")
**Deliverables:**
- Complete 4-1 Kyu curriculum
- ~110 total tutorials (cumulative)
- ~80 total skills (cumulative)
- Mental calculation training
- AI assistant
- Competition system
---
### Phase 4: Mastery Levels (Months 15-18) - "Dan Levels"
**Goal:** Championship-level speed and accuracy, mental calculation mastery
**Content Creation:**
- [ ] Dan level certification tests
- [ ] Advanced mental calculation curriculum
- [ ] Championship preparation materials
- [ ] Expert-level problem sets
**Advanced Features:**
- [ ] Customized training plans for dan levels
- [ ] Video lessons from expert abacus users
- [ ] Community forum for advanced learners
- [ ] Virtual competitions
- [ ] Certification/diploma generation (printable)
**Integration with Standards:**
- [ ] Align with League of Soroban of Americas standards
- [ ] Japan Abacus Committee certification mapping
- [ ] International competition preparation
**Deliverables:**
- 1-10 Dan curriculum
- Certification system
- Community features
- Championship training
---
### Phase 5: Ecosystem (Months 18+) - "Complete Platform"
**Content Management System:**
- [ ] Tutorial builder UI (create without code)
- [ ] Content versioning
- [ ] Community-contributed content (vetted)
- [ ] Multilingual support (Spanish, Japanese, Hindi)
**Classroom Features:**
- [ ] Teacher creates classes
- [ ] Bulk student enrollment
- [ ] Class-wide assignments
- [ ] Class leaderboards
- [ ] Live teaching mode (project for class)
**Analytics & Insights:**
- [ ] Student learning velocity
- [ ] Skill gap analysis
- [ ] Predictive success modeling
- [ ] Recommendations engine
- [ ] Export data for research
**Mobile App:**
- [ ] iOS and React Native apps
- [ ] Offline mode
- [ ] Sync across devices
**Integrations:**
- [ ] Google Classroom
- [ ] Canvas LMS
- [ ] Schoology
- [ ] Export to SIS systems
**Advanced Gamification:**
- [ ] Story mode (learning quest)
- [ ] Cooperative challenges
- [ ] Guild/team system
- [ ] Seasonal events
---
## Success Metrics
### Student Engagement
- **Daily Active Users (DAU):** Target 40% of registered students
- **Weekly Active Users (WAU):** Target 70% of registered students
- **Average session time:** 20-30 minutes
- **Completion rate per module:** >80%
- **Retention (30-day):** >60%
- **Streak length:** Average 7+ days
### Learning Outcomes
- **Certification pass rate:** >70% on first attempt per kyu level
- **Skill mastery rate:** >85% accuracy on mastered skills after 30 days
- **Time to mastery:** Track average time per kyu level
- **Progression velocity:** Students advance 1 kyu level per 4-8 weeks (varies by level)
### Content Quality
- **Tutorial completion rate:** >90%
- **Practice set completion rate:** >85%
- **Game play rate:** >60% of students play games weekly
- **Assessment completion rate:** >75%
### Platform Health
- **System uptime:** >99.5%
- **Load time:** <2 seconds
- **Error rate:** <0.1%
### Business/Growth
- **Monthly signups:** Track growth month-over-month
- **Paid conversion** (if applicable): Target 10-20%
- **Teacher/school adoption:** Track institutional users
- **Net Promoter Score (NPS):** Target >50
---
## Technical Architecture Changes
### Database Changes Priority
**Immediate (Phase 1):**
```sql
-- Skills and curriculum structure
CREATE TABLE skills (...)
CREATE TABLE modules (...)
CREATE TABLE skill_prerequisites (...)
-- Tutorial and practice content
CREATE TABLE tutorial_content (...)
CREATE TABLE practice_sets (...)
CREATE TABLE assessments (...)
-- User progress tracking
CREATE TABLE user_progress (...)
CREATE TABLE user_skill_history (...)
CREATE TABLE user_assessments (...)
CREATE TABLE user_kyu_levels (...)
-- Game-skill mapping
CREATE TABLE game_skill_mappings (...)
```
**Phase 2:**
- Add spaced repetition tables
- Achievement tracking enhancements
- Peer comparison data
**Phase 3:**
- Mental calculation tracking
- Competition results
- AI tutor interaction logs
### API Endpoints Needed
**Progress & Skills:**
- `GET /api/student/progress` - Current kyu level, skills, next steps
- `GET /api/student/skills/:skillId` - Skill details and progress
- `POST /api/student/skills/:skillId/practice` - Record practice attempt
- `GET /api/student/dashboard` - Dashboard data
**Curriculum:**
- `GET /api/curriculum/kyu/:level` - All modules for kyu level
- `GET /api/curriculum/modules/:moduleId` - Module details
- `GET /api/curriculum/tutorials/:tutorialId` - Tutorial content
- `GET /api/curriculum/next` - Next recommended activity
**Assessments:**
- `POST /api/assessments/placement` - Take placement test
- `POST /api/assessments/skill-check/:skillId` - Practice test
- `POST /api/assessments/certification/:kyuLevel` - Certification test
- `POST /api/assessments/:assessmentId/submit` - Submit answers
- `GET /api/assessments/:assessmentId/results` - Get results
**Games:**
- `GET /api/games/recommended` - Games for current skills
- `POST /api/games/:gameId/result` - Log game completion
- `GET /api/games/:gameId/skills` - Which skills this game teaches
**Teacher/Parent:**
- `GET /api/teacher/students` - List of students
- `GET /api/teacher/students/:studentId/progress` - Student progress
- `POST /api/teacher/assignments` - Create assignment
### Component Architecture
**New Components Needed:**
```
/src/components/curriculum/
- SkillCard.tsx - Display skill with progress
- ModuleCard.tsx - Module overview with skills
- CurriculumMap.tsx - Visual map of curriculum
- SkillTree.tsx - Dependency graph visualization
/src/components/practice/
- PracticeSession.tsx - Practice exercise UI
- ProblemDisplay.tsx - Show problem to solve
- AnswerInput.tsx - Accept answer (with abacus)
- FeedbackDisplay.tsx - Show correctness and hints
/src/components/assessment/
- PlacementTest.tsx - Initial assessment
- SkillCheckTest.tsx - Practice test
- CertificationTest.tsx - Formal kyu test
- TestResults.tsx - Detailed results page
/src/components/dashboard/
- StudentDashboard.tsx - Main dashboard
- ProgressOverview.tsx - Current level and progress
- NextActivity.tsx - Recommended next step
- AchievementShowcase.tsx - Badges and milestones
- ActivityFeed.tsx - Recent activity
/src/components/teacher/
- TeacherDashboard.tsx
- StudentRoster.tsx
- StudentDetail.tsx
- AssignmentCreator.tsx
```
---
## Content Creation Process
### Tutorial Creation Workflow
1. **Define Skill:** What specific skill does this teach?
2. **Outline Steps:** Break down into 5-10 learning steps
3. **Create Interactive Elements:**
- Which beads to highlight
- What movements to demonstrate
- Example problems
4. **Add Explanations:** Clear, kid-friendly language
5. **Test with Students:** Iterate based on confusion points
6. **Publish:** Add to curriculum map
### Tutorial Template
```typescript
{
id: "friends-of-5-intro",
title: "Friends of 5 - Introduction",
skillIds: ["friends-5-recognition"],
kyuLevel: 9,
estimatedDuration: 15,
steps: [
{
instruction: "Let's learn about friends of 5! These are pairs of numbers that add up to 5.",
problem: null,
highlighting: [],
explanation: "When you add friends together, they always make 5!"
},
{
instruction: "1 and 4 are friends! See how 1 + 4 = 5?",
problem: { operation: 'add', terms: [1, 4] },
highlighting: [
{ column: 0, value: 1, direction: 'activate' },
{ column: 0, value: 4, direction: 'up', step: 2 }
],
explanation: "We set 1 earth bead, then add 4 more by using the heaven bead (5) and removing 1."
},
// ... more steps
]
}
```
### Practice Set Template
```typescript
{
id: "friends-5-practice-1",
skillIds: ["friends-5-recognition", "friends-5-addition"],
problemCount: 20,
timeLimit: 300, // 5 minutes
passingAccuracy: 0.85,
problemGenerator: {
type: 'addition',
numberRange: [1, 9],
requiresFriends5: true,
maxTerms: 2
}
}
```
---
## File Structure for Curriculum
```
/apps/web/src/curriculum/
/schema/
- skills.ts (skill definitions)
- modules.ts (module definitions)
- assessments.ts (test definitions)
/content/
/10-kyu/
- module-1-intro.ts
- module-2-setting-numbers.ts
- module-3-basic-addition.ts
/9-kyu/
- module-4-friends-5-intro.ts
- module-5-friends-5-application.ts
/8-kyu/
... and so on
/tutorials/
/10-kyu/
- intro-to-abacus.ts
- place-value.ts
- setting-numbers.ts
- basic-addition.ts
- basic-subtraction.ts
/9-kyu/
- friends-5-concept.ts
- friends-5-addition.ts
- friends-5-subtraction.ts
... and so on
/practice/
/10-kyu/
- number-setting-practice.ts
- basic-addition-practice.ts
/9-kyu/
- friends-5-practice.ts
... and so on
/assessments/
- placement-test.ts
- 10-kyu-certification.ts
- 9-kyu-certification.ts
... and so on
- curriculum-map.ts (master curriculum definition)
- game-skill-mappings.ts (which games teach which skills)
```
---
## Next Immediate Steps
### Week 1: Database Schema Design
- [ ] Design complete schema for Phase 1
- [ ] Write migration scripts
- [ ] Document schema decisions
- [ ] Review with stakeholders
### Week 2-3: Content Planning
- [ ] Write detailed 10 Kyu curriculum outline
- [ ] Write detailed 9 Kyu curriculum outline
- [ ] Define all skills for 10-9 Kyu
- [ ] Map skills to existing games
### Week 4-5: Tutorial Content Creation
- [ ] Write 5 tutorials for 10 Kyu
- [ ] Write 3 tutorials for 9 Kyu
- [ ] Create interactive steps with highlighting
- [ ] Add kid-friendly explanations
### Week 6-7: Assessment System Build
- [ ] Build assessment component UI
- [ ] Implement grading engine
- [ ] Create placement test (20 problems)
- [ ] Create 10 Kyu certification test (30 problems)
- [ ] Create 9 Kyu certification test (40 problems)
### Week 8-9: Practice System
- [ ] Build practice session component
- [ ] Implement problem generator for each skill
- [ ] Add immediate feedback system
- [ ] Create hint system
### Week 10-11: Student Dashboard
- [ ] Design dashboard UI (kid-friendly)
- [ ] Build progress visualization
- [ ] Implement "next recommended activity" logic
- [ ] Add achievement display
### Week 12: Integration & Testing
- [ ] Connect all pieces: tutorials → practice → games → assessment
- [ ] Test complete user flow
- [ ] User testing with kids
- [ ] Iterate based on feedback
---
## Questions to Resolve
1. **Certification Validity:** Should kyu certifications expire? (Traditional abacus schools: no expiration)
2. **Retake Policy:** How many times can student retake certification test? (Suggest: unlimited, but must wait 24 hours)
3. **Grading Standards:** Strict adherence to Japanese standards or adjust for USA context?
4. **Physical Abacus:** Should we require physical abacus for certain levels? (Recommend: optional but encouraged)
5. **Age Restrictions:** Any minimum age? (Suggest: 6+ with parent/teacher supervision)
6. **Teacher Accounts:** Free for teachers? (Recommend: yes, free for teachers)
7. **Pricing Model:** Free tier + premium? School licensing? (TBD)
8. **Content Licensing:** Will curriculum be open source or proprietary? (Recommend: proprietary but allow teacher customization)
9. **Accessibility:** WCAG compliance level? (Recommend: AA minimum)
10. **Data Privacy:** COPPA compliance for users under 13? (Required: yes, must be compliant)
---
## Conclusion
This roadmap provides a clear path from current state (scattered features) to target state (complete educational platform). The phased approach allows incremental delivery while maintaining focus on core learning experience.
**Estimated Timeline:**
- Phase 1 (10-9 Kyu MVP): 3 months
- Phase 2 (8-5 Kyu): 5 months
- Phase 3 (4-1 Kyu): 6 months
- Phase 4 (Dan levels): 3 months
- Phase 5 (Ecosystem): Ongoing
**Total to Complete Platform:** ~17 months for core curriculum, then continuous improvement
**Priority:** Start with Phase 1 to prove the concept, get student feedback, and validate the learning loop before building the full system.

View File

@@ -0,0 +1,76 @@
# Panda CSS Dynamic Token Usage
## Problem: Dynamic Color Tokens Not Working
When using Panda CSS, color tokens like `blue.400`, `purple.400`, etc. don't work when used dynamically through variables in the `css()` function.
## Root Cause
Panda CSS's `css()` function requires **static values at build time**. It cannot process dynamic token references like:
```typescript
// ❌ This doesn't work
const color = 'blue.400'
css({ color: color }) // Panda can't resolve this at build time
```
The `css()` function performs static analysis during the build process to generate CSS classes. It cannot handle runtime-dynamic token paths.
## Solution: Use the `token()` Function
Panda CSS provides a `token()` function specifically for resolving token paths to their actual values at runtime:
```typescript
import { token } from '../../styled-system/tokens'
// ✅ This works
const stages = [
{ level: '10 Kyu', label: 'Beginner', color: 'colors.green.400' },
{ level: '5 Kyu', label: 'Intermediate', color: 'colors.blue.400' },
{ level: '1 Kyu', label: 'Advanced', color: 'colors.violet.400' },
{ level: 'Dan', label: 'Master', color: 'colors.amber.400' },
] as const
// Use with inline styles, not css()
<div style={{ color: token(stage.color) }}>
```
## Important Notes
1. **Use `as const`**: TypeScript needs the array marked as `const` so the token strings are treated as literal types, not generic strings. The `token()` function expects the `Token` literal type.
2. **Use inline styles**: When using `token()`, apply colors via the `style` prop, not through the `css()` function:
```typescript
// ✅ Correct
<div style={{ color: token(stage.color) }}>
// ❌ Won't work
<div className={css({ color: token(stage.color) })}>
```
3. **Static tokens in css()**: For static usage, you CAN use tokens directly in `css()`:
```typescript
// ✅ This works because it's static
css({ color: 'blue.400' })
```
## How token() Works
The `token()` function:
- Takes a token path like `"colors.blue.400"`
- Looks it up in the generated token registry (`styled-system/tokens/index.mjs`)
- Returns the actual CSS value (e.g., `"#60a5fa"`)
- Happens at runtime, not build time
## Token Type Definition
The `Token` type is a union of all valid token paths:
```typescript
type Token = "colors.blue.400" | "colors.green.400" | "colors.violet.400" | ...
```
This is defined in `styled-system/tokens/tokens.d.ts`.
## Reference Implementation
See `src/app/page.tsx` lines 404-434 for a working example of dynamic token usage in the "Your Journey" section.

View File

@@ -0,0 +1,222 @@
# Tutorial System Documentation
## Overview
The tutorial system is a sophisticated interactive learning platform for teaching soroban abacus concepts. It features step-by-step guidance, bead highlighting, pedagogical decomposition, and progress tracking.
## Key Components
### 1. TutorialPlayer (`/src/components/tutorial/TutorialPlayer.tsx`)
The main tutorial playback component that:
- Displays tutorial steps progressively
- Highlights specific beads users should interact with
- Provides real-time feedback and tooltips
- Shows step-by-step instructions for multi-step operations
- Tracks user progress through the tutorial
- Auto-advances to next step on correct completion
**Key Features:**
- **Bead Highlighting**: Visual indicators showing which beads to manipulate
- **Step Progress**: Shows current step out of total steps
- **Error Feedback**: Provides hints when user makes mistakes
- **Multi-Step Support**: Breaks complex operations into sequential sub-steps
- **Pedagogical Decomposition**: Explains the "why" behind each operation
### 2. TutorialEditor (`/src/components/tutorial/TutorialEditor.tsx`)
A full-featured editor for creating and editing tutorials:
- Visual step editor
- Bead highlight configuration
- Multi-step instruction editor
- Live preview
- Import/export functionality
- Access control
**Editor URL:** `/tutorial-editor`
### 3. Tutorial Data Structure (`/src/types/tutorial.ts`)
```typescript
interface Tutorial {
id: string
title: string
description: string
category: string
difficulty: 'beginner' | 'intermediate' | 'advanced'
estimatedDuration: number // minutes
steps: TutorialStep[]
tags: string[]
author: string
version: string
createdAt: Date
updatedAt: Date
isPublished: boolean
}
interface TutorialStep {
id: string
title: string
problem: string // e.g. "2 + 3"
description: string // User-facing explanation
startValue: number // Initial abacus value
targetValue: number // Goal value
expectedAction: 'add' | 'remove' | 'multi-step'
actionDescription: string
// Bead highlighting
highlightBeads?: Array<{
placeValue: number // 0=ones, 1=tens, etc.
beadType: 'heaven' | 'earth'
position?: number // For earth beads: 0-3
}>
// Progressive step highlighting
stepBeadHighlights?: Array<{
placeValue: number
beadType: 'heaven' | 'earth'
position?: number
stepIndex: number // Which instruction step
direction: 'up' | 'down' | 'activate' | 'deactivate'
order?: number // Order within step
}>
totalSteps?: number // For multi-step operations
multiStepInstructions?: string[] // Sequential instructions
// Tooltips and guidance
tooltip: {
content: string // Short title
explanation: string // Detailed explanation
}
}
```
### 4. Tutorial Converter (`/src/utils/tutorialConverter.ts`)
Utility that converts the original `GuidedAdditionTutorial` data into the new tutorial format:
- `guidedAdditionSteps`: Array of tutorial steps from basic addition to complements
- `convertGuidedAdditionTutorial()`: Converts to Tutorial object
- `getTutorialForEditor()`: Main export used in the app
**Current Tutorial Steps:**
1. Basic Addition (0+1, 1+1, 2+1, 3+1)
2. Heaven Bead Introduction (0+5, 5+1)
3. Five Complements (3+4, 2+3 using 5-complement method)
4. Complex Operations (6+2, 7+4 with carrying)
### 5. Supporting Utilities
**`/src/utils/abacusInstructionGenerator.ts`**
- Automatically generates step-by-step instructions from start/target values
- Creates bead highlight data
- Determines movement directions
**`/src/utils/beadDiff.ts`**
- Calculates differences between abacus states
- Generates visual feedback tooltips
- Explains what changed and why
## Usage Examples
### Basic Usage in a Page
```typescript
import { TutorialPlayer } from '@/components/tutorial/TutorialPlayer'
import { getTutorialForEditor } from '@/utils/tutorialConverter'
export function MyPage() {
return (
<TutorialPlayer
tutorial={getTutorialForEditor()}
isDebugMode={false}
showDebugPanel={false}
/>
)
}
```
### Using a Subset of Steps
```typescript
import { getTutorialForEditor } from '@/utils/tutorialConverter'
const fullTutorial = getTutorialForEditor()
// Extract specific steps (e.g., just "Friends of 5")
const friendsOf5Tutorial = {
...fullTutorial,
id: 'friends-of-5-demo',
title: 'Friends of 5',
steps: fullTutorial.steps.filter(step =>
step.id === 'complement-2' // The 2+3=5 step
)
}
return <TutorialPlayer tutorial={friendsOf5Tutorial} />
```
### Creating a Custom Tutorial
```typescript
const customTutorial: Tutorial = {
id: 'my-tutorial',
title: 'My Custom Tutorial',
description: 'Learning something new',
category: 'Custom',
difficulty: 'beginner',
estimatedDuration: 5,
steps: [
{
id: 'step-1',
title: 'Add 2',
problem: '0 + 2',
description: 'Move two earth beads up',
startValue: 0,
targetValue: 2,
expectedAction: 'add',
actionDescription: 'Add two earth beads',
highlightBeads: [
{ placeValue: 0, beadType: 'earth', position: 0 },
{ placeValue: 0, beadType: 'earth', position: 1 }
],
tooltip: {
content: 'Adding 2',
explanation: 'Push two earth beads up to represent 2'
}
}
],
tags: ['custom'],
author: 'Me',
version: '1.0.0',
createdAt: new Date(),
updatedAt: new Date(),
isPublished: true
}
```
## Current Implementation Locations
**Live Tutorials:**
- `/guide` - Second tab "Arithmetic Operations" contains the full guided addition tutorial
**Editor:**
- `/tutorial-editor` - Full tutorial editing interface
**Storybook:**
- Multiple tutorial stories in `/src/components/tutorial/*.stories.tsx`
## Key Design Principles
1. **Progressive Disclosure**: Users see one step at a time
2. **Immediate Feedback**: Real-time validation and hints
3. **Visual Guidance**: Bead highlighting shows exactly what to do
4. **Pedagogical Decomposition**: Multi-step operations broken into atomic actions
5. **Auto-Advancement**: Successful completion automatically moves to next step
6. **Error Recovery**: Helpful hints when user makes mistakes
## Notes
- The tutorial system uses the existing `AbacusReact` component
- Tutorials can be created/edited through the TutorialEditor
- Tutorial data can be exported/imported as JSON
- The system supports both single-step and multi-step operations
- Bead highlighting uses place value indexing (0=ones, 1=tens, etc.)

View File

@@ -102,7 +102,8 @@
"Bash(do gh run list --limit 1 --workflow=\"Build and Deploy\" --json conclusion,status,databaseId --jq '.[0] | \"\"\\(.status) - \\(.conclusion // \"\"running\"\") - Run ID: \\(.databaseId)\"\"')",
"Bash(node -e:*)",
"Bash(do gh run list --limit 1 --workflow=\"Build and Deploy\" --json conclusion,status,databaseId --jq '.[0] | \"\"\\(.status) - \\(.conclusion // \"\"running\"\") - Run \\(.databaseId)\"\"')",
"Bash(do ssh nas.home.network '/usr/local/bin/docker inspect soroban-abacus-flashcards --format=\"\"{{index .Config.Labels \\\"\"org.opencontainers.image.revision\\\"\"}}\"\"')"
"Bash(do ssh nas.home.network '/usr/local/bin/docker inspect soroban-abacus-flashcards --format=\"\"{{index .Config.Labels \\\"\"org.opencontainers.image.revision\\\"\"}}\"\"')",
"Bash(git rev-parse HEAD)"
],
"deny": [],
"ask": []

View File

@@ -290,10 +290,12 @@ export default function RoomPage() {
key={gameDef.manifest.name}
onClick={() => handleGameSelect(gameDef.manifest.name)}
disabled={isDisabled}
className={css({
style={{
background: gameDef.manifest.gradient,
border: '2px solid',
borderColor: gameDef.manifest.borderColor,
}}
className={css({
border: '2px solid',
borderRadius: '2xl',
padding: '6',
cursor: isDisabled ? 'not-allowed' : 'pointer',

View File

@@ -1,29 +1,92 @@
'use client'
import { useState } from 'react'
import Link from 'next/link'
import { PageWithNav } from '@/components/PageWithNav'
import { useEffect, useState } from 'react'
import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react'
import { PageWithNav } from '@/components/PageWithNav'
import { TutorialPlayer } from '@/components/tutorial/TutorialPlayer'
import { getTutorialForEditor } from '@/utils/tutorialConverter'
import { css } from '../../styled-system/css'
import { container, grid, hstack, stack } from '../../styled-system/patterns'
import { token } from '../../styled-system/tokens'
// Mini abacus that cycles through random 3-digit numbers
function MiniAbacus() {
const [currentValue, setCurrentValue] = useState(123)
const appConfig = useAbacusConfig()
useEffect(() => {
// Cycle through random 3-digit numbers every 2.5 seconds
const interval = setInterval(() => {
const randomNum = Math.floor(Math.random() * 1000) // 0-999
setCurrentValue(randomNum)
}, 2500)
return () => clearInterval(interval)
}, [])
// Dark theme styles for the abacus
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,
},
}
return (
<div
className={css({
width: '75px',
height: '80px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
})}
>
<div className={css({ transform: 'scale(0.6)', transformOrigin: 'center center' })}>
<AbacusReact
value={currentValue}
columns={3}
beadShape={appConfig.beadShape}
styles={darkStyles}
/>
</div>
</div>
)
}
export default function HomePage() {
const [abacusValue, setAbacusValue] = useState(1234567)
const appConfig = useAbacusConfig()
// Extract just the "Friends of 5" step (2+3=5) for homepage demo
const fullTutorial = getTutorialForEditor()
const friendsOf5Tutorial = {
...fullTutorial,
id: 'friends-of-5-demo',
title: 'Friends of 5',
description: 'Learn the "Friends of 5" technique: adding 3 to make 5',
steps: fullTutorial.steps.filter((step) => step.id === 'complement-2'),
}
return (
<PageWithNav navTitle="Soroban Mastery Platform" navEmoji="🧮">
<PageWithNav navTitle="Soroban Learning Platform" navEmoji="🧮">
<div className={css({ bg: 'gray.900', minHeight: '100vh' })}>
{/* Hero with Large Abacus */}
{/* Hero Section */}
<div
className={css({
background:
'linear-gradient(135deg, rgba(17, 24, 39, 1) 0%, rgba(88, 28, 135, 0.3) 50%, rgba(17, 24, 39, 1) 100%)',
color: 'white',
py: { base: '12', md: '20' },
py: { base: '12', md: '16' },
position: 'relative',
overflow: 'hidden',
})}
>
{/* Background pattern */}
<div
className={css({
position: 'absolute',
@@ -34,69 +97,105 @@ export default function HomePage() {
backgroundSize: '40px 40px',
})}
/>
<div className={container({ maxW: '6xl', px: '4', position: 'relative' })}>
<div className={css({ textAlign: 'center', maxW: '5xl', mx: 'auto' })}>
{/* Main headline */}
<h1
className={css({
fontSize: { base: '3xl', md: '5xl', lg: '6xl' },
fontWeight: 'bold',
mb: '6',
mb: '4',
lineHeight: 'tight',
background: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 50%, #fbbf24 100%)',
backgroundClip: 'text',
color: 'transparent',
})}
>
Master the Soroban
A structured path to soroban fluency
</h1>
{/* Large Featured Abacus */}
<div
className={css({
display: 'flex',
justifyContent: 'center',
my: '10',
p: '8',
bg: 'white',
borderRadius: '2xl',
border: '2px solid',
borderColor: 'purple.500/30',
boxShadow: '0 25px 50px -12px rgba(139, 92, 246, 0.25)',
color: '#1f2937',
})}
>
<AbacusReact
value={abacusValue}
columns={7}
beadShape={appConfig.beadShape}
colorScheme={appConfig.colorScheme}
hideInactiveBeads={appConfig.hideInactiveBeads}
interactive={true}
animated={true}
soundEnabled={true}
soundVolume={0.4}
scaleFactor={2.2}
showNumbers={true}
onValueChange={(newValue: number) => setAbacusValue(newValue)}
/>
</div>
{/* Subtitle */}
<p
className={css({
fontSize: { base: 'lg', md: 'xl' },
color: 'gray.300',
mb: '8',
mb: '6',
maxW: '3xl',
mx: 'auto',
lineHeight: '1.6',
})}
>
Interactive tutorials, multiplayer games, and beautiful flashcardsyour complete
soroban learning ecosystem
Designed for self-directed learning. Start where you are, practice the skills you
need, play games that reinforce concepts.
</p>
{/* Dev status badge */}
<div
className={css({
display: 'inline-block',
px: '4',
py: '2',
bg: 'rgba(139, 92, 246, 0.15)',
border: '1px solid rgba(139, 92, 246, 0.3)',
borderRadius: 'full',
fontSize: 'sm',
color: 'purple.300',
mb: '8',
})}
>
🏗 Curriculum system in active development
</div>
{/* Visual learning journey */}
<div
className={css({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: '2',
mb: '8',
flexWrap: 'wrap',
})}
>
{[
{ icon: '📖', label: 'Learn' },
{ icon: '→', label: '', isArrow: true },
{ icon: '✏️', label: 'Practice' },
{ icon: '→', label: '', isArrow: true },
{ icon: '🎮', label: 'Play' },
{ icon: '→', label: '', isArrow: true },
{ icon: '🎯', label: 'Master' },
].map((step, i) => (
<div
key={i}
className={css({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '1',
opacity: step.isArrow ? 0.5 : 1,
})}
>
<div
className={css({
fontSize: step.isArrow ? 'xl' : '2xl',
color: step.isArrow ? 'gray.500' : 'yellow.400',
})}
>
{step.icon}
</div>
{step.label && (
<div className={css({ fontSize: 'xs', color: 'gray.400' })}>{step.label}</div>
)}
</div>
))}
</div>
{/* Primary CTAs */}
<div className={hstack({ gap: '4', justify: 'center', flexWrap: 'wrap' })}>
<Link
href="/games"
href="/guide"
className={css({
px: '8',
py: '4',
@@ -113,10 +212,10 @@ export default function HomePage() {
transition: 'all 0.3s ease',
})}
>
🎮 Play Games
📚 Start Learning
</Link>
<Link
href="/guide"
href="/games"
className={css({
px: '8',
py: '4',
@@ -134,28 +233,7 @@ export default function HomePage() {
transition: 'all 0.3s ease',
})}
>
📚 Learn
</Link>
<Link
href="/create"
className={css({
px: '8',
py: '4',
bg: 'rgba(139, 92, 246, 0.2)',
color: 'white',
fontWeight: 'bold',
fontSize: 'lg',
rounded: 'xl',
border: '2px solid',
borderColor: 'purple.500',
_hover: {
bg: 'rgba(139, 92, 246, 0.3)',
transform: 'translateY(-2px)',
},
transition: 'all 0.3s ease',
})}
>
🎨 Create
🎮 Practice Through Games
</Link>
</div>
</div>
@@ -164,173 +242,453 @@ export default function HomePage() {
{/* Main content container */}
<div className={container({ maxW: '7xl', px: '4', py: '12' })}>
{/* Arcade Games Section */}
<section className={stack({ gap: '6', mt: '16' })}>
<div className={hstack({ justify: 'space-between', alignItems: 'center' })}>
<div>
<h2
className={css({
fontSize: { base: '2xl', md: '3xl' },
fontWeight: 'bold',
color: 'white',
mb: '2',
})}
>
🕹 Multiplayer Arcade
</h2>
<p className={css({ color: 'gray.400', fontSize: 'md' })}>
Compete with friends in real-time soroban games
</p>
</div>
<Link
href="/games"
{/* Learn by Doing Section - with inline tutorial demo */}
<section className={stack({ gap: '8', mb: '16' })}>
<div className={css({ textAlign: 'center' })}>
<h2
className={css({
fontSize: 'md',
color: 'yellow.400',
fontWeight: 'semibold',
_hover: { color: 'yellow.300' },
display: { base: 'none', md: 'block' },
fontSize: { base: '2xl', md: '3xl' },
fontWeight: 'bold',
color: 'white',
mb: '2',
})}
>
View All
</Link>
Learn by Doing
</h2>
<p className={css({ color: 'gray.400', fontSize: 'md', maxW: '2xl', mx: 'auto' })}>
Interactive tutorials teach you step-by-step. Try this example right now:
</p>
</div>
{/* Live demo and learning objectives */}
<div
className={css({
bg: 'rgba(0, 0, 0, 0.4)',
rounded: 'xl',
p: '8',
border: '1px solid',
borderColor: 'gray.700',
shadow: 'lg',
maxW: '1200px',
mx: 'auto',
})}
>
<div
className={css({
display: 'flex',
flexDirection: { base: 'column', md: 'row' },
gap: '8',
alignItems: { base: 'center', md: 'flex-start' },
})}
>
{/* Tutorial on the left */}
<div className={css({ flex: '1' })}>
<TutorialPlayer
tutorial={friendsOf5Tutorial}
isDebugMode={false}
showDebugPanel={false}
hideNavigation={true}
hideTooltip={true}
silentErrors={true}
abacusColumns={1}
theme="dark"
/>
</div>
{/* What you'll learn on the right */}
<div
className={css({
flex: '0 0 auto',
minW: '340px',
maxW: { base: '100%', md: '420px' },
})}
>
<h3
className={css({
fontSize: '2xl',
fontWeight: 'bold',
color: 'white',
mb: '6',
})}
>
What You'll Learn
</h3>
<div className={stack({ gap: '5' })}>
{[
{
icon: '🔢',
title: 'Read and set numbers',
desc: 'Master abacus number representation from zero to thousands',
example: '0-9999',
badge: 'Foundation',
},
{
icon: '🤝',
title: 'Friends techniques',
desc: 'Add and subtract using complement pairs and mental shortcuts',
example: '5 = 2+3',
badge: 'Core',
},
{
icon: '',
title: 'Multiply & divide',
desc: 'Fluent multi-digit calculations with advanced techniques',
example: '12×34',
badge: 'Advanced',
},
{
icon: '🧠',
title: 'Mental calculation',
desc: 'Visualize and compute without the physical tool (Anzan)',
example: 'Speed math',
badge: 'Expert',
},
].map((skill, i) => (
<div
key={i}
className={css({
bg: 'linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.03))',
borderRadius: 'xl',
p: '4',
border: '1px solid',
borderColor: 'rgba(255, 255, 255, 0.15)',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
transition: 'all 0.2s',
_hover: {
bg: 'linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05))',
borderColor: 'rgba(255, 255, 255, 0.25)',
transform: 'translateY(-2px)',
boxShadow: '0 6px 16px rgba(0, 0, 0, 0.4)',
},
})}
>
<div className={hstack({ gap: '3', alignItems: 'flex-start' })}>
<div
className={css({
fontSize: '3xl',
minW: '50px',
textAlign: 'center',
bg: 'rgba(255, 255, 255, 0.1)',
borderRadius: 'lg',
py: i === 0 ? '5' : '2',
px: i === 0 ? '3' : '2',
})}
>
{i === 0 ? <MiniAbacus /> : skill.icon}
</div>
<div className={stack({ gap: '2', flex: '1' })}>
<div className={hstack({ gap: '2', alignItems: 'center' })}>
<div
className={css({
color: 'white',
fontSize: 'md',
fontWeight: 'bold',
})}
>
{skill.title}
</div>
<div
className={css({
bg: 'rgba(250, 204, 21, 0.2)',
color: 'yellow.400',
fontSize: '2xs',
fontWeight: 'semibold',
px: '2',
py: '0.5',
borderRadius: 'md',
})}
>
{skill.badge}
</div>
</div>
<div
className={css({
color: 'gray.300',
fontSize: 'xs',
lineHeight: '1.5',
})}
>
{skill.desc}
</div>
<div
className={css({
color: 'yellow.400',
fontSize: 'xs',
fontFamily: 'mono',
fontWeight: 'semibold',
mt: '1',
bg: 'rgba(250, 204, 21, 0.1)',
px: '2',
py: '1',
borderRadius: 'md',
w: 'fit-content',
})}
>
{skill.example}
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</section>
{/* Current Offerings Section */}
<section className={stack({ gap: '6', mb: '16' })}>
<div className={css({ textAlign: 'center' })}>
<h2
className={css({
fontSize: { base: '2xl', md: '3xl' },
fontWeight: 'bold',
color: 'white',
mb: '2',
})}
>
Available Now
</h2>
<p className={css({ color: 'gray.400', fontSize: 'md' })}>
Foundation tutorials and reinforcement games ready to use
</p>
</div>
<div className={grid({ columns: { base: 1, sm: 2, lg: 4 }, gap: '5' })}>
<GameCard
icon="🧠"
title="Memory Lightning"
description="Memorize soroban numbers"
players="1-8 players"
tags={['Co-op', 'Competitive']}
tags={['Memory', 'Pattern Recognition']}
gradient="linear-gradient(135deg, #667eea 0%, #764ba2 100%)"
href="/games"
/>
<GameCard
icon="⚔️"
title="Matching Pairs"
description="Turn-based card battles"
description="Match complement numbers"
players="1-4 players"
tags={['Pattern Recognition']}
tags={['Friends of 5', 'Friends of 10']}
gradient="linear-gradient(135deg, #f093fb 0%, #f5576c 100%)"
href="/games"
/>
<GameCard
icon="🏁"
title="Speed Race"
description="Race AI with complements"
players="1-4 players + AI"
tags={['Practice', 'Sprint', 'Survival']}
title="Complement Race"
description="Race against time"
players="1-4 players"
tags={['Speed', 'Practice', 'Survival']}
gradient="linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)"
href="/games"
/>
<GameCard
icon="🔢"
title="Card Sorting"
description="Arrange cards visually"
description="Arrange numbers visually"
players="Solo challenge"
tags={['Visual Literacy']}
tags={['Visual Literacy', 'Ordering']}
gradient="linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)"
href="/games"
/>
</div>
</section>
{/* Interactive Learning & Flashcard Creator */}
<div className={grid({ columns: { base: 1, lg: 2 }, gap: '8', mt: '16' })}>
<FeaturePanel
icon="📚"
title="Interactive Learning"
description="Master soroban through hands-on guided tutorials"
features={[
'Visual tutorials on reading bead positions',
'Step-by-step arithmetic operations',
'Interactive exercises with instant feedback',
]}
ctaText="Start Learning →"
ctaHref="/guide"
accentColor="purple"
/>
<FeaturePanel
icon="🎨"
title="Flashcard Creator"
description="Design beautiful soroban flashcards for any purpose"
features={[
'Multiple export formats: PDF, PNG, SVG, HTML',
'Custom bead shapes, colors, and layouts',
'All paper sizes: A3, A4, A5, US Letter',
]}
ctaText="Create Flashcards →"
ctaHref="/create"
accentColor="blue"
/>
</div>
{/* For Kids & Families Section */}
<section className={stack({ gap: '6', mb: '16' })}>
<div className={css({ textAlign: 'center' })}>
<h2
className={css({
fontSize: { base: '2xl', md: '3xl' },
fontWeight: 'bold',
color: 'white',
mb: '2',
})}
>
For Kids & Families
</h2>
<p className={css({ color: 'gray.400', fontSize: 'md', maxW: '2xl', mx: 'auto' })}>
Simple enough for kids to start on their own, structured enough for parents to trust
</p>
</div>
<div className={grid({ columns: { base: 1, lg: 2 }, gap: '8' })}>
<FeaturePanel
icon="🧒"
title="Self-Directed for Children"
features={[
'Big, obvious buttons and clear instructions',
'Progress at your own pace',
'Works with or without a physical abacus',
]}
accentColor="purple"
/>
<FeaturePanel
icon="👨‍👩‍👧"
title="Trusted by Parents"
features={[
'Structured curriculum following Japanese methods',
'Traditional kyu/dan progression levels',
'Track progress and celebrate achievements',
]}
accentColor="blue"
/>
</div>
</section>
{/* Progression Visualization */}
<section className={stack({ gap: '6', mb: '16' })}>
<div className={css({ textAlign: 'center' })}>
<h2
className={css({
fontSize: { base: '2xl', md: '3xl' },
fontWeight: 'bold',
color: 'white',
mb: '2',
})}
>
Your Journey
</h2>
<p style={{ color: '#e5e7eb', fontSize: '16px' }}>Progress from beginner to master</p>
</div>
<div
className={css({
bg: 'rgba(0, 0, 0, 0.4)',
border: '1px solid',
borderColor: 'gray.700',
rounded: 'xl',
p: '8',
})}
>
<div
className={css({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
gap: '4',
flexWrap: 'wrap',
})}
>
{(
[
{ level: '10 Kyu', label: 'Beginner', color: 'colors.green.400' },
{ level: '5 Kyu', label: 'Intermediate', color: 'colors.blue.400' },
{ level: '1 Kyu', label: 'Advanced', color: 'colors.violet.400' },
{ level: 'Dan', label: 'Master', color: 'colors.amber.400' },
] as const
).map((stage, i) => (
<div
key={i}
className={stack({
gap: '2',
textAlign: 'center',
flex: '1',
position: 'relative',
})}
>
<div
className={css({
fontSize: 'xl',
fontWeight: 'bold',
})}
style={{ color: token(stage.color) }}
>
{stage.level}
</div>
<div
className={css({
fontSize: 'sm',
color: 'gray.300',
})}
>
{stage.label}
</div>
{i < 3 && (
<div
style={{
position: 'absolute',
left: '100%',
marginLeft: '0.5rem',
top: '50%',
transform: 'translate(-50%, -50%)',
fontSize: '20px',
color: '#9ca3af',
}}
className={css({
display: { base: 'none', md: 'block' },
})}
>
</div>
)}
</div>
))}
</div>
<div
style={{
textAlign: 'center',
fontSize: '14px',
color: '#d1d5db',
fontStyle: 'italic',
}}
className={css({
mt: '6',
})}
>
You'll progress through all these levels eventually
</div>
</div>
</section>
{/* Additional Tools Section */}
<section className={stack({ gap: '6' })}>
<div className={css({ textAlign: 'center' })}>
<h2
className={css({
fontSize: { base: '2xl', md: '3xl' },
fontWeight: 'bold',
color: 'white',
mb: '2',
})}
>
Additional Tools
</h2>
</div>
<div className={grid({ columns: { base: 1, lg: 2 }, gap: '8' })}>
<FeaturePanel
icon="🎨"
title="Flashcard Creator"
features={[
'Multiple formats: PDF, PNG, SVG, HTML',
'Custom bead shapes, colors, and layouts',
'All paper sizes: A3, A4, A5, US Letter',
]}
accentColor="blue"
ctaText="Create Flashcards →"
ctaHref="/create"
/>
<FeaturePanel
icon="🧮"
title="Interactive Abacus"
features={[
'Practice anytime in your browser',
'Multiple color schemes and bead styles',
'Sound effects and animations',
]}
accentColor="purple"
ctaText="Try the Abacus →"
ctaHref="/guide"
/>
</div>
</section>
</div>
</div>
</PageWithNav>
)
}
function ColorSchemeCard({
title,
description,
colorScheme,
value,
beadShape,
}: {
title: string
description: string
colorScheme: 'monochrome' | 'place-value' | 'heaven-earth' | 'alternating'
value: number
beadShape: 'diamond' | 'circle' | 'square'
}) {
return (
<div
className={css({
bg: 'rgba(0, 0, 0, 0.4)',
rounded: 'xl',
p: '6',
border: '2px solid',
borderColor: 'gray.700',
transition: 'all 0.3s ease',
_hover: {
borderColor: 'purple.500',
transform: 'translateY(-4px)',
boxShadow: '0 20px 40px rgba(139, 92, 246, 0.2)',
},
})}
>
<h3
className={css({
fontSize: 'lg',
fontWeight: 'bold',
color: 'white',
mb: '2',
})}
>
{title}
</h3>
<p className={css({ fontSize: 'sm', color: 'gray.400', mb: '4' })}>{description}</p>
<div
className={css({
display: 'flex',
justifyContent: 'center',
p: '4',
bg: 'rgba(255, 255, 255, 0.05)',
rounded: 'lg',
})}
>
<AbacusReact
value={value}
columns={3}
beadShape={beadShape}
colorScheme={colorScheme}
hideInactiveBeads={false}
/>
</div>
</div>
)
}
function GameCard({
icon,
title,
@@ -400,19 +758,17 @@ function GameCard({
function FeaturePanel({
icon,
title,
description,
features,
accentColor,
ctaText,
ctaHref,
accentColor,
}: {
icon: string
title: string
description: string
features: string[]
ctaText: string
ctaHref: string
accentColor: 'purple' | 'blue'
ctaText?: string
ctaHref?: string
}) {
const borderColor = accentColor === 'purple' ? 'purple.500/30' : 'blue.500/30'
const bgColor = accentColor === 'purple' ? 'purple.500/10' : 'blue.500/10'
@@ -432,8 +788,7 @@ function FeaturePanel({
<span className={css({ fontSize: '3xl' })}>{icon}</span>
<h2 className={css({ fontSize: '2xl', fontWeight: 'bold', color: 'white' })}>{title}</h2>
</div>
<p className={css({ fontSize: 'md', color: 'gray.300', mb: '6' })}>{description}</p>
<div className={stack({ gap: '3', mb: '6' })}>
<div className={stack({ gap: '3', mb: ctaText ? '6' : '0' })}>
{features.map((feature, i) => (
<div key={i} className={hstack({ gap: '3' })}>
<span className={css({ color: 'yellow.400', fontSize: 'lg' })}></span>
@@ -441,25 +796,27 @@ function FeaturePanel({
</div>
))}
</div>
<Link
href={ctaHref}
className={css({
display: 'block',
textAlign: 'center',
py: '3',
px: '6',
bg: bgColor,
color: 'white',
fontWeight: 'bold',
rounded: 'lg',
border: '2px solid',
borderColor,
_hover: { bg: hoverBg },
transition: 'all 0.2s ease',
})}
>
{ctaText}
</Link>
{ctaText && ctaHref && (
<Link
href={ctaHref}
className={css({
display: 'block',
textAlign: 'center',
py: '3',
px: '6',
bg: bgColor,
color: 'white',
fontWeight: 'bold',
rounded: 'lg',
border: '2px solid',
borderColor,
_hover: { bg: hoverBg },
transition: 'all 0.2s ease',
})}
>
{ctaText}
</Link>
)}
</div>
)
}

View File

@@ -215,6 +215,11 @@ interface TutorialPlayerProps {
initialStepIndex?: number
isDebugMode?: boolean
showDebugPanel?: boolean
hideNavigation?: boolean
hideTooltip?: boolean
silentErrors?: boolean
abacusColumns?: number
theme?: 'light' | 'dark'
onStepChange?: (stepIndex: number, step: TutorialStep) => void
onStepComplete?: (stepIndex: number, step: TutorialStep, success: boolean) => void
onTutorialComplete?: (score: number, timeSpent: number) => void
@@ -227,6 +232,11 @@ function TutorialPlayerContent({
initialStepIndex = 0,
isDebugMode = false,
showDebugPanel = false,
hideNavigation = false,
hideTooltip = false,
silentErrors = false,
abacusColumns = 5,
theme = 'light',
onStepChange,
onStepComplete,
onTutorialComplete,
@@ -379,16 +389,20 @@ function TutorialPlayerContent({
}
// Convert bead diff results to StepBeadHighlight format expected by AbacusReact
const stepBeadHighlights: StepBeadHighlight[] = beadDiff.changes.map((change, _index) => ({
placeValue: change.placeValue,
beadType: change.beadType,
position: change.position,
direction: change.direction,
stepIndex: currentMultiStep, // Use current multi-step index to match AbacusReact filtering
order: change.order,
}))
// Filter to only include beads from columns that exist
const minValidPlaceValue = Math.max(0, 5 - abacusColumns)
const stepBeadHighlights: StepBeadHighlight[] = beadDiff.changes
.filter((change) => change.placeValue < abacusColumns)
.map((change, _index) => ({
placeValue: change.placeValue,
beadType: change.beadType,
position: change.position,
direction: change.direction,
stepIndex: currentMultiStep, // Use current multi-step index to match AbacusReact filtering
order: change.order,
}))
return stepBeadHighlights
return stepBeadHighlights.length > 0 ? stepBeadHighlights : undefined
} catch (error) {
console.error('Error generating step beads with bead diff:', error)
return undefined
@@ -399,6 +413,7 @@ function TutorialPlayerContent({
expectedSteps,
currentMultiStep,
currentStep.stepBeadHighlights,
abacusColumns,
])
// Get the current step's bead diff summary for real-time user feedback
@@ -422,6 +437,14 @@ function TutorialPlayerContent({
// Get current step summary for real-time user feedback
const currentStepSummary = getCurrentStepSummary()
// Filter highlightBeads to only include valid columns
const filteredHighlightBeads = useMemo(() => {
if (!currentStep.highlightBeads) return undefined
return currentStep.highlightBeads.filter((highlight) => {
return highlight.placeValue < abacusColumns
})
}, [currentStep.highlightBeads, abacusColumns])
// Helper function to highlight the current mathematical term in the full decomposition
const renderHighlightedDecomposition = useCallback(() => {
if (!fullDecomposition || expectedSteps.length === 0) return null
@@ -519,16 +542,27 @@ function TutorialPlayerContent({
return null
}
// Validate that the bead is from a column that exists
if (topmostBead.placeValue >= abacusColumns) {
// Bead is from an invalid column, skip tooltip
return null
}
// Smart positioning logic: avoid covering active beads
const targetColumnIndex = 4 - topmostBead.placeValue // Convert placeValue to columnIndex (5 columns: 0-4)
// Convert placeValue to columnIndex based on actual number of columns
const targetColumnIndex = abacusColumns - 1 - topmostBead.placeValue
// Check if there are any active beads (against reckoning bar OR with arrows) in columns to the left
const hasActiveBeadsToLeft = (() => {
// Get current abacus state - we need to check which beads are against the reckoning bar
const abacusDigits = currentValue.toString().padStart(5, '0').split('').map(Number)
const abacusDigits = currentValue
.toString()
.padStart(abacusColumns, '0')
.split('')
.map(Number)
for (let col = 0; col < targetColumnIndex; col++) {
const _placeValue = 4 - col // Convert columnIndex back to placeValue
const placeValue = abacusColumns - 1 - col // Convert columnIndex back to placeValue
const digitValue = abacusDigits[col]
// Check if any beads are active (against reckoning bar) in this column
@@ -544,7 +578,7 @@ function TutorialPlayerContent({
// Also check if this column has beads with direction arrows (from current step)
const hasArrowsInColumn =
currentStepBeads?.some((bead) => {
const beadColumnIndex = 4 - bead.placeValue
const beadColumnIndex = abacusColumns - 1 - bead.placeValue
return beadColumnIndex === col && bead.direction && bead.direction !== 'none'
}) ?? false
if (hasArrowsInColumn) {
@@ -606,7 +640,7 @@ function TutorialPlayerContent({
maxWidth: '200px',
minWidth: '150px',
wordBreak: 'break-word',
zIndex: 1000,
zIndex: 50,
opacity: 0.95,
transition: 'all 0.3s ease',
transform: showCelebration ? 'scale(1.05)' : 'scale(1)',
@@ -670,6 +704,7 @@ function TutorialPlayerContent({
currentValue,
currentStep,
isMeaningfulDecomposition,
abacusColumns,
])
// Timer for smart help detection
@@ -864,8 +899,8 @@ function TutorialPlayerContent({
// Check if this is the correct action
if (currentStep.highlightBeads && Array.isArray(currentStep.highlightBeads)) {
const isCorrectBead = currentStep.highlightBeads.some((highlight) => {
// Get place value from highlight (convert columnIndex to placeValue if needed)
const highlightPlaceValue = highlight.placeValue ?? 4 - highlight.columnIndex
// Get place value from highlight
const highlightPlaceValue = highlight.placeValue
// Get place value from bead click event
const beadPlaceValue = beadInfo.bead ? beadInfo.bead.placeValue : 4 - beadInfo.columnIndex
@@ -876,10 +911,11 @@ function TutorialPlayerContent({
)
})
if (!isCorrectBead) {
if (!isCorrectBead && !silentErrors) {
const errorMessage = "That's not the highlighted bead. Try clicking the highlighted bead."
dispatch({
type: 'SET_ERROR',
error: currentStep.errorMessages.wrongBead,
error: errorMessage,
})
dispatch({
@@ -887,7 +923,7 @@ function TutorialPlayerContent({
event: {
type: 'ERROR_OCCURRED',
stepId: currentStep.id,
error: currentStep.errorMessages.wrongBead,
error: errorMessage,
timestamp: new Date(),
},
})
@@ -1003,14 +1039,21 @@ function TutorialPlayerContent({
// Memoize custom styles calculation to avoid expensive recalculation on every render
const customStyles = useMemo(() => {
// Calculate valid column range based on abacusColumns
const minValidColumn = 5 - abacusColumns
// Start with static highlights from step configuration
const staticHighlights: Record<number, any> = {}
if (currentStep.highlightBeads && Array.isArray(currentStep.highlightBeads)) {
currentStep.highlightBeads.forEach((highlight) => {
// Convert placeValue to columnIndex for AbacusReact compatibility
const columnIndex =
highlight.placeValue !== undefined ? 4 - highlight.placeValue : highlight.columnIndex
const columnIndex = abacusColumns - 1 - highlight.placeValue
// Skip highlights for columns that don't exist
if (columnIndex < minValidColumn) {
return
}
// Initialize column if it doesn't exist
if (!staticHighlights[columnIndex]) {
@@ -1041,6 +1084,12 @@ function TutorialPlayerContent({
const mergedHighlights = { ...staticHighlights }
Object.keys(dynamicColumnHighlights).forEach((columnIndexStr) => {
const columnIndex = parseInt(columnIndexStr, 10)
// Skip highlights for columns that don't exist
if (columnIndex < minValidColumn) {
return
}
if (!mergedHighlights[columnIndex]) {
mergedHighlights[columnIndex] = {}
}
@@ -1048,8 +1097,32 @@ function TutorialPlayerContent({
Object.assign(mergedHighlights[columnIndex], dynamicColumnHighlights[columnIndex])
})
return Object.keys(mergedHighlights).length > 0 ? { columns: mergedHighlights } : undefined
}, [currentStep.highlightBeads, dynamicColumnHighlights])
// Build the custom styles object
const styles: any = {}
// Add column highlights if any
if (Object.keys(mergedHighlights).length > 0) {
styles.columns = mergedHighlights
}
// Add frame styling for dark mode
if (theme === 'dark') {
// Column dividers (global for all columns)
styles.columnPosts = {
fill: 'rgba(255, 255, 255, 0.3)', // High contrast fill for visibility
stroke: 'rgba(255, 255, 255, 0.2)',
strokeWidth: 2,
}
// Reckoning bar (horizontal middle bar)
styles.reckoningBar = {
fill: 'rgba(255, 255, 255, 0.4)', // High contrast fill for visibility
stroke: 'rgba(255, 255, 255, 0.25)',
strokeWidth: 3,
}
}
return Object.keys(styles).length > 0 ? styles : undefined
}, [currentStep.highlightBeads, dynamicColumnHighlights, abacusColumns, theme])
if (!currentStep) {
return <div>No steps available</div>
@@ -1061,183 +1134,187 @@ function TutorialPlayerContent({
display: 'flex',
flexDirection: 'column',
height: '100%',
minHeight: '600px',
minHeight: hideNavigation ? 'auto' : '600px',
})} ${className || ''}`}
>
{/* Header */}
<div
className={css({
borderBottom: '1px solid',
borderColor: 'gray.200',
p: 4,
bg: 'white',
})}
>
{!hideNavigation && (
<div
className={hstack({
justifyContent: 'space-between',
alignItems: 'center',
className={css({
borderBottom: '1px solid',
borderColor: theme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'gray.200',
p: 4,
bg: theme === 'dark' ? 'rgba(30, 30, 40, 0.6)' : 'white',
})}
>
<div>
<h1 className={css({ fontSize: 'xl', fontWeight: 'bold' })}>{tutorial.title}</h1>
<p className={css({ fontSize: 'sm', color: 'gray.600' })}>
Step {currentStepIndex + 1} of {tutorial.steps.length}: {currentStep.title}
</p>
</div>
<div className={hstack({ gap: 2 })}>
{isDebugMode && (
<>
<button
onClick={toggleDebugPanel}
className={css({
px: 3,
py: 1,
fontSize: 'sm',
border: '1px solid',
borderColor: 'blue.300',
borderRadius: 'md',
bg: uiState.showDebugPanel ? 'blue.100' : 'white',
color: 'blue.700',
cursor: 'pointer',
_hover: { bg: 'blue.50' },
})}
>
Debug
</button>
<button
onClick={toggleStepList}
className={css({
px: 3,
py: 1,
fontSize: 'sm',
border: '1px solid',
borderColor: 'gray.300',
borderRadius: 'md',
bg: uiState.showStepList ? 'gray.100' : 'white',
cursor: 'pointer',
_hover: { bg: 'gray.50' },
})}
>
Steps
</button>
{/* Multi-step navigation controls */}
{currentStep.multiStepInstructions &&
currentStep.multiStepInstructions.length > 1 && (
<>
<div
className={css({
fontSize: 'xs',
color: 'gray.600',
px: 2,
borderLeft: '1px solid',
borderColor: 'gray.300',
ml: 2,
pl: 3,
})}
>
Multi-Step: {currentMultiStep + 1} /{' '}
{currentStep.multiStepInstructions.length}
</div>
<button
onClick={() => dispatch({ type: 'RESET_MULTI_STEP' })}
disabled={currentMultiStep === 0}
className={css({
px: 2,
py: 1,
fontSize: 'xs',
border: '1px solid',
borderColor: currentMultiStep === 0 ? 'gray.200' : 'orange.300',
borderRadius: 'md',
bg: currentMultiStep === 0 ? 'gray.100' : 'white',
color: currentMultiStep === 0 ? 'gray.400' : 'orange.700',
cursor: currentMultiStep === 0 ? 'not-allowed' : 'pointer',
_hover: currentMultiStep === 0 ? {} : { bg: 'orange.50' },
})}
>
First
</button>
<button
onClick={() => previousMultiStep()}
disabled={currentMultiStep === 0}
className={css({
px: 2,
py: 1,
fontSize: 'xs',
border: '1px solid',
borderColor: currentMultiStep === 0 ? 'gray.200' : 'orange.300',
borderRadius: 'md',
bg: currentMultiStep === 0 ? 'gray.100' : 'white',
color: currentMultiStep === 0 ? 'gray.400' : 'orange.700',
cursor: currentMultiStep === 0 ? 'not-allowed' : 'pointer',
_hover: currentMultiStep === 0 ? {} : { bg: 'orange.50' },
})}
>
Prev
</button>
<button
onClick={() => advanceMultiStep()}
disabled={currentMultiStep >= currentStep.multiStepInstructions.length - 1}
className={css({
px: 2,
py: 1,
fontSize: 'xs',
border: '1px solid',
borderColor:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'gray.200'
: 'green.300',
borderRadius: 'md',
bg:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'gray.100'
: 'white',
color:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'gray.400'
: 'green.700',
cursor:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'not-allowed'
: 'pointer',
_hover:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? {}
: { bg: 'green.50' },
})}
>
Next
</button>
</>
)}
<label className={hstack({ gap: 2, fontSize: 'sm' })}>
<input
type="checkbox"
checked={uiState.autoAdvance}
onChange={toggleAutoAdvance}
/>
Auto-advance
</label>
</>
)}
</div>
</div>
{/* Progress bar */}
<div className={css({ mt: 2, bg: 'gray.200', borderRadius: 'full', h: 2 })}>
<div
className={css({
bg: 'blue.500',
h: 'full',
borderRadius: 'full',
transition: 'width 0.3s ease',
className={hstack({
justifyContent: 'space-between',
alignItems: 'center',
})}
style={{ width: `${navigationState.completionPercentage}%` }}
/>
>
<div>
<h1 className={css({ fontSize: 'xl', fontWeight: 'bold' })}>{tutorial.title}</h1>
<p className={css({ fontSize: 'sm', color: 'gray.600' })}>
Step {currentStepIndex + 1} of {tutorial.steps.length}: {currentStep.title}
</p>
</div>
<div className={hstack({ gap: 2 })}>
{isDebugMode && (
<>
<button
onClick={toggleDebugPanel}
className={css({
px: 3,
py: 1,
fontSize: 'sm',
border: '1px solid',
borderColor: 'blue.300',
borderRadius: 'md',
bg: uiState.showDebugPanel ? 'blue.100' : 'white',
color: 'blue.700',
cursor: 'pointer',
_hover: { bg: 'blue.50' },
})}
>
Debug
</button>
<button
onClick={toggleStepList}
className={css({
px: 3,
py: 1,
fontSize: 'sm',
border: '1px solid',
borderColor: 'gray.300',
borderRadius: 'md',
bg: uiState.showStepList ? 'gray.100' : 'white',
cursor: 'pointer',
_hover: { bg: 'gray.50' },
})}
>
Steps
</button>
{/* Multi-step navigation controls */}
{currentStep.multiStepInstructions &&
currentStep.multiStepInstructions.length > 1 && (
<>
<div
className={css({
fontSize: 'xs',
color: 'gray.600',
px: 2,
borderLeft: '1px solid',
borderColor: 'gray.300',
ml: 2,
pl: 3,
})}
>
Multi-Step: {currentMultiStep + 1} /{' '}
{currentStep.multiStepInstructions.length}
</div>
<button
onClick={() => dispatch({ type: 'RESET_MULTI_STEP' })}
disabled={currentMultiStep === 0}
className={css({
px: 2,
py: 1,
fontSize: 'xs',
border: '1px solid',
borderColor: currentMultiStep === 0 ? 'gray.200' : 'orange.300',
borderRadius: 'md',
bg: currentMultiStep === 0 ? 'gray.100' : 'white',
color: currentMultiStep === 0 ? 'gray.400' : 'orange.700',
cursor: currentMultiStep === 0 ? 'not-allowed' : 'pointer',
_hover: currentMultiStep === 0 ? {} : { bg: 'orange.50' },
})}
>
First
</button>
<button
onClick={() => previousMultiStep()}
disabled={currentMultiStep === 0}
className={css({
px: 2,
py: 1,
fontSize: 'xs',
border: '1px solid',
borderColor: currentMultiStep === 0 ? 'gray.200' : 'orange.300',
borderRadius: 'md',
bg: currentMultiStep === 0 ? 'gray.100' : 'white',
color: currentMultiStep === 0 ? 'gray.400' : 'orange.700',
cursor: currentMultiStep === 0 ? 'not-allowed' : 'pointer',
_hover: currentMultiStep === 0 ? {} : { bg: 'orange.50' },
})}
>
Prev
</button>
<button
onClick={() => advanceMultiStep()}
disabled={
currentMultiStep >= currentStep.multiStepInstructions.length - 1
}
className={css({
px: 2,
py: 1,
fontSize: 'xs',
border: '1px solid',
borderColor:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'gray.200'
: 'green.300',
borderRadius: 'md',
bg:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'gray.100'
: 'white',
color:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'gray.400'
: 'green.700',
cursor:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? 'not-allowed'
: 'pointer',
_hover:
currentMultiStep >= currentStep.multiStepInstructions.length - 1
? {}
: { bg: 'green.50' },
})}
>
Next
</button>
</>
)}
<label className={hstack({ gap: 2, fontSize: 'sm' })}>
<input
type="checkbox"
checked={uiState.autoAdvance}
onChange={toggleAutoAdvance}
/>
Auto-advance
</label>
</>
)}
</div>
</div>
{/* Progress bar */}
<div className={css({ mt: 2, bg: 'gray.200', borderRadius: 'full', h: 2 })}>
<div
className={css({
bg: 'blue.500',
h: 'full',
borderRadius: 'full',
transition: 'width 0.3s ease',
})}
style={{ width: `${navigationState.completionPercentage}%` }}
/>
</div>
</div>
</div>
)}
<div className={hstack({ flex: 1, gap: 0 })}>
{/* Step list sidebar */}
@@ -1313,11 +1390,18 @@ function TutorialPlayerContent({
fontSize: '2xl',
fontWeight: 'bold',
mb: 2,
color: theme === 'dark' ? 'gray.200' : 'gray.900',
})}
>
{currentStep.problem}
</h2>
<p className={css({ fontSize: 'lg', color: 'gray.700', mb: 4 })}>
<p
className={css({
fontSize: 'lg',
color: theme === 'dark' ? 'gray.400' : 'gray.700',
mb: 4,
})}
>
{currentStep.description}
</p>
{/* Hide action description for multi-step problems since it duplicates pedagogical decomposition */}
@@ -1329,18 +1413,26 @@ function TutorialPlayerContent({
</div>
{/* Multi-step instructions panel */}
{currentStep.multiStepInstructions &&
{!hideTooltip &&
currentStep.multiStepInstructions &&
currentStep.multiStepInstructions.length > 0 && (
<div
className={css({
p: 5,
background:
'linear-gradient(135deg, rgba(255,248,225,0.95) 0%, rgba(254,252,232,0.95) 50%, rgba(255,245,157,0.15) 100%)',
theme === 'dark'
? 'linear-gradient(135deg, rgba(40,40,50,0.6) 0%, rgba(50,50,60,0.6) 50%, rgba(60,50,70,0.3) 100%)'
: 'linear-gradient(135deg, rgba(255,248,225,0.95) 0%, rgba(254,252,232,0.95) 50%, rgba(255,245,157,0.15) 100%)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(251,191,36,0.3)',
border:
theme === 'dark'
? '1px solid rgba(255,255,255,0.1)'
: '1px solid rgba(251,191,36,0.3)',
borderRadius: 'xl',
boxShadow:
'0 8px 32px rgba(251,191,36,0.1), 0 2px 8px rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.6)',
theme === 'dark'
? '0 4px 16px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.05)'
: '0 8px 32px rgba(251,191,36,0.1), 0 2px 8px rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.6)',
position: 'relative',
maxW: '600px',
w: 'full',
@@ -1350,7 +1442,9 @@ function TutorialPlayerContent({
inset: '0',
borderRadius: 'xl',
background:
'linear-gradient(135deg, rgba(251,191,36,0.1) 0%, rgba(168,85,247,0.05) 100%)',
theme === 'dark'
? 'linear-gradient(135deg, rgba(100,100,120,0.1) 0%, rgba(80,60,100,0.05) 100%)'
: 'linear-gradient(135deg, rgba(251,191,36,0.1) 0%, rgba(168,85,247,0.05) 100%)',
zIndex: -1,
},
})}
@@ -1359,10 +1453,10 @@ function TutorialPlayerContent({
className={css({
fontSize: 'base',
fontWeight: '600',
color: 'amber.900',
color: theme === 'dark' ? 'gray.300' : 'amber.900',
mb: 4,
letterSpacing: 'wide',
textShadow: '0 1px 2px rgba(0,0,0,0.1)',
textShadow: theme === 'dark' ? 'none' : '0 1px 2px rgba(0,0,0,0.1)',
})}
>
Guidance
@@ -1375,18 +1469,25 @@ function TutorialPlayerContent({
mb: 4,
p: 3,
background:
'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.9) 100%)',
border: '1px solid rgba(203,213,225,0.4)',
theme === 'dark'
? 'linear-gradient(135deg, rgba(50,50,60,0.4) 0%, rgba(40,40,50,0.5) 100%)'
: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.9) 100%)',
border:
theme === 'dark'
? '1px solid rgba(255,255,255,0.1)'
: '1px solid rgba(203,213,225,0.4)',
borderRadius: 'lg',
boxShadow:
'0 2px 8px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.7)',
theme === 'dark'
? '0 1px 4px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.05)'
: '0 2px 8px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.7)',
backdropFilter: 'blur(4px)',
})}
>
<p
<div
className={css({
fontSize: 'base',
color: 'slate.800',
color: theme === 'dark' ? 'gray.300' : 'slate.800',
fontFamily: 'mono',
fontWeight: '500',
letterSpacing: 'tight',
@@ -1398,14 +1499,14 @@ function TutorialPlayerContent({
termPositions={termPositions}
segments={pedagogicalSegments}
/>
</p>
</div>
</div>
)}
<div
className={css({
fontSize: 'sm',
color: 'amber.800',
color: theme === 'dark' ? 'gray.400' : 'amber.800',
fontWeight: '500',
lineHeight: '1.6',
})}
@@ -1449,7 +1550,10 @@ function TutorialPlayerContent({
className={css({
mb: 1,
fontWeight: 'bold',
color: 'yellow.900',
color: theme === 'dark' ? 'yellow.200' : 'yellow.900',
textShadow:
theme === 'dark' ? '0 0 12px rgba(251, 191, 36, 0.4)' : 'none',
fontSize: theme === 'dark' ? 'lg' : 'base',
})}
>
{currentInstruction}
@@ -1483,17 +1587,17 @@ function TutorialPlayerContent({
{/* Abacus */}
<div
className={css({
bg: 'white',
bg: theme === 'dark' ? 'rgba(30, 30, 40, 0.4)' : 'white',
border: '2px solid',
borderColor: 'gray.200',
borderColor: theme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'gray.200',
borderRadius: 'lg',
p: 6,
shadow: 'lg',
shadow: theme === 'dark' ? '0 4px 6px rgba(0, 0, 0, 0.3)' : 'lg',
})}
>
<AbacusReact
value={currentValue}
columns={5}
columns={abacusColumns}
interactive={true}
animated={true}
scaleFactor={2.5}
@@ -1502,7 +1606,7 @@ function TutorialPlayerContent({
hideInactiveBeads={abacusConfig.hideInactiveBeads}
soundEnabled={abacusConfig.soundEnabled}
soundVolume={abacusConfig.soundVolume}
highlightBeads={currentStep.highlightBeads}
highlightBeads={filteredHighlightBeads}
stepBeadHighlights={currentStepBeads}
currentStep={currentMultiStep}
showDirectionIndicators={true}
@@ -1567,7 +1671,7 @@ function TutorialPlayerContent({
</div>
{/* Tooltip */}
{currentStep.tooltip && (
{!hideTooltip && currentStep.tooltip && (
<div
className={css({
maxW: '500px',
@@ -1596,57 +1700,60 @@ function TutorialPlayerContent({
</div>
{/* Navigation controls */}
<div
className={css({
borderTop: '1px solid',
borderColor: 'gray.200',
p: 4,
bg: 'gray.50',
})}
>
<div className={hstack({ justifyContent: 'space-between' })}>
<button
onClick={goToPreviousStep}
disabled={!navigationState.canGoPrevious}
className={css({
px: 4,
py: 2,
border: '1px solid',
borderColor: 'gray.300',
borderRadius: 'md',
bg: 'white',
cursor: navigationState.canGoPrevious ? 'pointer' : 'not-allowed',
opacity: navigationState.canGoPrevious ? 1 : 0.5,
_hover: navigationState.canGoPrevious ? { bg: 'gray.50' } : {},
})}
>
Previous
</button>
{!hideNavigation && (
<div
className={css({
borderTop: '1px solid',
borderColor: 'gray.200',
p: 4,
bg: 'gray.50',
})}
>
<div className={hstack({ justifyContent: 'space-between' })}>
<button
onClick={goToPreviousStep}
disabled={!navigationState.canGoPrevious}
className={css({
px: 4,
py: 2,
border: '1px solid',
borderColor: 'gray.300',
borderRadius: 'md',
bg: 'white',
cursor: navigationState.canGoPrevious ? 'pointer' : 'not-allowed',
opacity: navigationState.canGoPrevious ? 1 : 0.5,
_hover: navigationState.canGoPrevious ? { bg: 'gray.50' } : {},
})}
>
Previous
</button>
<div className={css({ fontSize: 'sm', color: 'gray.600' })}>
Step {currentStepIndex + 1} of {navigationState.totalSteps}
<div className={css({ fontSize: 'sm', color: 'gray.600' })}>
Step {currentStepIndex + 1} of {navigationState.totalSteps}
</div>
<button
onClick={goToNextStep}
disabled={!navigationState.canGoNext && !isStepCompleted}
className={css({
px: 4,
py: 2,
border: '1px solid',
borderColor:
navigationState.canGoNext || isStepCompleted ? 'blue.300' : 'gray.300',
borderRadius: 'md',
bg: navigationState.canGoNext || isStepCompleted ? 'blue.500' : 'gray.200',
color: navigationState.canGoNext || isStepCompleted ? 'white' : 'gray.500',
cursor:
navigationState.canGoNext || isStepCompleted ? 'pointer' : 'not-allowed',
_hover: navigationState.canGoNext || isStepCompleted ? { bg: 'blue.600' } : {},
})}
>
{navigationState.canGoNext ? 'Next →' : 'Complete Tutorial'}
</button>
</div>
<button
onClick={goToNextStep}
disabled={!navigationState.canGoNext && !isStepCompleted}
className={css({
px: 4,
py: 2,
border: '1px solid',
borderColor:
navigationState.canGoNext || isStepCompleted ? 'blue.300' : 'gray.300',
borderRadius: 'md',
bg: navigationState.canGoNext || isStepCompleted ? 'blue.500' : 'gray.200',
color: navigationState.canGoNext || isStepCompleted ? 'white' : 'gray.500',
cursor: navigationState.canGoNext || isStepCompleted ? 'pointer' : 'not-allowed',
_hover: navigationState.canGoNext || isStepCompleted ? { bg: 'blue.600' } : {},
})}
>
{navigationState.canGoNext ? 'Next →' : 'Complete Tutorial'}
</button>
</div>
</div>
)}
</div>
{/* Debug panel */}

View File

@@ -21,52 +21,52 @@ export const GAME_THEMES = {
blue: {
color: 'blue',
gradient: 'linear-gradient(135deg, #dbeafe, #bfdbfe)', // blue.100 to blue.200
borderColor: 'blue.200',
borderColor: '#bfdbfe', // blue.200
},
purple: {
color: 'purple',
gradient: 'linear-gradient(135deg, #e9d5ff, #ddd6fe)', // purple.100 to purple.200
borderColor: 'purple.200',
borderColor: '#ddd6fe', // purple.200
},
green: {
color: 'green',
gradient: 'linear-gradient(135deg, #d1fae5, #a7f3d0)', // green.100 to green.200
borderColor: 'green.200',
borderColor: '#a7f3d0', // green.200
},
teal: {
color: 'teal',
gradient: 'linear-gradient(135deg, #ccfbf1, #99f6e4)', // teal.100 to teal.200
borderColor: 'teal.200',
borderColor: '#99f6e4', // teal.200
},
indigo: {
color: 'indigo',
gradient: 'linear-gradient(135deg, #e0e7ff, #c7d2fe)', // indigo.100 to indigo.200
borderColor: 'indigo.200',
borderColor: '#c7d2fe', // indigo.200
},
pink: {
color: 'pink',
gradient: 'linear-gradient(135deg, #fce7f3, #fbcfe8)', // pink.100 to pink.200
borderColor: 'pink.200',
borderColor: '#fbcfe8', // pink.200
},
orange: {
color: 'orange',
gradient: 'linear-gradient(135deg, #ffedd5, #fed7aa)', // orange.100 to orange.200
borderColor: 'orange.200',
borderColor: '#fed7aa', // orange.200
},
yellow: {
color: 'yellow',
gradient: 'linear-gradient(135deg, #fef3c7, #fde68a)', // yellow.100 to yellow.200
borderColor: 'yellow.200',
borderColor: '#fde68a', // yellow.200
},
red: {
color: 'red',
gradient: 'linear-gradient(135deg, #fee2e2, #fecaca)', // red.100 to red.200
borderColor: 'red.200',
borderColor: '#fecaca', // red.200
},
gray: {
color: 'gray',
gradient: 'linear-gradient(135deg, #f3f4f6, #e5e7eb)', // gray.100 to gray.200
borderColor: 'gray.200',
borderColor: '#e5e7eb', // gray.200
},
} as const satisfies Record<string, GameTheme>

View File

@@ -1,6 +1,6 @@
{
"name": "soroban-monorepo",
"version": "4.14.6",
"version": "4.24.1",
"private": true,
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
"workspaces": [

View File

@@ -27,6 +27,7 @@ export interface BeadStyle {
}
export interface ColumnPostStyle {
fill?: string;
stroke?: string;
strokeWidth?: number;
opacity?: number;
@@ -34,6 +35,7 @@ export interface ColumnPostStyle {
}
export interface ReckoningBarStyle {
fill?: string;
stroke?: string;
strokeWidth?: number;
opacity?: number;
@@ -1979,7 +1981,10 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
const columnStyles = customStyles?.columns?.[colIndex];
const globalColumnPosts = customStyles?.columnPosts;
const rodStyle = {
fill: "rgb(0, 0, 0, 0.1)", // Default Typst color
fill:
columnStyles?.columnPost?.fill ||
globalColumnPosts?.fill ||
"rgb(0, 0, 0, 0.1)", // Default Typst color
stroke:
columnStyles?.columnPost?.stroke ||
globalColumnPosts?.stroke ||
@@ -2017,8 +2022,10 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
(effectiveColumns - 1) * dimensions.rodSpacing + dimensions.beadSize
}
height={dimensions.barThickness}
fill="black" // Typst uses black
stroke="none"
fill={customStyles?.reckoningBar?.fill || "black"} // Typst default is black
stroke={customStyles?.reckoningBar?.stroke || "none"}
strokeWidth={customStyles?.reckoningBar?.strokeWidth ?? 0}
opacity={customStyles?.reckoningBar?.opacity ?? 1}
/>
{/* Beads */}