From fe761ce0f05b6cc166f37b30b5def8c4218ab2a7 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 14 Sep 2026 22:14:28 +0000 Subject: [PATCH] Add toggle to include or omit speaker names on transcript export SRT/VTT/TXT/MD/DOCX/PDF previously always prefixed cues or turns with speaker labels. Make that optional (default on); JSON still keeps speaker ids and names for round-trip re-import. Co-authored-by: Wassim Gharbi --- components/ExportDialog.tsx | 17 ++++++++ lib/i18n/messages/de.ts | 1 + lib/i18n/messages/en.ts | 1 + lib/i18n/messages/es.ts | 1 + lib/i18n/messages/fr.ts | 1 + lib/i18n/messages/ja.ts | 1 + lib/i18n/messages/ko.ts | 1 + lib/i18n/messages/pt.ts | 1 + lib/i18n/messages/zh-CN.ts | 1 + lib/i18n/messages/zh-TW.ts | 1 + lib/serializeTranscript.ts | 70 +++++++++++++++++++++++------- tests/serialize-transcript-test.ts | 41 +++++++++++++++++ 12 files changed, 121 insertions(+), 16 deletions(-) diff --git a/components/ExportDialog.tsx b/components/ExportDialog.tsx index 7023f95..abe97bc 100644 --- a/components/ExportDialog.tsx +++ b/components/ExportDialog.tsx @@ -113,6 +113,7 @@ export default function ExportDialog() { const [textFormat, setTextFormat] = useState("txt"); const [includeTimestamps, setIncludeTimestamps] = useState(false); const [shortCues, setShortCues] = useState(true); + const [speakerLabels, setSpeakerLabels] = useState(true); const [timelineFormat, setTimelineFormat] = useState("resolve"); const [timelineFrameRate, setTimelineFrameRate] = @@ -273,6 +274,7 @@ export default function ExportDialog() { const textSupportsTimestamps = DOC_FORMATS.has(textFormat); const textSupportsShortCues = SUBTITLE_FORMATS.has(textFormat); + const textSupportsSpeakerLabels = textFormat !== "json"; const exportText = useCallback(() => { // Read live store state at click time so a rename / replace-in-project @@ -287,6 +289,7 @@ export default function ExportDialog() { speakers: speakersFromWords(s.words, s.speakers), ...(textSupportsTimestamps ? { timestamps: includeTimestamps } : {}), ...(textSupportsShortCues ? { shortCues } : {}), + ...(textSupportsSpeakerLabels ? { speakerLabels } : {}), }); setError(null); trackEvent("export_completed", { @@ -294,6 +297,7 @@ export default function ExportDialog() { format: textFormat, ...(textSupportsTimestamps ? { timestamps: includeTimestamps } : {}), ...(textSupportsShortCues ? { shortCues } : {}), + ...(textSupportsSpeakerLabels ? { speakerLabels } : {}), }); } catch (err) { setError(err instanceof Error ? err.message : en["error.export"]); @@ -302,8 +306,10 @@ export default function ExportDialog() { textFormat, textSupportsTimestamps, textSupportsShortCues, + textSupportsSpeakerLabels, includeTimestamps, shortCues, + speakerLabels, baseName, ]); @@ -514,6 +520,17 @@ export default function ExportDialog() { {t("export.includeTimestamps")} )} + {textSupportsSpeakerLabels && ( + + )} {textSupportsShortCues && (