Compare commits

...

2 Commits

Author SHA1 Message Date
semantic-release-bot
d312969747 chore(release): 2.14.1 [skip ci]
## [2.14.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.14.0...v2.14.1) (2025-10-09)

### Bug Fixes

* prevent avatar fly-in and hide local player's own hover ([7f65a67](7f65a67cef))
2025-10-09 21:10:10 +00:00
Thomas Hallock
7f65a67cef fix: prevent avatar fly-in and hide local player's own hover
Two critical UX fixes for hover avatars:

1. PREVENT FLY-IN FROM TOP-LEFT:
   - Initialize position state as null instead of {x:0, y:0}
   - Don't render avatar until position is calculated from card element
   - Set immediate:true on first render to prevent animation
   - Add opacity spring (0→1) for smooth fade-in
   - Result: Avatar appears exactly where it should, no fly-in animation

2. HIDE YOUR OWN HOVER AVATAR:
   - Filter out local player (isLocal === true) from hover avatar rendering
   - Only show remote players' hover avatars
   - You see opponents hovering, but not your own cursor
   - Result: Cleaner UX, less visual noise on your own session

BEFORE:
- Avatar would fly in from (0,0) top-left corner when first hovering
- You'd see your own avatar following your mouse (distracting)

AFTER:
- Avatar appears smoothly at the correct card position
- Only remote players' avatars are visible
- Feels like watching someone else's cursor, not your own

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-09 16:09:19 -05:00
3 changed files with 23 additions and 4 deletions

View File

@@ -1,3 +1,10 @@
## [2.14.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.14.0...v2.14.1) (2025-10-09)
### Bug Fixes
* prevent avatar fly-in and hide local player's own hover ([7f65a67](https://github.com/antialias/soroban-abacus-flashcards/commit/7f65a67cef3d7f0ebce1bd7417972a6138acfc46))
## [2.14.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v2.13.0...v2.14.0) (2025-10-09)

View File

@@ -92,7 +92,7 @@ function HoverAvatar({
playerInfo: { emoji: string; name: string; color?: string }
cardElement: HTMLElement | null
}) {
const [position, setPosition] = useState({ x: 0, y: 0 })
const [position, setPosition] = useState<{ x: number; y: number } | null>(null)
// Update position when card element changes
useEffect(() => {
@@ -106,22 +106,29 @@ function HoverAvatar({
}, [cardElement])
// Smooth spring animation for position changes
// Only animate if we have a position (prevents fly-in from 0,0)
const springProps = useSpring({
x: position.x,
y: position.y,
x: position?.x ?? 0,
y: position?.y ?? 0,
opacity: position ? 1 : 0, // Fade in when position is set
config: {
tension: 280,
friction: 60,
mass: 1,
},
immediate: !position, // No animation on first render
})
// Don't render until we have a position
if (!position) return null
return (
<animated.div
style={{
position: 'fixed',
left: springProps.x,
top: springProps.y,
opacity: springProps.opacity, // Fade in smoothly
width: '48px',
height: '48px',
borderRadius: '50%',
@@ -363,6 +370,11 @@ export function MemoryGrid() {
{state.playerHovers &&
Object.entries(state.playerHovers)
.filter(([_, cardId]) => cardId !== null) // Only show if hovering a card
.filter(([playerId]) => {
// Don't show your own hover avatar (only show remote players)
const player = playerMap.get(playerId)
return player?.isLocal !== true
})
.map(([playerId, cardId]) => {
const playerInfo = getPlayerHoverInfo(playerId)
const cardElement = cardId ? cardRefs.current.get(cardId) : null

View File

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