From 0c8b9070bbdbd814c1a6d646bf940f6c38d01328 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Sun, 10 May 2026 00:13:42 +0100 Subject: [PATCH] add: add str editing panel --- src/components/editor/EditorControls.tsx | 3 + src/components/editor/EditorSrtPanel.tsx | 161 +++++++++++++++++++++++ src/index.css | 71 ++++++++++ 3 files changed, 235 insertions(+) create mode 100644 src/components/editor/EditorSrtPanel.tsx diff --git a/src/components/editor/EditorControls.tsx b/src/components/editor/EditorControls.tsx index 143d666..e441db0 100644 --- a/src/components/editor/EditorControls.tsx +++ b/src/components/editor/EditorControls.tsx @@ -12,6 +12,7 @@ import { Save, Undo2, } from "lucide-react"; +import { EditorSrtPanel } from "@/components/editor/EditorSrtPanel"; import type { MapNode, TransformMode } from "@/types/editor/editor"; interface EditorControlsProps { @@ -236,6 +237,8 @@ export function EditorControls({ : `Selected node ${selectedNodeIndex + 1} raw lines`} + + ); diff --git a/src/components/editor/EditorSrtPanel.tsx b/src/components/editor/EditorSrtPanel.tsx new file mode 100644 index 0000000..4ee739e --- /dev/null +++ b/src/components/editor/EditorSrtPanel.tsx @@ -0,0 +1,161 @@ +import { useEffect, useState } from "react"; +import { Download, RefreshCw } from "lucide-react"; +import type { SubtitleLanguage } from "@/managers/stores/useSettingsStore"; +import type { + DialogueSpeaker, + DialogueVoiceId, +} from "@/types/dialogues/dialogues"; + +interface SrtVoiceOption { + id: DialogueVoiceId; + label: DialogueSpeaker; +} + +const SRT_VOICES: SrtVoiceOption[] = [ + { id: "narrateur", label: "Narrateur" }, + { id: "fermier", label: "Fermier" }, + { id: "leonie", label: "Leonie" }, +]; +const DEFAULT_SRT_VOICE: SrtVoiceOption = { + id: "narrateur", + label: "Narrateur", +}; + +const SRT_LANGUAGES: SubtitleLanguage[] = ["fr", "en"]; + +function getSrtPath( + voice: DialogueVoiceId, + language: SubtitleLanguage, +): string { + return `/sounds/dialogue/subtitles/${language}/${voice}.srt`; +} + +function createEmptySrtTemplate(speaker: DialogueSpeaker): string { + return `1\n00:00:00,000 --> 00:00:02,000\n${speaker}: Nouveau sous-titre\n`; +} + +function downloadSrtFile( + voice: DialogueVoiceId, + language: SubtitleLanguage, + content: string, +): void { + const blob = new Blob([content], { type: "text/plain;charset=utf-8" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = `${voice}.${language}.srt`; + link.click(); + window.setTimeout(() => URL.revokeObjectURL(url), 0); +} + +export function EditorSrtPanel(): React.JSX.Element { + const [voice, setVoice] = useState("narrateur"); + const [language, setLanguage] = useState("fr"); + const [content, setContent] = useState(""); + const [status, setStatus] = useState("Chargement du SRT..."); + const selectedVoice = + SRT_VOICES.find((item) => item.id === voice) ?? DEFAULT_SRT_VOICE; + + useEffect(() => { + let mounted = true; + const srtPath = getSrtPath(voice, language); + + void fetch(srtPath) + .then(async (response) => { + if (!mounted) return; + + if (!response.ok) { + setContent(createEmptySrtTemplate(selectedVoice.label)); + setStatus("Fichier absent, template local cree"); + return; + } + + setContent(await response.text()); + setStatus(`Charge depuis ${srtPath}`); + }) + .catch(() => { + if (!mounted) return; + setContent(createEmptySrtTemplate(selectedVoice.label)); + setStatus("Erreur de chargement, template local cree"); + }); + + return () => { + mounted = false; + }; + }, [language, selectedVoice.label, voice]); + + return ( +
+
+

SRT

+ {language.toUpperCase()} +
+ +
+ + + +
+ +