UI improvements, moved file name and moved buttons left
This commit is contained in:
@ -1,22 +1,32 @@
|
||||
import { useCallback, useRef, useEffect, useMemo, useState } from 'react';
|
||||
import { useEditorStore } from '../store/editorStore';
|
||||
import { Virtuoso } from 'react-virtuoso';
|
||||
import { Scissors, VolumeX, SlidersHorizontal, RotateCcw } from 'lucide-react';
|
||||
import { Scissors, VolumeX, SlidersHorizontal, Gauge, RotateCcw } from 'lucide-react';
|
||||
|
||||
interface TranscriptEditorProps {
|
||||
cutMode: boolean;
|
||||
muteMode: boolean;
|
||||
gainMode: boolean;
|
||||
gainModeDb: number;
|
||||
speedMode: boolean;
|
||||
speedModeValue: number;
|
||||
}
|
||||
|
||||
export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainModeDb }: TranscriptEditorProps) {
|
||||
export default function TranscriptEditor({
|
||||
cutMode,
|
||||
muteMode,
|
||||
gainMode,
|
||||
gainModeDb,
|
||||
speedMode,
|
||||
speedModeValue,
|
||||
}: TranscriptEditorProps) {
|
||||
const words = useEditorStore((s) => s.words);
|
||||
const segments = useEditorStore((s) => s.segments);
|
||||
const deletedRanges = useEditorStore((s) => s.deletedRanges);
|
||||
const cutRanges = useEditorStore((s) => s.cutRanges);
|
||||
const muteRanges = useEditorStore((s) => s.muteRanges);
|
||||
const gainRanges = useEditorStore((s) => s.gainRanges);
|
||||
const speedRanges = useEditorStore((s) => s.speedRanges);
|
||||
const selectedWordIndices = useEditorStore((s) => s.selectedWordIndices);
|
||||
const hoveredWordIndex = useEditorStore((s) => s.hoveredWordIndex);
|
||||
const setSelectedWordIndices = useEditorStore((s) => s.setSelectedWordIndices);
|
||||
@ -25,9 +35,11 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
const removeCutRange = useEditorStore((s) => s.removeCutRange);
|
||||
const removeMuteRange = useEditorStore((s) => s.removeMuteRange);
|
||||
const removeGainRange = useEditorStore((s) => s.removeGainRange);
|
||||
const removeSpeedRange = useEditorStore((s) => s.removeSpeedRange);
|
||||
const addCutRange = useEditorStore((s) => s.addCutRange);
|
||||
const addMuteRange = useEditorStore((s) => s.addMuteRange);
|
||||
const addGainRange = useEditorStore((s) => s.addGainRange);
|
||||
const addSpeedRange = useEditorStore((s) => s.addSpeedRange);
|
||||
const getWordAtTime = useEditorStore((s) => s.getWordAtTime);
|
||||
|
||||
const selectionStart = useRef<number | null>(null);
|
||||
@ -84,7 +96,7 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
return;
|
||||
}
|
||||
|
||||
if (cutMode || muteMode || gainMode) {
|
||||
if (cutMode || muteMode || gainMode || speedMode) {
|
||||
zoneDragStart.current = index;
|
||||
setZoneDragRange({ start: index, end: index });
|
||||
selectionStart.current = null;
|
||||
@ -104,7 +116,7 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
setSelectedWordIndices([index]);
|
||||
}
|
||||
},
|
||||
[words, selectedWordIndices, setSelectedWordIndices, cutMode, muteMode, gainMode],
|
||||
[words, selectedWordIndices, setSelectedWordIndices, cutMode, muteMode, gainMode, speedMode],
|
||||
);
|
||||
|
||||
const handleWordMouseEnter = useCallback(
|
||||
@ -137,12 +149,13 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
if (cutMode) addCutRange(startWord.start, endWord.end);
|
||||
if (muteMode) addMuteRange(startWord.start, endWord.end);
|
||||
if (gainMode) addGainRange(startWord.start, endWord.end, gainModeDb);
|
||||
if (speedMode) addSpeedRange(startWord.start, endWord.end, speedModeValue);
|
||||
}
|
||||
}
|
||||
zoneDragStart.current = null;
|
||||
setZoneDragRange(null);
|
||||
selectionStart.current = null;
|
||||
}, [zoneDragRange, words, cutMode, muteMode, gainMode, gainModeDb, addCutRange, addMuteRange, addGainRange]);
|
||||
}, [zoneDragRange, words, cutMode, muteMode, gainMode, gainModeDb, speedMode, speedModeValue, addCutRange, addMuteRange, addGainRange, addSpeedRange]);
|
||||
|
||||
const handleClickOutside = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
@ -186,6 +199,14 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
addGainRange(startTime, endTime, gainModeDb);
|
||||
}, [selectedWordIndices, words, addGainRange, gainModeDb]);
|
||||
|
||||
const speedSelectedWords = useCallback(() => {
|
||||
if (selectedWordIndices.length === 0) return;
|
||||
const sorted = [...selectedWordIndices].sort((a, b) => a - b);
|
||||
const startTime = words[sorted[0]].start;
|
||||
const endTime = words[sorted[sorted.length - 1]].end;
|
||||
addSpeedRange(startTime, endTime, speedModeValue);
|
||||
}, [selectedWordIndices, words, addSpeedRange, speedModeValue]);
|
||||
|
||||
const getCutRangeForWord = useCallback(
|
||||
(wordIndex: number) => {
|
||||
const word = words[wordIndex];
|
||||
@ -213,6 +234,15 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
[words, gainRanges],
|
||||
);
|
||||
|
||||
const getSpeedRangeForWord = useCallback(
|
||||
(wordIndex: number) => {
|
||||
const word = words[wordIndex];
|
||||
if (!word) return null;
|
||||
return speedRanges.find((r) => word.start >= r.start && word.end <= r.end);
|
||||
},
|
||||
[words, speedRanges],
|
||||
);
|
||||
|
||||
const renderSegment = useCallback(
|
||||
(index: number) => {
|
||||
const segment = segments[index];
|
||||
@ -238,6 +268,7 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
const cutRange = getCutRangeForWord(globalIndex);
|
||||
const muteRange = getMuteRangeForWord(globalIndex);
|
||||
const gainRange = getGainRangeForWord(globalIndex);
|
||||
const speedRange = getSpeedRangeForWord(globalIndex);
|
||||
|
||||
return (
|
||||
<span
|
||||
@ -254,12 +285,14 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
${cutRange ? 'bg-red-500/20 text-red-100' : ''}
|
||||
${muteRange ? 'bg-blue-500/20 text-blue-100' : ''}
|
||||
${gainRange ? 'bg-amber-500/20 text-amber-100' : ''}
|
||||
${speedRange ? 'bg-emerald-500/20 text-emerald-100' : ''}
|
||||
${isZoneDragSelected && cutMode ? 'bg-red-500/30 ring-1 ring-red-400/60' : ''}
|
||||
${isZoneDragSelected && muteMode ? 'bg-blue-500/30 ring-1 ring-blue-400/60' : ''}
|
||||
${isZoneDragSelected && gainMode ? 'bg-amber-500/30 ring-1 ring-amber-400/60' : ''}
|
||||
${isSelected && !isDeleted && !cutRange && !muteRange && !gainRange ? 'bg-editor-word-selected text-white' : ''}
|
||||
${isActive && !isDeleted && !isSelected && !cutRange && !muteRange && !gainRange ? 'bg-editor-accent/20 text-editor-accent' : ''}
|
||||
${isHovered && !isDeleted && !isSelected && !isActive && !cutRange && !muteRange && !gainRange ? 'bg-editor-word-hover' : ''}
|
||||
${isZoneDragSelected && speedMode ? 'bg-emerald-500/30 ring-1 ring-emerald-400/60' : ''}
|
||||
${isSelected && !isDeleted && !cutRange && !muteRange && !gainRange && !speedRange ? 'bg-editor-word-selected text-white' : ''}
|
||||
${isActive && !isDeleted && !isSelected && !cutRange && !muteRange && !gainRange && !speedRange ? 'bg-editor-accent/20 text-editor-accent' : ''}
|
||||
${isHovered && !isDeleted && !isSelected && !isActive && !cutRange && !muteRange && !gainRange && !speedRange ? 'bg-editor-word-hover' : ''}
|
||||
`}
|
||||
>
|
||||
{word.word}{' '}
|
||||
@ -274,13 +307,14 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
<RotateCcw className="w-2.5 h-2.5" /> Restore
|
||||
</button>
|
||||
)}
|
||||
{(cutRange || muteRange || gainRange) && isHovered && (
|
||||
{(cutRange || muteRange || gainRange || speedRange) && isHovered && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (cutRange) removeCutRange(cutRange.id);
|
||||
if (muteRange) removeMuteRange(muteRange.id);
|
||||
if (gainRange) removeGainRange(gainRange.id);
|
||||
if (speedRange) removeSpeedRange(speedRange.id);
|
||||
}}
|
||||
className="absolute -top-5 left-1/2 -translate-x-1/2 flex items-center gap-0.5 px-1.5 py-0.5 bg-editor-surface border border-editor-border rounded text-[10px] text-editor-success whitespace-nowrap z-10"
|
||||
>
|
||||
@ -294,7 +328,7 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[segments, deletedSet, selectedSet, activeWordIndex, hoveredWordIndex, handleWordMouseDown, handleWordMouseEnter, setHoveredWordIndex, getRangeForWord, getCutRangeForWord, getMuteRangeForWord, getGainRangeForWord, restoreRange, removeCutRange, removeMuteRange, removeGainRange, zoneDragRange, cutMode, muteMode, gainMode],
|
||||
[segments, deletedSet, selectedSet, activeWordIndex, hoveredWordIndex, handleWordMouseDown, handleWordMouseEnter, setHoveredWordIndex, getRangeForWord, getCutRangeForWord, getMuteRangeForWord, getGainRangeForWord, getSpeedRangeForWord, restoreRange, removeCutRange, removeMuteRange, removeGainRange, removeSpeedRange, zoneDragRange, cutMode, muteMode, gainMode, speedMode],
|
||||
);
|
||||
|
||||
return (
|
||||
@ -302,6 +336,7 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
<div className="flex items-center gap-2 px-4 py-2 border-b border-editor-border shrink-0">
|
||||
<span className="text-xs text-editor-text-muted flex-1">
|
||||
{words.length} words · {deletedRanges.length} cuts · {cutRanges.length} cut ranges · {muteRanges.length} mute ranges · {gainRanges.length} gain ranges
|
||||
· {speedRanges.length} speed ranges
|
||||
</span>
|
||||
{selectedWordIndices.length > 0 && (
|
||||
<div className="flex items-center gap-1">
|
||||
@ -326,6 +361,13 @@ export default function TranscriptEditor({ cutMode, muteMode, gainMode, gainMode
|
||||
<SlidersHorizontal className="w-3 h-3" />
|
||||
Gain ({gainModeDb > 0 ? '+' : ''}{gainModeDb.toFixed(1)} dB)
|
||||
</button>
|
||||
<button
|
||||
onClick={speedSelectedWords}
|
||||
className="flex items-center gap-1 px-2 py-1 text-xs bg-emerald-500/20 text-emerald-300 rounded hover:bg-emerald-500/30 transition-colors"
|
||||
>
|
||||
<Gauge className="w-3 h-3" />
|
||||
Speed {speedModeValue.toFixed(2)}x
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user