diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 77a293ee..2ba8fe42 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -148,7 +148,8 @@ "Bash(awk:*)", "Bash(gh release list:*)", "Bash(gh release view:*)", - "Bash(git pull:*)" + "Bash(git pull:*)", + "WebFetch(domain:antialias.github.io)" ], "deny": [], "ask": [] diff --git a/.github/workflows/publish-abacus-react.yml b/.github/workflows/publish-abacus-react.yml new file mode 100644 index 00000000..3cf63401 --- /dev/null +++ b/.github/workflows/publish-abacus-react.yml @@ -0,0 +1,67 @@ +name: Publish @soroban/abacus-react + +on: + push: + branches: + - main + paths: + - 'packages/abacus-react/**' + - '.github/workflows/publish-abacus-react.yml' + +permissions: + contents: write + issues: write + pull-requests: write + id-token: write + +jobs: + publish: + name: Publish abacus-react package + runs-on: ubuntu-latest + if: "!contains(github.event.head_commit.message, '[skip ci]')" + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: + version: 8.0.0 + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build abacus-react package + run: pnpm --filter @soroban/abacus-react build + + - name: Run tests + run: pnpm --filter @soroban/abacus-react test:run + + - name: Semantic Release + working-directory: packages/abacus-react + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npx semantic-release \ No newline at end of file diff --git a/.releaserc.json b/.releaserc.json index bd1d1ea2..fd3350eb 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -1,8 +1,37 @@ { "branches": ["main"], "plugins": [ - "@semantic-release/commit-analyzer", - "@semantic-release/release-notes-generator", + [ + "@semantic-release/commit-analyzer", + { + "preset": "conventionalcommits", + "releaseRules": [ + { "type": "feat", "scope": "!abacus-react", "release": "minor" }, + { "type": "fix", "scope": "!abacus-react", "release": "patch" }, + { "type": "perf", "scope": "!abacus-react", "release": "patch" }, + { "type": "refactor", "scope": "!abacus-react", "release": "patch" }, + { "breaking": true, "scope": "!abacus-react", "release": "major" }, + { "scope": "abacus-react", "release": false } + ] + } + ], + [ + "@semantic-release/release-notes-generator", + { + "preset": "conventionalcommits", + "presetConfig": { + "types": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance Improvements" }, + { "type": "refactor", "section": "Code Refactoring" }, + { "type": "docs", "section": "Documentation" }, + { "type": "style", "section": "Styles" }, + { "type": "test", "section": "Tests" } + ] + } + } + ], [ "@semantic-release/changelog", { diff --git a/apps/web/src/app/games/matching/components/MemoryGrid.tsx b/apps/web/src/app/games/matching/components/MemoryGrid.tsx index daa78958..dd7158c6 100644 --- a/apps/web/src/app/games/matching/components/MemoryGrid.tsx +++ b/apps/web/src/app/games/matching/components/MemoryGrid.tsx @@ -36,11 +36,11 @@ export function MemoryGrid() { return (
{/* Game Info Header */} @@ -50,37 +50,43 @@ export function MemoryGrid() { alignItems: 'center', width: '100%', maxWidth: '800px', - padding: '16px 24px', + padding: { base: '12px 16px', sm: '14px 20px', md: '16px 24px' }, background: 'linear-gradient(135deg, rgba(255,255,255,0.9), rgba(248,250,252,0.9))', borderRadius: '16px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)', border: '1px solid rgba(255,255,255,0.8)' })}> -
+
-
+
{state.matchedPairs}
-
+
Matched
-
+
{state.moves}
-
+
Moves
-
+
{state.totalPairs}
-
+
Total Pairs
diff --git a/apps/web/src/app/games/matching/components/MemoryPairsGame.tsx b/apps/web/src/app/games/matching/components/MemoryPairsGame.tsx index 0cabe09c..9671c281 100644 --- a/apps/web/src/app/games/matching/components/MemoryPairsGame.tsx +++ b/apps/web/src/app/games/matching/components/MemoryPairsGame.tsx @@ -26,32 +26,36 @@ export function MemoryPairsGame() { ref={gameRef} className={css({ minHeight: '100vh', - background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', - padding: '20px', - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - position: 'relative' - })}> + background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)', + padding: { base: '12px', sm: '16px', md: '20px' }, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + position: 'relative' + })}> {/* Note: Fullscreen restore prompt removed - client-side navigation preserves fullscreen */}

Memory Pairs Challenge

Match pairs of abacus representations with their numerical values, or find complement pairs that add up to 5 or 10!

@@ -61,10 +65,13 @@ export function MemoryPairsGame() { width: '100%', maxWidth: '1200px', background: 'rgba(255,255,255,0.95)', - borderRadius: '20px', - padding: '40px', + borderRadius: { base: '12px', md: '20px' }, + padding: { base: '16px', sm: '24px', md: '32px', lg: '40px' }, boxShadow: '0 10px 30px rgba(0,0,0,0.2)', - minHeight: '500px' + minHeight: { base: '60vh', md: '500px' }, + flex: 1, + display: 'flex', + flexDirection: 'column' })}> {state.gamePhase === 'setup' && } {state.gamePhase === 'playing' && } diff --git a/apps/web/src/app/games/matching/components/SetupPhase.tsx b/apps/web/src/app/games/matching/components/SetupPhase.tsx index e5bce2c3..65a23004 100644 --- a/apps/web/src/app/games/matching/components/SetupPhase.tsx +++ b/apps/web/src/app/games/matching/components/SetupPhase.tsx @@ -47,13 +47,13 @@ export function SetupPhase() { const getButtonStyles = (isSelected: boolean, variant: 'primary' | 'secondary' | 'difficulty' = 'primary') => { const baseStyles = { border: 'none', - borderRadius: '16px', - padding: '16px 24px', - fontSize: '16px', + borderRadius: { base: '12px', md: '16px' }, + padding: { base: '12px 16px', sm: '14px 20px', md: '16px 24px' }, + fontSize: { base: '14px', sm: '15px', md: '16px' }, fontWeight: 'bold', cursor: 'pointer', transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', - minWidth: '160px', + minWidth: { base: '120px', sm: '140px', md: '160px' }, textAlign: 'center' as const, position: 'relative' as const, overflow: 'hidden' as const, @@ -130,13 +130,16 @@ export function SetupPhase() { return (

@@ -144,46 +147,48 @@ export function SetupPhase() {

Configure your memory challenge. Choose your preferred mode, game type, and difficulty level.

{/* Current Player Setup */}

🎮 Current Setup

{activePlayerCount} player{activePlayerCount !== 1 ? 's' : ''} selected

-

+

{activePlayerCount === 1 ? 'Solo challenge mode - focus & memory' : `${activePlayerCount}-player battle mode - compete for the most pairs` @@ -211,31 +216,34 @@ export function SetupPhase() {

{state.gameType === 'abacus-numeral' ? 'Match abacus representations with their numerical values' @@ -273,18 +283,21 @@ export function SetupPhase() {

{([6, 8, 12, 15] as const).map(difficulty => { const difficultyInfo = { @@ -377,15 +390,15 @@ export function SetupPhase() { )} {/* Start Game Button */} -
+