Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3564bd51dc | ||
|
|
cc315645de | ||
|
|
035d8312c7 | ||
|
|
5f9b2dfe2b | ||
|
|
1bfde8fb25 | ||
|
|
48647e4fb5 | ||
|
|
318f9469a0 | ||
|
|
4bace36561 | ||
|
|
8c2ddca28d | ||
|
|
eff44b3ad1 | ||
|
|
f81b88ae30 | ||
|
|
71b1b933b5 | ||
|
|
92d50673e5 | ||
|
|
c229faffac | ||
|
|
463841e191 | ||
|
|
3a3706cc6f | ||
|
|
721dfe426d | ||
|
|
9dba75c9d9 | ||
|
|
f9a7cb7f05 | ||
|
|
ae7463d917 | ||
|
|
02b6c70b7a |
73
CHANGELOG.md
73
CHANGELOG.md
@@ -1,3 +1,76 @@
|
||||
## [4.48.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.48.1...v4.48.2) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **nav:** ensure nav bar appears above tutorial tooltips ([cc31564](https://github.com/antialias/soroban-abacus-flashcards/commit/cc315645de30218d1b034da3e130458fe2961a69))
|
||||
|
||||
|
||||
### Styles
|
||||
|
||||
* **hero:** unify background with rest of homepage ([035d831](https://github.com/antialias/soroban-abacus-flashcards/commit/035d8312c707cbf5b0e2a725d7b1d8ff406f842d))
|
||||
|
||||
## [4.48.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.48.0...v4.48.1) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hero:** prevent SSR hydration mismatch for subtitle ([1bfde8f](https://github.com/antialias/soroban-abacus-flashcards/commit/1bfde8fb251b227ccd2528bfe1c47acffd79fa49))
|
||||
|
||||
## [4.48.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.47.2...v4.48.0) (2025-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **hero:** persist random subtitle per-session ([318f946](https://github.com/antialias/soroban-abacus-flashcards/commit/318f9469a0805c200c55ce4024a95fd7b8dbe6a2))
|
||||
|
||||
## [4.47.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.47.1...v4.47.2) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **nav:** prevent thrashing by using fixed position always ([eff44b3](https://github.com/antialias/soroban-abacus-flashcards/commit/eff44b3ad1ea0535c6965ad58012f9275cb143ec))
|
||||
* **nav:** remove unnecessary borders from transparent nav ([8c2ddca](https://github.com/antialias/soroban-abacus-flashcards/commit/8c2ddca28dbdd7743227eed4d19a9a8f662a72b5))
|
||||
|
||||
## [4.47.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.47.0...v4.47.1) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hero:** prevent nav thrashing with hysteresis ([71b1b93](https://github.com/antialias/soroban-abacus-flashcards/commit/71b1b933b598c0a6a8aef1bc9f8c598c1871b2eb))
|
||||
|
||||
## [4.47.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.46.2...v4.47.0) (2025-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **nav:** add transparent nav bar with borders when hero visible ([463841e](https://github.com/antialias/soroban-abacus-flashcards/commit/463841e1910f4ddb9af662f036e4efb867836a83))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hero:** use Number.isNaN instead of global isNaN ([c229faf](https://github.com/antialias/soroban-abacus-flashcards/commit/c229faffac525f3eebeb12155cb5ca4dff744472))
|
||||
|
||||
|
||||
### Styles
|
||||
|
||||
* **hero-abacus:** add purple bead colors for dark theme ([721dfe4](https://github.com/antialias/soroban-abacus-flashcards/commit/721dfe426db4fe259f6cdeac587d008339df769b))
|
||||
* **hero:** adjust spacing between title, subtitle, and abacus ([3a3706c](https://github.com/antialias/soroban-abacus-flashcards/commit/3a3706cc6fb694c7762f065f4ab4996bb8608dc4))
|
||||
|
||||
## [4.46.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.46.1...v4.46.2) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **types:** properly type HomeHeroContext in AppNavBar ([f9a7cb7](https://github.com/antialias/soroban-abacus-flashcards/commit/f9a7cb7f05dfddf291d89212a77ba1c11c00c9c7))
|
||||
|
||||
## [4.46.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.46.0...v4.46.1) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hero-abacus:** restructure layout to prevent visual overlap ([02b6c70](https://github.com/antialias/soroban-abacus-flashcards/commit/02b6c70b7a52f7de2954e5e0efddbed64d419d6c))
|
||||
|
||||
## [4.46.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.45.0...v4.46.0) (2025-10-20)
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,11 @@ import { AbacusDisplayDropdown } from './AbacusDisplayDropdown'
|
||||
// Import HomeHeroContext for optional usage
|
||||
import type { Subtitle } from '../data/abaciOneSubtitles'
|
||||
|
||||
type HomeHeroContextValue = {
|
||||
subtitle: Subtitle
|
||||
isHeroVisible: boolean
|
||||
} | null
|
||||
|
||||
// HomeHeroContext - imported dynamically to avoid circular deps
|
||||
let HomeHeroContextModule: any = null
|
||||
try {
|
||||
@@ -23,10 +28,11 @@ try {
|
||||
// Context not available
|
||||
}
|
||||
|
||||
const HomeHeroContext = HomeHeroContextModule?.HomeHeroContext || React.createContext(null)
|
||||
const HomeHeroContext: React.Context<HomeHeroContextValue> =
|
||||
HomeHeroContextModule?.HomeHeroContext || React.createContext<HomeHeroContextValue>(null)
|
||||
|
||||
// Use HomeHeroContext without requiring it
|
||||
function useOptionalHomeHero(): { subtitle: Subtitle; isHeroVisible: boolean } | null {
|
||||
function useOptionalHomeHero(): HomeHeroContextValue {
|
||||
return useContext(HomeHeroContext)
|
||||
}
|
||||
|
||||
@@ -563,17 +569,23 @@ export function AppNavBar({ variant = 'full', navSlot }: AppNavBarProps) {
|
||||
)
|
||||
}
|
||||
|
||||
// Check if we should use transparent styling (when hero is visible)
|
||||
const isTransparent = homeHero?.isHeroVisible
|
||||
|
||||
return (
|
||||
<Tooltip.Provider delayDuration={200}>
|
||||
<header
|
||||
className={css({
|
||||
bg: 'white',
|
||||
shadow: 'sm',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'gray.200',
|
||||
position: 'sticky',
|
||||
bg: isTransparent ? 'transparent' : 'white',
|
||||
shadow: isTransparent ? 'none' : 'sm',
|
||||
borderBottom: isTransparent ? 'none' : '1px solid',
|
||||
borderColor: isTransparent ? 'transparent' : 'gray.200',
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
zIndex: 30,
|
||||
left: 0,
|
||||
right: 0,
|
||||
zIndex: 1000,
|
||||
transition: 'all 0.3s ease',
|
||||
})}
|
||||
>
|
||||
<div className={container({ maxW: '7xl', px: '4', py: '3' })}>
|
||||
@@ -650,13 +662,13 @@ export function AppNavBar({ variant = 'full', navSlot }: AppNavBarProps) {
|
||||
<div className={hstack({ gap: '6', alignItems: 'center' })}>
|
||||
{/* Navigation Links */}
|
||||
<nav className={hstack({ gap: '4' })}>
|
||||
<NavLink href="/create" currentPath={pathname}>
|
||||
<NavLink href="/create" currentPath={pathname} isTransparent={isTransparent}>
|
||||
Create
|
||||
</NavLink>
|
||||
<NavLink href="/guide" currentPath={pathname}>
|
||||
<NavLink href="/guide" currentPath={pathname} isTransparent={isTransparent}>
|
||||
Guide
|
||||
</NavLink>
|
||||
<NavLink href="/games" currentPath={pathname}>
|
||||
<NavLink href="/games" currentPath={pathname} isTransparent={isTransparent}>
|
||||
Games
|
||||
</NavLink>
|
||||
</nav>
|
||||
@@ -693,10 +705,12 @@ function NavLink({
|
||||
href,
|
||||
currentPath,
|
||||
children,
|
||||
isTransparent,
|
||||
}: {
|
||||
href: string
|
||||
currentPath: string | null
|
||||
children: React.ReactNode
|
||||
isTransparent?: boolean
|
||||
}) {
|
||||
const isActive = currentPath === href || (href !== '/' && currentPath?.startsWith(href))
|
||||
|
||||
@@ -710,8 +724,20 @@ function NavLink({
|
||||
minW: { base: '44px', md: 'auto' },
|
||||
fontSize: 'sm',
|
||||
fontWeight: 'medium',
|
||||
color: isActive ? 'brand.700' : 'gray.600',
|
||||
bg: isActive ? 'brand.50' : 'transparent',
|
||||
color: isTransparent
|
||||
? isActive
|
||||
? 'white'
|
||||
: 'rgba(255, 255, 255, 0.8)'
|
||||
: isActive
|
||||
? 'brand.700'
|
||||
: 'gray.600',
|
||||
bg: isTransparent
|
||||
? isActive
|
||||
? 'rgba(255, 255, 255, 0.15)'
|
||||
: 'transparent'
|
||||
: isActive
|
||||
? 'brand.50'
|
||||
: 'transparent',
|
||||
rounded: 'lg',
|
||||
transition: 'all',
|
||||
textDecoration: 'none',
|
||||
@@ -719,8 +745,8 @@ function NavLink({
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
_hover: {
|
||||
color: isActive ? 'brand.800' : 'gray.900',
|
||||
bg: isActive ? 'brand.100' : 'gray.50',
|
||||
color: isTransparent ? 'white' : isActive ? 'brand.800' : 'gray.900',
|
||||
bg: isTransparent ? 'rgba(255, 255, 255, 0.2)' : isActive ? 'brand.100' : 'gray.50',
|
||||
},
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -6,20 +6,27 @@ import { css } from '../../styled-system/css'
|
||||
import { useHomeHero } from '../contexts/HomeHeroContext'
|
||||
|
||||
export function HeroAbacus() {
|
||||
const { subtitle, abacusValue, setAbacusValue, setIsHeroVisible } = useHomeHero()
|
||||
const {
|
||||
subtitle,
|
||||
abacusValue,
|
||||
setAbacusValue,
|
||||
setIsHeroVisible,
|
||||
isAbacusLoaded,
|
||||
isSubtitleLoaded,
|
||||
} = useHomeHero()
|
||||
const appConfig = useAbacusConfig()
|
||||
const heroRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
// Dark theme styles for the abacus (matching the mini abacus on homepage)
|
||||
const darkStyles = {
|
||||
// Styling for structural elements (solid, no translucency)
|
||||
const structuralStyles = {
|
||||
columnPosts: {
|
||||
fill: 'rgba(255, 255, 255, 0.3)',
|
||||
stroke: 'rgba(255, 255, 255, 0.2)',
|
||||
fill: 'rgb(255, 255, 255)',
|
||||
stroke: 'rgb(200, 200, 200)',
|
||||
strokeWidth: 2,
|
||||
},
|
||||
reckoningBar: {
|
||||
fill: 'rgba(255, 255, 255, 0.4)',
|
||||
stroke: 'rgba(255, 255, 255, 0.25)',
|
||||
fill: 'rgb(255, 255, 255)',
|
||||
stroke: 'rgb(200, 200, 200)',
|
||||
strokeWidth: 3,
|
||||
},
|
||||
}
|
||||
@@ -43,30 +50,20 @@ export function HeroAbacus() {
|
||||
return () => observer.disconnect()
|
||||
}, [setIsHeroVisible])
|
||||
|
||||
// Auto-cycle through random numbers (optional - user can also interact)
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
const randomNum = Math.floor(Math.random() * 10000) // 0-9999
|
||||
setAbacusValue(randomNum)
|
||||
}, 4000)
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [setAbacusValue])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={heroRef}
|
||||
className={css({
|
||||
minHeight: '100vh',
|
||||
height: '100vh',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background:
|
||||
'linear-gradient(135deg, rgba(17, 24, 39, 1) 0%, rgba(88, 28, 135, 0.3) 50%, rgba(17, 24, 39, 1) 100%)',
|
||||
justifyContent: 'space-between',
|
||||
bg: 'gray.900',
|
||||
position: 'relative',
|
||||
overflow: 'hidden',
|
||||
px: '4',
|
||||
py: '12',
|
||||
})}
|
||||
>
|
||||
{/* Background pattern */}
|
||||
@@ -81,18 +78,17 @@ export function HeroAbacus() {
|
||||
})}
|
||||
/>
|
||||
|
||||
{/* Content */}
|
||||
{/* Title and Subtitle Section - DIRECT CHILD */}
|
||||
<div
|
||||
className={css({
|
||||
position: 'relative',
|
||||
textAlign: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '8',
|
||||
gap: '2',
|
||||
zIndex: 10,
|
||||
})}
|
||||
>
|
||||
{/* Title */}
|
||||
<h1
|
||||
className={css({
|
||||
fontSize: { base: '4xl', md: '6xl', lg: '7xl' },
|
||||
@@ -100,36 +96,44 @@ export function HeroAbacus() {
|
||||
background: 'linear-gradient(135deg, #fbbf24 0%, #f59e0b 50%, #fbbf24 100%)',
|
||||
backgroundClip: 'text',
|
||||
color: 'transparent',
|
||||
mb: '4',
|
||||
})}
|
||||
>
|
||||
Abaci One
|
||||
</h1>
|
||||
|
||||
{/* Subtitle */}
|
||||
<p
|
||||
className={css({
|
||||
fontSize: { base: 'xl', md: '2xl' },
|
||||
fontWeight: 'medium',
|
||||
color: 'purple.300',
|
||||
fontStyle: 'italic',
|
||||
mb: '16',
|
||||
zIndex: 10,
|
||||
position: 'relative',
|
||||
marginBottom: '8',
|
||||
opacity: isSubtitleLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.5s ease-in-out',
|
||||
})}
|
||||
>
|
||||
{subtitle.text}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Large Interactive Abacus */}
|
||||
{/* Large Interactive Abacus - DIRECT CHILD */}
|
||||
<div
|
||||
className={css({
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flex: '1',
|
||||
width: '100%',
|
||||
zIndex: 10,
|
||||
opacity: isAbacusLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.5s ease-in-out',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
transform: { base: 'scale(2)', md: 'scale(3)', lg: 'scale(4)' },
|
||||
transformOrigin: 'center center',
|
||||
mb: { base: '12', md: '16', lg: '20' },
|
||||
transition: 'all 0.5s cubic-bezier(0.4, 0, 0.2, 1)',
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
})}
|
||||
>
|
||||
<AbacusReact
|
||||
@@ -137,25 +141,30 @@ export function HeroAbacus() {
|
||||
columns={4}
|
||||
beadShape={appConfig.beadShape}
|
||||
showNumbers={true}
|
||||
customStyles={darkStyles}
|
||||
interactive={true}
|
||||
animated={true}
|
||||
customStyles={structuralStyles}
|
||||
onValueChange={setAbacusValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Subtle hint to scroll */}
|
||||
<div
|
||||
className={css({
|
||||
fontSize: 'sm',
|
||||
color: 'gray.400',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '2',
|
||||
animation: 'bounce 2s ease-in-out infinite',
|
||||
})}
|
||||
>
|
||||
<span>Scroll to explore</span>
|
||||
<span>↓</span>
|
||||
</div>
|
||||
{/* Subtle hint to scroll - DIRECT CHILD */}
|
||||
<div
|
||||
className={css({
|
||||
position: 'relative',
|
||||
fontSize: 'sm',
|
||||
color: 'gray.400',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '2',
|
||||
animation: 'bounce 2s ease-in-out infinite',
|
||||
zIndex: 10,
|
||||
})}
|
||||
>
|
||||
<span>Scroll to explore</span>
|
||||
<span>↓</span>
|
||||
</div>
|
||||
|
||||
{/* Keyframes for bounce animation */}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import type React from 'react'
|
||||
import { createContext, useContext, useEffect, useMemo, useState } from 'react'
|
||||
import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { Subtitle } from '../data/abaciOneSubtitles'
|
||||
import { getRandomSubtitle, subtitles } from '../data/abaciOneSubtitles'
|
||||
import { subtitles } from '../data/abaciOneSubtitles'
|
||||
|
||||
interface HomeHeroContextValue {
|
||||
subtitle: Subtitle
|
||||
@@ -11,6 +11,8 @@ interface HomeHeroContextValue {
|
||||
setAbacusValue: (value: number) => void
|
||||
isHeroVisible: boolean
|
||||
setIsHeroVisible: (visible: boolean) => void
|
||||
isAbacusLoaded: boolean
|
||||
isSubtitleLoaded: boolean
|
||||
}
|
||||
|
||||
const HomeHeroContext = createContext<HomeHeroContextValue | null>(null)
|
||||
@@ -20,14 +22,82 @@ export { HomeHeroContext }
|
||||
export function HomeHeroProvider({ children }: { children: React.ReactNode }) {
|
||||
// Use first subtitle for SSR, then select random one on client mount
|
||||
const [subtitle, setSubtitle] = useState<Subtitle>(subtitles[0])
|
||||
const [isSubtitleLoaded, setIsSubtitleLoaded] = useState(false)
|
||||
|
||||
// Select random subtitle only on client side to avoid SSR mismatch
|
||||
// Select random subtitle only on client side, persist per-session
|
||||
useEffect(() => {
|
||||
setSubtitle(getRandomSubtitle())
|
||||
// Check if we have a stored subtitle index for this session
|
||||
const storedIndex = sessionStorage.getItem('heroSubtitleIndex')
|
||||
|
||||
if (storedIndex !== null) {
|
||||
// Use the stored subtitle index
|
||||
const index = parseInt(storedIndex, 10)
|
||||
if (!Number.isNaN(index) && index >= 0 && index < subtitles.length) {
|
||||
setSubtitle(subtitles[index])
|
||||
setIsSubtitleLoaded(true)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new random index and store it
|
||||
const randomIndex = Math.floor(Math.random() * subtitles.length)
|
||||
sessionStorage.setItem('heroSubtitleIndex', randomIndex.toString())
|
||||
setSubtitle(subtitles[randomIndex])
|
||||
setIsSubtitleLoaded(true)
|
||||
}, [])
|
||||
|
||||
// Shared abacus value (so it stays consistent during morph)
|
||||
const [abacusValue, setAbacusValue] = useState(1234)
|
||||
// Shared abacus value - always start at 0 for SSR/hydration consistency
|
||||
const [abacusValue, setAbacusValue] = useState(0)
|
||||
const [isAbacusLoaded, setIsAbacusLoaded] = useState(false)
|
||||
const isLoadingFromStorage = useRef(false)
|
||||
|
||||
// Load from sessionStorage after mount (client-only, no hydration mismatch)
|
||||
useEffect(() => {
|
||||
console.log('[HeroAbacus] Loading from sessionStorage...')
|
||||
isLoadingFromStorage.current = true // Block saves during load
|
||||
|
||||
const saved = sessionStorage.getItem('heroAbacusValue')
|
||||
console.log('[HeroAbacus] Saved value from storage:', saved)
|
||||
|
||||
if (saved) {
|
||||
const parsedValue = parseInt(saved, 10)
|
||||
console.log('[HeroAbacus] Parsed value:', parsedValue)
|
||||
if (!Number.isNaN(parsedValue)) {
|
||||
console.log('[HeroAbacus] Setting abacus value to:', parsedValue)
|
||||
setAbacusValue(parsedValue)
|
||||
}
|
||||
} else {
|
||||
console.log('[HeroAbacus] No saved value found, staying at 0')
|
||||
}
|
||||
|
||||
// Use setTimeout to ensure the value has been set before we allow saves
|
||||
setTimeout(() => {
|
||||
isLoadingFromStorage.current = false
|
||||
setIsAbacusLoaded(true)
|
||||
console.log('[HeroAbacus] Load complete, allowing saves now and fading in')
|
||||
}, 0)
|
||||
}, [])
|
||||
|
||||
// Persist value to sessionStorage when it changes (but skip during load)
|
||||
useEffect(() => {
|
||||
console.log(
|
||||
'[HeroAbacus] Save effect triggered. Value:',
|
||||
abacusValue,
|
||||
'isLoadingFromStorage:',
|
||||
isLoadingFromStorage.current
|
||||
)
|
||||
|
||||
if (!isLoadingFromStorage.current) {
|
||||
console.log('[HeroAbacus] Saving to sessionStorage:', abacusValue)
|
||||
sessionStorage.setItem('heroAbacusValue', abacusValue.toString())
|
||||
console.log(
|
||||
'[HeroAbacus] Saved successfully. Storage now contains:',
|
||||
sessionStorage.getItem('heroAbacusValue')
|
||||
)
|
||||
} else {
|
||||
console.log('[HeroAbacus] Skipping save (currently loading from storage)')
|
||||
}
|
||||
}, [abacusValue])
|
||||
|
||||
// Track hero visibility for nav branding
|
||||
const [isHeroVisible, setIsHeroVisible] = useState(true)
|
||||
@@ -39,8 +109,10 @@ export function HomeHeroProvider({ children }: { children: React.ReactNode }) {
|
||||
setAbacusValue,
|
||||
isHeroVisible,
|
||||
setIsHeroVisible,
|
||||
isAbacusLoaded,
|
||||
isSubtitleLoaded,
|
||||
}),
|
||||
[subtitle, abacusValue, isHeroVisible]
|
||||
[subtitle, abacusValue, isHeroVisible, isAbacusLoaded, isSubtitleLoaded]
|
||||
)
|
||||
|
||||
return <HomeHeroContext.Provider value={value}>{children}</HomeHeroContext.Provider>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soroban-monorepo",
|
||||
"version": "4.46.0",
|
||||
"version": "4.48.2",
|
||||
"private": true,
|
||||
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
|
||||
"workspaces": [
|
||||
|
||||
Reference in New Issue
Block a user