From 9969e86e9cbb9737708e6cec8bc78cb612cedfe2 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Sun, 10 May 2026 00:33:18 +0100 Subject: [PATCH] update: confort + ui --- src/components/editor/EditorSrtPanel.tsx | 46 +++++++++++++++++++++++- src/index.css | 17 +++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/components/editor/EditorSrtPanel.tsx b/src/components/editor/EditorSrtPanel.tsx index 27c438f..8fc21cf 100644 --- a/src/components/editor/EditorSrtPanel.tsx +++ b/src/components/editor/EditorSrtPanel.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import { Download, RefreshCw, Save } from "lucide-react"; import type { SubtitleLanguage } from "@/managers/stores/useSettingsStore"; import type { @@ -21,6 +21,11 @@ interface SrtDiagnostic { errors: string[]; } +interface TextRange { + start: number; + end: number; +} + const SRT_VOICES: SrtVoiceOption[] = [ { id: "narrateur", label: "Narrateur" }, { id: "fermier", label: "Fermier" }, @@ -164,6 +169,23 @@ function getExpectedDialogues( .sort((a, b) => a.subtitleCueIndex - b.subtitleCueIndex); } +function findCueBlockRange( + content: string, + cueIndex: number, +): TextRange | null { + const normalizedContent = content.replace(/\r/g, ""); + const cuePattern = new RegExp(`(^|\\n)${cueIndex}\\n`, "m"); + const match = normalizedContent.match(cuePattern); + + if (!match || match.index === undefined) return null; + + const start = match.index + (match[1] ? 1 : 0); + const nextBlockIndex = normalizedContent.indexOf("\n\n", start); + const end = nextBlockIndex === -1 ? normalizedContent.length : nextBlockIndex; + + return { start, end }; +} + function downloadSrtFile( voice: DialogueVoiceId, language: SubtitleLanguage, @@ -198,6 +220,7 @@ async function saveSrtFile( } export function EditorSrtPanel(): React.JSX.Element { + const textareaRef = useRef(null); const [voice, setVoice] = useState("narrateur"); const [language, setLanguage] = useState("fr"); const [content, setContent] = useState(""); @@ -240,6 +263,19 @@ export function EditorSrtPanel(): React.JSX.Element { } } + function handleJumpToCue(cueIndex: number): void { + const range = findCueBlockRange(content, cueIndex); + + if (!range || !textareaRef.current) { + setStatus(`Cue ${cueIndex} introuvable dans le SRT.`); + return; + } + + textareaRef.current.focus(); + textareaRef.current.setSelectionRange(range.start, range.end); + setStatus(`Cue ${cueIndex} selectionnee dans le SRT.`); + } + useEffect(() => { let mounted = true; @@ -353,11 +389,19 @@ export function EditorSrtPanel(): React.JSX.Element { controls src={selectedDialogue.audio} /> + )}