diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index a041d4c..58922d5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -48,9 +48,12 @@ export default function App() { language, isTranscribing, transcriptionStatus, + markInTime, + markOutTime, loadVideo, setProjectFilePath, setBackendUrl, + clearMarkRange, setTranscription, setTranscriptionModel, setTranscribing, @@ -333,6 +336,17 @@ export default function App() { }; const handleCut = () => { + if (markInTime !== null && markOutTime !== null) { + const startTime = Math.min(markInTime, markOutTime); + const endTime = Math.max(markInTime, markOutTime); + if (endTime - startTime >= 0.01) { + addCutRange(startTime, endTime); + setActivePanel('zones'); + } + clearMarkRange(); + return; + } + if (selectedWordIndices.length > 0) { // If words are selected, apply cut immediately const sorted = [...selectedWordIndices].sort((a, b) => a - b); @@ -349,6 +363,17 @@ export default function App() { }; const handleMute = () => { + if (markInTime !== null && markOutTime !== null) { + const startTime = Math.min(markInTime, markOutTime); + const endTime = Math.max(markInTime, markOutTime); + if (endTime - startTime >= 0.01) { + addMuteRange(startTime, endTime); + setActivePanel('zones'); + } + clearMarkRange(); + return; + } + if (selectedWordIndices.length > 0) { // If words are selected, apply mute immediately const sorted = [...selectedWordIndices].sort((a, b) => a - b); @@ -365,6 +390,17 @@ export default function App() { }; const handleGain = () => { + if (markInTime !== null && markOutTime !== null) { + const startTime = Math.min(markInTime, markOutTime); + const endTime = Math.max(markInTime, markOutTime); + if (endTime - startTime >= 0.01) { + addGainRange(startTime, endTime, gainModeDb); + setActivePanel('zones'); + } + clearMarkRange(); + return; + } + if (selectedWordIndices.length > 0) { const sorted = [...selectedWordIndices].sort((a, b) => a - b); const startTime = words[sorted[0]].start; @@ -379,6 +415,17 @@ export default function App() { }; const handleSpeed = () => { + if (markInTime !== null && markOutTime !== null) { + const startTime = Math.min(markInTime, markOutTime); + const endTime = Math.max(markInTime, markOutTime); + if (endTime - startTime >= 0.01) { + addSpeedRange(startTime, endTime, speedModeValue); + setActivePanel('zones'); + } + clearMarkRange(); + return; + } + if (selectedWordIndices.length > 0) { const sorted = [...selectedWordIndices].sort((a, b) => a - b); const startTime = words[sorted[0]].start; diff --git a/frontend/src/components/WaveformTimeline.tsx b/frontend/src/components/WaveformTimeline.tsx index 4dee0b1..31deff7 100644 --- a/frontend/src/components/WaveformTimeline.tsx +++ b/frontend/src/components/WaveformTimeline.tsx @@ -278,11 +278,13 @@ export default function WaveformTimeline({ const [showMuteZones, setShowMuteZones] = useState(true); const [showGainZones, setShowGainZones] = useState(true); const [showSpeedZones, setShowSpeedZones] = useState(true); + const [showAdjustedTimeline, setShowAdjustedTimeline] = useState(false); const sourceDuration = duration || waveformDataRef.current?.duration || 0; + const timelineCutRanges = showAdjustedTimeline ? cutRanges : []; const { segments: timelineSegments, displayDuration } = useMemo( - () => buildTimelineSegments(sourceDuration, cutRanges), - [sourceDuration, cutRanges], + () => buildTimelineSegments(sourceDuration, timelineCutRanges), + [sourceDuration, timelineCutRanges], ); useEffect(() => { @@ -447,15 +449,16 @@ export default function WaveformTimeline({ const x1 = (sourceToDisplayTime(range.start, timelineSegments, dur) - scroll) * pxPerSec; const x2 = (sourceToDisplayTime(range.end, timelineSegments, dur) - scroll) * pxPerSec; const isSelected = selectedZone?.type === 'cut' && selectedZone.id === range.id; - - ctx.fillStyle = isSelected ? 'rgba(239, 68, 68, 0.5)' : 'rgba(239, 68, 68, 0.3)'; + + ctx.fillStyle = isSelected ? 'rgba(239, 68, 68, 0.62)' : 'rgba(239, 68, 68, 0.46)'; ctx.fillRect(x1, waveTop, x2 - x1, waveH); + + // Keep cut ranges clearly visible even when not selected. + ctx.strokeStyle = isSelected ? '#ef4444' : 'rgba(239, 68, 68, 0.9)'; + ctx.lineWidth = isSelected ? 2.8 : 1.8; + ctx.strokeRect(x1, waveTop, x2 - x1, waveH); if (isSelected) { - ctx.strokeStyle = '#ef4444'; - ctx.lineWidth = 2; - ctx.strokeRect(x1, waveTop, x2 - x1, waveH); - // Draw resize handles ctx.fillStyle = '#ef4444'; ctx.beginPath(); @@ -634,6 +637,7 @@ export default function WaveformTimeline({ gainMode, speedMode, selectedZone, + showAdjustedTimeline, markInTime, markOutTime, displayDuration, @@ -1184,35 +1188,47 @@ export default function WaveformTimeline({ {markOutTime !== null && O {markOutTime.toFixed(2)}s}