Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8baeba6987 | ||
|
|
e028e342ad | ||
|
|
263237a152 | ||
|
|
4ec1b952f2 | ||
|
|
aa9d389540 | ||
|
|
d1423420e6 | ||
|
|
47640f3486 | ||
|
|
cb7595c95b | ||
|
|
8b4eceebfa | ||
|
|
850fd33943 | ||
|
|
8835e1c57a | ||
|
|
b635ed1c2d | ||
|
|
4e6cecfe27 | ||
|
|
d52ba6373a | ||
|
|
002c2888ac | ||
|
|
5d85e898d6 | ||
|
|
eed890dc81 | ||
|
|
57fabffe60 | ||
|
|
89fb670f93 | ||
|
|
8e51390018 | ||
|
|
e7e54619ae | ||
|
|
9c51cc94ee |
75
CHANGELOG.md
75
CHANGELOG.md
@@ -1,3 +1,78 @@
|
||||
## [4.24.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.23.0...v4.24.0) (2025-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **homepage:** add animated mini abacus to "Read and set numbers" card ([e028e34](https://github.com/antialias/soroban-abacus-flashcards/commit/e028e342ad4bc01491e05a4ba074628155926fd8))
|
||||
|
||||
## [4.23.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.22.0...v4.23.0) (2025-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **homepage:** add more visual embellishments to learning cards ([4ec1b95](https://github.com/antialias/soroban-abacus-flashcards/commit/4ec1b952f202d50f6db287c41732ec65ca17c142))
|
||||
|
||||
## [4.22.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.21.1...v4.22.0) (2025-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **homepage:** enhance "What You'll Learn" with visual cards ([d142342](https://github.com/antialias/soroban-abacus-flashcards/commit/d1423420e653b26b2f89d9d17ae5d597807d6979))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tutorial:** reduce tooltip z-index to scroll under nav bar ([47640f3](https://github.com/antialias/soroban-abacus-flashcards/commit/47640f3486c6d4a7107d59bdcce043f76fabbb1d))
|
||||
|
||||
## [4.21.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.21.0...v4.21.1) (2025-10-20)
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **homepage:** rearrange tutorial demo layout side by side ([8b4ecee](https://github.com/antialias/soroban-abacus-flashcards/commit/8b4eceebfaaaf07e38ea64c7fe015aec86ac754f))
|
||||
|
||||
## [4.21.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.7...v4.21.0) (2025-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **tutorial:** add silentErrors prop to suppress error messages ([8835e1c](https://github.com/antialias/soroban-abacus-flashcards/commit/8835e1c57ab8adcecefe0db082360dd98fbfaac7))
|
||||
|
||||
## [4.20.7](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.6...v4.20.7) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **home:** use Panda CSS token() for dynamic colors and center arrows properly ([d52ba63](https://github.com/antialias/soroban-abacus-flashcards/commit/d52ba6373a4577655dc1e5f5ff4926af7f7d96c3))
|
||||
|
||||
## [4.20.6](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.5...v4.20.6) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **homepage:** use inline styles for journey level colors ([5d85e89](https://github.com/antialias/soroban-abacus-flashcards/commit/5d85e898d65d44d8d09bee952fad44b5a9c0cd20)), closes [#4ade80](https://github.com/antialias/soroban-abacus-flashcards/issues/4ade80) [#60a5](https://github.com/antialias/soroban-abacus-flashcards/issues/60a5) [#a78](https://github.com/antialias/soroban-abacus-flashcards/issues/a78) [#fbbf24](https://github.com/antialias/soroban-abacus-flashcards/issues/fbbf24)
|
||||
|
||||
## [4.20.5](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.4...v4.20.5) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **docker:** include Panda CSS styled-system in production image ([57fabff](https://github.com/antialias/soroban-abacus-flashcards/commit/57fabffe605d953b4a4d7e05032401cbf1ab2d14))
|
||||
|
||||
## [4.20.4](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.3...v4.20.4) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **homepage:** use inline styles for Your Journey text contrast ([8e51390](https://github.com/antialias/soroban-abacus-flashcards/commit/8e5139001818d7013e1b2654ac707f7429316d58)), closes [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#e5e7](https://github.com/antialias/soroban-abacus-flashcards/issues/e5e7) [#9ca3](https://github.com/antialias/soroban-abacus-flashcards/issues/9ca3) [#d1d5](https://github.com/antialias/soroban-abacus-flashcards/issues/d1d5)
|
||||
|
||||
## [4.20.3](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.2...v4.20.3) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **homepage:** use explicit RGBA colors for Your Journey text ([9c51cc9](https://github.com/antialias/soroban-abacus-flashcards/commit/9c51cc94eec4efcab9c0b9d1190f5b79c0c7d365))
|
||||
|
||||
## [4.20.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.1...v4.20.2) (2025-10-19)
|
||||
|
||||
|
||||
|
||||
@@ -59,6 +59,9 @@ RUN adduser --system --uid 1001 nextjs
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next ./apps/web/.next
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
|
||||
|
||||
# Copy Panda CSS generated styles
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/styled-system ./apps/web/styled-system
|
||||
|
||||
# Copy server files (compiled from TypeScript)
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/server.js ./apps/web/
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/apps/web/dist ./apps/web/dist
|
||||
|
||||
76
apps/web/.claude/PANDA_CSS_DYNAMIC_TOKENS.md
Normal file
76
apps/web/.claude/PANDA_CSS_DYNAMIC_TOKENS.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Panda CSS Dynamic Token Usage
|
||||
|
||||
## Problem: Dynamic Color Tokens Not Working
|
||||
|
||||
When using Panda CSS, color tokens like `blue.400`, `purple.400`, etc. don't work when used dynamically through variables in the `css()` function.
|
||||
|
||||
## Root Cause
|
||||
|
||||
Panda CSS's `css()` function requires **static values at build time**. It cannot process dynamic token references like:
|
||||
|
||||
```typescript
|
||||
// ❌ This doesn't work
|
||||
const color = 'blue.400'
|
||||
css({ color: color }) // Panda can't resolve this at build time
|
||||
```
|
||||
|
||||
The `css()` function performs static analysis during the build process to generate CSS classes. It cannot handle runtime-dynamic token paths.
|
||||
|
||||
## Solution: Use the `token()` Function
|
||||
|
||||
Panda CSS provides a `token()` function specifically for resolving token paths to their actual values at runtime:
|
||||
|
||||
```typescript
|
||||
import { token } from '../../styled-system/tokens'
|
||||
|
||||
// ✅ This works
|
||||
const stages = [
|
||||
{ level: '10 Kyu', label: 'Beginner', color: 'colors.green.400' },
|
||||
{ level: '5 Kyu', label: 'Intermediate', color: 'colors.blue.400' },
|
||||
{ level: '1 Kyu', label: 'Advanced', color: 'colors.violet.400' },
|
||||
{ level: 'Dan', label: 'Master', color: 'colors.amber.400' },
|
||||
] as const
|
||||
|
||||
// Use with inline styles, not css()
|
||||
<div style={{ color: token(stage.color) }}>
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Use `as const`**: TypeScript needs the array marked as `const` so the token strings are treated as literal types, not generic strings. The `token()` function expects the `Token` literal type.
|
||||
|
||||
2. **Use inline styles**: When using `token()`, apply colors via the `style` prop, not through the `css()` function:
|
||||
```typescript
|
||||
// ✅ Correct
|
||||
<div style={{ color: token(stage.color) }}>
|
||||
|
||||
// ❌ Won't work
|
||||
<div className={css({ color: token(stage.color) })}>
|
||||
```
|
||||
|
||||
3. **Static tokens in css()**: For static usage, you CAN use tokens directly in `css()`:
|
||||
```typescript
|
||||
// ✅ This works because it's static
|
||||
css({ color: 'blue.400' })
|
||||
```
|
||||
|
||||
## How token() Works
|
||||
|
||||
The `token()` function:
|
||||
- Takes a token path like `"colors.blue.400"`
|
||||
- Looks it up in the generated token registry (`styled-system/tokens/index.mjs`)
|
||||
- Returns the actual CSS value (e.g., `"#60a5fa"`)
|
||||
- Happens at runtime, not build time
|
||||
|
||||
## Token Type Definition
|
||||
|
||||
The `Token` type is a union of all valid token paths:
|
||||
```typescript
|
||||
type Token = "colors.blue.400" | "colors.green.400" | "colors.violet.400" | ...
|
||||
```
|
||||
|
||||
This is defined in `styled-system/tokens/tokens.d.ts`.
|
||||
|
||||
## Reference Implementation
|
||||
|
||||
See `src/app/page.tsx` lines 404-434 for a working example of dynamic token usage in the "Your Journey" section.
|
||||
@@ -1,11 +1,46 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react'
|
||||
import { PageWithNav } from '@/components/PageWithNav'
|
||||
import { TutorialPlayer } from '@/components/tutorial/TutorialPlayer'
|
||||
import { getTutorialForEditor } from '@/utils/tutorialConverter'
|
||||
import { css } from '../../styled-system/css'
|
||||
import { container, grid, hstack, stack } from '../../styled-system/patterns'
|
||||
import { token } from '../../styled-system/tokens'
|
||||
|
||||
// Mini abacus that cycles through random 3-digit numbers
|
||||
function MiniAbacus() {
|
||||
const [currentValue, setCurrentValue] = useState(0)
|
||||
const abacusConfig = useAbacusConfig({
|
||||
abacusColumns: 3,
|
||||
theme: 'dark',
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
// Cycle through random 3-digit numbers every 2 seconds
|
||||
const interval = setInterval(() => {
|
||||
const randomNum = Math.floor(Math.random() * 1000) // 0-999
|
||||
setCurrentValue(randomNum)
|
||||
}, 2000)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
className={css({
|
||||
height: '75px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
})}
|
||||
>
|
||||
<AbacusReact value={currentValue} config={abacusConfig} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function HomePage() {
|
||||
// Extract just the "Friends of 5" step (2+3=5) for homepage demo
|
||||
@@ -215,54 +250,169 @@ export default function HomePage() {
|
||||
border: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
shadow: 'lg',
|
||||
maxW: '900px',
|
||||
maxW: '1200px',
|
||||
mx: 'auto',
|
||||
})}
|
||||
>
|
||||
{/* What you'll learn - above tutorial */}
|
||||
<div
|
||||
className={css({
|
||||
mb: '8',
|
||||
pb: '6',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
display: 'flex',
|
||||
flexDirection: { base: 'column', md: 'row' },
|
||||
gap: '8',
|
||||
alignItems: { base: 'center', md: 'flex-start' },
|
||||
})}
|
||||
>
|
||||
<h3
|
||||
{/* Tutorial on the left */}
|
||||
<div className={css({ flex: '1' })}>
|
||||
<TutorialPlayer
|
||||
tutorial={friendsOf5Tutorial}
|
||||
isDebugMode={false}
|
||||
showDebugPanel={false}
|
||||
hideNavigation={true}
|
||||
hideTooltip={true}
|
||||
silentErrors={true}
|
||||
abacusColumns={1}
|
||||
theme="dark"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* What you'll learn on the right */}
|
||||
<div
|
||||
className={css({
|
||||
fontSize: 'xl',
|
||||
fontWeight: 'bold',
|
||||
color: 'white',
|
||||
mb: '4',
|
||||
textAlign: 'center',
|
||||
flex: '0 0 auto',
|
||||
minW: '340px',
|
||||
maxW: { base: '100%', md: '420px' },
|
||||
})}
|
||||
>
|
||||
What You'll Learn
|
||||
</h3>
|
||||
<div className={grid({ columns: { base: 1, sm: 2 }, gap: '3' })}>
|
||||
{[
|
||||
'Read and set numbers on an abacus',
|
||||
'Add and subtract with "friends" techniques',
|
||||
'Multiply and divide fluently',
|
||||
'Calculate mentally without the abacus',
|
||||
].map((skill, i) => (
|
||||
<div key={i} className={hstack({ gap: '3' })}>
|
||||
<span className={css({ color: 'yellow.400', fontSize: 'lg' })}>✓</span>
|
||||
<span className={css({ color: 'gray.300', fontSize: 'sm' })}>{skill}</span>
|
||||
</div>
|
||||
))}
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: '2xl',
|
||||
fontWeight: 'bold',
|
||||
color: 'white',
|
||||
mb: '6',
|
||||
})}
|
||||
>
|
||||
What You'll Learn
|
||||
</h3>
|
||||
<div className={stack({ gap: '5' })}>
|
||||
{[
|
||||
{
|
||||
icon: '🔢',
|
||||
title: 'Read and set numbers',
|
||||
desc: 'Master abacus number representation from zero to thousands',
|
||||
example: '0-9999',
|
||||
badge: 'Foundation',
|
||||
},
|
||||
{
|
||||
icon: '🤝',
|
||||
title: 'Friends techniques',
|
||||
desc: 'Add and subtract using complement pairs and mental shortcuts',
|
||||
example: '5 = 2+3',
|
||||
badge: 'Core',
|
||||
},
|
||||
{
|
||||
icon: '✖️➗',
|
||||
title: 'Multiply & divide',
|
||||
desc: 'Fluent multi-digit calculations with advanced techniques',
|
||||
example: '12×34',
|
||||
badge: 'Advanced',
|
||||
},
|
||||
{
|
||||
icon: '🧠',
|
||||
title: 'Mental calculation',
|
||||
desc: 'Visualize and compute without the physical tool (Anzan)',
|
||||
example: 'Speed math',
|
||||
badge: 'Expert',
|
||||
},
|
||||
].map((skill, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={css({
|
||||
bg: 'linear-gradient(135deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.03))',
|
||||
borderRadius: 'xl',
|
||||
p: '4',
|
||||
border: '1px solid',
|
||||
borderColor: 'rgba(255, 255, 255, 0.15)',
|
||||
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)',
|
||||
transition: 'all 0.2s',
|
||||
_hover: {
|
||||
bg: 'linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05))',
|
||||
borderColor: 'rgba(255, 255, 255, 0.25)',
|
||||
transform: 'translateY(-2px)',
|
||||
boxShadow: '0 6px 16px rgba(0, 0, 0, 0.4)',
|
||||
},
|
||||
})}
|
||||
>
|
||||
<div className={hstack({ gap: '3', alignItems: 'flex-start' })}>
|
||||
<div
|
||||
className={css({
|
||||
fontSize: '3xl',
|
||||
minW: '50px',
|
||||
textAlign: 'center',
|
||||
bg: 'rgba(255, 255, 255, 0.1)',
|
||||
borderRadius: 'lg',
|
||||
p: '2',
|
||||
})}
|
||||
>
|
||||
{i === 0 ? <MiniAbacus /> : skill.icon}
|
||||
</div>
|
||||
<div className={stack({ gap: '2', flex: '1' })}>
|
||||
<div className={hstack({ gap: '2', alignItems: 'center' })}>
|
||||
<div
|
||||
className={css({
|
||||
color: 'white',
|
||||
fontSize: 'md',
|
||||
fontWeight: 'bold',
|
||||
})}
|
||||
>
|
||||
{skill.title}
|
||||
</div>
|
||||
<div
|
||||
className={css({
|
||||
bg: 'rgba(250, 204, 21, 0.2)',
|
||||
color: 'yellow.400',
|
||||
fontSize: '2xs',
|
||||
fontWeight: 'semibold',
|
||||
px: '2',
|
||||
py: '0.5',
|
||||
borderRadius: 'md',
|
||||
})}
|
||||
>
|
||||
{skill.badge}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={css({
|
||||
color: 'gray.300',
|
||||
fontSize: 'xs',
|
||||
lineHeight: '1.5',
|
||||
})}
|
||||
>
|
||||
{skill.desc}
|
||||
</div>
|
||||
<div
|
||||
className={css({
|
||||
color: 'yellow.400',
|
||||
fontSize: 'xs',
|
||||
fontFamily: 'mono',
|
||||
fontWeight: 'semibold',
|
||||
mt: '1',
|
||||
bg: 'rgba(250, 204, 21, 0.1)',
|
||||
px: '2',
|
||||
py: '1',
|
||||
borderRadius: 'md',
|
||||
w: 'fit-content',
|
||||
})}
|
||||
>
|
||||
{skill.example}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TutorialPlayer
|
||||
tutorial={friendsOf5Tutorial}
|
||||
isDebugMode={false}
|
||||
showDebugPanel={false}
|
||||
hideNavigation={true}
|
||||
hideTooltip={true}
|
||||
abacusColumns={2}
|
||||
theme="dark"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -379,9 +529,7 @@ export default function HomePage() {
|
||||
>
|
||||
Your Journey
|
||||
</h2>
|
||||
<p className={css({ color: 'gray.200', fontSize: 'md' })}>
|
||||
Progress from beginner to master
|
||||
</p>
|
||||
<p style={{ color: '#e5e7eb', fontSize: '16px' }}>Progress from beginner to master</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
@@ -402,12 +550,14 @@ export default function HomePage() {
|
||||
flexWrap: 'wrap',
|
||||
})}
|
||||
>
|
||||
{[
|
||||
{ level: '10 Kyu', label: 'Beginner', color: 'green.400' },
|
||||
{ level: '5 Kyu', label: 'Intermediate', color: 'blue.400' },
|
||||
{ level: '1 Kyu', label: 'Advanced', color: 'purple.400' },
|
||||
{ level: 'Dan', label: 'Master', color: 'yellow.400' },
|
||||
].map((stage, i) => (
|
||||
{(
|
||||
[
|
||||
{ level: '10 Kyu', label: 'Beginner', color: 'colors.green.400' },
|
||||
{ level: '5 Kyu', label: 'Intermediate', color: 'colors.blue.400' },
|
||||
{ level: '1 Kyu', label: 'Advanced', color: 'colors.violet.400' },
|
||||
{ level: 'Dan', label: 'Master', color: 'colors.amber.400' },
|
||||
] as const
|
||||
).map((stage, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className={stack({
|
||||
@@ -421,20 +571,32 @@ export default function HomePage() {
|
||||
className={css({
|
||||
fontSize: 'xl',
|
||||
fontWeight: 'bold',
|
||||
color: stage.color,
|
||||
})}
|
||||
style={{ color: token(stage.color) }}
|
||||
>
|
||||
{stage.level}
|
||||
</div>
|
||||
<div className={css({ fontSize: 'sm', color: 'gray.200' })}>{stage.label}</div>
|
||||
<div
|
||||
className={css({
|
||||
fontSize: 'sm',
|
||||
color: 'gray.300',
|
||||
})}
|
||||
>
|
||||
{stage.label}
|
||||
</div>
|
||||
{i < 3 && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: '100%',
|
||||
marginLeft: '0.5rem',
|
||||
top: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
fontSize: '20px',
|
||||
color: '#9ca3af',
|
||||
}}
|
||||
className={css({
|
||||
display: { base: 'none', md: 'block' },
|
||||
position: 'absolute',
|
||||
right: '-50%',
|
||||
fontSize: 'xl',
|
||||
color: 'gray.400',
|
||||
})}
|
||||
>
|
||||
→
|
||||
@@ -444,12 +606,14 @@ export default function HomePage() {
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
textAlign: 'center',
|
||||
fontSize: '14px',
|
||||
color: '#d1d5db',
|
||||
fontStyle: 'italic',
|
||||
}}
|
||||
className={css({
|
||||
mt: '6',
|
||||
textAlign: 'center',
|
||||
fontSize: 'sm',
|
||||
color: 'gray.300',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
You'll progress through all these levels eventually ↑
|
||||
|
||||
@@ -217,6 +217,7 @@ interface TutorialPlayerProps {
|
||||
showDebugPanel?: boolean
|
||||
hideNavigation?: boolean
|
||||
hideTooltip?: boolean
|
||||
silentErrors?: boolean
|
||||
abacusColumns?: number
|
||||
theme?: 'light' | 'dark'
|
||||
onStepChange?: (stepIndex: number, step: TutorialStep) => void
|
||||
@@ -233,6 +234,7 @@ function TutorialPlayerContent({
|
||||
showDebugPanel = false,
|
||||
hideNavigation = false,
|
||||
hideTooltip = false,
|
||||
silentErrors = false,
|
||||
abacusColumns = 5,
|
||||
theme = 'light',
|
||||
onStepChange,
|
||||
@@ -638,7 +640,7 @@ function TutorialPlayerContent({
|
||||
maxWidth: '200px',
|
||||
minWidth: '150px',
|
||||
wordBreak: 'break-word',
|
||||
zIndex: 1000,
|
||||
zIndex: 50,
|
||||
opacity: 0.95,
|
||||
transition: 'all 0.3s ease',
|
||||
transform: showCelebration ? 'scale(1.05)' : 'scale(1)',
|
||||
@@ -909,7 +911,7 @@ function TutorialPlayerContent({
|
||||
)
|
||||
})
|
||||
|
||||
if (!isCorrectBead) {
|
||||
if (!isCorrectBead && !silentErrors) {
|
||||
const errorMessage = "That's not the highlighted bead. Try clicking the highlighted bead."
|
||||
dispatch({
|
||||
type: 'SET_ERROR',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soroban-monorepo",
|
||||
"version": "4.20.2",
|
||||
"version": "4.24.0",
|
||||
"private": true,
|
||||
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
|
||||
"workspaces": [
|
||||
|
||||
Reference in New Issue
Block a user