diff --git a/web/frontend/src/components/transcript/TranscriptView.tsx b/web/frontend/src/components/transcript/TranscriptView.tsx index 1722cba5..90a46da7 100644 --- a/web/frontend/src/components/transcript/TranscriptView.tsx +++ b/web/frontend/src/components/transcript/TranscriptView.tsx @@ -55,13 +55,13 @@ interface TranscriptViewProps { export const TranscriptView = forwardRef(({ transcript, mode, - // currentWordIndex, + // currentWordIndex, currentTime, isPlaying, - // notes, + // notes, // highlightedWordRef, speakerMappings, - // autoScrollEnabled, + autoScrollEnabled, onSeek, className }, ref) => { @@ -143,6 +143,33 @@ export const TranscriptView = forwardRef(({ }); }, [transcript]); + // Compute which segment is currently active based on playback time + const activeSegmentIndex = useMemo(() => { + if (!expandedData.length) return -1; + // Find the latest segment that has started (search backwards) + for (let i = expandedData.length - 1; i >= 0; i--) { + if (expandedData[i].start <= currentTime) return i; + } + return 0; + }, [expandedData, currentTime]); + + // Track previous active segment to only scroll on segment change + const prevActiveSegmentRef = useRef(-1); + + // Auto-scroll to active segment during playback + useEffect(() => { + if (mode !== 'expanded' || !autoScrollEnabled || !isPlaying) return; + if (activeSegmentIndex < 0) return; + // Only scroll when the segment actually changes + if (activeSegmentIndex === prevActiveSegmentRef.current) return; + prevActiveSegmentRef.current = activeSegmentIndex; + + const el = segmentRefs.current[activeSegmentIndex]; + if (el) { + el.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + }, [activeSegmentIndex, autoScrollEnabled, isPlaying, mode]); + // 2. Highlight Effect for Expanded View useEffect(() => { if (mode !== 'expanded' || !expandedData.length || !isPlaying) return; @@ -262,7 +289,15 @@ export const TranscriptView = forwardRef(({ return (
{/* Reduced spacing from space-y-6 */} {expandedData.map((segment, i) => ( -
+
{/* Timestamp & Speaker */}