Spaces:
Running
on
A100
Running
on
A100
File size: 3,992 Bytes
ae238b3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
import React, { useState, useEffect } from 'react';
import { InformationCircleIcon } from '@heroicons/react/24/outline';
import { useTranscriptionStore } from '../stores/transcriptionStore';
import { formatTime } from '../utils/subtitleUtils';
const DEFAULT_MERGE_THRESHOLD = 2;
const MediaEditControls: React.FC = () => {
const [mergeThreshold, setMergeThreshold] = useState(DEFAULT_MERGE_THRESHOLD);
const {
transcription,
currentTime,
activeSegmentIndex,
currentSegments,
undo,
redo,
canUndo,
canRedo,
mergeSegmentsByProximity,
} = useTranscriptionStore();
const MAX_MERGE_INTERVAL_SECONDS = 30;
if (!transcription) {
return null;
}
const displaySegments = currentSegments || transcription.aligned_segments;
// Handle merge threshold changes
useEffect(() => {
mergeSegmentsByProximity(mergeThreshold);
}, [mergeThreshold, mergeSegmentsByProximity]);
// Keyboard shortcuts for undo/redo
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
e.preventDefault();
undo();
} else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
e.preventDefault();
redo();
}
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [undo, redo]);
return (
<div className="flex items-center justify-center">
<div className="flex items-center space-x-4">
{/* Current Status Info */}
<div className="flex items-center space-x-4 text-xs text-gray-400">
<span className="text-green-400">
{formatTime(currentTime)} / {formatTime(transcription.total_duration)}
</span>
<span className="text-blue-400">
{activeSegmentIndex !== null
? `Segment ${activeSegmentIndex + 1}/${displaySegments.length}`
: "No active segment"}
</span>
</div>
{/* Combine Segments Slider */}
<div className="flex items-center space-x-2">
<label className="text-xs text-gray-300 whitespace-nowrap flex items-center space-x-1">
<span>Combine Words:</span>
<div className="tooltip" data-tip="This slider merges nearby words. The higher the more words are combined">
<InformationCircleIcon className="w-4 h-4 text-gray-100 hover:text-gray-300 cursor-help inline ml-1" />
</div>
</label>
<input
type="range"
min="0"
max={MAX_MERGE_INTERVAL_SECONDS}
step="0.5"
value={mergeThreshold}
onChange={(e) => setMergeThreshold(Number(e.target.value))}
className="w-20 h-1 bg-gray-600 rounded-lg appearance-none cursor-pointer slider"
style={{
background: `linear-gradient(to right, #3B82F6 0%, #3B82F6 ${(mergeThreshold / MAX_MERGE_INTERVAL_SECONDS) * 100}%, #4B5563 ${(mergeThreshold / MAX_MERGE_INTERVAL_SECONDS) * 100}%, #4B5563 100%)`
}}
/>
</div>
{/* Undo/Redo Buttons */}
<div className="flex items-center space-x-2">
<button
onClick={undo}
disabled={!canUndo}
className="px-3 py-1 text-xs bg-blue-600 hover:bg-blue-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white rounded transition-colors"
title="Undo (Ctrl+Z)"
>
↶ Undo
</button>
<button
onClick={redo}
disabled={!canRedo}
className="px-3 py-1 text-xs bg-blue-600 hover:bg-blue-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white rounded transition-colors"
title="Redo (Ctrl+Y)"
>
↷ Redo
</button>
</div>
</div>
</div>
);
};
export default MediaEditControls;
|