Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Universal Gradio Interfacer</title> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <style> | |
| body { font-family: 'Inter', sans-serif; background-color: #f8fafc; color: #1e293b; margin: 0; padding: 20px; } | |
| .container { max-width: 800px; margin: 0 auto; padding: 20px; } | |
| header { text-align: center; margin-bottom: 30px; } | |
| h1 { margin: 0 0 10px 0; color: #0f172a; } | |
| .card { background: white; border: 1px solid #e2e8f0; padding: 24px; border-radius: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.05); margin-bottom: 20px; } | |
| .form-group { margin-bottom: 16px; } | |
| label { display: block; font-weight: 600; margin-bottom: 6px; } | |
| input[type="text"], textarea, input[type="number"] { width: 100%; padding: 10px; border: 1px solid #cbd5e1; border-radius: 6px; box-sizing: border-box; font-size: 14px; } | |
| textarea { height: 120px; font-family: monospace; } | |
| button { width: 100%; padding: 12px; border: none; border-radius: 6px; font-weight: 600; font-size: 16px; cursor: pointer; color: white; } | |
| .btn-blue { background-color: #3b82f6; } | |
| .btn-green { background-color: #10b981; } | |
| .status { background: #eff6ff; border: 1px solid #bfdbfe; color: #1e40af; padding: 12px; border-radius: 8px; margin-bottom: 20px; display: none; } | |
| .dynamic-form { background: #f1f5f9; border: 1px solid #e2e8f0; } | |
| .result-card { text-align: center; background: #f8fafc; border: 1px solid #e2e8f0; } | |
| .result-card img { max-width: 100%; height: auto; border-radius: 8px; margin-top: 10px; box-shadow: 0 4px 6px rgba(0,0,0,0.05); } | |
| pre { text-align: left; background: #0f172a; color: #38bdf8; padding: 15px; border-radius: 6px; overflow-x: auto; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <h1>🤖 Dynamic Gradio Space Interfacer</h1> | |
| <p>Tempel app.py, AI akan membangun form UI secara otomatis.</p> | |
| </header> | |
| <div class="card"> | |
| <div class="form-group"> | |
| <label>1. Target Hugging Face Space URL:</label> | |
| <input type="text" id="spaceUrl" value="mrfakename/z-image-turbo"> | |
| </div> | |
| <div class="form-group"> | |
| <label>2. Isi Kode Python (app.py):</label> | |
| <textarea id="appPy" placeholder="import gradio as gr..."></textarea> | |
| </div> | |
| <button class="btn-blue" onclick="analyzeCode()">⚡ Bedah Kode & Generate UI</button> | |
| </div> | |
| <div id="statusLog" class="status"></div> | |
| <div id="dynamicFormContainer" class="card dynamic-form" style="display: none;"> | |
| <h3 style="margin-top:0;">✨ Control Panel (Generated by AI)</h3> | |
| <div id="dynamicFields"></div> | |
| <button class="btn-green" style="margin-top: 15px;" onclick="executeGradio()">🚀 Run & Generate</button> | |
| </div> | |
| <div id="resultContainer" class="card result-card" style="display: none;"> | |
| <h3>🎉 Hasil Output:</h3> | |
| <div id="resultOutput"></div> | |
| </div> | |
| </div> | |
| <script> | |
| let formSchema = []; | |
| async function analyzeCode() { | |
| const appPy = document.getElementById('appPy').value; | |
| const statusLog = document.getElementById('statusLog'); | |
| if(!appPy.trim()) return alert("Paste isi app.py dulu!"); | |
| statusLog.style.display = "block"; | |
| statusLog.innerText = "DeepSeek (NoteGPT) sedang menganalisis kode..."; | |
| try { | |
| const systemPrompt = `Analisis kode Python Gradio berikut. Keluarkan daftar input yang dibutuhkan dalam format JSON array yang valid. Jangan berikan teks penjelasan atau markdown, cukup JSON saja. Format objek harus berupa: {"name": string, "type": "text"|"number"|"boolean", "default": any}. Berikut kodenya:\n\n${appPy}`; | |
| // Panggil endpoint internal backend kita | |
| const response = await fetch('/api/analyze-code', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ prompt: systemPrompt }) | |
| }); | |
| const data = await response.json(); | |
| if (!data.success) throw new Error(data.error); | |
| // Membersihkan jika AI menyertakan format markdown (```json ... ```) | |
| const cleanJson = data.result.replace(/```json|```/g, '').trim(); | |
| formSchema = JSON.parse(cleanJson); | |
| renderForm(formSchema); | |
| statusLog.innerText = "Form UI sukses dibuat oleh DeepSeek!"; | |
| } catch (e) { | |
| alert("Gagal memproses skema dari NoteGPT. Pastikan respon berupa JSON bersih."); | |
| console.error(e); | |
| statusLog.style.display = "none"; | |
| } | |
| } | |
| function renderForm(schema) { | |
| const fieldsDiv = document.getElementById('dynamicFields'); | |
| fieldsDiv.innerHTML = ''; | |
| schema.forEach(field => { | |
| const group = document.createElement('div'); | |
| group.className = 'form-group'; | |
| const label = document.createElement('label'); | |
| label.innerText = `${field.name} (${field.type})`; | |
| group.appendChild(label); | |
| if(field.type === 'boolean') { | |
| const input = document.createElement('input'); | |
| input.type = 'checkbox'; | |
| input.id = `input_${field.name}`; | |
| input.checked = field.default; | |
| group.appendChild(input); | |
| } else if(field.type === 'number') { | |
| const input = document.createElement('input'); | |
| input.type = 'number'; | |
| input.id = `input_${field.name}`; | |
| input.value = field.default; | |
| group.appendChild(input); | |
| } else { | |
| const input = document.createElement('input'); | |
| input.type = 'text'; | |
| input.id = `input_${field.name}`; | |
| input.value = field.default || ''; | |
| group.appendChild(input); | |
| } | |
| fieldsDiv.appendChild(group); | |
| }); | |
| document.getElementById('dynamicFormContainer').style.display = "block"; | |
| } | |
| async function executeGradio() { | |
| const statusLog = document.getElementById('statusLog'); | |
| const spaceUrl = document.getElementById('spaceUrl').value; | |
| statusLog.innerText = "Menghubungkan ke Space & memproses gambar..."; | |
| // Kumpulkan data input terurut dari form sesuai instruksi skema | |
| const inputs = formSchema.map(field => { | |
| const el = document.getElementById(`input_${field.name}`); | |
| if(field.type === 'boolean') return el.checked; | |
| if(field.type === 'number') return Number(el.value); | |
| return el.value; | |
| }); | |
| try { | |
| const res = await fetch('/api/run-gradio', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ spaceUrl, inputs }) | |
| }); | |
| const resData = await res.json(); | |
| if(resData.success) { | |
| const outDiv = document.getElementById('resultOutput'); | |
| outDiv.innerHTML = ''; | |
| let imageFound = false; | |
| // Lakukan looping pada seluruh array output untuk mencari gambar asli | |
| for (let outputData of resData.data) { | |
| // Skema 1: Output berupa objek yang memiliki properti .url | |
| if (outputData && typeof outputData === 'object' && outputData.url) { | |
| outDiv.innerHTML = `<img src="${outputData.url}" alt="Output Gambar">`; | |
| imageFound = true; | |
| break; | |
| } | |
| // Skema 2: Output berupa string URL langsung atau string base64 gambar | |
| else if (typeof outputData === 'string' && (outputData.startsWith('http') || outputData.startsWith('data:image'))) { | |
| outDiv.innerHTML = `<img src="${outputData}" alt="Output Gambar">`; | |
| imageFound = true; | |
| break; | |
| } | |
| } | |
| // Jika di dalam array tidak ada gambar sama sekali (misal model teks/chatbot) | |
| if (!imageFound) { | |
| // Filter output agar tidak menampilkan bumbu metadata '__type__' Gradio di layar | |
| const cleanOutput = resData.data.filter(item => !(item && item.__type__)); | |
| outDiv.innerHTML = `<pre>${JSON.stringify(cleanOutput, null, 2)}</pre>`; | |
| } | |
| document.getElementById('resultContainer').style.display = "block"; | |
| statusLog.innerText = "Proses Berhasil Selesai!"; | |
| } else { | |
| alert("Error: " + resData.error); | |
| } | |
| } catch (err) { | |
| alert("Gagal mengeksekusi API Backend."); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |