Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df674426c5 | ||
|
|
24d120004d | ||
|
|
88f57ce6df | ||
|
|
3a5dc0f1c8 | ||
|
|
3fff9ef140 | ||
|
|
ca1c6d8602 | ||
|
|
e6bcf20807 | ||
|
|
1ee25b3dd2 | ||
|
|
468bdebe3a | ||
|
|
2eb3ff3406 | ||
|
|
efbe99a9e2 | ||
|
|
fdc882cb04 | ||
|
|
a7778c648d | ||
|
|
7e2f580877 |
48
CHANGELOG.md
48
CHANGELOG.md
@@ -1,3 +1,51 @@
|
||||
## [4.20.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.1...v4.20.2) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **homepage:** improve text contrast in Your Journey section ([24d1200](https://github.com/antialias/soroban-abacus-flashcards/commit/24d120004dccecc1ce2f08c1b73eec902868fb23))
|
||||
* **tutorial:** resolve TypeScript errors in TutorialPlayer ([88f57ce](https://github.com/antialias/soroban-abacus-flashcards/commit/88f57ce6df125142d6ea7feec60c475926bd4929))
|
||||
|
||||
## [4.20.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.20.0...v4.20.1) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **homepage:** correct positioning of progression arrows in Your Journey section ([3fff9ef](https://github.com/antialias/soroban-abacus-flashcards/commit/3fff9ef140bf1f462042f8319ed6c5e2a376e4ba))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **homepage:** move What You'll Learn above tutorial ([ca1c6d8](https://github.com/antialias/soroban-abacus-flashcards/commit/ca1c6d86029c891e019a96ba161e49b08b5be1bf))
|
||||
|
||||
## [4.20.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.19.0...v4.20.0) (2025-10-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **tutorial:** add hideTooltip prop and improve dark mode coaching bar ([1ee25b3](https://github.com/antialias/soroban-abacus-flashcards/commit/1ee25b3dd2f0ee9dd7ed571ba818b7ca5a247f85))
|
||||
|
||||
## [4.19.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.18.1...v4.19.0) (2025-10-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **tutorial:** add fill color support for dark mode column posts and reckoning bar ([2eb3ff3](https://github.com/antialias/soroban-abacus-flashcards/commit/2eb3ff340613301df20bf14f5b461371a27d7f05))
|
||||
|
||||
## [4.18.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.18.0...v4.18.1) (2025-10-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tutorial:** use correct customStyles API for dark mode frame styling ([fdc882c](https://github.com/antialias/soroban-abacus-flashcards/commit/fdc882cb046e3d8835fbca59841e9af5329bcc52))
|
||||
|
||||
## [4.18.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.2...v4.18.0) (2025-10-19)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **tutorial:** add dark mode styling for coaching bar and abacus frame ([7e2f580](https://github.com/antialias/soroban-abacus-flashcards/commit/7e2f580877af9d21409f427778fa3569c950fcf5))
|
||||
|
||||
## [4.17.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.17.1...v4.17.2) (2025-10-19)
|
||||
|
||||
|
||||
|
||||
@@ -219,21 +219,12 @@ export default function HomePage() {
|
||||
mx: 'auto',
|
||||
})}
|
||||
>
|
||||
<TutorialPlayer
|
||||
tutorial={friendsOf5Tutorial}
|
||||
isDebugMode={false}
|
||||
showDebugPanel={false}
|
||||
hideNavigation={true}
|
||||
abacusColumns={2}
|
||||
theme="dark"
|
||||
/>
|
||||
|
||||
{/* What you'll learn - below tutorial */}
|
||||
{/* What you'll learn - above tutorial */}
|
||||
<div
|
||||
className={css({
|
||||
mt: '8',
|
||||
pt: '6',
|
||||
borderTop: '1px solid',
|
||||
mb: '8',
|
||||
pb: '6',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'gray.700',
|
||||
})}
|
||||
>
|
||||
@@ -262,6 +253,16 @@ export default function HomePage() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TutorialPlayer
|
||||
tutorial={friendsOf5Tutorial}
|
||||
isDebugMode={false}
|
||||
showDebugPanel={false}
|
||||
hideNavigation={true}
|
||||
hideTooltip={true}
|
||||
abacusColumns={2}
|
||||
theme="dark"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -378,7 +379,7 @@ export default function HomePage() {
|
||||
>
|
||||
Your Journey
|
||||
</h2>
|
||||
<p className={css({ color: 'gray.400', fontSize: 'md' })}>
|
||||
<p className={css({ color: 'gray.200', fontSize: 'md' })}>
|
||||
Progress from beginner to master
|
||||
</p>
|
||||
</div>
|
||||
@@ -407,7 +408,15 @@ export default function HomePage() {
|
||||
{ level: '1 Kyu', label: 'Advanced', color: 'purple.400' },
|
||||
{ level: 'Dan', label: 'Master', color: 'yellow.400' },
|
||||
].map((stage, i) => (
|
||||
<div key={i} className={stack({ gap: '2', textAlign: 'center', flex: '1' })}>
|
||||
<div
|
||||
key={i}
|
||||
className={stack({
|
||||
gap: '2',
|
||||
textAlign: 'center',
|
||||
flex: '1',
|
||||
position: 'relative',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
fontSize: 'xl',
|
||||
@@ -417,7 +426,7 @@ export default function HomePage() {
|
||||
>
|
||||
{stage.level}
|
||||
</div>
|
||||
<div className={css({ fontSize: 'sm', color: 'gray.400' })}>{stage.label}</div>
|
||||
<div className={css({ fontSize: 'sm', color: 'gray.200' })}>{stage.label}</div>
|
||||
{i < 3 && (
|
||||
<div
|
||||
className={css({
|
||||
@@ -425,7 +434,7 @@ export default function HomePage() {
|
||||
position: 'absolute',
|
||||
right: '-50%',
|
||||
fontSize: 'xl',
|
||||
color: 'gray.600',
|
||||
color: 'gray.400',
|
||||
})}
|
||||
>
|
||||
→
|
||||
@@ -439,7 +448,7 @@ export default function HomePage() {
|
||||
mt: '6',
|
||||
textAlign: 'center',
|
||||
fontSize: 'sm',
|
||||
color: 'gray.500',
|
||||
color: 'gray.300',
|
||||
fontStyle: 'italic',
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -216,6 +216,7 @@ interface TutorialPlayerProps {
|
||||
isDebugMode?: boolean
|
||||
showDebugPanel?: boolean
|
||||
hideNavigation?: boolean
|
||||
hideTooltip?: boolean
|
||||
abacusColumns?: number
|
||||
theme?: 'light' | 'dark'
|
||||
onStepChange?: (stepIndex: number, step: TutorialStep) => void
|
||||
@@ -231,6 +232,7 @@ function TutorialPlayerContent({
|
||||
isDebugMode = false,
|
||||
showDebugPanel = false,
|
||||
hideNavigation = false,
|
||||
hideTooltip = false,
|
||||
abacusColumns = 5,
|
||||
theme = 'light',
|
||||
onStepChange,
|
||||
@@ -437,8 +439,7 @@ function TutorialPlayerContent({
|
||||
const filteredHighlightBeads = useMemo(() => {
|
||||
if (!currentStep.highlightBeads) return undefined
|
||||
return currentStep.highlightBeads.filter((highlight) => {
|
||||
const placeValue = highlight.placeValue ?? 4 - (highlight.columnIndex ?? 0)
|
||||
return placeValue < abacusColumns
|
||||
return highlight.placeValue < abacusColumns
|
||||
})
|
||||
}, [currentStep.highlightBeads, abacusColumns])
|
||||
|
||||
@@ -896,8 +897,8 @@ function TutorialPlayerContent({
|
||||
// Check if this is the correct action
|
||||
if (currentStep.highlightBeads && Array.isArray(currentStep.highlightBeads)) {
|
||||
const isCorrectBead = currentStep.highlightBeads.some((highlight) => {
|
||||
// Get place value from highlight (convert columnIndex to placeValue if needed)
|
||||
const highlightPlaceValue = highlight.placeValue ?? 4 - highlight.columnIndex
|
||||
// Get place value from highlight
|
||||
const highlightPlaceValue = highlight.placeValue
|
||||
// Get place value from bead click event
|
||||
const beadPlaceValue = beadInfo.bead ? beadInfo.bead.placeValue : 4 - beadInfo.columnIndex
|
||||
|
||||
@@ -909,9 +910,10 @@ function TutorialPlayerContent({
|
||||
})
|
||||
|
||||
if (!isCorrectBead) {
|
||||
const errorMessage = "That's not the highlighted bead. Try clicking the highlighted bead."
|
||||
dispatch({
|
||||
type: 'SET_ERROR',
|
||||
error: currentStep.errorMessages.wrongBead,
|
||||
error: errorMessage,
|
||||
})
|
||||
|
||||
dispatch({
|
||||
@@ -919,7 +921,7 @@ function TutorialPlayerContent({
|
||||
event: {
|
||||
type: 'ERROR_OCCURRED',
|
||||
stepId: currentStep.id,
|
||||
error: currentStep.errorMessages.wrongBead,
|
||||
error: errorMessage,
|
||||
timestamp: new Date(),
|
||||
},
|
||||
})
|
||||
@@ -1044,8 +1046,7 @@ function TutorialPlayerContent({
|
||||
if (currentStep.highlightBeads && Array.isArray(currentStep.highlightBeads)) {
|
||||
currentStep.highlightBeads.forEach((highlight) => {
|
||||
// Convert placeValue to columnIndex for AbacusReact compatibility
|
||||
const columnIndex =
|
||||
highlight.placeValue !== undefined ? 4 - highlight.placeValue : highlight.columnIndex
|
||||
const columnIndex = abacusColumns - 1 - highlight.placeValue
|
||||
|
||||
// Skip highlights for columns that don't exist
|
||||
if (columnIndex < minValidColumn) {
|
||||
@@ -1094,8 +1095,32 @@ function TutorialPlayerContent({
|
||||
Object.assign(mergedHighlights[columnIndex], dynamicColumnHighlights[columnIndex])
|
||||
})
|
||||
|
||||
return Object.keys(mergedHighlights).length > 0 ? { columns: mergedHighlights } : undefined
|
||||
}, [currentStep.highlightBeads, dynamicColumnHighlights, abacusColumns])
|
||||
// Build the custom styles object
|
||||
const styles: any = {}
|
||||
|
||||
// Add column highlights if any
|
||||
if (Object.keys(mergedHighlights).length > 0) {
|
||||
styles.columns = mergedHighlights
|
||||
}
|
||||
|
||||
// Add frame styling for dark mode
|
||||
if (theme === 'dark') {
|
||||
// Column dividers (global for all columns)
|
||||
styles.columnPosts = {
|
||||
fill: 'rgba(255, 255, 255, 0.3)', // High contrast fill for visibility
|
||||
stroke: 'rgba(255, 255, 255, 0.2)',
|
||||
strokeWidth: 2,
|
||||
}
|
||||
// Reckoning bar (horizontal middle bar)
|
||||
styles.reckoningBar = {
|
||||
fill: 'rgba(255, 255, 255, 0.4)', // High contrast fill for visibility
|
||||
stroke: 'rgba(255, 255, 255, 0.25)',
|
||||
strokeWidth: 3,
|
||||
}
|
||||
}
|
||||
|
||||
return Object.keys(styles).length > 0 ? styles : undefined
|
||||
}, [currentStep.highlightBeads, dynamicColumnHighlights, abacusColumns, theme])
|
||||
|
||||
if (!currentStep) {
|
||||
return <div>No steps available</div>
|
||||
@@ -1115,9 +1140,9 @@ function TutorialPlayerContent({
|
||||
<div
|
||||
className={css({
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'gray.200',
|
||||
borderColor: theme === 'dark' ? 'rgba(255, 255, 255, 0.1)' : 'gray.200',
|
||||
p: 4,
|
||||
bg: 'white',
|
||||
bg: theme === 'dark' ? 'rgba(30, 30, 40, 0.6)' : 'white',
|
||||
})}
|
||||
>
|
||||
<div
|
||||
@@ -1386,7 +1411,8 @@ function TutorialPlayerContent({
|
||||
</div>
|
||||
|
||||
{/* Multi-step instructions panel */}
|
||||
{currentStep.multiStepInstructions &&
|
||||
{!hideTooltip &&
|
||||
currentStep.multiStepInstructions &&
|
||||
currentStep.multiStepInstructions.length > 0 && (
|
||||
<div
|
||||
className={css({
|
||||
@@ -1522,7 +1548,10 @@ function TutorialPlayerContent({
|
||||
className={css({
|
||||
mb: 1,
|
||||
fontWeight: 'bold',
|
||||
color: 'yellow.900',
|
||||
color: theme === 'dark' ? 'yellow.200' : 'yellow.900',
|
||||
textShadow:
|
||||
theme === 'dark' ? '0 0 12px rgba(251, 191, 36, 0.4)' : 'none',
|
||||
fontSize: theme === 'dark' ? 'lg' : 'base',
|
||||
})}
|
||||
>
|
||||
{currentInstruction}
|
||||
@@ -1640,7 +1669,7 @@ function TutorialPlayerContent({
|
||||
</div>
|
||||
|
||||
{/* Tooltip */}
|
||||
{currentStep.tooltip && (
|
||||
{!hideTooltip && currentStep.tooltip && (
|
||||
<div
|
||||
className={css({
|
||||
maxW: '500px',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soroban-monorepo",
|
||||
"version": "4.17.2",
|
||||
"version": "4.20.2",
|
||||
"private": true,
|
||||
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
|
||||
"workspaces": [
|
||||
|
||||
@@ -27,6 +27,7 @@ export interface BeadStyle {
|
||||
}
|
||||
|
||||
export interface ColumnPostStyle {
|
||||
fill?: string;
|
||||
stroke?: string;
|
||||
strokeWidth?: number;
|
||||
opacity?: number;
|
||||
@@ -34,6 +35,7 @@ export interface ColumnPostStyle {
|
||||
}
|
||||
|
||||
export interface ReckoningBarStyle {
|
||||
fill?: string;
|
||||
stroke?: string;
|
||||
strokeWidth?: number;
|
||||
opacity?: number;
|
||||
@@ -1979,7 +1981,10 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
|
||||
const columnStyles = customStyles?.columns?.[colIndex];
|
||||
const globalColumnPosts = customStyles?.columnPosts;
|
||||
const rodStyle = {
|
||||
fill: "rgb(0, 0, 0, 0.1)", // Default Typst color
|
||||
fill:
|
||||
columnStyles?.columnPost?.fill ||
|
||||
globalColumnPosts?.fill ||
|
||||
"rgb(0, 0, 0, 0.1)", // Default Typst color
|
||||
stroke:
|
||||
columnStyles?.columnPost?.stroke ||
|
||||
globalColumnPosts?.stroke ||
|
||||
@@ -2017,8 +2022,10 @@ export const AbacusReact: React.FC<AbacusConfig> = ({
|
||||
(effectiveColumns - 1) * dimensions.rodSpacing + dimensions.beadSize
|
||||
}
|
||||
height={dimensions.barThickness}
|
||||
fill="black" // Typst uses black
|
||||
stroke="none"
|
||||
fill={customStyles?.reckoningBar?.fill || "black"} // Typst default is black
|
||||
stroke={customStyles?.reckoningBar?.stroke || "none"}
|
||||
strokeWidth={customStyles?.reckoningBar?.strokeWidth ?? 0}
|
||||
opacity={customStyles?.reckoningBar?.opacity ?? 1}
|
||||
/>
|
||||
|
||||
{/* Beads */}
|
||||
|
||||
Reference in New Issue
Block a user