Compare commits

...

20 Commits

Author SHA1 Message Date
semantic-release-bot
be2c3f63b0 chore(release): 4.44.2 [skip ci]
## [4.44.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.44.1...v4.44.2) (2025-10-20)

### Bug Fixes

* **levels:** match top/bottom margins to left padding on kyu detail boxes ([aa0bdcf](aa0bdcf686))
2025-10-20 16:37:17 +00:00
Thomas Hallock
aa0bdcf686 fix(levels): match top/bottom margins to left padding on kyu detail boxes
- Reduced my (vertical margins) from '6' to '2' to match pl (left padding)
- Ensures consistent spacing on all sides of the detail box grid

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:36:11 -05:00
semantic-release-bot
baea602000 chore(release): 4.44.1 [skip ci]
## [4.44.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.44.0...v4.44.1) (2025-10-20)

### Bug Fixes

* **levels:** add fixed dimensions and margins to kyu detail boxes ([05dd0b3](05dd0b30d3))
2025-10-20 16:35:46 +00:00
Thomas Hallock
05dd0b30d3 fix(levels): add fixed dimensions and margins to kyu detail boxes
- Set fixed width (200px) and height (180px) for operator boxes to prevent shifting
- Add vertical margins (my: 6) to grid container for better spacing
- Ensures boxes stay in consistent positions when sliding through levels
- Large enough to accommodate longest content (11 digits)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:34:32 -05:00
semantic-release-bot
4febf5905b chore(release): 4.44.0 [skip ci]
## [4.44.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.43.2...v4.44.0) (2025-10-20)

### Features

* **levels:** redesign kyu details with larger operators and prominent digits ([6739d59](6739d59f2b))
2025-10-20 16:31:02 +00:00
Thomas Hallock
6739d59f2b feat(levels): redesign kyu details with larger operators and prominent digits
- Increase operator icon size from xl to 4xl for better visibility
- Center-align card layout with operator icon at top
- Extract and prominently display digit count in 2xl bold text
- Change hover effect from slide to scale for centered design
- Increase base font size from sm to md

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:29:47 -05:00
semantic-release-bot
cb20019c16 chore(release): 4.43.2 [skip ci]
## [4.43.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.43.1...v4.43.2) (2025-10-20)

### Code Refactoring

* **levels:** remove Time and Pass sections from kyu details ([d90b5d5](d90b5d5532))
2025-10-20 16:29:27 +00:00
Thomas Hallock
d90b5d5532 refactor(levels): remove Time and Pass sections from kyu details
Remove the Time and Pass requirement cards since we don't have actual tests
implemented. Now only showing Add/Sub, Multiply, and Divide requirements.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:28:19 -05:00
semantic-release-bot
7028db0263 chore(release): 4.43.1 [skip ci]
## [4.43.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.43.0...v4.43.1) (2025-10-20)

### Bug Fixes

* **levels:** use two-column grid for kyu details to prevent clipping ([fa3b73c](fa3b73c691))
2025-10-20 16:28:08 +00:00
Thomas Hallock
fa3b73c691 fix(levels): use two-column grid for kyu details to prevent clipping
Change from single-column flex to two-column grid layout to avoid vertical
overflow. Increased max width to 480px to accommodate the wider layout.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:27:01 -05:00
semantic-release-bot
fd4d25c2d1 chore(release): 4.43.0 [skip ci]
## [4.43.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.42.1...v4.43.0) (2025-10-20)

### Features

* **levels:** add structured kyu exam details with card UI ([6501b07](6501b073b1))
2025-10-20 16:26:37 +00:00
Thomas Hallock
6501b073b1 feat(levels): add structured kyu exam details with card UI
Parse kyu level requirements into structured sections with icons and color-coded
labels. Display in clean card layout with hover effects. Maintain consistent
font sizing across all levels.

Features:
- Parse Japanese exam data into structured sections (Add/Sub, Multiply, Divide, Time, Pass)
- Icon-based visual hierarchy (, ✖️, , ⏱️, )
- Color-coded labels matching level colors
- Card UI with hover effects
- Consistent sizing for better readability

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:25:34 -05:00
semantic-release-bot
9b4d9c21df chore(release): 4.42.1 [skip ci]
## [4.42.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.42.0...v4.42.1) (2025-10-20)

### Code Refactoring

