Compare commits

...

6 Commits

Author SHA1 Message Date
semantic-release-bot
653db575ff chore(release): 4.63.7 [skip ci]
## [4.63.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.6...v4.63.7) (2025-10-21)

### Bug Fixes

* **mobile:** reduce height of Your Journey section on mobile ([8944035](89440355bf))
2025-10-21 17:03:24 +00:00
Thomas Hallock
89440355bf fix(mobile): reduce height of Your Journey section on mobile
Added maxHeight constraint and reduced padding to ensure the abacus
stays visible while scrubbing the slider on mobile devices.

Changes:
- Added maxHeight: 500px on mobile (base), none on desktop (md)
- Reduced padding from 6 to 4 on mobile (base)

This ensures users can see both the slider and abacus simultaneously
on iPhone displays without scrolling.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 12:02:10 -05:00
semantic-release-bot
632e840ca7 chore(release): 4.63.6 [skip ci]
## [4.63.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.5...v4.63.6) (2025-10-21)

### Bug Fixes

* **mobile:** optimize Your Journey section for iPhone displays ([9167fb4](9167fb40d6))
2025-10-21 16:57:23 +00:00
Thomas Hallock
9167fb40d6 fix(mobile): optimize Your Journey section for iPhone displays
Fixed horizontal overflow issues on small screens (iPhone 14, 390px viewport):

Changes to LevelSliderDisplay component:
- Made abacus display container stack vertically on mobile (base) and horizontal on large screens (lg)
- Made level details grid responsive: 2 cols (mobile), 3 cols (sm), 2 cols (lg)
- Removed fixed widths from detail cards, using flexible widths with min/max constraints
- Added horizontal scroll (overflow-x: auto) to abacus container as fallback
- Reduced slider thumb size on mobile: 120px x 96px (base) vs 180px x 128px (md)
- Scaled down bead in slider thumb to 75% on mobile
- Reduced emoji tick sizes: 2xl (mobile), 3xl (sm), 4xl (md)

These changes ensure the "Your Journey" slider and abacus display fit properly
on mobile devices without horizontal overflow while maintaining the desktop experience.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 11:56:07 -05:00
semantic-release-bot
1d7486ed48 chore(release): 4.63.5 [skip ci]
## [4.63.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.4...v4.63.5) (2025-10-21)

### Bug Fixes

* **flashcards:** store grab offset in local coordinates to prevent jump ([39d93a9](39d93a9e9f))
2025-10-21 16:29:31 +00:00
Thomas Hallock
39d93a9e9f fix(flashcards): store grab offset in local coordinates to prevent jump
Problem: Cards jumped when grabbed, especially if already rotated. The grab
point would slip during rotation instead of staying under the cursor.

Root cause: Grab offset was stored in screen space with the card already
rotated. When we later rotated this offset during drag, we were applying
rotation on top of rotation.

Solution: Convert grab offset to local (unrotated) coordinates when grabbing:
- Calculate screen-space offset from card center
- Rotate by -currentRotation to get local coordinates
- Store in grabOffsetRef
- During drag, rotate this local offset by the current rotation angle

This ensures the grab point stays perfectly under the cursor regardless of
the card's initial or current rotation.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 11:28:18 -05:00
4 changed files with 66 additions and 16 deletions

View File

