Download components/caption-editor.tsx from d3evil4/scrape: direct link, hf CLI and curl.
- Browser
- Download file 2.48 kB
-
https://huggingface.co/spaces/d3evil4/scrape/resolve/main/components/caption-editor.tsx
- Command line
-
hf download hf://spaces/d3evil4/scrape/components/caption-editor.tsx
-
curl -L -o caption-editor.tsx https://huggingface.co/spaces/d3evil4/scrape/resolve/main/components/caption-editor.tsx
2.48 kB
| "use client"; | |
| import { useRef } from "react"; | |
| import { FileUp, Sparkles, Trash2 } from "lucide-react"; | |
| import type { Caption } from "@/types"; | |
| import { Button } from "@/components/ui/button"; | |
| import { Textarea } from "@/components/ui/textarea"; | |
| import { parseSrt } from "@/lib/srt-parser"; | |
| import { autoSyncCaptions, captionsToText } from "@/lib/timing"; | |
| interface CaptionEditorProps { | |
| text: string; | |
| captions: Caption[]; | |
| animationSpeed: number; | |
| onTextChange: (text: string) => void; | |
| onCaptionsChange: (captions: Caption[]) => void; | |
| } | |
| export function CaptionEditor({ | |
| text, | |
| captions, | |
| animationSpeed, | |
| onTextChange, | |
| onCaptionsChange, | |
| }: CaptionEditorProps) { | |
| const fileRef = useRef<HTMLInputElement>(null); | |
| const handleAutoSync = () => { | |
| if (!text.trim()) return; | |
| onCaptionsChange(autoSyncCaptions(text, animationSpeed)); | |
| }; | |
| const handleSrtUpload = async (file: File) => { | |
| const content = await file.text(); | |
| const parsed = parseSrt(content); | |
| if (parsed.length === 0) return; | |
| onCaptionsChange(parsed); | |
| onTextChange(captionsToText(parsed)); | |
| }; | |
| return ( | |
| <div className="space-y-3"> | |
| <div className="flex items-center justify-between"> | |
| <h3 className="text-sm font-semibold uppercase tracking-wide text-zinc-500">Caption Text</h3> | |
| <span className="text-xs text-zinc-400">{captions.length} words</span> | |
| </div> | |
| <Textarea | |
| value={text} | |
| onChange={(e) => onTextChange(e.target.value)} | |
| placeholder="Type or paste your captions here..." | |
| className="min-h-[160px] font-medium" | |
| /> | |
| <div className="flex flex-wrap gap-2"> | |
| <Button variant="secondary" size="sm" onClick={handleAutoSync}> | |
| <Sparkles className="h-4 w-4" /> Auto-sync | |
| </Button> | |
| <Button variant="outline" size="sm" onClick={() => fileRef.current?.click()}> | |
| <FileUp className="h-4 w-4" /> Upload .srt | |
| </Button> | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| onClick={() => { | |
| onTextChange(""); | |
| onCaptionsChange([]); | |
| }} | |
| > | |
| <Trash2 className="h-4 w-4" /> Clear | |
| </Button> | |
| <input | |
| ref={fileRef} | |
| type="file" | |
| accept=".srt" | |
| className="hidden" | |
| onChange={(e) => { | |
| const file = e.target.files?.[0]; | |
| if (file) void handleSrtUpload(file); | |
| }} | |
| /> | |
| </div> | |
| </div> | |
| ); | |
| } | |