* **levels:** store kyu data verbatim, add formatting layer ([53d23f1](53d23f19bc))
2025-10-20 16:18:49 +00:00
Thomas Hallock
53d23f19bc refactor(levels): store kyu data verbatim, add formatting layer
Store level details exactly as provided from shuzan.jp (with Japanese
characters), then translate and format creatively in the display layer.

Changes:
- Restore verbatim data with 口, 字, 実+法, 法+商, 題
- Add formatKyuDetails() helper to translate on display
- Translate: 口→rows, 字→chars, 実+法→total, 法+商→total, 題→sets
- Use operator symbols: + / −, ×, ÷
- Maintain separation between data source and presentation

This approach keeps the original data intact while allowing creative
freedom in how we display it.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:17:43 -05:00
semantic-release-bot
6c14012b97 chore(release): 4.42.0 [skip ci]
## [4.42.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.41.0...v4.42.0) (2025-10-20)

### Features

* **levels:** add kyu level details display with English translations ([c650ffa](c650ffa193))
2025-10-20 16:16:03 +00:00
Thomas Hallock
c650ffa193 feat(levels): add kyu level details display with English translations
Add comprehensive exam requirements for each Kyu level displayed on
the left side of the abacus pane:

- Create kyuLevelDetails data file with English translations
- Display level details only for Kyu levels (hidden for Dan)
- Implement responsive font sizing based on abacus size
- Center abacus for Dan levels, right-align for Kyu
- Format details in clean, readable layout with bullet points

Details include:
- Addition/Subtraction requirements (rows, characters)
- Multiplication/Division requirements (digit counts, problem counts)
- Exam time limits and passing scores

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:14:50 -05:00
semantic-release-bot
28834e8a3e chore(release): 4.41.0 [skip ci]
## [4.41.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.40.1...v4.41.0) (2025-10-20)

### Features

* **levels:** right-align abacus display ([8681b17](8681b17340))
2025-10-20 16:10:19 +00:00
Thomas Hallock
8681b17340 feat(levels): right-align abacus display
Change abacus display container from center to right-aligned layout for
better visual balance on the levels page.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:09:14 -05:00
semantic-release-bot
d52cc608eb chore(release): 4.40.1 [skip ci]
## [4.40.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.40.0...v4.40.1) (2025-10-20)

### Bug Fixes

* **levels:** increase animation speed to 10ms for 10th Dan ([6f89d9e](6f89d9e274))
2025-10-20 16:00:14 +00:00
Thomas Hallock
6f89d9e274 fix(levels): increase animation speed to 10ms for 10th Dan
Update animation speed progression to be more dramatic, reaching 10ms
(0.01 seconds) at 10th Dan instead of 50ms. Speed progression now runs
from 1st Dan to 10th Dan (not from Pre-1st Dan).

Speed progression:
- Kyu levels: 500ms (constant)
- Pre-1st Dan: 500ms
- 1st Dan: 500ms
- 10th Dan: 10ms
- Linear interpolation between 1st and 10th Dan

This creates an extremely fast blur effect at the highest mastery level,
better representing the extraordinary calculation speed expected at 10th Dan.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 10:58:59 -05:00
4 changed files with 427 additions and 24 deletions

View File

@@ -1,3 +1,73 @@
## [4.44.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.44.1...v4.44.2) (2025-10-20)
### Bug Fixes
* **levels:** match top/bottom margins to left padding on kyu detail boxes ([aa0bdcf](https://github.com/antialias/soroban-abacus-flashcards/commit/aa0bdcf686adcbfd1a145cf67121181d1f1194d9))
## [4.44.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.44.0...v4.44.1) (2025-10-20)
### Bug Fixes
* **levels:** add fixed dimensions and margins to kyu detail boxes ([05dd0b3](https://github.com/antialias/soroban-abacus-flashcards/commit/05dd0b30d3c397b82b7b7cc93a5ea575f3aada6d))
## [4.44.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.43.2...v4.44.0) (2025-10-20)
### Features
* **levels:** redesign kyu details with larger operators and prominent digits ([6739d59](https://github.com/antialias/soroban-abacus-flashcards/commit/6739d59f2b6189a98570e23e04c20d86d774ccce))
## [4.43.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.43.1...v4.43.2) (2025-10-20)
### Code Refactoring
* **levels:** remove Time and Pass sections from kyu details ([d90b5d5](https://github.com/antialias/soroban-abacus-flashcards/commit/d90b5d55322e75dd28b95376614663a506c829d4))
## [4.43.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.43.0...v4.43.1) (2025-10-20)
### Bug Fixes
* **levels:** use two-column grid for kyu details to prevent clipping ([fa3b73c](https://github.com/antialias/soroban-abacus-flashcards/commit/fa3b73c69169b4694201ffa19ae3f8b5a68dfe32))
## [4.43.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.42.1...v4.43.0) (2025-10-20)
### Features
* **levels:** add structured kyu exam details with card UI ([6501b07](https://github.com/antialias/soroban-abacus-flashcards/commit/6501b073b100a00982cff1ca3140921e74f31a9c))
## [4.42.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.42.0...v4.42.1) (2025-10-20)
### Code Refactoring
* **levels:** store kyu data verbatim, add formatting layer ([53d23f1](https://github.com/antialias/soroban-abacus-flashcards/commit/53d23f19bc06459462afb76ed94d9b99d583a32d))
## [4.42.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.41.0...v4.42.0) (2025-10-20)
### Features
* **levels:** add kyu level details display with English translations ([c650ffa](https://github.com/antialias/soroban-abacus-flashcards/commit/c650ffa1935fe370d37190b2843c0deecdcce8e7))
## [4.41.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.40.1...v4.41.0) (2025-10-20)
### Features
* **levels:** right-align abacus display ([8681b17](https://github.com/antialias/soroban-abacus-flashcards/commit/8681b17340e757cf04d17f884a780a251645bb33))
## [4.40.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.40.0...v4.40.1) (2025-10-20)
### Bug Fixes
* **levels:** increase animation speed to 10ms for 10th Dan ([6f89d9e](https://github.com/antialias/soroban-abacus-flashcards/commit/6f89d9e274082908fc090a9c0ba310f2cb06f014))
## [4.40.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.39.1...v4.40.0) (2025-10-20)

View File

@@ -7,6 +7,7 @@ import { AbacusReact, StandaloneBead } from '@soroban/abacus-react'
import { PageWithNav } from '@/components/PageWithNav'
import { css } from '../../../styled-system/css'
import { container, stack } from '../../../styled-system/patterns'
import { kyuLevelDetails } from '@/data/kyuLevelDetails'
// Combine all levels into one array for the slider
const allLevels = [
@@ -181,6 +182,85 @@ const allLevels = [
},
] as const
// Helper function to map level names to kyuLevelDetails keys
function getLevelDetailsKey(levelName: string): string | null {
// Convert "10th Kyu" → "10-kyu", "3rd Kyu" → "3-kyu", etc.
const match = levelName.match(/^(\d+)(?:st|nd|rd|th)\s+Kyu$/)
if (match) {
return `${match[1]}-kyu`
}
return null
}
// Parse and format kyu level details into structured sections with icons
function parseKyuDetails(rawText: string) {
const lines = rawText.split('\n').filter((line) => line.trim() && !line.includes('shuzan.jp'))
// Always return sections in consistent order: Add/Sub, Multiply, Divide
const sections: Array<{
type: 'addSub' | 'multiply' | 'divide'
icon: string
label: string
digits: string | null
rows: string | null
chars: string | null
problems: string | null
}> = [
{
type: 'addSub',
icon: '',
label: 'Add/Sub',
digits: null,
rows: null,
chars: null,
problems: null,
},
{
type: 'multiply',
icon: '✖️',
label: 'Multiply',
digits: null,
rows: null,
chars: null,
problems: null,
},
{
type: 'divide',
icon: '➗',
label: 'Divide',
digits: null,
rows: null,
chars: null,
problems: null,
},
]
for (const line of lines) {
if (line.includes('Add/Sub:')) {
const match = line.match(/(\d+)-digit.*?(\d+)口.*?(\d+)字/)
if (match) {
sections[0].digits = match[1]
sections[0].rows = match[2]
sections[0].chars = match[3]
}
} else if (line.includes('×:')) {
const match = line.match(/(\d+) digits.*?\((\d+)/)
if (match) {
sections[1].digits = match[1]
sections[1].problems = match[2]
}
} else if (line.includes('÷:')) {
const match = line.match(/(\d+) digits.*?\((\d+)/)
if (match) {
sections[2].digits = match[1]
sections[2].problems = match[2]
}
}
}
return sections
}
export default function LevelsPage() {
const [currentIndex, setCurrentIndex] = useState(0)
const [isHovering, setIsHovering] = useState(false)
@@ -202,17 +282,18 @@ export default function LevelsPage() {
useEffect(() => {
// Calculate animation speed based on level
// Kyu levels: 500ms
// Dan levels: interpolate from 500ms (Pre-1st Dan) to 50ms (10th Dan)
// Pre-1st Dan: 500ms
// 1st-10th Dan: interpolate from 500ms to 10ms
const getAnimationInterval = () => {
if (currentIndex < 10) {
// Kyu levels: constant 500ms
if (currentIndex < 11) {
// Kyu levels and Pre-1st Dan: constant 500ms
return 500
}
// Dan levels: speed up from 500ms to 50ms
// Index 10 (Pre-1st Dan) → 500ms
// Index 20 (10th Dan) → 50ms
const danProgress = (currentIndex - 10) / 10 // 0.0 to 1.0
return 500 - danProgress * 450 // 500ms down to 50ms
// 1st Dan through 10th Dan: speed up from 500ms to 10ms
// Index 11 (1st Dan) → 500ms
// Index 20 (10th Dan) → 10ms
const danProgress = (currentIndex - 11) / 9 // 0.0 to 1.0
return 500 - danProgress * 490 // 500ms down to 10ms
}
const intervalMs = getAnimationInterval()
@@ -575,12 +656,11 @@ export default function LevelsPage() {
</div>
</div>
{/* Abacus Display */}
{/* Abacus Display with Level Details */}
<div
className={css({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: '4',
p: '6',
bg: 'rgba(0, 0, 0, 0.3)',
rounded: 'lg',
@@ -590,19 +670,149 @@ export default function LevelsPage() {
flex: 1,
})}
>
<animated.div
style={{
transform: animatedProps.scaleFactor.to((s) => `scale(${s / scaleFactor})`),
}}
{/* Level Details (only for Kyu levels) */}
{currentLevel.type === 'kyu' &&
(() => {
const detailsKey = getLevelDetailsKey(currentLevel.level)
const rawText = detailsKey
? kyuLevelDetails[detailsKey as keyof typeof kyuLevelDetails]
: null
const sections = rawText ? parseKyuDetails(rawText) : []
// Use consistent sizing across all levels
const sizing = { fontSize: 'md', gap: '3', iconSize: '4xl' }
return sections.length > 0 ? (
<div
className={css({
flex: '0 0 auto',
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: sizing.gap,
pr: '4',
pl: '2',
borderRight: '1px solid',
borderColor: 'gray.600',
maxW: '480px',
alignContent: 'center',
my: '2',
})}
>
{sections.map((section, idx) => {
const hasData = section.digits !== null
const levelColor =
currentLevel.color === 'green'
? 'green.300'
: currentLevel.color === 'blue'
? 'blue.300'
: 'violet.300'
return (
<div
key={idx}
className={css({
bg: hasData ? 'rgba(0, 0, 0, 0.4)' : 'rgba(0, 0, 0, 0.2)',
border: '1px solid',
borderColor: hasData ? 'gray.700' : 'gray.800',
rounded: 'md',
p: '4',
transition: 'all 0.2s',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '2',
opacity: hasData ? 1 : 0.3,
width: '200px',
height: '180px',
_hover: hasData
? {
borderColor: 'gray.500',
transform: 'scale(1.05)',
}
: {},
})}
>
<span className={css({ fontSize: sizing.iconSize, lineHeight: '1' })}>
{section.icon}
</span>
{hasData && section.digits && (
<>
<div
className={css({
fontSize: '2xl',
fontWeight: 'bold',
color: levelColor,
})}
>
{section.digits} digits
</div>
{(section.rows || section.chars) && (
<div
className={css({
fontSize: 'sm',
color: 'gray.400',
textAlign: 'center',
})}
>
{section.rows && `${section.rows} rows`}
{section.rows && section.chars && ' • '}
{section.chars && `${section.chars} chars`}
</div>
)}
{section.problems && (
<div
className={css({
fontSize: 'sm',
color: 'gray.400',
textAlign: 'center',
})}
>
{section.problems} problems
</div>
)}
</>
)}
<div
className={css({
fontSize: 'xs',
color: hasData ? 'gray.500' : 'gray.700',
textAlign: 'center',
fontWeight: hasData ? 'normal' : 'bold',
})}
>
{section.label}
</div>
</div>
)
})}
</div>
) : null
})()}
{/* Abacus (right-aligned for Kyu, centered for Dan) */}
<div
className={css({
display: 'flex',
justifyContent: currentLevel.type === 'kyu' ? 'flex-end' : 'center',
alignItems: 'center',
flex: 1,
})}
>
<AbacusReact
value={displayValue}
columns={currentLevel.digits}
scaleFactor={scaleFactor}
showNumbers={true}
customStyles={darkStyles}
/>
</animated.div>
<animated.div
style={{
transform: animatedProps.scaleFactor.to((s) => `scale(${s / scaleFactor})`),
}}
>
<AbacusReact
value={displayValue}
columns={currentLevel.digits}
scaleFactor={scaleFactor}
showNumbers={true}
customStyles={darkStyles}
/>
</animated.div>
</div>
</div>
{/* Digit Count */}

View File

@@ -0,0 +1,123 @@
/**
* Detailed requirements for each Kyu level in the Soroban certification system
* Source: shuzan.jp
*
* Note: Stored verbatim from source. Display formatting/translation happens in the UI layer.
*/
export const kyuLevelDetails = {
'10-kyu': `Add/Sub: 2-digit, 5口, 10字
×: 実+法 = 3 digits (20 problems)
Time: 20 min; Pass ≥ 60/200.
shuzan.jp`,
'9-kyu': `Add/Sub: 2-digit, 5口, 10字
×: 実+法 = 3 digits (20)
Time: 20 min; Pass ≥ 120/200. (If only one part clears, it's treated as 10-kyu per federation notes.)
shuzan.jp`,
'8-kyu': `Add/Sub: 2-digit, 8口, 16字
×: 実+法 = 4 digits (10)
÷: 法+商 = 3 digits (10)
Time: 20 min; Pass ≥ 120/200.
shuzan.jp`,
'7-kyu': `Add/Sub: 2-digit, 10口, 20字
×: 実+法 = 4 digits (10)
÷: 法+商 = 4 digits (10)
Time: 20 min; Pass ≥ 120/200.
shuzan.jp`,
'6-kyu': `Add/Sub: 10口, 30字
×: 実+法 = 5 digits (20)
÷: 法+商 = 4 digits (20)
Time: 30 min; Pass ≥ 210/300.
shuzan.jp`,
'5-kyu': `Add/Sub: 10口, 40字
×: 実+法 = 6 digits (20)
÷: 法+商 = 5 digits (20)
Time: 30 min; Pass ≥ 210/300.
shuzan.jp`,
'4-kyu': `Add/Sub: 10口, 50字
×: 実+法 = 7 digits (20)
÷: 法+商 = 6 digits (20)
Time: 30 min; Pass ≥ 210/300.
shuzan.jp`,
'Pre-3-kyu': `Add/Sub: 10口, 50字 ×5題 and 10口, 60字 ×5題 (total 10)
×: 実+法 = 7 digits (20)
÷: 法+商 = 6 digits (20)
Time: 30 min; Pass ≥ 240/300.
shuzan.jp`,
'3-kyu': `Add/Sub: 10口, 60字
×: 実+法 = 7 digits (20)
÷: 法+商 = 6 digits (20)
Time: 30 min; Pass ≥ 240/300.
shuzan.jp`,
'Pre-2-kyu': `Add/Sub: 10口, 70字
×: 実+法 = 8 digits (20)
÷: 法+商 = 7 digits (20)
Time: 30 min; Pass ≥ 240/300.
shuzan.jp`,
'2-kyu': `Add/Sub: 10口, 80字
×: 実+法 = 9 digits (20)
÷: 法+商 = 8 digits (20)
Time: 30 min; Pass ≥ 240/300.
shuzan.jp`,
'Pre-1-kyu': `Add/Sub: 10口, 90字
×: 実+法 = 10 digits (20)
÷: 法+商 = 9 digits (20)
Time: 30 min; Pass ≥ 240/300.
shuzan.jp`,
'1-kyu': `Add/Sub: 10口, 100字
×: 実+法 = 11 digits (20)
÷: 法+商 = 10 digits (20)
Time: 30 min; Pass ≥ 240/300.
shuzan.jp`,
} as const
export type KyuLevel = keyof typeof kyuLevelDetails

View File

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