FOUNDRY
C8 Platform
← Tasks

[UI-03] TranscriptOverlay — real-time scrolling transcript (retry 1) (retry 2)

completedcode_genP1

Description

## UI-03: TranscriptOverlay — real-time scrolling transcript Section: UI Components | Milestone: M4 | Owner: AG Lu ### Failing Tests - **transcript_overlay** (file_exists): File `c8-audio/components/TranscriptOverlay.tsx` must exist - **transcript_data** (grep): Pattern `transcript|caption|subtitle` in `c8-audio/components/TranscriptOverlay.tsx` (need 1+ matches) ### Acceptance Criteria - File `c8-audio/components/TranscriptOverlay.tsx` must exist - File matching `c8-audio/components/TranscriptOverlay.tsx` must contain pattern `transcript|caption|subtitle` ### Codebase Conventions - Trilingual comments: // EN: ... // VI: ... // ES: ... - SPEC section refs in comments - Zod at module level with .safeParse() - (supabase as any) with eslint-disable - NextResponse.json() responses - Error format: { error: 'message' } ### Dependencies - LK-02: FAIL
Assignee
-
Claimed By
Cloud Lu → Claude Sonnet
Created
1d ago
Completed
1d ago

Quality

Overall
99%
Pass
PASS
Graded By
-
Graded At
-

Cost

Input Tokens
-
Output Tokens
-
Est. USD
-

Result

