"use client"; import { useState, useCallback, useRef } from "react"; import { createSpecStreamCompiler } from "@json-render/core"; import { Player, PlayerRef } from "@remotion/player"; import { Renderer, type TimelineSpec } from "@json-render/remotion"; /** * Check if spec is complete enough to render */ function isSpecComplete(spec: TimelineSpec): spec is Required { return !!( spec.composition && spec.tracks && Array.isArray(spec.clips) && spec.clips.length > 0 ); } const EXAMPLE_PROMPTS = [ "Create a 10-second product intro with title cards", "Make a social media promo video with stats", "Build a testimonial video with quotes", "Design a company intro with split screens", ]; export default function Home() { const [prompt, setPrompt] = useState(""); const [isGenerating, setIsGenerating] = useState(false); const [spec, setSpec] = useState(null); const [error, setError] = useState(null); const inputRef = useRef(null); const playerRef = useRef(null); const generate = useCallback(async () => { if (!prompt.trim() || isGenerating) return; setIsGenerating(true); setError(null); setSpec(null); try { const response = await fetch("/api/generate", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt }), }); if (!response.ok) { throw new Error("Generation failed"); } const reader = response.body?.getReader(); if (!reader) throw new Error("No response body"); const decoder = new TextDecoder(); const compiler = createSpecStreamCompiler(); while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value, { stream: true }); const { result, newPatches } = compiler.push(chunk); if (newPatches.length > 0) { setSpec(result); } } // Get final result (processes any remaining buffer) const finalSpec = compiler.getResult(); setSpec(finalSpec); // Final validation and auto-play if (isSpecComplete(finalSpec)) { // Auto-play after a short delay to ensure Player is mounted setTimeout(() => { playerRef.current?.play(); }, 100); } else { setError("Generated timeline is incomplete"); } } catch (err) { setError(err instanceof Error ? err.message : "Generation failed"); } finally { setIsGenerating(false); } }, [prompt, isGenerating]); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); generate(); }; const handleExampleClick = (examplePrompt: string) => { setPrompt(examplePrompt); setTimeout(() => inputRef.current?.focus(), 0); }; const totalTime = spec?.composition ? spec.composition.durationInFrames / spec.composition.fps : 0; return (
{/* Hero */}
@json-render/remotion

AI → json-render → Video

Define a video catalog. Users prompt. AI outputs timeline JSON constrained to your components. Remotion renders it.

{/* Demo */}
{/* Prompt Input */}
> setPrompt(e.target.value)} placeholder="Describe the video you want to create..." className="flex-1 bg-transparent outline-none placeholder:text-muted-foreground/50" disabled={isGenerating} maxLength={500} /> {isGenerating ? ( ) : ( )}
{/* Example prompts */}
{EXAMPLE_PROMPTS.map((examplePrompt) => ( ))}
{error && (
{error}
)}
{/* JSON Panel */}
timeline.json {isGenerating && ( generating... )}
{spec ? (
                    
                      {JSON.stringify(spec, null, 2)}
                    
                  
) : isGenerating ? (
Generating timeline...
) : (
Enter a prompt to generate a video timeline
)}
{/* Video Preview Panel */}
preview {spec?.composition && ( {totalTime.toFixed(1)}s )}
{spec && isSpecComplete(spec) ? ( ) : (
{isGenerating ? (
Generating timeline...
) : spec ? (
Building timeline...
) : ( "Enter a prompt to generate a video" )}
)}
); }