Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4febf5905b | ||
|
|
6739d59f2b | ||
|
|
cb20019c16 | ||
|
|
d90b5d5532 | ||
|
|
7028db0263 | ||
|
|
fa3b73c691 |
21
CHANGELOG.md
21
CHANGELOG.md
@@ -1,3 +1,24 @@
|
||||
## [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)
|
||||
|
||||
|
||||
|
||||
@@ -229,23 +229,8 @@ function parseKyuDetails(rawText: string) {
|
||||
value: `${match[1]}-digit total (${match[2]} problems)`,
|
||||
})
|
||||
}
|
||||
} else if (line.includes('Time:')) {
|
||||
// Parse time and pass requirements
|
||||
const timeMatch = line.match(/(\d+) min/)
|
||||
const passMatch = line.match(/≥\s*(\d+)\/(\d+)/)
|
||||
if (timeMatch && passMatch) {
|
||||
sections.push({
|
||||
icon: '⏱️',
|
||||
label: 'Time',
|
||||
value: `${timeMatch[1]} minutes`,
|
||||
})
|
||||
sections.push({
|
||||
icon: '✅',
|
||||
label: 'Pass',
|
||||
value: `≥${passMatch[1]}/${passMatch[2]} pts`,
|
||||
})
|
||||
}
|
||||
}
|
||||
// Skip Time and Pass requirements since we don't have tests implemented
|
||||
}
|
||||
|
||||
return sections
|
||||
@@ -670,77 +655,80 @@ export default function LevelsPage() {
|
||||
const sections = rawText ? parseKyuDetails(rawText) : []
|
||||
|
||||
// Use consistent sizing across all levels
|
||||
const sizing = { fontSize: 'sm', gap: '3', iconSize: 'xl' }
|
||||
const sizing = { fontSize: 'md', gap: '3', iconSize: '4xl' }
|
||||
|
||||
return sections.length > 0 ? (
|
||||
<div
|
||||
className={css({
|
||||
flex: '0 0 auto',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(2, 1fr)',
|
||||
gap: sizing.gap,
|
||||
pr: '4',
|
||||
pl: '2',
|
||||
borderRight: '1px solid',
|
||||
borderColor: 'gray.600',
|
||||
maxW: '320px',
|
||||
maxW: '480px',
|
||||
alignContent: 'center',
|
||||
})}
|
||||
>
|
||||
{sections.map((section, idx) => (
|
||||
<div
|
||||
key={idx}
|
||||
className={css({
|
||||
bg: 'rgba(0, 0, 0, 0.4)',
|
||||
border: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
rounded: 'md',
|
||||
p: '2',
|
||||
transition: 'all 0.2s',
|
||||
_hover: {
|
||||
borderColor: 'gray.500',
|
||||
transform: 'translateX(4px)',
|
||||
},
|
||||
})}
|
||||
>
|
||||
{sections.map((section, idx) => {
|
||||
// Extract the digit count (e.g., "4" from "4-digit total")
|
||||
const digitMatch = section.value.match(/(\d+)-digit/)
|
||||
const digitCount = digitMatch ? digitMatch[1] : null
|
||||
|
||||
return (
|
||||
<div
|
||||
key={idx}
|
||||
className={css({
|
||||
bg: 'rgba(0, 0, 0, 0.4)',
|
||||
border: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
rounded: 'md',
|
||||
p: '4',
|
||||
transition: 'all 0.2s',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: '2',
|
||||
mb: '1',
|
||||
_hover: {
|
||||
borderColor: 'gray.500',
|
||||
transform: 'scale(1.05)',
|
||||
},
|
||||
})}
|
||||
>
|
||||
<span className={css({ fontSize: sizing.iconSize })}>
|
||||
<span className={css({ fontSize: sizing.iconSize, lineHeight: '1' })}>
|
||||
{section.icon}
|
||||
</span>
|
||||
<span
|
||||
{digitCount && (
|
||||
<div
|
||||
className={css({
|
||||
fontSize: '2xl',
|
||||
fontWeight: 'bold',
|
||||
color:
|
||||
currentLevel.color === 'green'
|
||||
? 'green.300'
|
||||
: currentLevel.color === 'blue'
|
||||
? 'blue.300'
|
||||
: 'violet.300',
|
||||
})}
|
||||
>
|
||||
{digitCount} digits
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={css({
|
||||
fontSize: sizing.fontSize,
|
||||
fontWeight: 'semibold',
|
||||
color:
|
||||
currentLevel.color === 'green'
|
||||
? 'green.300'
|
||||
: currentLevel.color === 'blue'
|
||||
? 'blue.300'
|
||||
: 'violet.300',
|
||||
fontSize: 'xs',
|
||||
color: 'gray.500',
|
||||
textAlign: 'center',
|
||||
})}
|
||||
>
|
||||
{section.label}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={css({
|
||||
fontSize: sizing.fontSize,
|
||||
color: 'gray.400',
|
||||
lineHeight: '1.4',
|
||||
pl: sizing.gap === '3' ? '6' : sizing.gap === '2' ? '5' : '4',
|
||||
})}
|
||||
>
|
||||
{section.value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
) : null
|
||||
})()}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soroban-monorepo",
|
||||
"version": "4.43.0",
|
||||
"version": "4.44.0",
|
||||
"private": true,
|
||||
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
|
||||
"workspaces": [
|
||||
|
||||
Reference in New Issue
Block a user