import { useState, useCallback } from "react"; import { RotateCcw, AlertTriangle } from "lucide-react"; import { useLLM } from "../hooks/useLLM"; import { ImageUpload } from "./ImageUpload"; import { ExampleGallery } from "./ExampleGallery"; import { DetectionOverlay } from "./DetectionOverlay"; import { ResultPanel } from "./ResultPanel"; import { urlToDataUrl } from "../utils/image-utils"; import { DEFAULT_DETECTION_PROMPT } from "../utils/coco-examples"; export function DetectionApp() { const { detect, isGenerating, tps, dtype, result, stop } = useLLM(); const [currentImage, setCurrentImage] = useState(null); const [prompt, setPrompt] = useState(DEFAULT_DETECTION_PROMPT); const [loading, setLoading] = useState(false); const handleImageSelect = useCallback( (dataUrl: string) => { if (!dataUrl) { setCurrentImage(null); return; } setCurrentImage(dataUrl); detect(dataUrl, prompt); }, [detect, prompt], ); const handleExampleSelect = useCallback( async (url: string, examplePrompt: string) => { setLoading(true); try { const dataUrl = await urlToDataUrl(url); setCurrentImage(dataUrl); setPrompt(examplePrompt); detect(dataUrl, examplePrompt); } catch (err) { console.error("[Gemma4] Failed to load example:", err); alert(`Failed to load example image (likely CORS):\n${err instanceof Error ? err.message : String(err)}`); } setLoading(false); }, [detect], ); const handleRetry = useCallback(() => { if (currentImage) { detect(currentImage, prompt); } }, [currentImage, prompt, detect]); return (
{/* Header */}
Gemma 4
{isGenerating && ( )} {currentImage && !isGenerating && ( )}
{/* Main content */}
{/* q4 slow-path warning */} {dtype === "q4" && (

Running on q4 (slow path) — inference may fail with OOM on 8GB GPUs.

For faster, stabler inference, enable{" "} chrome://flags/#enable-unsafe-webgpu and{" "} chrome://flags/#enable-vulkan, then fully restart Chrome.

)} {/* Prompt input */}