({ onCreated }: Props)
| 31 | } |
| 32 | |
| 33 | export function TextToVideoForm({ onCreated }: Props) { |
| 34 | const [models, setModels] = useState<VideoModel[]>([]); |
| 35 | const [modelsError, setModelsError] = useState<string | null>(null); |
| 36 | |
| 37 | const [model, setModel] = useState(""); |
| 38 | const [prompt, setPrompt] = useState(""); |
| 39 | const [duration, setDuration] = useState(""); |
| 40 | const [resolution, setResolution] = useState(""); |
| 41 | const [aspectRatio, setAspectRatio] = useState(""); |
| 42 | const [generateAudio, setGenerateAudio] = useState(true); |
| 43 | const [startFrame, setStartFrame] = useState<File | null>(null); |
| 44 | const [endFrame, setEndFrame] = useState<File | null>(null); |
| 45 | const [referenceFrames, setReferenceFrames] = useState<File[]>([]); |
| 46 | |
| 47 | const [submitting, setSubmitting] = useState(false); |
| 48 | const [error, setError] = useState<string | null>(null); |
| 49 | |
| 50 | const costs = useActionCosts(); |
| 51 | const fileInputRef = useRef<HTMLInputElement>(null); |
| 52 | |
| 53 | useEffect(() => { |
| 54 | fetchModels() |
| 55 | .then((m) => { |
| 56 | setModels(m); |
| 57 | if (m[0]) setModel(m[0].id); |
| 58 | }) |
| 59 | .catch((err) => setModelsError(err.message)); |
| 60 | }, []); |
| 61 | |
| 62 | const selectedModel = useMemo(() => models.find((m) => m.id === model), [models, model]); |
| 63 | const resolutions = selectedModel?.supported_resolutions?.length |
| 64 | ? selectedModel.supported_resolutions |
| 65 | : FALLBACK_RESOLUTIONS; |
| 66 | const aspectRatios = selectedModel?.supported_aspect_ratios?.length |
| 67 | ? selectedModel.supported_aspect_ratios |
| 68 | : FALLBACK_ASPECT_RATIOS; |
| 69 | |
| 70 | // Only show models that support the chosen duration (spec 09). |
| 71 | const availableModels = useMemo( |
| 72 | () => modelsForDuration(models, duration ? Number(duration) : null), |
| 73 | [models, duration], |
| 74 | ); |
| 75 | |
| 76 | function handleDurationChange(value: string) { |
| 77 | setDuration(value); |
| 78 | const allowed = modelsForDuration(models, value ? Number(value) : null); |
| 79 | if (model && !allowed.some((m) => m.id === model)) { |
| 80 | setModel(allowed[0]?.id ?? ""); |
| 81 | } |
| 82 | } |
| 83 | |
| 84 | async function handleSubmit(e: React.FormEvent) { |
| 85 | e.preventDefault(); |
| 86 | setError(null); |
| 87 | if (!model || !prompt.trim()) { |
| 88 | setError("Model and prompt are required."); |
| 89 | return; |
| 90 | } |
nothing calls this directly
no test coverage detected