feat: implement Phase 7 (Course Player v2, Furigana, XP System, Integrated Vocab)

This commit is contained in:
2026-01-23 18:15:51 +07:00
parent 82fe5f8a79
commit 7aa4eb89df
8 changed files with 386 additions and 73 deletions

View File

@@ -0,0 +1,48 @@
import React from 'react';
/**
* Parses a string with Japanese Furigana format {kanji|reading}
* into HTML <ruby> tags.
*/
export function parseFurigana(text: string): React.ReactNode[] {
if (!text) return [];
const regex = /\{([^|]+)\|([^}]+)\}/g;
const parts = [];
let lastIndex = 0;
let match;
while ((match = regex.exec(text)) !== null) {
// Add text before match
if (match.index > lastIndex) {
parts.push(text.substring(lastIndex, match.index));
}
// Add ruby tag
const [fullMatch, kanji, reading] = match;
parts.push(
<ruby key={match.index} className="ruby-text">
{kanji}
<rt className="text-[0.6em] opacity-80">{reading}</rt>
</ruby>
);
lastIndex = regex.lastIndex;
}
// Add remaining text
if (lastIndex < text.length) {
parts.push(text.substring(lastIndex));
}
return parts;
}
interface FuriganaTextProps {
text: string;
className?: string;
}
export const FuriganaText: React.FC<FuriganaTextProps> = ({ text, className }) => {
return <span className={className}>{parseFurigana(text)}</span>;
};