| | import React from 'react'; |
| |
|
| | export interface SubtitleSegment { |
| | id: number; |
| | startTime: string; |
| | endTime: string; |
| | duration: string; |
| | sourceText: string; |
| | targetText?: string; |
| | isCurrent?: boolean; |
| | } |
| |
|
| | export interface VideoInfo { |
| | title: string; |
| | duration: string; |
| | totalSegments: number; |
| | currentSegment: number; |
| | } |
| |
|
| | interface SubtitlingModuleProps { |
| | videoInfo: VideoInfo; |
| | subtitleSegments: SubtitleSegment[]; |
| | currentDisplayedSubtitle?: string; |
| | onSegmentClick?: (id: number) => void; |
| | getSegmentButtonClass?: (id: number) => string; |
| | onPlayPause?: () => void; |
| | isPlaying?: boolean; |
| | onSaveSegment?: (id: number, text: string) => void; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | const SubtitlingModule: React.FC<SubtitlingModuleProps> = ({ |
| | videoInfo, |
| | subtitleSegments, |
| | currentDisplayedSubtitle, |
| | onSegmentClick, |
| | getSegmentButtonClass, |
| | onPlayPause, |
| | isPlaying, |
| | }) => { |
| | return ( |
| | <div className="grid grid-cols-1 lg:grid-cols-2 gap-8"> |
| | <div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6"> |
| | <div className="mb-4"> |
| | <h3 className="text-xl font-bold text-gray-900 mb-2">{videoInfo.title}</h3> |
| | </div> |
| | <div className="bg-black rounded-lg aspect-video overflow-hidden relative"> |
| | <div className="absolute inset-0 flex items-center justify-center text-white/70 text-sm">Video Placeholder</div> |
| | {currentDisplayedSubtitle && ( |
| | <div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 z-10"> |
| | <div className="bg-black bg-opacity-80 text-white px-6 py-3 rounded-lg text-center max-w-lg"> |
| | <p className={`text-base font-medium leading-relaxed tracking-wide ${currentDisplayedSubtitle.length <= 42 ? 'whitespace-nowrap' : 'whitespace-pre-line'}`}> |
| | {currentDisplayedSubtitle} |
| | </p> |
| | </div> |
| | </div> |
| | )} |
| | </div> |
| | </div> |
| | |
| | <div className="bg-white rounded-xl shadow-lg border border-gray-100 p-6"> |
| | <h3 className="text-xl font-bold text-gray-900 mb-4">Translation Workspace</h3> |
| | <div className="grid grid-cols-9 gap-1 mb-6"> |
| | {subtitleSegments.map((segment) => ( |
| | <button |
| | key={segment.id} |
| | onClick={() => onSegmentClick && onSegmentClick(segment.id)} |
| | className={`px-1 py-1 rounded text-xs w-full ${getSegmentButtonClass ? getSegmentButtonClass(segment.id) : ''}`} |
| | > |
| | {segment.id} |
| | </button> |
| | ))} |
| | </div> |
| | <div className="flex items-center gap-2 mb-4"> |
| | <button onClick={onPlayPause} className="px-3 py-1.5 text-sm rounded-md border border-gray-300"> |
| | {isPlaying ? 'Pause' : 'Play'} |
| | </button> |
| | <div className="text-sm text-gray-600">{videoInfo.currentSegment}/{videoInfo.totalSegments}</div> |
| | </div> |
| | <div className="space-y-4"> |
| | {subtitleSegments.map(seg => ( |
| | <div key={seg.id} className="border border-gray-200 rounded-lg p-3"> |
| | <div className="text-xs text-gray-500 mb-1">{seg.startTime} → {seg.endTime} ({seg.duration})</div> |
| | <div className="text-sm text-gray-800 mb-2">{seg.sourceText}</div> |
| | <textarea className="w-full border border-gray-300 rounded p-2 text-sm" defaultValue={seg.targetText || ''} rows={2} /> |
| | </div> |
| | ))} |
| | </div> |
| | </div> |
| | </div> |
| | ); |
| | }; |
| |
|
| | export default SubtitlingModule; |
| |
|
| |
|
| |
|