@@ -1,3 +1,24 @@
## [4.63.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.6...v4.63.7) (2025-10-21)
### Bug Fixes
* **mobile:** reduce height of Your Journey section on mobile ([8944035](https://github.com/antialias/soroban-abacus-flashcards/commit/89440355bf494e54072d2d1a1f228c33ec43d52d))
## [4.63.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.5...v4.63.6) (2025-10-21)
### Bug Fixes
* **mobile:** optimize Your Journey section for iPhone displays ([9167fb4](https://github.com/antialias/soroban-abacus-flashcards/commit/9167fb40d68b7bdbe310b647083586434ceb6043))
## [4.63.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.4...v4.63.5) (2025-10-21)
### Bug Fixes
* **flashcards:** store grab offset in local coordinates to prevent jump ([39d93a9](https://github.com/antialias/soroban-abacus-flashcards/commit/39d93a9e9f48a7d1ce10763cad62a600851a41d5))
## [4.63.4](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.3...v4.63.4) (2025-10-21)

View File

@@ -125,17 +125,27 @@ function DraggableCard({ card, containerRef }: DraggableCardProps) {
cardY: position.y,
}
// Calculate grab offset from card center
// Calculate grab offset from card center IN LOCAL COORDINATES (unrotated)
if (cardRef.current) {
const rect = cardRef.current.getBoundingClientRect()
const cardCenterX = rect.left + rect.width / 2
const cardCenterY = rect.top + rect.height / 2
// Screen-space offset from center
const screenOffsetX = e.clientX - cardCenterX
const screenOffsetY = e.clientY - cardCenterY
// Convert to local coordinates by rotating by -rotation
const currentRotationRad = (rotation * Math.PI) / 180
const cosRot = Math.cos(-currentRotationRad)
const sinRot = Math.sin(-currentRotationRad)
grabOffsetRef.current = {
x: e.clientX - cardCenterX,
y: e.clientY - cardCenterY,
x: screenOffsetX * cosRot - screenOffsetY * sinRot,
y: screenOffsetX * sinRot + screenOffsetY * cosRot,
}
console.log(
`[GrabPoint] Grabbed at offset: (${grabOffsetRef.current.x.toFixed(0)}, ${grabOffsetRef.current.y.toFixed(0)})px from center`
`[GrabPoint] Grabbed at local offset: (${grabOffsetRef.current.x.toFixed(0)}, ${grabOffsetRef.current.y.toFixed(0)})px (screen offset: ${screenOffsetX.toFixed(0)}, ${screenOffsetY.toFixed(0)}px, rotation: ${rotation.toFixed(1)}°)`
)
}

View File

@@ -412,8 +412,9 @@ export function LevelSliderDisplay({
? 'violet.500'
: 'amber.500',
rounded: 'xl',
p: { base: '6', md: '8' },
p: { base: '4', md: '8' },
height: { base: 'auto', md: '700px' },
maxHeight: { base: '500px', md: 'none' },
display: 'flex',
flexDirection: 'column',
})}
@@ -448,7 +449,7 @@ export function LevelSliderDisplay({
key={index}
onClick={() => setCurrentIndex(index)}
className={css({
fontSize: '4xl',
fontSize: { base: '2xl', sm: '3xl', md: '4xl' },
opacity: index === currentIndex ? '1' : '0.3',
transition: 'all 0.2s',
cursor: 'pointer',
@@ -500,8 +501,8 @@ export function LevelSliderDisplay({
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
w: '180px',
h: '128px',
w: { base: '120px', md: '180px' },
h: { base: '96px', md: '128px' },
bg: 'transparent',
cursor: 'grab',
transition: 'transform 0.15s ease-out, left 0.3s ease-out',
@@ -514,7 +515,12 @@ export function LevelSliderDisplay({
_active: { cursor: 'grabbing' },
})}
>
<div className={css({ opacity: 0.75 })}>
<div
className={css({
opacity: 0.75,
transform: { base: 'scale(0.75)', md: 'scale(1)' },
})}
>
<StandaloneBead
size={128}
color={currentLevel.color === 'violet' ? '#8b5cf6' : '#22c55e'}
@@ -607,8 +613,9 @@ export function LevelSliderDisplay({
<div
className={css({
display: 'flex',
flexDirection: { base: 'column', lg: 'row' },
gap: '4',
p: '6',
p: { base: '4', md: '6' },
bg: 'rgba(0, 0, 0, 0.3)',
rounded: 'lg',
border: '1px solid',
@@ -634,11 +641,17 @@ export function LevelSliderDisplay({
className={css({
flex: '0 0 auto',
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gridTemplateColumns: {
base: 'repeat(2, 1fr)',
sm: 'repeat(3, 1fr)',
lg: 'repeat(2, 1fr)',
},
gap: '2',
p: '2',
maxW: '400px',
w: '100%',
maxW: { base: '100%', lg: '400px' },
alignContent: 'center',
justifyItems: 'center',
})}
>
{sections.map((section, idx) => {
@@ -666,8 +679,10 @@ export function LevelSliderDisplay({
justifyContent: 'center',
gap: '1.5',
opacity: hasData ? 1 : 0.3,
width: '170px',
height: '150px',
w: { base: '100%', sm: 'auto' },
minW: { sm: '140px' },
maxW: { base: '170px', sm: '170px' },
minH: '150px',
_hover: hasData
? {
borderColor: 'gray.500',
@@ -737,9 +752,13 @@ export function LevelSliderDisplay({
<div
className={css({
display: 'flex',
justifyContent: currentLevel.type === 'kyu' ? 'flex-end' : 'center',
justifyContent:
currentLevel.type === 'kyu' ? { base: 'center', lg: 'flex-end' } : 'center',
alignItems: 'center',
flex: 1,
overflowX: 'auto',
overflowY: 'hidden',
minW: 0, // Allow flex shrinking
})}
>
<animated.div

View File

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