fix: janky captions experience
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo, useRef } from "react";
|
||||||
import type { Transcript } from "@/api/types";
|
import type { Transcript } from "@/api/types";
|
||||||
|
|
||||||
type Sentence = Transcript["paragraphs"][number]["sentences"][number];
|
type Sentence = Transcript["paragraphs"][number]["sentences"][number];
|
||||||
@@ -41,15 +41,33 @@ export function TranscriptOverlay({
|
|||||||
const activeWord =
|
const activeWord =
|
||||||
activeWordIndex !== null ? transcript.words[activeWordIndex] : null;
|
activeWordIndex !== null ? transcript.words[activeWordIndex] : null;
|
||||||
|
|
||||||
// Find which chunk contains the active word
|
// Remember the last spoken word so highlights hold during pauses
|
||||||
|
const lastSpokenWordRef = useRef<Word | null>(null);
|
||||||
|
if (activeWord) {
|
||||||
|
lastSpokenWordRef.current = activeWord;
|
||||||
|
}
|
||||||
|
const highlightWord = activeWord ?? lastSpokenWordRef.current;
|
||||||
|
|
||||||
|
const lastChunkRef = useRef<Word[] | null>(null);
|
||||||
|
|
||||||
|
// Find which chunk contains the active word, holding the last one during pauses
|
||||||
const activeChunk = useMemo(() => {
|
const activeChunk = useMemo(() => {
|
||||||
if (!activeWord) return chunks[0] ?? null;
|
if (activeWord) {
|
||||||
for (const chunk of chunks) {
|
for (const chunk of chunks) {
|
||||||
if (chunk.some((w) => w.start === activeWord.start && w.end === activeWord.end)) {
|
if (chunk.some((w) => w.start === activeWord.start && w.end === activeWord.end)) {
|
||||||
return chunk;
|
lastChunkRef.current = chunk;
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return chunks[0] ?? null;
|
// No active word (speaker pausing) — hold the last chunk
|
||||||
|
if (lastChunkRef.current && chunks.some((c) => c === lastChunkRef.current)) {
|
||||||
|
return lastChunkRef.current;
|
||||||
|
}
|
||||||
|
// Sentence changed, last chunk no longer valid — use first chunk
|
||||||
|
const fallback = chunks[0] ?? null;
|
||||||
|
lastChunkRef.current = fallback;
|
||||||
|
return fallback;
|
||||||
}, [chunks, activeWord]);
|
}, [chunks, activeWord]);
|
||||||
|
|
||||||
if (!activeSentence || !activeChunk || activeChunk.length === 0) return null;
|
if (!activeSentence || !activeChunk || activeChunk.length === 0) return null;
|
||||||
@@ -62,7 +80,7 @@ export function TranscriptOverlay({
|
|||||||
<p className="rounded-lg px-5 py-3 text-2xl text-center max-w-lg">
|
<p className="rounded-lg px-5 py-3 text-2xl text-center max-w-lg">
|
||||||
{activeChunk.map((word, i) => {
|
{activeChunk.map((word, i) => {
|
||||||
const isSpoken =
|
const isSpoken =
|
||||||
activeWord !== null && word.start <= activeWord.end;
|
highlightWord !== null && word.start <= highlightWord.end;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
|
|||||||
Reference in New Issue
Block a user