|
<!DOCTYPE html> |
|
<html lang="id"> |
|
<head> |
|
<meta charset="UTF-8" /> |
|
<meta name="viewport" content="width=device-width, initial-scale=1" /> |
|
<title>🤖 Multi AI Chat</title> |
|
<style> |
|
body { font-family: sans-serif; background: #f9fafb; color: #111; display: flex; flex-direction: column; align-items: center; padding: 2em; } |
|
h1 { color: #9333ea; } |
|
input, button { padding: .6em 1em; border-radius: .5em; border: 1px solid #d1d5db; font-size: 1em; } |
|
input { width: 80%; max-width: 600px; margin-bottom: .6em; } |
|
button { background: #9333ea; color: #fff; border: none; cursor: pointer; } |
|
button:hover { background: #7e22ce; } |
|
#output img { max-width: 100%; border: 1px solid #d1d5db; border-radius: .5em; margin-top: 1em; } |
|
.row { width: 80%; max-width: 600px; display: flex; gap: .5em; align-items: center; } |
|
.muted { color: #6b7280; font-size: .9em; margin: .25em 0 1em; width: 80%; max-width: 600px; } |
|
.right { margin-left: auto; } |
|
</style> |
|
</head> |
|
<body> |
|
<h1>🤖 Multi AI Chat</h1> |
|
|
|
<div class="row"> |
|
<input type="password" id="apiKey" placeholder="Masukkan API key Google AI (mulai dengan AIza...)" /> |
|
<button id="toggleKey" title="Tampilkan/Sembunyikan">👁️</button> |
|
</div> |
|
<label class="muted"> |
|
<input type="checkbox" id="rememberKey" checked /> |
|
Simpan key di browser ini (localStorage). Kamu juga bisa pakai URL ?key=YOUR_KEY |
|
</label> |
|
|
|
<input type="text" id="prompt" placeholder="Describe image..." /> |
|
<div class="row"> |
|
<button id="generate">Generate</button> |
|
<span class="muted right" id="status"></span> |
|
</div> |
|
|
|
<div id="output"></div> |
|
|
|
<script> |
|
const apiKeyInput = document.getElementById('apiKey'); |
|
const rememberKey = document.getElementById('rememberKey'); |
|
const promptInput = document.getElementById('prompt'); |
|
const output = document.getElementById('output'); |
|
const statusEl = document.getElementById('status'); |
|
const toggleKeyBtn = document.getElementById('toggleKey'); |
|
|
|
|
|
const savedKey = localStorage.getItem('gl_api_key') || new URLSearchParams(location.search).get('key') || ''; |
|
if (savedKey) apiKeyInput.value = savedKey; |
|
|
|
toggleKeyBtn.onclick = () => { |
|
apiKeyInput.type = apiKeyInput.type === 'password' ? 'text' : 'password'; |
|
}; |
|
|
|
rememberKey.addEventListener('change', () => { |
|
if (!rememberKey.checked) localStorage.removeItem('gl_api_key'); |
|
}); |
|
|
|
async function generateImage(prompt, key) { |
|
if (!key) throw new Error('API key belum diisi.'); |
|
const endpoint = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-image-preview:generateContent?key=${encodeURIComponent(key)}`; |
|
const body = { |
|
contents: [{ role: 'user', parts: [{ text: prompt }] }], |
|
generationConfig: { temperature: 0.7 } |
|
}; |
|
|
|
const res = await fetch(endpoint, { |
|
method: 'POST', |
|
headers: { 'Content-Type': 'application/json' }, |
|
body: JSON.stringify(body) |
|
}); |
|
|
|
const data = await res.json().catch(() => ({})); |
|
if (!res.ok) { |
|
const msg = data?.error?.message || `${res.status} ${res.statusText}`; |
|
throw new Error(msg); |
|
} |
|
|
|
|
|
const partWithImage = data?.candidates?.[0]?.content?.parts?.find(p => p.inlineData); |
|
const base64 = partWithImage?.inlineData?.data; |
|
const mime = partWithImage?.inlineData?.mimeType || 'image/png'; |
|
|
|
if (!base64) throw new Error('Tidak ada gambar pada respons.'); |
|
const imgSrc = `data:${mime};base64,${base64}`; |
|
|
|
|
|
return ` |
|
<img alt="Generated" src="${imgSrc}"> |
|
<div style="margin-top:.5em"> |
|
<a download="image.${mime.split('/')[1] || 'png'}" href="${imgSrc}">⬇️ Download</a> |
|
</div> |
|
`; |
|
} |
|
|
|
document.getElementById('generate').onclick = async () => { |
|
const prompt = promptInput.value.trim(); |
|
const key = apiKeyInput.value.trim(); |
|
|
|
if (!prompt) return alert('Masukkan prompt!'); |
|
if (!key) return alert('Masukkan API key!'); |
|
|
|
if (rememberKey.checked) localStorage.setItem('gl_api_key', key); |
|
|
|
output.innerHTML = ''; |
|
statusEl.textContent = '⏳ Generating...'; |
|
try { |
|
const html = await generateImage(prompt, key); |
|
output.innerHTML = html; |
|
statusEl.textContent = ''; |
|
} catch (err) { |
|
output.innerHTML = ''; |
|
statusEl.textContent = ''; |
|
alert('Gagal: ' + (err?.message || err)); |
|
} |
|
}; |
|
|
|
|
|
promptInput.addEventListener('keydown', (e) => { |
|
if (e.key === 'Enter') document.getElementById('generate').click(); |
|
}); |
|
</script> |
|
</body> |
|
</html> |