|
|
|
|
|
|
|
|
const HF_API_KEY = 'YOUR_HF_API_KEY'; |
|
|
const SFW_MODELS = { |
|
|
'Stable Diffusion XL': 'stabilityai/stable-diffusion-xl-base-1.0', |
|
|
'DreamShaper XL': 'lykon/dreamshaper-xl', |
|
|
'Anime XL': 'cagliostrolab/animagine-xl-3.0', |
|
|
'Cyberpunk Anime': 'DGSpitzer/Cyberpunk-Anime-Diffusion', |
|
|
'Realistic Vision': 'SG161222/Realistic_Vision_V5.1', |
|
|
'Juggernaut XL': 'stabilityai/stable-diffusion-xl-base-1.0', |
|
|
'Epic Realism': 'epicrealism/epicrealism-natural-sin', |
|
|
'Pixel Art': 'nerijs/pixel-art-xl' |
|
|
}; |
|
|
|
|
|
const NSFW_MODELS = { |
|
|
'Erotica XL': 'gsdf/Counterfeit-V3.0', |
|
|
'Hentai XL': 'nitrosocke/henmixReal', |
|
|
'Adult XL': 'gsdf/Erotica', |
|
|
'NSFW Anime': 'cagliostro/hentai-diffusion', |
|
|
'Lewd Realism': 'gsdf/lewd-diffusion', |
|
|
'Boudoir': 'gsdf/boudoir-diffusion', |
|
|
'Kinky Art': 'gsdf/kinky-diffusion' |
|
|
}; |
|
|
|
|
|
let currentModel = SFW_MODELS['Stable Diffusion XL']; |
|
|
let isNSFW = false; |
|
|
|
|
|
const promptInput = document.querySelector('textarea'); |
|
|
const styleSelect = document.querySelector('select:nth-of-type(1)'); |
|
|
const modelSelect = document.querySelector('select:nth-of-type(2)'); |
|
|
const generateBtn = document.querySelector('button'); |
|
|
const previewBox = document.querySelector('.aspect-square'); |
|
|
const gallerySection = document.getElementById('gallery'); |
|
|
const nsfwToggle = document.createElement('div'); |
|
|
|
|
|
|
|
|
function initNSFWToggle() { |
|
|
nsfwToggle.className = 'flex items-center gap-2 mb-4'; |
|
|
nsfwToggle.innerHTML = ` |
|
|
<span class="text-sm font-medium">Content Filter</span> |
|
|
<label class="relative inline-flex items-center cursor-pointer"> |
|
|
<input type="checkbox" class="sr-only peer" ${isNSFW ? 'checked' : ''}> |
|
|
<div class="w-11 h-6 bg-gray-700 rounded-full peer peer-checked:bg-primary-500 peer-focus:ring-2 peer-focus:ring-primary-500"> |
|
|
<div class="absolute top-0.5 left-[2px] bg-white w-5 h-5 rounded-full transition-transform peer-checked:translate-x-5"></div> |
|
|
</div> |
|
|
<span class="ml-2 text-sm">${isNSFW ? 'NSFW' : 'SFW'}</span> |
|
|
</label> |
|
|
`; |
|
|
document.querySelector('#generate .space-y-4').prepend(nsfwToggle); |
|
|
nsfwToggle.querySelector('input').addEventListener('change', toggleNSFW); |
|
|
} |
|
|
|
|
|
|
|
|
function toggleNSFW(e) { |
|
|
isNSFW = e.target.checked; |
|
|
updateModelOptions(); |
|
|
nsfwToggle.querySelector('span:last-child').textContent = isNSFW ? 'NSFW' : 'SFW'; |
|
|
} |
|
|
|
|
|
|
|
|
function updateModelOptions() { |
|
|
modelSelect.innerHTML = ''; |
|
|
const models = isNSFW ? NSFW_MODELS : SFW_MODELS; |
|
|
|
|
|
for (const [name, modelId] of Object.entries(models)) { |
|
|
const option = document.createElement('option'); |
|
|
option.value = modelId; |
|
|
option.textContent = name; |
|
|
if (modelId === currentModel) option.selected = true; |
|
|
modelSelect.appendChild(option); |
|
|
} |
|
|
} |
|
|
|
|
|
async function generateImage() { |
|
|
const prompt = promptInput.value.trim(); |
|
|
if (!prompt) { |
|
|
showToast('Please enter a prompt', 'error'); |
|
|
return; |
|
|
} |
|
|
|
|
|
generateBtn.disabled = true; |
|
|
generateBtn.innerHTML = '<i data-feather="loader" class="animate-spin"></i> Weaving Pixels...'; |
|
|
feather.replace(); |
|
|
|
|
|
try { |
|
|
|
|
|
const modelStatus = await fetch(`https://api-inference.huggingface.co/status/${currentModel}`, { |
|
|
headers: { 'Authorization': `Bearer ${HF_API_KEY}` } |
|
|
}); |
|
|
|
|
|
if (!modelStatus.ok) throw new Error('Model status check failed'); |
|
|
|
|
|
const statusData = await modelStatus.json(); |
|
|
if (statusData.loaded !== true) { |
|
|
showToast('Model is loading, please wait...', 'warning'); |
|
|
return; |
|
|
} |
|
|
|
|
|
|
|
|
const response = await fetch(`https://api-inference.huggingface.co/models/${currentModel}`, { |
|
|
method: 'POST', |
|
|
headers: { |
|
|
'Authorization': `Bearer ${HF_API_KEY}`, |
|
|
'Content-Type': 'application/json' |
|
|
}, |
|
|
body: JSON.stringify({ |
|
|
inputs: prompt, |
|
|
parameters: { |
|
|
width: 1024, |
|
|
height: 1024, |
|
|
num_inference_steps: 50, |
|
|
guidance_scale: 7.5 |
|
|
}, |
|
|
options: { wait_for_model: true } |
|
|
}) |
|
|
}); |
|
|
|
|
|
if (!response.ok) { |
|
|
const error = await response.json(); |
|
|
throw new Error(error.error || 'Generation failed'); |
|
|
} |
|
|
|
|
|
const imageBlob = await response.blob(); |
|
|
const imageUrl = URL.createObjectURL(imageBlob); |
|
|
|
|
|
|
|
|
previewBox.innerHTML = ` |
|
|
<div class="relative w-full h-full"> |
|
|
<img src="${imageUrl}" alt="Generated image" class="w-full h-full object-cover rounded-lg"> |
|
|
<div class="absolute bottom-0 left-0 right-0 bg-black/70 text-white p-2 text-xs"> |
|
|
${prompt.substring(0, 100)}${prompt.length > 100 ? '...' : ''} |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
addToGallery(imageUrl, prompt); |
|
|
showToast('Image generated successfully!', 'success'); |
|
|
|
|
|
} catch (error) { |
|
|
console.error('Error:', error); |
|
|
showToast(error.message || 'Generation failed. Please try again.', 'error'); |
|
|
} finally { |
|
|
generateBtn.disabled = false; |
|
|
generateBtn.innerHTML = '<i data-feather="zap"></i> Generate Magic'; |
|
|
feather.replace(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function showToast(message, type = 'info') { |
|
|
const toast = document.createElement('div'); |
|
|
toast.className = `fixed bottom-4 right-4 px-4 py-2 rounded-lg shadow-lg text-white ${ |
|
|
type === 'error' ? 'bg-red-500' : |
|
|
type === 'success' ? 'bg-green-500' : |
|
|
type === 'warning' ? 'bg-yellow-500' : 'bg-primary-500' |
|
|
}`; |
|
|
toast.textContent = message; |
|
|
document.body.appendChild(toast); |
|
|
|
|
|
setTimeout(() => { |
|
|
toast.classList.add('opacity-0', 'transition-opacity', 'duration-300'); |
|
|
setTimeout(() => toast.remove(), 300); |
|
|
}, 3000); |
|
|
} |
|
|
|
|
|
function addToGallery(imageUrl, prompt) { |
|
|
const galleryItem = document.createElement('div'); |
|
|
galleryItem.className = 'relative group rounded-lg overflow-hidden'; |
|
|
galleryItem.innerHTML = ` |
|
|
<img src="${imageUrl}" alt="${prompt}" class="w-full h-48 object-cover rounded-lg hover:scale-105 transition-transform cursor-pointer"> |
|
|
<div class="absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center p-4"> |
|
|
<p class="text-white text-sm text-center">${prompt}</p> |
|
|
</div> |
|
|
`; |
|
|
gallerySection.querySelector('.grid').prepend(galleryItem); |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => { |
|
|
initNSFWToggle(); |
|
|
updateModelOptions(); |
|
|
|
|
|
|
|
|
generateBtn.addEventListener('click', generateImage); |
|
|
modelSelect.addEventListener('change', (e) => { |
|
|
currentModel = e.target.value; |
|
|
}); |
|
|
|
|
|
|
|
|
feather.replace(); |
|
|
}); |
|
|
|