Compare commits

...

2 Commits

Author SHA1 Message Date
semantic-release-bot
8de4015250 chore(release): 1.1.2 [skip ci]
## [1.1.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v1.1.1...v1.1.2) (2025-09-28)

### Bug Fixes

* upgrade Node.js to version 20 for Storybook compatibility ([4c33872](4c338726c1))
2025-09-28 13:16:16 +00:00
Thomas Hallock
4c338726c1 fix: upgrade Node.js to version 20 for Storybook compatibility 2025-09-28 08:15:22 -05:00
6 changed files with 135 additions and 91 deletions

View File

@@ -31,7 +31,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v2

View File

@@ -1,3 +1,10 @@
## [1.1.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v1.1.1...v1.1.2) (2025-09-28)
### Bug Fixes
* upgrade Node.js to version 20 for Storybook compatibility ([4c33872](https://github.com/antialias/soroban-abacus-flashcards/commit/4c338726c13af623b1536f75fe6a18e0ab529377))
## [1.1.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v1.1.0...v1.1.1) (2025-09-28)

View File

@@ -690,7 +690,7 @@ export function EnhancedChampionArena({ onGameModeChange, onConfigurePlayer, cla
</div>
</div>
{/* Prominent Game Selector - takes remaining space */}
{/* Prominent Game Selector - constrained to available space */}
<div className={css({
flex: 1,
mt: { base: '1', md: '2' },
@@ -698,7 +698,9 @@ export function EnhancedChampionArena({ onGameModeChange, onConfigurePlayer, cla
borderTop: '2px solid',
borderColor: 'gray.200',
minHeight: 0,
overflow: 'auto'
overflow: 'hidden',
display: 'flex',
flexDirection: 'column'
})}>
<GameSelector
variant="detailed"

View File

@@ -52,7 +52,10 @@ export function GameCard({
cursor: available ? 'pointer' : 'not-allowed',
transition: 'all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275)',
position: 'relative',
overflow: 'visible',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
height: '100%',
_hover: available ? {
transform: variant === 'compact' ? 'translateY(-2px) scale(1.02)' : 'translateY(-8px) scale(1.02)',
boxShadow: variant === 'compact'
@@ -65,101 +68,125 @@ export function GameCard({
{/* Game icon with enhanced styling */}
<div className={css({
textAlign: 'center',
mb: variant === 'compact' ? '1' : { base: '1', md: '2' }
flex: 1,
display: 'flex',
flexDirection: 'column',
justifyContent: 'space-between',
minHeight: 0
})}>
{/* Top section - icon and title */}
<div className={css({
fontSize: variant === 'compact' ? 'xl' : { base: 'xl', md: '2xl', lg: '3xl' },
mb: variant === 'compact' ? '1' : { base: '1', md: '2' },
display: 'inline-block',
transform: 'perspective(1000px)',
transition: 'all 0.3s ease'
flexShrink: 0
})}>
{config.icon}
<div className={css({
fontSize: variant === 'compact' ? 'xl' : { base: 'xl', md: '2xl', lg: '3xl' },
mb: variant === 'compact' ? '1' : { base: '1', md: '2' },
display: 'inline-block',
transform: 'perspective(1000px)',
transition: 'all 0.3s ease'
})}>
{config.icon}
</div>
<h4 className={css({
fontSize: variant === 'compact' ? 'md' : { base: 'lg', md: 'xl', lg: '2xl' },
fontWeight: 'bold',
color: 'gray.900',
mb: variant === 'compact' ? '0.5' : { base: '1', md: '2' }
})}>
{variant === 'detailed' ? config.fullName || config.name : config.name}
</h4>
</div>
<h4 className={css({
fontSize: variant === 'compact' ? 'md' : { base: 'lg', md: 'xl', lg: '2xl' },
fontWeight: 'bold',
color: 'gray.900',
mb: variant === 'compact' ? '0.5' : { base: '1', md: '2' }
})}>
{variant === 'detailed' ? config.fullName || config.name : config.name}
</h4>
{/* Middle section - description (flexible) */}
{variant === 'detailed' && (
<p className={css({
fontSize: { base: 'xs', md: 'sm', lg: 'base' },
color: 'gray.600',
mb: { base: '2', md: '3' },
lineHeight: 'relaxed',
display: { base: 'none', sm: 'block' }
})}>
{config.description}
</p>
)}
{/* Feature chips */}
{variant === 'detailed' && config.chips && (
<div className={css({
flex: 1,
display: 'flex',
flexWrap: 'wrap',
gap: { base: '1', md: '2' },
flexDirection: 'column',
justifyContent: 'center',
mb: { base: '2', md: '3' }
minHeight: 0
})}>
{config.chips.map((chip, index) => (
<span
key={index}
className={css({
px: { base: '2', md: '3' },
py: { base: '0.5', md: '1' },
background: config.color === 'green'
? 'linear-gradient(135deg, #d1fae5, #a7f3d0)'
: config.color === 'purple'
? 'linear-gradient(135deg, #e0e7ff, #c7d2fe)'
: 'linear-gradient(135deg, #dbeafe, #bfdbfe)',
color: config.color === 'green'
? 'green.800'
: config.color === 'purple'
? 'indigo.800'
: 'blue.800',
rounded: 'full',
fontSize: { base: '2xs', md: 'xs' },
fontWeight: 'semibold',
border: '1px solid',
borderColor: config.color === 'green'
? 'green.200'
: config.color === 'purple'
? 'indigo.200'
: 'blue.200',
opacity: available ? 1 : 0.8
})}
>
{chip}
</span>
))}
<p className={css({
fontSize: { base: 'xs', md: 'sm', lg: 'base' },
color: 'gray.600',
lineHeight: 'relaxed',
display: { base: 'none', sm: 'block' },
overflow: 'hidden'
})}>
{config.description}
</p>
</div>
)}
{/* Player availability indicator */}
{/* Bottom section - chips and status */}
<div className={css({
fontSize: variant === 'compact' ? '2xs' : { base: '2xs', md: 'xs', lg: 'sm' },
color: available ? 'green.600' : 'red.600',
fontWeight: 'semibold',
display: 'inline-flex',
alignItems: 'center',
gap: '1',
px: { base: '1.5', md: '2' },
py: { base: '0.5', md: '1' },
background: available ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
rounded: 'full',
border: '1px solid',
borderColor: available ? 'green.200' : 'red.200'
flexShrink: 0,
mt: 'auto'
})}>
{activePlayerCount <= config.maxPlayers
? `${activePlayerCount}/${config.maxPlayers} ${activePlayerCount === 1 ? 'player' : 'players'}`
: `✗ Too many players (max ${config.maxPlayers})`
}
{/* Feature chips */}
{variant === 'detailed' && config.chips && (
<div className={css({
display: 'flex',
flexWrap: 'wrap',
gap: { base: '1', md: '2' },
justifyContent: 'center',
mb: { base: '2', md: '3' }
})}>
{config.chips.slice(0, 2).map((chip, index) => (
<span
key={index}
className={css({
px: { base: '2', md: '3' },
py: { base: '0.5', md: '1' },
background: config.color === 'green'
? 'linear-gradient(135deg, #d1fae5, #a7f3d0)'
: config.color === 'purple'
? 'linear-gradient(135deg, #e0e7ff, #c7d2fe)'
: 'linear-gradient(135deg, #dbeafe, #bfdbfe)',
color: config.color === 'green'
? 'green.800'
: config.color === 'purple'
? 'indigo.800'
: 'blue.800',
rounded: 'full',
fontSize: { base: '2xs', md: 'xs' },
fontWeight: 'semibold',
border: '1px solid',
borderColor: config.color === 'green'
? 'green.200'
: config.color === 'purple'
? 'indigo.200'
: 'blue.200',
opacity: available ? 1 : 0.8
})}
>
{chip}
</span>
))}
</div>
)}
{/* Player availability indicator */}
<div className={css({
fontSize: variant === 'compact' ? '2xs' : { base: '2xs', md: 'xs', lg: 'sm' },
color: available ? 'green.600' : 'red.600',
fontWeight: 'semibold',
display: 'inline-flex',
alignItems: 'center',
gap: '1',
px: { base: '1.5', md: '2' },
py: { base: '0.5', md: '1' },
background: available ? 'rgba(16, 185, 129, 0.1)' : 'rgba(239, 68, 68, 0.1)',
rounded: 'full',
border: '1px solid',
borderColor: available ? 'green.200' : 'red.200'
})}>
{activePlayerCount <= config.maxPlayers
? `${activePlayerCount}/${config.maxPlayers} ${activePlayerCount === 1 ? 'player' : 'players'}`
: `✗ Too many players (max ${config.maxPlayers})`
}
</div>
</div>
</div>
</div>

View File

@@ -84,14 +84,20 @@ export function GameSelector({
const { activePlayerCount } = useGameMode()
return (
<div className={className}>
<div className={css({
height: '100%',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden'
}, className)}>
{showHeader && (
<h3 className={css({
fontSize: variant === 'compact' ? 'lg' : 'xl',
fontSize: variant === 'compact' ? 'lg' : { base: 'lg', md: 'xl' },
fontWeight: 'bold',
color: 'gray.800',
mb: '4',
textAlign: 'center'
mb: { base: '2', md: '3' },
textAlign: 'center',
flexShrink: 0
})}>
🎮 Available Games
</h3>
@@ -112,9 +118,11 @@ export function GameSelector({
<div className={css({
display: 'grid',
gridTemplateColumns: { base: '1fr', md: 'repeat(2, 1fr)' },
gridTemplateRows: { base: 'repeat(4, 1fr)', md: 'repeat(2, 1fr)' },
gap: variant === 'compact' ? '2' : { base: '2', md: '3' },
height: '100%',
alignItems: 'start'
flex: 1,
minHeight: 0,
overflow: 'hidden'
})}>
{Object.entries(GAMES_CONFIG).map(([gameType, config]) => (
<GameCard

View File

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