import React, { useMemo } from "react"; import { Text, View } from "react-native"; import { Palette } from "../styles"; import { FONT_FAMILY } from "../styles/Fonts"; export function groupAlignedWordsToLines(alignedWords = [], { removeTags = true } = {}) { const out = []; let buf = []; let start = null; const clean = (txt) => String(txt || "") .replace(/\s+/g, " ") .trim(); const isSentenceEnd = (txt) => /[.!?]$/.test((txt || "").trim()); const isSectionTag = (txt) => /^\s*\[[^\]]+\]\s*$/i.test((txt || "").trim()); for (let i = 0; i < alignedWords.length; i++) { const w = alignedWords[i] || {}; const original = String(w.word || ""); const textNoNewline = original.replace(/\n/g, " "); const textNoTag = removeTags ? textNoNewline.replace(/^\s*\[[^\]]+\]\s*/i, "") : textNoNewline; const next = alignedWords[i + 1] || null; const gapToNext = next ? Math.max(0, Number(next.startS || 0) - Number(w.endS || 0)) : 0; if (buf.length === 0) start = Number(w.startS || 0); if (isSectionTag(textNoNewline)) { if (!removeTags) { const joined = clean(textNoNewline); if (joined) out.push({ text: joined, startS: start ?? Number(w.startS || 0), endS: Number(w.endS || 0), }); } buf = []; start = null; continue; } if (!clean(textNoTag)) { continue; } buf.push(textNoTag); const eolByNewline = /\n/.test(original); const eolByPause = gapToNext >= 0.6; // threshold for a logical break const eolByPunct = isSentenceEnd(textNoTag); const isLast = i === alignedWords.length - 1; if (eolByNewline || eolByPause || eolByPunct || isLast) { const joined = clean(buf.join(" ")); if (joined) out.push({ text: joined, startS: start ?? Number(w.startS || 0), endS: Number(w.endS || 0), }); buf = []; start = null; } } return out; } export default function KaraokeLyrics({ alignedWords = [], currentTimeS = 0, showContext = true, removeTags = true, }) { const lines = useMemo( () => groupAlignedWordsToLines(alignedWords, { removeTags }), [alignedWords, removeTags] ); const currentLineIdx = useMemo(() => { if (!lines || lines.length === 0) return -1; for (let i = 0; i < lines.length; i++) { const L = lines[i]; if (currentTimeS >= (L.startS || 0) && currentTimeS <= (L.endS || 0)) return i; } if (currentTimeS > (lines[lines.length - 1]?.endS || 0)) return lines.length - 1; return -1; }, [lines, currentTimeS]); if (!lines.length) return null; const prev = showContext && currentLineIdx > 0 ? lines[currentLineIdx - 1]?.text : ""; const curr = currentLineIdx >= 0 ? lines[currentLineIdx]?.text : lines[0]?.text; const next = showContext && currentLineIdx + 1 < lines.length ? lines[currentLineIdx + 1]?.text : ""; return ( {/* {prev ? ( {prev} ) : null} */} {curr} {next ? ( {next} ) : null} ); }