{
  "cost": {
    "input_tokens": 300,
    "estimated_usd": 0.040545,
    "output_tokens": 2643
  },
  "model": "claude-sonnet-4-20250514",
  "status": "success",
  "transport": "anthropic_api",
  "latency_ms": 33399,
  "response_text": "I'll create the TranscriptOverlay component for real-time scrolling transcript display.\n\n```tsx\n// c8-audio/components/TranscriptOverlay.tsx\n'use client';\n\nimport React, { useState, useEffect, useRef } from 'react';\nimport { z } from 'zod';\n\n// EN: Schema for transcript segment data\n// VI: Schema cho dữ liệu đoạn transcript\n// ES: Schema para datos de segmento de transcripción\nconst TranscriptSegmentSchema = z.object({\n  id: z.string(),\n  text: z.string(),\n  startTime: z.number(),\n  endTime: z.number(),\n  speaker: z.string().optional(),\n  confidence: z.number().min(0).max(1).optional()\n});\n\nconst TranscriptDataSchema = z.object({\n  segments: z.array(TranscriptSegmentSchema),\n  currentTime: z.number().min(0),\n  isLive: z.boolean().optional().default(false)\n});\n\ntype TranscriptSegment = z.infer<typeof TranscriptSegmentSchema>;\ntype TranscriptData = z.infer<typeof TranscriptDataSchema>;\n\ninterface TranscriptOverlayProps {\n  transcriptData: TranscriptData;\n  isVisible: boolean;\n  onToggleVisibility: () => void;\n  className?: string;\n  autoScroll?: boolean;\n  showTimestamps?: boolean;\n  showSpeakers?: boolean;\n}\n\n// EN: Real-time scrolling transcript overlay component - SPEC UI-03\n// VI: Component overlay transcript cuộn thời gian thực - SPEC UI-03  \n// ES: Componente de superposición de transcripción con desplazamiento en tiempo real - SPEC UI-03\nexport default function TranscriptOverlay({\n  transcriptData,\n  isVisible,\n  onToggleVisibility,\n  className = '',\n  autoScroll = true,\n  showTimestamps = false,\n  showSpeakers = true\n}: TranscriptOverlayProps) {\n  const [validatedData, setValidatedData] = useState<TranscriptData | null>(null);\n  const [activeSegmentId, setActiveSegmentId] = useState<string | null>(null);\n  const scrollContainerRef = useRef<HTMLDivElement>(null);\n  const activeSegmentRef = useRef<HTMLDivElement>(null);\n\n  // EN: Validate transcript data on prop changes\n  // VI: Xác thực dữ liệu transcript khi props thay đổi\n  // ES: Validar datos de transcripción cuando cambian las props\n  useEffect(() => {\n    const result = TranscriptDataSchema.safeParse(transcriptData);\n    if (result.success) {\n      setValidatedData(result.data);\n    } else {\n      console.error('Invalid transcript data:', result.error);\n      setValidatedData(null);\n    }\n  }, [transcriptData]);\n\n  // EN: Update active segment based on current time\n  // VI: Cập nhật đoạn đang hoạt động dựa trên thời gian hiện tại\n  // ES: Actualizar segmento activo basado en el tiempo actual\n  useEffect(() => {\n    if (!validatedData) return;\n\n    const currentSegment = validatedData.segments.find(segment => \n      validatedData.currentTime >= segment.startTime && \n      validatedData.currentTime <= segment.endTime\n    );\n\n    setActiveSegmentId(currentSegment?.id || null);\n  }, [validatedData]);\n\n  // EN: Auto-scroll to active segment\n  // VI: Tự động cuộn đến đoạn đang hoạt động\n  // ES: Desplazamiento automático al segmento activo\n  useEffect(() => {\n    if (autoScroll && activeSegmentRef.current && scrollContainerRef.current) {\n      activeSegmentRef.current.scrollIntoView({\n        behavior: 'smooth',\n        block: 'center'\n      });\n    }\n  }, [activeSegmentId, autoScroll]);\n\n  // EN: Format time for display\n  // VI: Định dạng thời gian để hiển thị\n  // ES: Formatear tiempo para mostrar\n  const formatTime = (seconds: number): string => {\n    const mins = Math.floor(seconds / 60);\n    const secs = Math.floor(seconds % 60);\n    return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;\n  };\n\n  // EN: Get confidence color class\n  // VI: Lấy class màu độ tin cậy\n  // ES: Obtener clase de color de confianza\n  const getConfidenceColor = (confidence?: number): string => {\n    if (!confidence) return '';\n    if (confidence >= 0.8) return 'text-green-600';\n    if (confidence >= 0.6) return 'text-yellow-600';\n    return 'text-red-600';\n  };\n\n  if (!isVisible) return null;\n\n  return (\n    <div className={`transcript-overlay ${className}`}>\n      {/* EN: Overlay header with controls */}\n      {/* VI: Header overlay với các điều khiển */}\n      {/* ES: Encabezado de superposición con controles */}\n      <div className=\"transcript-header bg-black bg-opacity-75 text-white p-3 flex justify-between items-center\">\n        <div className=\"flex items-center gap-4\">\n          <h3 className=\"text-lg font-semibold\">\n            {validatedData?.isLive ? 'Live Transcript' : 'Transcript'}\n          </h3>\n          {validatedData?.isLive && (\n            <div className=\"flex items-center gap-2\">\n              <div className=\"w-2 h-2 bg-red-500 rounded-full animate-pulse\"></div>\n              <span className=\"text-sm\">LIVE</span>\n            </div>\n          )}\n        </div>\n        \n        <div className=\"flex items-center gap-2\">\n          <button\n            onClick={onToggleVisibility}\n            className=\"text-white hover:text-gray-300 text-xl\"\n            aria-label=\"Close transcript\"\n          >\n            ×\n          </button>\n        </div>\n      </div>\n\n      {/* EN: Scrollable transcript content */}\n      {/* VI: Nội dung transcript có thể cuộn */}\n      {/* ES: Contenido de transcripción desplazable */}\n      <div \n        ref={scrollContainerRef}\n        className=\"transcript-content bg-black bg-opacity-60 text-white p-4 max-h-96 overflow-y-auto\"\n      >\n        {!validatedData ? (\n          <div className=\"text-center text-gray-400\">\n            Invalid transcript data\n          </div>\n        ) : validatedData.segments.length === 0 ? (\n          <div className=\"text-center text-gray-400\">\n            {validatedData.isLive ? 'Waiting for transcript...' : 'No transcript available'}\n          </div>\n        ) : (\n          <div className=\"space-y-2\">\n            {validatedData.segments.map((segment) => (\n              <div\n                key={segment.id}\n                ref={activeSegmentId === segment.id ? activeSegmentRef : null}\n                className={`transcript-segment p-2 rounded transition-colors duration-200 ${\n                  activeSegmentId === segment.id \n                    ? 'bg-blue-600 bg-opacity-50' \n                    : 'hover:bg-white hover:bg-opacity-10'\n                }`}\n              >\n                {/* EN: Segment metadata */}\n                {/* VI: Metadata của đoạn */}\n                {/* ES: Metadatos del segmento */}\n                {(showTimestamps || showSpeakers) && (\n                  <div className=\"segment-meta flex items-center gap-3 text-xs text-gray-300 mb-1\">\n                    {showTimestamps && (\n                      <span className=\"timestamp\">\n                        {formatTime(segment.startTime)}\n                      </span>\n                    )}\n                    {showSpeakers && segment.speaker && (\n                      <span className=\"speaker font-medium\">\n                        {segment.speaker}:\n                      </span>\n                    )}\n                    {segment.confidence && (\n                      <span className={`confidence ${getConfidenceColor(segment.confidence)}`}>\n                        ({Math.round(segment.confidence * 100)}%)\n                      </span>\n                    )}\n                  </div>\n                )}\n\n                {/* EN: Segment text content */}\n                {/* VI: Nội dung văn bản của đoạn */}\n                {/* ES: Contenido de texto del segmento */}\n                <div className=\"segment-text leading-relaxed\">\n                  {segment.text}\n                </div>\n              </div>\n            ))}\n          </div>\n        )}\n      </div>\n\n      {/* EN: Caption/subtitle display for accessibility */}\n      {/* VI: Hiển thị caption/subtitle cho khả năng tiếp cận */}\n      {/* ES: Visualización de subtítulos/caption para accesibilidad */}\n      {validatedData && activeSegmentId && (\n        <div className=\"caption-display sr-only\" aria-live=\"polite\">\n          {validatedData.segments.find(s => s.id === activeSegmentId)?.text}\n        </div>\n      )}\n    </div>\n  );\n}\n\n// EN: Export types for external use\n// VI: Export các type để sử dụng bên ngoài\n// ES: Exportar tipos para uso externo\nexport type { TranscriptOverlayProps, TranscriptSegment, TranscriptData };\n```\n\nThis TranscriptOverlay component provides:\n\n1. **Real-time transcript display** with auto-scrolling to active segments\n2. **Live transcript support** with visual indicators \n3. **Zod validation** at module level with `.safeParse()`\n4. **Trilingual comments** following the convention\n5. **SPEC section references** in comments\n6. **Accessibility features** including screen reader support\n7. **Flexible display options** for timestamps, speakers, and confidence scores\n8. **Smooth scrolling** and visual highlighting of current segments\n9. **Pattern matching** for transcript/caption/subtitle keywords as required by tests\n\nThe component handles both live and pre-recorded transcript data, with proper validation and error handling."
}
Task ID: ba6de73f-412d-42e4-81fe-b105268a4700