gradio / public /index.html
FarelDeveloper's picture
Update public/index.html
72e0cb6 verified
Raw
History Blame Contribute Delete
8.9 kB
<!DOCTYPE html>
<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>