refactor: replace captions tab switcher with import action button
This commit is contained in:
parent
d8214916e0
commit
7cb6983294
|
|
@ -7,7 +7,6 @@ import {
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
||||||
import { useReducer, useRef, useState } from "react";
|
import { useReducer, useRef, useState } 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";
|
||||||
|
|
@ -32,8 +31,8 @@ import {
|
||||||
SectionField,
|
SectionField,
|
||||||
SectionFields,
|
SectionFields,
|
||||||
} from "@/components/section";
|
} from "@/components/section";
|
||||||
|
import { CloudUploadIcon } from "@hugeicons/core-free-icons";
|
||||||
type CaptionsView = "generate" | "import";
|
import { HugeiconsIcon } from "@hugeicons/react";
|
||||||
|
|
||||||
type ProcessingState =
|
type ProcessingState =
|
||||||
| { status: "idle"; error: string | null; warnings: string[] }
|
| { status: "idle"; error: string | null; warnings: string[] }
|
||||||
|
|
@ -65,7 +64,6 @@ function processingReducer(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Captions() {
|
export function Captions() {
|
||||||
const [view, setView] = useState<CaptionsView>("generate");
|
|
||||||
const [selectedLanguage, setSelectedLanguage] =
|
const [selectedLanguage, setSelectedLanguage] =
|
||||||
useState<TranscriptionLanguage>("auto");
|
useState<TranscriptionLanguage>("auto");
|
||||||
const [processing, dispatch] = useReducer(processingReducer, IDLE_STATE);
|
const [processing, dispatch] = useReducer(processingReducer, IDLE_STATE);
|
||||||
|
|
@ -207,17 +205,20 @@ export function Captions() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PanelView
|
<PanelView
|
||||||
|
title="Captions"
|
||||||
contentClassName="px-0 flex flex-col h-full"
|
contentClassName="px-0 flex flex-col h-full"
|
||||||
actions={
|
actions={
|
||||||
<Tabs
|
<Button
|
||||||
value={view}
|
type="button"
|
||||||
onValueChange={(value) => setView(value as CaptionsView)}
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={handleImportClick}
|
||||||
|
disabled={isProcessing}
|
||||||
|
className="items-center justify-center gap-1.5"
|
||||||
>
|
>
|
||||||
<TabsList>
|
<HugeiconsIcon icon={CloudUploadIcon} />
|
||||||
<TabsTrigger value="generate">Generate</TabsTrigger>
|
Import
|
||||||
<TabsTrigger value="import">Import</TabsTrigger>
|
</Button>
|
||||||
</TabsList>
|
|
||||||
</Tabs>
|
|
||||||
}
|
}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
>
|
>
|
||||||
|
|
@ -228,88 +229,54 @@ export function Captions() {
|
||||||
className="hidden"
|
className="hidden"
|
||||||
onChange={(event) => void handleFileChange({ event })}
|
onChange={(event) => void handleFileChange({ event })}
|
||||||
/>
|
/>
|
||||||
{view === "generate" && (
|
<Section showTopBorder={false} showBottomBorder={false} className="flex-1">
|
||||||
<Section
|
<SectionContent className="flex flex-col gap-4 h-full pt-1">
|
||||||
showTopBorder={false}
|
<SectionFields>
|
||||||
showBottomBorder={false}
|
<SectionField label="Language">
|
||||||
className="flex-1"
|
<Select
|
||||||
>
|
value={selectedLanguage}
|
||||||
<SectionContent className="flex flex-col gap-4 h-full pt-1">
|
onValueChange={(value) => handleLanguageChange({ value })}
|
||||||
<SectionFields>
|
>
|
||||||
<SectionField label="Language">
|
<SelectTrigger>
|
||||||
<Select
|
<SelectValue placeholder="Select a language" />
|
||||||
value={selectedLanguage}
|
</SelectTrigger>
|
||||||
onValueChange={(value) => handleLanguageChange({ value })}
|
<SelectContent>
|
||||||
>
|
<SelectItem value="auto">Auto detect</SelectItem>
|
||||||
<SelectTrigger>
|
{TRANSCRIPTION_LANGUAGES.map((language) => (
|
||||||
<SelectValue placeholder="Select a language" />
|
<SelectItem key={language.code} value={language.code}>
|
||||||
</SelectTrigger>
|
{language.name}
|
||||||
<SelectContent>
|
</SelectItem>
|
||||||
<SelectItem value="auto">Auto detect</SelectItem>
|
|
||||||
{TRANSCRIPTION_LANGUAGES.map((language) => (
|
|
||||||
<SelectItem key={language.code} value={language.code}>
|
|
||||||
{language.name}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</SectionField>
|
|
||||||
</SectionFields>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
className="mt-auto w-full"
|
|
||||||
onClick={handleGenerateTranscript}
|
|
||||||
disabled={isProcessing}
|
|
||||||
>
|
|
||||||
{isProcessing && <Spinner className="mr-1" />}
|
|
||||||
{isProcessing ? processing.step : "Generate transcript"}
|
|
||||||
</Button>
|
|
||||||
{error && (
|
|
||||||
<div className="bg-destructive/10 border-destructive/20 rounded-md border p-3">
|
|
||||||
<p className="text-destructive text-sm">{error}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</SectionContent>
|
|
||||||
</Section>
|
|
||||||
)}
|
|
||||||
{view === "import" && (
|
|
||||||
<Section
|
|
||||||
showTopBorder={false}
|
|
||||||
showBottomBorder={false}
|
|
||||||
className="flex-1"
|
|
||||||
>
|
|
||||||
<SectionContent className="flex flex-col gap-4 h-full pt-1">
|
|
||||||
<p className="text-muted-foreground text-sm">
|
|
||||||
Import an existing <code>.srt</code> or <code>.ass</code> subtitle
|
|
||||||
file into the timeline.
|
|
||||||
</p>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
className="mt-auto w-full"
|
|
||||||
onClick={handleImportClick}
|
|
||||||
disabled={isProcessing}
|
|
||||||
>
|
|
||||||
{isProcessing && <Spinner className="mr-1" />}
|
|
||||||
{isProcessing ? processing.step : "Import subtitles"}
|
|
||||||
</Button>
|
|
||||||
{error && (
|
|
||||||
<div className="bg-destructive/10 border-destructive/20 rounded-md border p-3">
|
|
||||||
<p className="text-destructive text-sm">{error}</p>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{warnings.length > 0 && (
|
|
||||||
<div className="rounded-md border border-amber-500/20 bg-amber-500/10 p-3">
|
|
||||||
<ul className="space-y-1 text-sm text-amber-700">
|
|
||||||
{warnings.map((warning) => (
|
|
||||||
<li key={warning}>{warning}</li>
|
|
||||||
))}
|
))}
|
||||||
</ul>
|
</SelectContent>
|
||||||
</div>
|
</Select>
|
||||||
)}
|
</SectionField>
|
||||||
</SectionContent>
|
</SectionFields>
|
||||||
</Section>
|
|
||||||
)}
|
<Button
|
||||||
|
type="button"
|
||||||
|
className="mt-auto w-full"
|
||||||
|
onClick={handleGenerateTranscript}
|
||||||
|
disabled={isProcessing}
|
||||||
|
>
|
||||||
|
{isProcessing && <Spinner className="mr-1" />}
|
||||||
|
{isProcessing ? processing.step : "Generate transcript"}
|
||||||
|
</Button>
|
||||||
|
{error && (
|
||||||
|
<div className="bg-destructive/10 border-destructive/20 rounded-md border p-3">
|
||||||
|
<p className="text-destructive text-sm">{error}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{warnings.length > 0 && (
|
||||||
|
<div className="rounded-md border border-amber-500/20 bg-amber-500/10 p-3">
|
||||||
|
<ul className="space-y-1 text-sm text-amber-700">
|
||||||
|
{warnings.map((warning) => (
|
||||||
|
<li key={warning}>{warning}</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SectionContent>
|
||||||
|
</Section>
|
||||||
</PanelView>
|
</PanelView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue