fix: captions view ui + use BatchCommand to add text elements in transcription at once

This commit is contained in:
Maze Winther
2026-03-04 21:52:22 +01:00
parent a2580216f8
commit 5638974f4b
@@ -11,6 +11,11 @@ import { useState, useRef } from "react";
import { extractTimelineAudio } from "@/lib/media/mediabunny"; import { extractTimelineAudio } from "@/lib/media/mediabunny";
import { useEditor } from "@/hooks/use-editor"; import { useEditor } from "@/hooks/use-editor";
import { DEFAULT_TEXT_ELEMENT } from "@/constants/text-constants"; import { DEFAULT_TEXT_ELEMENT } from "@/constants/text-constants";
import {
BatchCommand,
AddTrackCommand,
InsertElementCommand,
} from "@/lib/commands";
import { TRANSCRIPTION_LANGUAGES } from "@/constants/transcription-constants"; import { TRANSCRIPTION_LANGUAGES } from "@/constants/transcription-constants";
import type { import type {
TranscriptionLanguage, TranscriptionLanguage,
@@ -20,7 +25,12 @@ import { transcriptionService } from "@/services/transcription/service";
import { decodeAudioToFloat32 } from "@/lib/media/audio"; import { decodeAudioToFloat32 } from "@/lib/media/audio";
import { buildCaptionChunks } from "@/lib/transcription/caption"; import { buildCaptionChunks } from "@/lib/transcription/caption";
import { Spinner } from "@/components/ui/spinner"; import { Spinner } from "@/components/ui/spinner";
import { Label } from "@/components/ui/label"; import {
Section,
SectionContent,
SectionField,
SectionFields,
} from "@/components/editor/panels/properties/section";
export function Captions() { export function Captions() {
const [selectedLanguage, setSelectedLanguage] = const [selectedLanguage, setSelectedLanguage] =
@@ -63,15 +73,10 @@ export function Captions() {
setProcessingStep("Generating captions..."); setProcessingStep("Generating captions...");
const captionChunks = buildCaptionChunks({ segments: result.segments }); const captionChunks = buildCaptionChunks({ segments: result.segments });
const captionTrackId = editor.timeline.addTrack({ const addTrackCommand = new AddTrackCommand("text", 0);
type: "text", const insertCommands = captionChunks.map((caption, i) =>
index: 0, new InsertElementCommand({
}); placement: { mode: "explicit", trackId: addTrackCommand.getTrackId() },
for (let i = 0; i < captionChunks.length; i++) {
const caption = captionChunks[i];
editor.timeline.insertElement({
placement: { mode: "explicit", trackId: captionTrackId },
element: { element: {
...DEFAULT_TEXT_ELEMENT, ...DEFAULT_TEXT_ELEMENT,
name: `Caption ${i + 1}`, name: `Caption ${i + 1}`,
@@ -81,8 +86,12 @@ export function Captions() {
fontSize: 65, fontSize: 65,
fontWeight: "bold", fontWeight: "bold",
}, },
})
);
editor.command.execute({
command: new BatchCommand([addTrackCommand, ...insertCommands]),
}); });
}
} catch (error) { } catch (error) {
console.error("Transcription failed:", error); console.error("Transcription failed:", error);
setError( setError(
@@ -108,9 +117,11 @@ export function Captions() {
}; };
return ( return (
<PanelView title="Captions" ref={containerRef}> <PanelView title="Captions" contentClassName="px-0 flex flex-col h-full" ref={containerRef}>
<div className="flex flex-col gap-3"> <Section showTopBorder={false} className="flex-1">
<Label>Language</Label> <SectionContent className="flex flex-col gap-4 h-full">
<SectionFields>
<SectionField label="Language">
<Select <Select
value={selectedLanguage} value={selectedLanguage}
onValueChange={(value) => handleLanguageChange({ value })} onValueChange={(value) => handleLanguageChange({ value })}
@@ -127,15 +138,18 @@ export function Captions() {
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </SectionField>
</SectionFields>
<div className="flex flex-col gap-4">
{error && ( {error && (
<div className="bg-destructive/10 border-destructive/20 rounded-md border p-3"> <div className="bg-destructive/10 border-destructive/20 rounded-md border p-3">
<p className="text-destructive text-sm">{error}</p> <p className="text-destructive text-sm">{error}</p>
</div> </div>
)} )}
</SectionContent>
</Section>
<Section showBottomBorder={false} showTopBorder={false}>
<SectionContent>
<Button <Button
className="w-full" className="w-full"
onClick={handleGenerateTranscript} onClick={handleGenerateTranscript}
@@ -144,7 +158,8 @@ export function Captions() {
{isProcessing && <Spinner className="mr-1" />} {isProcessing && <Spinner className="mr-1" />}
{isProcessing ? processingStep : "Generate transcript"} {isProcessing ? processingStep : "Generate transcript"}
</Button> </Button>
</div> </SectionContent>
</Section>
</PanelView> </PanelView>
); );
} }