From 91e1232112421438ce73b9361896f9b58c84fb3e Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Tue, 14 Apr 2026 23:34:57 +0200 Subject: feat: transcription diagnostics --- .../editor/panels/assets/views/captions.tsx | 99 +++++++++++++++++----- 1 file changed, 78 insertions(+), 21 deletions(-) (limited to 'apps/web/src/components/editor/panels/assets/views/captions.tsx') diff --git a/apps/web/src/components/editor/panels/assets/views/captions.tsx b/apps/web/src/components/editor/panels/assets/views/captions.tsx index 7b6ae5ca..9e394cfc 100644 --- a/apps/web/src/components/editor/panels/assets/views/captions.tsx +++ b/apps/web/src/components/editor/panels/assets/views/captions.tsx @@ -10,9 +10,8 @@ import { import { useReducer, useRef, useState } from "react"; import { extractTimelineAudio } from "@/lib/media/mediabunny"; import { useEditor } from "@/hooks/use-editor"; -import { - DEFAULT_TRANSCRIPTION_SAMPLE_RATE, -} from "@/lib/transcription/audio"; +import { TRANSCRIPTION_DIAGNOSTICS_SCOPE } from "@/lib/transcription/diagnostics"; +import { DEFAULT_TRANSCRIPTION_SAMPLE_RATE } from "@/lib/transcription/audio"; import { TRANSCRIPTION_LANGUAGES } from "@/lib/transcription/supported-languages"; import type { CaptionChunk, @@ -31,8 +30,23 @@ import { SectionField, SectionFields, } from "@/components/section"; -import { CloudUploadIcon } from "@hugeicons/core-free-icons"; +import { AlertCircleIcon, CloudUploadIcon } from "@hugeicons/core-free-icons"; import { HugeiconsIcon } from "@hugeicons/react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import type { DiagnosticSeverity } from "@/lib/diagnostics/types"; + +const DIAGNOSTIC_BUTTON_VARIANT: Record< + DiagnosticSeverity, + "caution" | "destructive-foreground" +> = { + caution: "caution", + error: "destructive-foreground", +}; type ProcessingState = | { status: "idle"; error: string | null; warnings: string[] } @@ -44,7 +58,11 @@ type ProcessingAction = | { type: "succeed"; warnings: string[] } | { type: "fail"; error: string }; -const IDLE_STATE: ProcessingState = { status: "idle", error: null, warnings: [] }; +const IDLE_STATE: ProcessingState = { + status: "idle", + error: null, + warnings: [], +}; function processingReducer( state: ProcessingState, @@ -73,6 +91,10 @@ export function Captions() { const isProcessing = processing.status === "processing"; + const activeDiagnostics = useEditor((e) => + e.diagnostics.getActive({ scope: TRANSCRIPTION_DIAGNOSTICS_SCOPE }), + ); + const handleProgress = (progress: TranscriptionProgress) => { if (progress.status === "loading-model") { dispatch({ @@ -84,7 +106,11 @@ export function Captions() { } }; - const insertCaptions = ({ captions }: { captions: CaptionChunk[] }): boolean => { + const insertCaptions = ({ + captions, + }: { + captions: CaptionChunk[]; + }): boolean => { const trackId = insertCaptionChunksAsTextTrack({ editor, captions }); return trackId !== null; }; @@ -123,7 +149,10 @@ export function Captions() { console.error("Transcription failed:", error); dispatch({ type: "fail", - error: error instanceof Error ? error.message : "An unexpected error occurred", + error: + error instanceof Error + ? error.message + : "An unexpected error occurred", }); } }; @@ -168,7 +197,10 @@ export function Captions() { console.error("Subtitle import failed:", error); dispatch({ type: "fail", - error: error instanceof Error ? error.message : "An unexpected error occurred", + error: + error instanceof Error + ? error.message + : "An unexpected error occurred", }); } }; @@ -208,17 +240,38 @@ export function Captions() { title="Captions" contentClassName="px-0 flex flex-col h-full" actions={ - + +
+ {!isProcessing && + activeDiagnostics.map((diagnostic) => ( + + + + + + {diagnostic.message} + + + ))} + +
+
} ref={containerRef} > @@ -229,7 +282,11 @@ export function Captions() { className="hidden" onChange={(event) => void handleFileChange({ event })} /> -
+
@@ -256,7 +313,7 @@ export function Captions() { type="button" className="mt-auto w-full" onClick={handleGenerateTranscript} - disabled={isProcessing} + disabled={isProcessing || activeDiagnostics.length > 0} > {isProcessing && } {isProcessing ? processing.step : "Generate transcript"} -- cgit v1.3.1