import React, { useState, useEffect } from 'react'; import { Image as ImageIcon, Loader2, Sparkles, Terminal, AlertCircle, Settings, Layers, Cpu, Maximize, Zap, Wifi, WifiOff, ChevronDown, ChevronUp, Play, RefreshCw } from 'lucide-react'; // ⚠️ CẤU HÌNH: DÁN LINK NGROK CỦA BẠN VÀO ĐÂY const API_URL = "https://thay-link-ngrok-cua-ban-vao-day.ngrok-free.app"; // Danh sách Sampler (Giữ nguyên vì đây là config cứng của Diffusers) const SAMPLERS = [ "Euler a", "Euler", "LMS", "Heun", "DPM2", "DPM2 a", "DPM++ 2S a", "DPM++ 2M", "DPM++ SDE", "DPM++ 2M SDE", "DPM++ 2M Karras", "DPM++ SDE Karras", "DDIM", "UniPC" ]; const App = () => { // --- STATE --- // Core Inputs const [prompt, setPrompt] = useState('A futuristic city with neon lights, cyberpunk style'); const [negPrompt, setNegPrompt] = useState('blurry, bad quality, watermark, text, ugly, distorted, nsfw'); // Dynamic Lists (Dữ liệu từ Server) const [checkpoints, setCheckpoints] = useState(["Loading..."]); const [loras, setLoras] = useState(["None"]); const [vaes, setVaes] = useState(["Default"]); const [upscalers, setUpscalers] = useState(["None"]); // Selections const [selectedCheckpoint, setSelectedCheckpoint] = useState(""); const [selectedLora, setSelectedLora] = useState("None"); const [selectedVae, setSelectedVae] = useState("Default"); const [selectedUpscaler, setSelectedUpscaler] = useState("None"); // Advanced Settings const [steps, setSteps] = useState(30); const [cfgScale, setCfgScale] = useState(7.0); const [seed, setSeed] = useState(-1); const [sampler, setSampler] = useState("DPM++ 2M Karras"); const [upscaleStrength, setUpscaleStrength] = useState(0.35); const [upscaleFactor, setUpscaleFactor] = useState(1.0); // 1.0 = No upscale const [showAdvanced, setShowAdvanced] = useState(false); // System const [generatedImage, setGeneratedImage] = useState(null); const [loading, setLoading] = useState(false); const [fetchingInfo, setFetchingInfo] = useState(false); const [error, setError] = useState(''); const [logs, setLogs] = useState([]); const [serverStatus, setServerStatus] = useState('unknown'); // --- FUNCTIONS --- const addLog = (message) => { const timestamp = new Date().toLocaleTimeString(); setLogs(prev => [`[${timestamp}] ${message}`, ...prev].slice(0, 50)); }; // Hàm lấy danh sách model từ Kaggle const fetchServerInfo = async () => { if (API_URL.includes("thay-link")) return; setFetchingInfo(true); try { addLog("Connecting to Kaggle Server..."); const res = await fetch(`${API_URL}/info`); if (!res.ok) throw new Error("Server not ready"); const data = await res.json(); // Cập nhật danh sách if (data.models?.length > 0) { setCheckpoints(data.models); setSelectedCheckpoint(data.models[0]); // Auto select first } if (data.loras?.length > 0) setLoras(data.loras); if (data.vaes?.length > 0) setVaes(data.vaes); if (data.upscalers?.length > 0) setUpscalers(data.upscalers); setServerStatus('connected'); addLog(`Connected! Found ${data.models.length} checkpoints.`); } catch (err) { console.error(err); setServerStatus('disconnected'); addLog("Failed to fetch model list. Is server running?"); } finally { setFetchingInfo(false); } }; // Auto fetch khi mount useEffect(() => { fetchServerInfo(); }, []); const handleGenerate = async () => { if (serverStatus === 'disconnected') { setError('Chưa kết nối được Server!'); return; } setLoading(true); setError(''); addLog(`Generating... ${selectedCheckpoint}`); try { // Logic Upscaler: Ưu tiên Model name, nếu ko chọn model thì dùng factor (High-Res Fix) let upscalerValue = "None"; if (selectedUpscaler !== "None") { upscalerValue = selectedUpscaler; } else if (upscaleFactor > 1) { upscalerValue = upscaleFactor.toString(); } const payload = { prompt: prompt, negative_prompt: negPrompt, steps: steps, cfg_scale: cfgScale, seed: seed, sampler_name: sampler, checkpoint: selectedCheckpoint, lora: selectedLora, vae: selectedVae, upscaler: upscalerValue, upscale_strength: upscaleStrength }; const response = await fetch(`${API_URL}/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (!response.ok) throw new Error(`Server Error: ${response.status}`); const data = await response.json(); if (data.image) { setGeneratedImage(data.image); addLog('Image finished!'); } } catch (err) { setError(err.message); addLog(`Error: ${err.message}`); } finally { setLoading(false); } }; return (