coyotte508/fastvlm-webgpu
0
1import { useState, useEffect, useRef, useCallback, useMemo } from "react";2import LoadingScreen from "./components/LoadingScreen";3import CaptioningView from "./components/CaptioningView";4import WelcomeScreen from "./components/WelcomeScreen";5import WebcamPermissionDialog from "./components/WebcamPermissionDialog";6import type { AppState } from "./types";7 8export default function App() {9 const [appState, setAppState] = useState<AppState>("requesting-permission");10 const [webcamStream, setWebcamStream] = useState<MediaStream | null>(null);11 const [isVideoReady, setIsVideoReady] = useState(false);12 const videoRef = useRef<HTMLVideoElement | null>(null);13 14 const handlePermissionGranted = useCallback((stream: MediaStream) => {15 setWebcamStream(stream);16 setAppState("welcome");17 }, []);18 19 const handleStart = useCallback(() => {20 setAppState("loading");21 }, []);22 23 const handleLoadingComplete = useCallback(() => {24 setAppState("captioning");25 }, []);26 27 const playVideo = useCallback(async (video: HTMLVideoElement) => {28 try {29 await video.play();30 } catch (error) {31 console.error("Failed to play video:", error);32 }33 }, []);34 35 const setupVideo = useCallback(36 (video: HTMLVideoElement, stream: MediaStream) => {37 video.srcObject = stream;38 39 const handleCanPlay = () => {40 setIsVideoReady(true);41 playVideo(video);42 };43 44 video.addEventListener("canplay", handleCanPlay, { once: true });45 46 return () => {47 video.removeEventListener("canplay", handleCanPlay);48 };49 },50 [playVideo],51 );52 53 useEffect(() => {54 if (webcamStream && videoRef.current) {55 const video = videoRef.current;56 57 video.srcObject = null;58 video.load();59 60 const cleanup = setupVideo(video, webcamStream);61 return cleanup;62 }63 }, [webcamStream, setupVideo]);64 65 const videoBlurState = useMemo(() => {66 switch (appState) {67 case "requesting-permission":68 return "blur(20px) brightness(0.2) saturate(0.5)";69 case "welcome":70 return "blur(12px) brightness(0.3) saturate(0.7)";71 case "loading":72 return "blur(8px) brightness(0.4) saturate(0.8)";73 case "captioning":74 return "none";75 default:76 return "blur(20px) brightness(0.2) saturate(0.5)";77 }78 }, [appState]);79 80 return (81 <div className="App relative h-screen overflow-hidden">82 <div className="absolute inset-0 bg-gray-900" />83 84 {webcamStream && (85 <video86 ref={videoRef}87 autoPlay88 muted89 playsInline90 className="absolute inset-0 w-full h-full object-cover transition-all duration-1000 ease-out"91 style={{92 filter: videoBlurState,93 opacity: isVideoReady ? 1 : 0,94 }}95 />96 )}97 98 {appState !== "captioning" && <div className="absolute inset-0 bg-gray-900/80 backdrop-blur-sm" />}99 100 {appState === "requesting-permission" && <WebcamPermissionDialog onPermissionGranted={handlePermissionGranted} />}101 102 {appState === "welcome" && <WelcomeScreen onStart={handleStart} />}103 104 {appState === "loading" && <LoadingScreen onComplete={handleLoadingComplete} />}105 106 {appState === "captioning" && <CaptioningView videoRef={videoRef} />}107 </div>108 );109}110 