Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f18a89974a | ||
|
|
bf1ced43f8 | ||
|
|
6435027147 | ||
|
|
4d906ec20e | ||
|
|
ff7b711fe0 | ||
|
|
d42f9b2d9a | ||
|
|
faaefbacff |
26
CHANGELOG.md
26
CHANGELOG.md
@@ -1,3 +1,29 @@
|
||||
## [4.17.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.1...v4.17.2) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tutorial:** correct column index calculation for variable column counts ([bf1ced4](https://github.com/antialias/soroban-abacus-flashcards/commit/bf1ced43f801938b05f01548eea5fe771de1b58f))
|
||||
|
||||
## [4.17.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.0...v4.17.1) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tutorial:** filter bead highlights when using fewer columns ([4d906ec](https://github.com/antialias/soroban-abacus-flashcards/commit/4d906ec20e90a9b0b3838f9b8428e0c68992f381))
|
||||
|
||||
## [4.17.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.16.0...v4.17.0) (2025-10-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **tutorial:** add dark theme and column control props ([d42f9b2](https://github.com/antialias/soroban-abacus-flashcards/commit/d42f9b2d9ad630826c55b753dc581c469e8f9083))
|
||||
|
||||
|
||||
### Styles
|
||||
|
||||
* **homepage:** soften tutorial styling for dark theme cohesion ([faaefba](https://github.com/antialias/soroban-abacus-flashcards/commit/faaefbacff419b337aa0fac4a101d5106a18c77c)), closes [#f9](https://github.com/antialias/soroban-abacus-flashcards/issues/f9)
|
||||
|
||||
## [4.16.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.15.1...v4.16.0) (2025-10-19)
|
||||
|
||||
|
||||
|
||||
@@ -292,10 +292,10 @@ export default function RoomPage() {
|
||||
disabled={isDisabled}
|
||||
style={{
|
||||
background: gameDef.manifest.gradient,
|
||||
borderColor: gameDef.manifest.borderColor,
|
||||
}}
|
||||
className={css({
|
||||
border: '2px solid',
|
||||
borderColor: gameDef.manifest.borderColor,
|
||||
borderRadius: '2xl',
|
||||
padding: '6',
|
||||
cursor: isDisabled ? 'not-allowed' : 'pointer',
|
||||
|
||||
@@ -206,42 +206,49 @@ export default function HomePage() {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={grid({ columns: { base: 1, lg: 2 }, gap: '8' })}>
|
||||
{/* Live demo */}
|
||||
{/* Live demo and learning objectives */}
|
||||
<div
|
||||
className={css({
|
||||
bg: 'rgba(0, 0, 0, 0.4)',
|
||||
rounded: 'xl',
|
||||
p: '8',
|
||||
border: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
shadow: 'lg',
|
||||
maxW: '900px',
|
||||
mx: 'auto',
|
||||
})}
|
||||
>
|
||||
<TutorialPlayer
|
||||
tutorial={friendsOf5Tutorial}
|
||||
isDebugMode={false}
|
||||
showDebugPanel={false}
|
||||
hideNavigation={true}
|
||||
abacusColumns={2}
|
||||
theme="dark"
|
||||
/>
|
||||
|
||||
{/* What you'll learn - below tutorial */}
|
||||
<div
|
||||
className={css({
|
||||
bg: 'rgba(0, 0, 0, 0.4)',
|
||||
rounded: 'xl',
|
||||
p: '6',
|
||||
border: '1px solid',
|
||||
mt: '8',
|
||||
pt: '6',
|
||||
borderTop: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
shadow: 'lg',
|
||||
})}
|
||||
>
|
||||
<TutorialPlayer
|
||||
tutorial={friendsOf5Tutorial}
|
||||
isDebugMode={false}
|
||||
showDebugPanel={false}
|
||||
hideNavigation={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* What you'll learn */}
|
||||
<div
|
||||
className={stack({
|
||||
gap: '4',
|
||||
bg: 'rgba(0, 0, 0, 0.4)',
|
||||
p: '6',
|
||||
borderRadius: 'xl',
|
||||
border: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
justifyContent: 'center',
|
||||
})}
|
||||
>
|
||||
<h3 className={css({ fontSize: 'xl', fontWeight: 'bold', color: 'white' })}>
|
||||
<h3
|
||||
className={css({
|
||||
fontSize: 'xl',
|
||||
fontWeight: 'bold',
|
||||
color: 'white',
|
||||
mb: '4',
|
||||
textAlign: 'center',
|
||||
})}
|
||||
>
|
||||
What You'll Learn
|
||||
</h3>
|
||||
<div className={stack({ gap: '3' })}>
|
||||
<div className={grid({ columns: { base: 1, sm: 2 }, gap: '3' })}>
|
||||
{[
|
||||
'Read and set numbers on an abacus',
|
||||
'Add and subtract with "friends" techniques',
|
||||
@@ -250,7 +257,7 @@ export default function HomePage() {
|
||||
].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: 'md' })}>{skill}</span>
|
||||
<span className={css({ color: 'gray.300', fontSize: 'sm' })}>{skill}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -216,6 +216,8 @@ interface TutorialPlayerProps {
|
||||
isDebugMode?: boolean
|
||||
showDebugPanel?: boolean
|
||||
hideNavigation?: boolean
|
||||
abacusColumns?: number
|
||||
theme?: 'light' | 'dark'
|
||||
onStepChange?: (stepIndex: number, step: TutorialStep) => void
|
||||
onStepComplete?: (stepIndex: number, step: TutorialStep, success: boolean) => void
|
||||
onTutorialComplete?: (score: number, timeSpent: number) => void
|
||||
@@ -229,6 +231,8 @@ function TutorialPlayerContent({
|
||||
isDebugMode = false,
|
||||
showDebugPanel = false,
|
||||
hideNavigation = false,
|
||||
abacusColumns = 5,
|
||||
theme = 'light',
|
||||
onStepChange,
|
||||
onStepComplete,
|
||||
onTutorialComplete,
|
||||
@@ -381,16 +385,20 @@ function TutorialPlayerContent({
|
||||
}
|
||||
|
||||
// Convert bead diff results to StepBeadHighlight format expected by AbacusReact
|
||||
const stepBeadHighlights: StepBeadHighlight[] = beadDiff.changes.map((change, _index) => ({
|
||||
placeValue: change.placeValue,
|
||||
beadType: change.beadType,
|
||||
position: change.position,
|
||||
direction: change.direction,
|
||||
stepIndex: currentMultiStep, // Use current multi-step index to match AbacusReact filtering
|
||||
order: change.order,
|
||||
}))
|
||||
// Filter to only include beads from columns that exist
|
||||
const minValidPlaceValue = Math.max(0, 5 - abacusColumns)
|
||||
const stepBeadHighlights: StepBeadHighlight[] = beadDiff.changes
|
||||
.filter((change) => change.placeValue < abacusColumns)
|
||||
.map((change, _index) => ({
|
||||
placeValue: change.placeValue,
|
||||
beadType: change.beadType,
|
||||
position: change.position,
|
||||
direction: change.direction,
|
||||
stepIndex: currentMultiStep, // Use current multi-step index to match AbacusReact filtering
|
||||
order: change.order,
|
||||
}))
|
||||
|
||||
return stepBeadHighlights
|
||||
return stepBeadHighlights.length > 0 ? stepBeadHighlights : undefined
|
||||
} catch (error) {
|
||||
console.error('Error generating step beads with bead diff:', error)
|
||||
return undefined
|
||||
@@ -401,6 +409,7 @@ function TutorialPlayerContent({
|
||||
expectedSteps,
|
||||
currentMultiStep,
|
||||
currentStep.stepBeadHighlights,
|
||||
abacusColumns,
|
||||
])
|
||||
|
||||
// Get the current step's bead diff summary for real-time user feedback
|
||||
@@ -424,6 +433,15 @@ function TutorialPlayerContent({
|
||||
// Get current step summary for real-time user feedback
|
||||
const currentStepSummary = getCurrentStepSummary()
|
||||
|
||||
// Filter highlightBeads to only include valid columns
|
||||
const filteredHighlightBeads = useMemo(() => {
|
||||
if (!currentStep.highlightBeads) return undefined
|
||||
return currentStep.highlightBeads.filter((highlight) => {
|
||||
const placeValue = highlight.placeValue ?? 4 - (highlight.columnIndex ?? 0)
|
||||
return placeValue < abacusColumns
|
||||
})
|
||||
}, [currentStep.highlightBeads, abacusColumns])
|
||||
|
||||
// Helper function to highlight the current mathematical term in the full decomposition
|
||||
const renderHighlightedDecomposition = useCallback(() => {
|
||||
if (!fullDecomposition || expectedSteps.length === 0) return null
|
||||
@@ -521,16 +539,27 @@ function TutorialPlayerContent({
|
||||
return null
|
||||
}
|
||||
|
||||
// Validate that the bead is from a column that exists
|
||||
if (topmostBead.placeValue >= abacusColumns) {
|
||||
// Bead is from an invalid column, skip tooltip
|
||||
return null
|
||||
}
|
||||
|
||||
// Smart positioning logic: avoid covering active beads
|
||||
const targetColumnIndex = 4 - topmostBead.placeValue // Convert placeValue to columnIndex (5 columns: 0-4)
|
||||
// Convert placeValue to columnIndex based on actual number of columns
|
||||
const targetColumnIndex = abacusColumns - 1 - topmostBead.placeValue
|
||||
|
||||
// Check if there are any active beads (against reckoning bar OR with arrows) in columns to the left
|
||||
const hasActiveBeadsToLeft = (() => {
|
||||
// Get current abacus state - we need to check which beads are against the reckoning bar
|
||||
const abacusDigits = currentValue.toString().padStart(5, '0').split('').map(Number)
|
||||
const abacusDigits = currentValue
|
||||
.toString()
|
||||
.padStart(abacusColumns, '0')
|
||||
.split('')
|
||||
.map(Number)
|
||||
|
||||
for (let col = 0; col < targetColumnIndex; col++) {
|
||||
const _placeValue = 4 - col // Convert columnIndex back to placeValue
|
||||
const placeValue = abacusColumns - 1 - col // Convert columnIndex back to placeValue
|
||||
const digitValue = abacusDigits[col]
|
||||
|
||||
// Check if any beads are active (against reckoning bar) in this column
|
||||
@@ -546,7 +575,7 @@ function TutorialPlayerContent({
|
||||
// Also check if this column has beads with direction arrows (from current step)
|
||||
const hasArrowsInColumn =
|
||||
currentStepBeads?.some((bead) => {
|
||||
const beadColumnIndex = 4 - bead.placeValue
|
||||
const beadColumnIndex = abacusColumns - 1 - bead.placeValue
|
||||
return beadColumnIndex === col && bead.direction && bead.direction !== 'none'
|
||||
}) ?? false
|
||||
if (hasArrowsInColumn) {
|
||||
@@ -672,6 +701,7 @@ function TutorialPlayerContent({
|
||||
currentValue,
|
||||
currentStep,
|
||||
isMeaningfulDecomposition,
|
||||
abacusColumns,
|
||||
])
|
||||
|
||||
// Timer for smart help detection
|
||||
@@ -1005,6 +1035,9 @@ function TutorialPlayerContent({
|
||||
|
||||
// Memoize custom styles calculation to avoid expensive recalculation on every render
|
||||
const customStyles = useMemo(() => {
|
||||
// Calculate valid column range based on abacusColumns
|
||||
const minValidColumn = 5 - abacusColumns
|
||||
|
||||
// Start with static highlights from step configuration
|
||||
const staticHighlights: Record<number, any> = {}
|
||||
|
||||
@@ -1014,6 +1047,11 @@ function TutorialPlayerContent({
|
||||
const columnIndex =
|
||||
highlight.placeValue !== undefined ? 4 - highlight.placeValue : highlight.columnIndex
|
||||
|
||||
// Skip highlights for columns that don't exist
|
||||
if (columnIndex < minValidColumn) {
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize column if it doesn't exist
|
||||
if (!staticHighlights[columnIndex]) {
|
||||
staticHighlights[columnIndex] = {}
|
||||
@@ -1043,6 +1081,12 @@ function TutorialPlayerContent({
|
||||
const mergedHighlights = { ...staticHighlights }
|
||||
Object.keys(dynamicColumnHighlights).forEach((columnIndexStr) => {
|
||||
const columnIndex = parseInt(columnIndexStr, 10)
|
||||
|
||||
// Skip highlights for columns that don't exist
|
||||
if (columnIndex < minValidColumn) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!mergedHighlights[columnIndex]) {
|
||||
mergedHighlights[columnIndex] = {}
|
||||
}
|
||||
@@ -1051,7 +1095,7 @@ function TutorialPlayerContent({
|
||||
})
|
||||
|
||||
return Object.keys(mergedHighlights).length > 0 ? { columns: mergedHighlights } : undefined
|
||||
}, [currentStep.highlightBeads, dynamicColumnHighlights])
|
||||
}, [currentStep.highlightBeads, dynamicColumnHighlights, abacusColumns])
|
||||
|
||||
if (!currentStep) {
|
||||
return <div>No steps available</div>
|
||||
@@ -1319,11 +1363,18 @@ function TutorialPlayerContent({
|
||||
fontSize: '2xl',
|
||||
fontWeight: 'bold',
|
||||
mb: 2,
|
||||
color: theme === 'dark' ? 'gray.200' : 'gray.900',
|
||||
})}
|
||||
>
|
||||
{currentStep.problem}
|
||||
</h2>
|
||||
<p className={css({ fontSize: 'lg', color: 'gray.700', mb: 4 })}>
|
||||
<p
|
||||
className={css({
|
||||
fontSize: 'lg',
|
||||
color: theme === 'dark' ? 'gray.400' : 'gray.700',
|
||||
mb: 4,
|
||||
})}
|
||||
>
|
||||
{currentStep.description}
|
||||
</p>
|
||||
{/* Hide action description for multi-step problems since it duplicates pedagogical decomposition */}
|
||||
@@ -1341,12 +1392,19 @@ function TutorialPlayerContent({
|
||||
className={css({
|
||||
p: 5,
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(255,248,225,0.95) 0%, rgba(254,252,232,0.95) 50%, rgba(255,245,157,0.15) 100%)',
|
||||
theme === 'dark'
|
||||
? 'linear-gradient(135deg, rgba(40,40,50,0.6) 0%, rgba(50,50,60,0.6) 50%, rgba(60,50,70,0.3) 100%)'
|
||||
: 'linear-gradient(135deg, rgba(255,248,225,0.95) 0%, rgba(254,252,232,0.95) 50%, rgba(255,245,157,0.15) 100%)',
|
||||
backdropFilter: 'blur(10px)',
|
||||
border: '1px solid rgba(251,191,36,0.3)',
|
||||
border:
|
||||
theme === 'dark'
|
||||
? '1px solid rgba(255,255,255,0.1)'
|
||||
: '1px solid rgba(251,191,36,0.3)',
|
||||
borderRadius: 'xl',
|
||||
boxShadow:
|
||||
'0 8px 32px rgba(251,191,36,0.1), 0 2px 8px rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.6)',
|
||||
theme === 'dark'
|
||||
? '0 4px 16px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.05)'
|
||||
: '0 8px 32px rgba(251,191,36,0.1), 0 2px 8px rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.6)',
|
||||
position: 'relative',
|
||||
maxW: '600px',
|
||||
w: 'full',
|
||||
@@ -1356,7 +1414,9 @@ function TutorialPlayerContent({
|
||||
inset: '0',
|
||||
borderRadius: 'xl',
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(251,191,36,0.1) 0%, rgba(168,85,247,0.05) 100%)',
|
||||
theme === 'dark'
|
||||
? 'linear-gradient(135deg, rgba(100,100,120,0.1) 0%, rgba(80,60,100,0.05) 100%)'
|
||||
: 'linear-gradient(135deg, rgba(251,191,36,0.1) 0%, rgba(168,85,247,0.05) 100%)',
|
||||
zIndex: -1,
|
||||
},
|
||||
})}
|
||||
@@ -1365,10 +1425,10 @@ function TutorialPlayerContent({
|
||||
className={css({
|
||||
fontSize: 'base',
|
||||
fontWeight: '600',
|
||||
color: 'amber.900',
|
||||
color: theme === 'dark' ? 'gray.300' : 'amber.900',
|
||||
mb: 4,
|
||||
letterSpacing: 'wide',
|
||||
textShadow: '0 1px 2px rgba(0,0,0,0.1)',
|
||||
textShadow: theme === 'dark' ? 'none' : '0 1px 2px rgba(0,0,0,0.1)',
|
||||
})}
|
||||
>
|
||||
Guidance
|
||||
@@ -1381,18 +1441,25 @@ function TutorialPlayerContent({
|
||||
mb: 4,
|
||||
p: 3,
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.9) 100%)',
|
||||
border: '1px solid rgba(203,213,225,0.4)',
|
||||
theme === 'dark'
|
||||
? 'linear-gradient(135deg, rgba(50,50,60,0.4) 0%, rgba(40,40,50,0.5) 100%)'
|
||||
: 'linear-gradient(135deg, rgba(255,255,255,0.8) 0%, rgba(248,250,252,0.9) 100%)',
|
||||
border:
|
||||
theme === 'dark'
|
||||
? '1px solid rgba(255,255,255,0.1)'
|
||||
: '1px solid rgba(203,213,225,0.4)',
|
||||
borderRadius: 'lg',
|
||||
boxShadow:
|
||||
'0 2px 8px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.7)',
|
||||
theme === 'dark'
|
||||
? '0 1px 4px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.05)'
|
||||
: '0 2px 8px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.7)',
|
||||
backdropFilter: 'blur(4px)',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
fontSize: 'base',
|
||||
color: 'slate.800',
|
||||
color: theme === 'dark' ? 'gray.300' : 'slate.800',
|
||||
fontFamily: 'mono',
|
||||
fontWeight: '500',
|
||||
letterSpacing: 'tight',
|
||||
@@ -1411,7 +1478,7 @@ function TutorialPlayerContent({
|
||||
<div
|
||||
className={css({
|
||||
fontSize: 'sm',
|
||||
color: 'amber.800',
|
||||
color: theme === 'dark' ? 'gray.400' : 'amber.800',
|
||||
fontWeight: '500',
|
||||
lineHeight: '1.6',
|
||||
})}
|
||||
@@ -1489,17 +1556,17 @@ function TutorialPlayerContent({
|
||||
{/* Abacus */}
|
||||
<div
|
||||
className={css({
|
||||
bg: 'white',
|
||||
bg: theme === 'dark' ? 'rgba(30, 30, 40, 0.4)' : 'white',
|
||||
border: '2px solid',
|
||||
borderColor: 'gray.200',
|
||||
borderColor: theme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'gray.200',
|
||||
borderRadius: 'lg',
|
||||
p: 6,
|
||||
shadow: 'lg',
|
||||
shadow: theme === 'dark' ? '0 4px 6px rgba(0, 0, 0, 0.3)' : 'lg',
|
||||
})}
|
||||
>
|
||||
<AbacusReact
|
||||
value={currentValue}
|
||||
columns={5}
|
||||
columns={abacusColumns}
|
||||
interactive={true}
|
||||
animated={true}
|
||||
scaleFactor={2.5}
|
||||
@@ -1508,7 +1575,7 @@ function TutorialPlayerContent({
|
||||
hideInactiveBeads={abacusConfig.hideInactiveBeads}
|
||||
soundEnabled={abacusConfig.soundEnabled}
|
||||
soundVolume={abacusConfig.soundVolume}
|
||||
highlightBeads={currentStep.highlightBeads}
|
||||
highlightBeads={filteredHighlightBeads}
|
||||
stepBeadHighlights={currentStepBeads}
|
||||
currentStep={currentMultiStep}
|
||||
showDirectionIndicators={true}
|
||||
|
||||
@@ -21,52 +21,52 @@ export const GAME_THEMES = {
|
||||
blue: {
|
||||
color: 'blue',
|
||||
gradient: 'linear-gradient(135deg, #dbeafe, #bfdbfe)', // blue.100 to blue.200
|
||||
borderColor: 'blue.200',
|
||||
borderColor: '#bfdbfe', // blue.200
|
||||
},
|
||||
purple: {
|
||||
color: 'purple',
|
||||
gradient: 'linear-gradient(135deg, #e9d5ff, #ddd6fe)', // purple.100 to purple.200
|
||||
borderColor: 'purple.200',
|
||||
borderColor: '#ddd6fe', // purple.200
|
||||
},
|
||||
green: {
|
||||
color: 'green',
|
||||
gradient: 'linear-gradient(135deg, #d1fae5, #a7f3d0)', // green.100 to green.200
|
||||
borderColor: 'green.200',
|
||||
borderColor: '#a7f3d0', // green.200
|
||||
},
|
||||
teal: {
|
||||
color: 'teal',
|
||||
gradient: 'linear-gradient(135deg, #ccfbf1, #99f6e4)', // teal.100 to teal.200
|
||||
borderColor: 'teal.200',
|
||||
borderColor: '#99f6e4', // teal.200
|
||||
},
|
||||
indigo: {
|
||||
color: 'indigo',
|
||||
gradient: 'linear-gradient(135deg, #e0e7ff, #c7d2fe)', // indigo.100 to indigo.200
|
||||
borderColor: 'indigo.200',
|
||||
borderColor: '#c7d2fe', // indigo.200
|
||||
},
|
||||
pink: {
|
||||
color: 'pink',
|
||||
gradient: 'linear-gradient(135deg, #fce7f3, #fbcfe8)', // pink.100 to pink.200
|
||||
borderColor: 'pink.200',
|
||||
borderColor: '#fbcfe8', // pink.200
|
||||
},
|
||||
orange: {
|
||||
color: 'orange',
|
||||
gradient: 'linear-gradient(135deg, #ffedd5, #fed7aa)', // orange.100 to orange.200
|
||||
borderColor: 'orange.200',
|
||||
borderColor: '#fed7aa', // orange.200
|
||||
},
|
||||
yellow: {
|
||||
color: 'yellow',
|
||||
gradient: 'linear-gradient(135deg, #fef3c7, #fde68a)', // yellow.100 to yellow.200
|
||||
borderColor: 'yellow.200',
|
||||
borderColor: '#fde68a', // yellow.200
|
||||
},
|
||||
red: {
|
||||
color: 'red',
|
||||
gradient: 'linear-gradient(135deg, #fee2e2, #fecaca)', // red.100 to red.200
|
||||
borderColor: 'red.200',
|
||||
borderColor: '#fecaca', // red.200
|
||||
},
|
||||
gray: {
|
||||
color: 'gray',
|
||||
gradient: 'linear-gradient(135deg, #f3f4f6, #e5e7eb)', // gray.100 to gray.200
|
||||
borderColor: 'gray.200',
|
||||
borderColor: '#e5e7eb', // gray.200
|
||||
},
|
||||
} as const satisfies Record<string, GameTheme>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soroban-monorepo",
|
||||
"version": "4.16.0",
|
||||
"version": "4.17.2",
|
||||
"private": true,
|
||||
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
|
||||
"workspaces": [
|
||||
|
||||
Reference in New Issue
Block a user