Compare commits

...

5 Commits

Author SHA1 Message Date
semantic-release-bot
1e6459f9c1 chore(release): 4.37.0 [skip ci]
## [4.37.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.36.0...v4.37.0) (2025-10-20)

### Features

* **levels:** add hover tracking to slider for real-time level preview ([477a0b3](477a0b367e))
2025-10-20 15:35:35 +00:00
Thomas Hallock
477a0b367e feat(levels): add hover tracking to slider for real-time level preview
Added mouse hover functionality to the slider so it responds as you move
your mouse across the track:
- Calculates hover position based on mouse X coordinate
- Updates slider value in real-time as you hover
- Tracks hover state with isHovering flag
- Slider thumb follows your mouse smoothly with CSS transitions

Now you can explore levels by simply hovering across the slider track,
in addition to clicking emoji tick marks or dragging the thumb.

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

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

### Features

* **levels:** make emoji tick marks clickable and remove redundant UI ([07c783a](07c783a794))

### Bug Fixes

* **levels:** add smooth CSS transitions for slider thumb movement ([ca8cef1](ca8cef1c36))
2025-10-20 15:32:54 +00:00
Thomas Hallock
07c783a794 feat(levels): make emoji tick marks clickable and remove redundant UI
- Added click handlers to emoji tick marks so you can click any emoji to jump to that level
- Added hover effects (opacity 0.6) and pointer cursor to tick marks
- Enabled pointer events on tick marks (parent has pointerEvents: 'none')
- Removed redundant "Drag or click the beads" instruction text
- Removed duplicated level info text below slider (info now only shows on slider thumb)

This simplifies the UI and makes the slider more interactive - you can now
click, drag, or hover over any emoji to explore different levels.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 10:31:40 -05:00
Thomas Hallock
ca8cef1c36 fix(levels): add smooth CSS transitions for slider thumb movement
Fixed slider transitions to be smooth but responsive:
- Balanced animation speeds (scale: tension 350/friction 45, emoji: 120ms)
- Added 0.3s CSS transition specifically for thumb position (left property)
- Removed complex React Spring state management that wasn't triggering re-renders
- Simplified to basic state management with CSS handling the smoothness

This gives a nice gliding effect when clicking between levels while
keeping the interface snappy and responsive.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 10:31:40 -05:00
3 changed files with 40 additions and 53 deletions

View File

@@ -1,3 +1,22 @@
## [4.37.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.36.0...v4.37.0) (2025-10-20)
### Features
* **levels:** add hover tracking to slider for real-time level preview ([477a0b3](https://github.com/antialias/soroban-abacus-flashcards/commit/477a0b367e32749b865b5a5405846e86d5bcef6a))
## [4.36.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.35.1...v4.36.0) (2025-10-20)
### Features
* **levels:** make emoji tick marks clickable and remove redundant UI ([07c783a](https://github.com/antialias/soroban-abacus-flashcards/commit/07c783a79454f50e7302b19684be6d2e5930154d))
### Bug Fixes
* **levels:** add smooth CSS transitions for slider thumb movement ([ca8cef1](https://github.com/antialias/soroban-abacus-flashcards/commit/ca8cef1c36efeb1c8c214c74f8bd383f9295be3b))
## [4.35.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.35.0...v4.35.1) (2025-10-20)

View File

@@ -183,8 +183,18 @@ const allLevels = [
export default function LevelsPage() {
const [currentIndex, setCurrentIndex] = useState(0)
const [isHovering, setIsHovering] = useState(false)
const currentLevel = allLevels[currentIndex]
// Handle hover on slider track
const handleSliderHover = (e: React.MouseEvent<HTMLSpanElement>) => {
const rect = e.currentTarget.getBoundingClientRect()
const x = e.clientX - rect.left
const percentage = x / rect.width
const index = Math.round(percentage * (allLevels.length - 1))
setCurrentIndex(Math.max(0, Math.min(allLevels.length - 1, index)))
}
// Calculate scale factor based on number of columns to fit the page
// Use constrained range to prevent huge size differences between levels
// Min 1.2 (for 30-column Dan levels) to Max 2.0 (for 2-column Kyu levels)
@@ -193,7 +203,7 @@ export default function LevelsPage() {
// Animate scale factor with React Spring for smooth transitions
const animatedProps = useSpring({
scaleFactor,
config: { tension: 400, friction: 40 },
config: { tension: 350, friction: 45 },
})
// Animate emoji with proper cross-fade (old fades out, new fades in)
@@ -202,7 +212,7 @@ export default function LevelsPage() {
from: { opacity: 0 },
enter: { opacity: 1 },
leave: { opacity: 0 },
config: { duration: 80 },
config: { duration: 120 },
})
// Generate an interesting non-zero number to display on the abacus
@@ -319,12 +329,6 @@ export default function LevelsPage() {
>
{/* Abacus-themed Radix Slider */}
<div className={css({ mb: '6', px: { base: '2', md: '8' } })}>
<div className={css({ mb: '3', textAlign: 'center' })}>
<p className={css({ fontSize: 'sm', color: 'gray.400' })}>
Drag or click the beads to explore all levels
</p>
</div>
<div className={css({ position: 'relative', py: '12' })}>
{/* Emoji tick marks */}
<div
@@ -351,10 +355,14 @@ export default function LevelsPage() {
{allLevels.map((level, index) => (
<div
key={index}
onClick={() => setCurrentIndex(index)}
className={css({
fontSize: '4xl',
opacity: index === currentIndex ? '1' : '0.3',
transition: 'all 0.2s',
cursor: 'pointer',
pointerEvents: 'auto',
_hover: { opacity: index === currentIndex ? '1' : '0.6' },
})}
>
{level.emoji}
@@ -369,6 +377,9 @@ export default function LevelsPage() {
min={0}
max={allLevels.length - 1}
step={1}
onMouseMove={handleSliderHover}
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
className={css({
position: 'relative',
display: 'flex',
@@ -402,7 +413,7 @@ export default function LevelsPage() {
h: '128px',
bg: 'transparent',
cursor: 'grab',
transition: 'all 0.1s ease-out',
transition: 'transform 0.15s ease-out, left 0.3s ease-out',
zIndex: 10,
_hover: { transform: 'scale(1.15)' },
_focus: {
@@ -499,49 +510,6 @@ export default function LevelsPage() {
<span>1st Kyu</span>
<span>10th Dan</span>
</div>
{/* Level Text - integrated with slider */}
<div
className={css({
textAlign: 'center',
mt: '4',
mb: '2',
})}
>
<h2
className={css({
fontSize: { base: 'xl', md: '2xl' },
fontWeight: 'bold',
color:
currentLevel.color === 'green'
? 'green.400'
: currentLevel.color === 'blue'
? 'blue.400'
: currentLevel.color === 'violet'
? 'violet.400'
: 'amber.400',
mb: '1',
})}
>
{currentLevel.level}
</h2>
{'name' in currentLevel && (
<div
className={css({
fontSize: 'sm',
color: 'gray.400',
mb: '1',
})}
>
{currentLevel.name}
</div>
)}
{'minScore' in currentLevel && (
<div className={css({ fontSize: 'xs', color: 'gray.500' })}>
Minimum Score: {currentLevel.minScore} points
</div>
)}
</div>
</div>
{/* Abacus Display */}

View File

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