implemented 15,12,18 didn't check 18
This commit is contained in:
@ -49,6 +49,10 @@ export default function TranscriptEditor({
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [activeMatchIdx, setActiveMatchIdx] = useState(0);
|
||||
const searchInputRef = useRef<HTMLInputElement | null>(null);
|
||||
const updateWordText = useEditorStore((s) => s.updateWordText);
|
||||
const [editingWordIndex, setEditingWordIndex] = useState<number | null>(null);
|
||||
const [editText, setEditText] = useState('');
|
||||
const editInputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const selectedSet = useMemo(() => new Set(selectedWordIndices), [selectedWordIndices]);
|
||||
const matchIndices = useMemo(() => {
|
||||
@ -224,6 +228,61 @@ export default function TranscriptEditor({
|
||||
[setSelectedWordIndices],
|
||||
);
|
||||
|
||||
const startEditing = useCallback((index: number) => {
|
||||
const word = words[index];
|
||||
if (!word) return;
|
||||
setEditingWordIndex(index);
|
||||
setEditText(word.word);
|
||||
requestAnimationFrame(() => {
|
||||
editInputRef.current?.focus();
|
||||
editInputRef.current?.select();
|
||||
});
|
||||
}, [words]);
|
||||
|
||||
const commitEdit = useCallback(() => {
|
||||
if (editingWordIndex === null) return;
|
||||
const trimmed = editText.trim();
|
||||
if (trimmed && trimmed !== words[editingWordIndex]?.word) {
|
||||
updateWordText(editingWordIndex, trimmed);
|
||||
}
|
||||
setEditingWordIndex(null);
|
||||
setEditText('');
|
||||
}, [editingWordIndex, editText, words, updateWordText]);
|
||||
|
||||
const cancelEdit = useCallback(() => {
|
||||
setEditingWordIndex(null);
|
||||
setEditText('');
|
||||
}, []);
|
||||
|
||||
const handleWordDoubleClick = useCallback((index: number) => {
|
||||
if (cutMode || muteMode || gainMode || speedMode) return;
|
||||
startEditing(index);
|
||||
}, [cutMode, muteMode, gainMode, speedMode, startEditing]);
|
||||
|
||||
// Focus edit input when it appears
|
||||
useEffect(() => {
|
||||
if (editingWordIndex !== null && editInputRef.current) {
|
||||
editInputRef.current.focus();
|
||||
editInputRef.current.select();
|
||||
}
|
||||
}, [editingWordIndex]);
|
||||
|
||||
// Global key handler for edit mode
|
||||
useEffect(() => {
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (editingWordIndex === null) return;
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
commitEdit();
|
||||
} else if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
cancelEdit();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
return () => window.removeEventListener('keydown', onKeyDown);
|
||||
}, [editingWordIndex, commitEdit, cancelEdit]);
|
||||
|
||||
const cutSelectedWords = useCallback(() => {
|
||||
if (selectedWordIndices.length === 0) return;
|
||||
const sorted = [...selectedWordIndices].sort((a, b) => a - b);
|
||||
@ -319,15 +378,25 @@ export default function TranscriptEditor({
|
||||
const isSearchMatch = matchSet.has(globalIndex);
|
||||
const isActiveSearchMatch = matchIndices.length > 0 && matchIndices[safeActiveMatchIdx] === globalIndex;
|
||||
|
||||
const isEditing = globalIndex === editingWordIndex;
|
||||
|
||||
// Low-confidence highlighting
|
||||
const CONFIDENCE_THRESHOLD_KEY = 'talkedit:confidenceThreshold';
|
||||
const storedThreshold = typeof window !== 'undefined' ? Number(window.localStorage.getItem(CONFIDENCE_THRESHOLD_KEY)) : 0;
|
||||
const confidenceThreshold = Number.isFinite(storedThreshold) ? storedThreshold : 0.6;
|
||||
const isLowConfidence = word.confidence > 0 && word.confidence < confidenceThreshold && !cutRange && !muteRange && !gainRange && !speedRange;
|
||||
const confidencePct = word.confidence > 0 ? Math.round(word.confidence * 100) : null;
|
||||
|
||||
return (
|
||||
<span
|
||||
key={globalIndex}
|
||||
id={`word-${globalIndex}`}
|
||||
data-word-index={globalIndex}
|
||||
title={`${word.start.toFixed(2)}s — Ctrl+click to seek`}
|
||||
title={`${word.start.toFixed(2)}s — confidence: ${confidencePct !== null ? confidencePct + '%' : 'N/A'}${isLowConfidence ? ' ⚠️ Low confidence' : ''} — Ctrl+click to seek, double-click to edit`}
|
||||
onMouseDown={(e) => handleWordMouseDown(globalIndex, e)}
|
||||
onMouseEnter={() => handleWordMouseEnter(globalIndex)}
|
||||
onMouseLeave={() => setHoveredWordIndex(null)}
|
||||
onDoubleClick={() => handleWordDoubleClick(globalIndex)}
|
||||
className={`
|
||||
relative px-[2px] py-[1px] rounded cursor-pointer transition-colors
|
||||
${cutRange ? 'bg-red-500/20 text-red-100' : ''}
|
||||
@ -343,9 +412,21 @@ export default function TranscriptEditor({
|
||||
${isSelected && !cutRange && !muteRange && !gainRange && !speedRange ? 'bg-editor-word-selected text-white' : ''}
|
||||
${isActive && !isSelected && !cutRange && !muteRange && !gainRange && !speedRange ? 'bg-editor-accent/20 text-editor-accent' : ''}
|
||||
${isHovered && !isSelected && !isActive && !cutRange && !muteRange && !gainRange && !speedRange ? 'bg-editor-word-hover' : ''}
|
||||
${isLowConfidence ? 'border-b border-dashed border-orange-400/60' : ''}
|
||||
`}
|
||||
>
|
||||
{word.word}{' '}
|
||||
{isEditing ? (
|
||||
<input
|
||||
ref={editInputRef}
|
||||
value={editText}
|
||||
onChange={(e) => setEditText(e.target.value)}
|
||||
onBlur={commitEdit}
|
||||
className="w-24 px-1 py-0 text-xs bg-editor-bg border border-editor-accent rounded text-editor-text focus:outline-none"
|
||||
style={{ minWidth: `${Math.max(word.word.length * 8, 48)}px` }}
|
||||
/>
|
||||
) : (
|
||||
<>{word.word}{' '}</>
|
||||
)}
|
||||
{(cutRange || muteRange || gainRange || speedRange) && isHovered && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
@ -367,7 +448,7 @@ export default function TranscriptEditor({
|
||||
</div>
|
||||
);
|
||||
},
|
||||
[segments, selectedSet, matchSet, matchIndices, safeActiveMatchIdx, activeWordIndex, hoveredWordIndex, handleWordMouseDown, handleWordMouseEnter, setHoveredWordIndex, getCutRangeForWord, getMuteRangeForWord, getGainRangeForWord, getSpeedRangeForWord, removeCutRange, removeMuteRange, removeGainRange, removeSpeedRange, zoneDragRange, cutMode, muteMode, gainMode, speedMode],
|
||||
[segments, selectedSet, matchSet, matchIndices, safeActiveMatchIdx, activeWordIndex, hoveredWordIndex, handleWordMouseDown, handleWordMouseEnter, setHoveredWordIndex, getCutRangeForWord, getMuteRangeForWord, getGainRangeForWord, getSpeedRangeForWord, removeCutRange, removeMuteRange, removeGainRange, removeSpeedRange, zoneDragRange, cutMode, muteMode, gainMode, speedMode, editingWordIndex, editText, editInputRef, handleWordDoubleClick, commitEdit, setEditText],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user