/** * main.js — YOOtheme Alchemy Controller * Architecture: ES6 Modules | Async/Await | Event Delegation * * "The Hand that wields the logic." */ // === CONFIGURATION === const CONFIG = { API_ENDPOINT: "/gradio_api/call/predict", // Points to local Gradio instance ANIMATION_DURATION: 300, DEBOUNCE_DELAY: 150 }; // === UTILITIES === const Utils = { debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; }, sanitize(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; }, copyToClipboard(text) { navigator.clipboard.writeText(text).then(() => { UI.showToast("✨ Code copied to clipboard", "success"); }).catch(err => { UI.showToast("❌ Failed to copy", "error"); console.error(err); }); } }; // === API CLIENT === class AlchemyClient { constructor(endpoint) { this.endpoint = endpoint; } async generate(prompt, componentType) { try { // Simulating connection to the Gradio Backend (app.py) // Note: Actual implementation depends on specific Gradio API exposure const response = await fetch(this.endpoint, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ data: [prompt, [], componentType] // Standard Gradio payload structure }) }); if (!response.ok) throw new Error(`HTTP Error: ${response.status}`); const result = await response.json(); return result.data ? result.data[0] : null; } catch (error) { console.error("Alchemy API Error:", error); throw error; } } } // === UI CONTROLLER === class UIController { constructor() { this.elements = { input: document.getElementById('user-prompt'), generateBtn: document.getElementById('generate-btn'), outputContainer: document.getElementById('code-output'), outputPre: document.getElementById('output-pre'), componentSelect: document.getElementById('component-select'), loader: document.getElementById('alchemy-loader'), toast: document.getElementById('toast-container') }; this.client = new AlchemyClient(CONFIG.API_ENDPOINT); this.initEventListeners(); } initEventListeners() { // Generate Button this.elements.generateBtn.addEventListener('click', () => this.handleGeneration()); // Ctrl+Enter Shortcut this.elements.input.addEventListener('keydown', (e) => { if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { this.handleGeneration(); } }); // Copy Button (Event Delegation) document.addEventListener('click', (e) => { if (e.target.closest('.copy-btn')) { const code = this.elements.outputPre.textContent; Utils.copyToClipboard(code); } }); } setLoading(isLoading) { if (isLoading) { this.elements.generateBtn.disabled = true; this.elements.generateBtn.innerHTML = `Transmuting...
`; this.elements.outputContainer.classList.add('opacity-50', 'blur-sm'); } else { this.elements.generateBtn.disabled = false; this.elements.generateBtn.innerHTML = `Generate Code `; this.elements.outputContainer.classList.remove('opacity-50', 'blur-sm'); } } async handleGeneration() { const prompt = this.elements.input.value.trim(); const type = this.elements.componentSelect.value; if (!prompt) { this.showToast("⚠️ Please enter a prompt first.", "warning"); return; } this.setLoading(true); try { // For demo purposes, if API is not connected, we mock a response // to show the UI behavior (remove this in production) const result = await this.client.generate(prompt, type).catch(() => { // Fallback mock for UI demonstration if backend isn't reachable return new Promise(resolve => setTimeout(() => resolve(`\n
\n
\n

${Utils.sanitize(prompt)}

\n \n
\n
`), 1500)); }); this.renderOutput(result); this.showToast("✅ Generation Complete", "success"); } catch (error) { this.showToast("❌ System Error: Could not reach Alchemy Core.", "error"); } finally { this.setLoading(false); } } renderOutput(code) { // Simple syntax highlighting simulation const highlighted = Utils.sanitize(code) .replace(/</g, '<').replace(/>/g, '>') // Revert for display .replace(/(".+?")/g, '$1') .replace(/(<\/?.+?>)/g, '$1'); this.elements.outputPre.innerHTML = highlighted; // Animate in this.elements.outputContainer.classList.remove('hidden'); this.elements.outputContainer.animate([ { opacity: 0, transform: 'translateY(20px)' }, { opacity: 1, transform: 'translateY(0)' } ], { duration: 400, easing: 'cubic-bezier(0.2, 0.8, 0.2, 1)' }); } showToast(message, type = 'info') { const toast = document.createElement('div'); toast.className = `toast toast-${type}`; toast.textContent = message; this.elements.toast.appendChild(toast); // Remove after 3s setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 3000); } } // === BOOTSTRAP === document.addEventListener('DOMContentLoaded', () => { window.UI = new UIController(); console.log("⚡ Alchemy Client Initialized"); });