Compare commits

...

10 Commits

Author SHA1 Message Date
semantic-release-bot
6dabb71600 chore(release): 4.63.9 [skip ci]
## [4.63.9](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.8...v4.63.9) (2025-10-21)

### Bug Fixes

* **homepage:** use app-wide abacus config in interactive flashcards ([cf1be2d](cf1be2d173))
2025-10-21 17:09:40 +00:00
Thomas Hallock
cf1be2d173 fix(homepage): use app-wide abacus config in interactive flashcards
The interactive flashcards section at the bottom of the homepage was
hardcoding beadShape="circle" instead of respecting the app-wide abacus
configuration context that all other abaci on the site use.

Changes to InteractiveFlashcards.tsx:
- Import useAbacusConfig hook from @soroban/abacus-react
- Call useAbacusConfig() in DraggableCard component
- Use appConfig.beadShape instead of hardcoded "circle"

This ensures visual consistency across all abacus displays on the site
and respects user preferences for bead styling.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 12:08:26 -05:00
semantic-release-bot
0169ab5128 chore(release): 4.63.8 [skip ci]
## [4.63.8](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.7...v4.63.8) (2025-10-21)

### Bug Fixes

* **mobile:** restore abacus visibility in "Your Journey" section ([c96036d](c96036d86b))
2025-10-21 17:08:03 +00:00
Thomas Hallock
c96036d86b fix(mobile): restore abacus visibility in "Your Journey" section
On mobile screens (base breakpoint), the level details cards were taking
up all vertical space within the 500px maxHeight constraint, pushing the
abacus completely out of view.

Solution: Hide level details on mobile (display: { base: 'none', lg: 'grid' })
so mobile users see only the slider and abacus, while desktop users see
all components. Also added minH: 0 to containers to ensure proper flex
shrinking behavior.

Changes to LevelSliderDisplay.tsx:
- Level details grid now hidden on base breakpoint, visible on lg+
- Simplified grid columns to single value since it's desktop-only
- Added minH: 0 to flex containers for proper shrinking

Tested on iPhone 14 (390px viewport).

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 12:06:48 -05:00
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 82 additions and 18 deletions

View File

@@ -1,3 +1,38 @@
## [4.63.9](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.8...v4.63.9) (2025-10-21)
### Bug Fixes
* **homepage:** use app-wide abacus config in interactive flashcards ([cf1be2d](https://github.com/antialias/soroban-abacus-flashcards/commit/cf1be2d1730543bd30836a87d9cbdfd2cf48360e))
## [4.63.8](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.63.7...v4.63.8) (2025-10-21)
### Bug Fixes
* **mobile:** restore abacus visibility in "Your Journey" section ([c96036d](https://github.com/antialias/soroban-abacus-flashcards/commit/c96036d86b6de2e25f7ecd3d00dd36221badc3b1))
## [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

@@ -1,6 +1,6 @@
'use client'
import { AbacusReact } from '@soroban/abacus-react'
import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react'
import { useEffect, useRef, useState } from 'react'
import { css } from '../../styled-system/css'
@@ -93,6 +93,8 @@ interface DraggableCardProps {
}
function DraggableCard({ card, containerRef }: DraggableCardProps) {
const appConfig = useAbacusConfig()
// Track position - starts at initial, updates when dragged
const [position, setPosition] = useState({ x: card.initialX, y: card.initialY })
const [rotation, setRotation] = useState(card.initialRotation) // Now dynamic!
@@ -125,17 +127,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)}°)`
)
}
@@ -327,7 +339,7 @@ function DraggableCard({ card, containerRef }: DraggableCardProps) {
transformOrigin: 'center',
})}
>
<AbacusReact value={card.number} columns={3} beadShape="circle" />
<AbacusReact value={card.number} columns={3} beadShape={appConfig.beadShape} />
</div>
{/* Number display */}

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,14 +613,16 @@ 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',
borderColor: 'gray.700',
overflow: 'hidden',
flex: 1,
minH: 0, // Allow flex shrinking
})}
>
{/* Level Details (only for Kyu levels) */}
@@ -633,12 +641,14 @@ export function LevelSliderDisplay({
<div
className={css({
flex: '0 0 auto',
display: 'grid',
display: { base: 'none', lg: 'grid' },
gridTemplateColumns: 'repeat(2, 1fr)',
gap: '2',
p: '2',
w: '100%',
maxW: '400px',
alignContent: 'center',
justifyItems: 'center',
})}
>
{sections.map((section, idx) => {
@@ -666,8 +676,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',
@@ -733,13 +745,18 @@ export function LevelSliderDisplay({
) : null
})()}
{/* Abacus (right-aligned for Kyu, centered for Dan) */}
{/* Abacus (centered on mobile, right-aligned for Kyu on desktop, centered for Dan) */}
<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
minH: 0, // Allow flex shrinking
})}
>
<animated.div

View File

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