diff --git a/index.html b/index.html new file mode 100644 index 0000000..de09098 --- /dev/null +++ b/index.html @@ -0,0 +1,78 @@ + + + + + + AI Website Builder + + + +
+

AI Website Builder

+

Enter a description of the website you want to create, and watch AI models build it for you in real-time.

+ +
+ + + +
+ + + +
+ +
+

Your generated websites will appear here

+

Get started by entering a prompt above.

+
+
+ +
+
+

Live Preview

+ +
+ + + +
+ +
+
+ +
+
+ +
+ +
+ +
+ + + + diff --git a/script.js b/script.js new file mode 100644 index 0000000..4c80a99 --- /dev/null +++ b/script.js @@ -0,0 +1,574 @@ +// Configuration +const API_CONFIG = { + baseUrl: 'https://apis.iflow.cn/v1', + models: [ + { + name: 'GLM-4.6', + model: 'glm-4.6', + baseUrl: 'https://apis.iflow.cn/v1', + }, + { + name: 'qwen3-max', + model: 'qwen3-max', + baseUrl: 'https://apis.iflow.cn/v1', + }, + { + name: 'qwen3-coder', + model: 'qwen3-coder-plus', + baseUrl: 'https://apis.iflow.cn/v1', + }, + { + name: 'tbao', + model: 'tstars2.0', + baseUrl: 'https://apis.iflow.cn/v1', + }, + { + name: 'kimi-k2', + model: 'kimi-k2-0905', + baseUrl: 'https://apis.iflow.cn/v1', + }, + ] +}; + +// State management +let currentResults = []; +let isGenerating = false; +let currentCodeForModal = ''; +let activeTypingAnimations = {}; + +// Initialize +document.addEventListener('DOMContentLoaded', () => { + initializeEventListeners(); + checkEmptyState(); + console.log('AI Website Builder initialized'); +}); + +function initializeEventListeners() { + // Enter key shortcut + document.getElementById('promptInput').addEventListener('keydown', (e) => { + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') { + generateWebsites(); + } + }); + + document.getElementById('generateBtn').addEventListener('click', generateWebsites); + + // Close modal on outside click + document.getElementById('codeModal').addEventListener('click', (e) => { + if (e.target.id === 'codeModal') { + closeModal(); + } + }); + + // Close fullscreen on ESC key + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + closeFullscreen(); + } + }); +} + +async function generateWebsites() { + const prompt = document.getElementById('promptInput').value.trim(); + + if (!prompt) { + showToast('Please enter a website description', 'error'); + return; + } + + if (isGenerating) { + showToast('Generation already in progress', 'warning'); + return; + } + + console.log('Starting generation with prompt:', prompt); + + isGenerating = true; + const generateBtn = document.getElementById('generateBtn'); + const resultsContainer = document.getElementById('resultsContainer'); + const emptyState = document.getElementById('emptyState'); + const progressSection = document.getElementById('progressSection'); + + // Update UI states + generateBtn.classList.add('loading'); + generateBtn.disabled = true; + generateBtn.querySelector('.btn-text').textContent = 'Generating...'; + emptyState.style.display = 'none'; + progressSection.style.display = 'block'; + + // Clear previous results + resultsContainer.innerHTML = ''; + currentResults = []; + + // Clear any active typing animations + Object.values(activeTypingAnimations).forEach(animation => clearInterval(animation)); + activeTypingAnimations = {}; + + // Animate LLM badges + const badges = document.querySelectorAll('.llm-badge'); + badges.forEach(badge => badge.classList.remove('active')); + + try { + const models = API_CONFIG.models; + const totalModels = models.length; + + for (let i = 0; i < models.length; i++) { + const model = models[i]; + + console.log(`Processing model ${i + 1}/${totalModels}: ${model.name}`); + + // Update progress + updateProgress((i / totalModels) * 100, `Generating with ${model.name}...`); + + // Activate current badge + if (badges[i]) badges[i].classList.add('active'); + + // Create result card with loading state + const cardId = `result-${i}`; + const card = createResultCard({ + id: cardId, + llm: model.name, + status: 'loading', + html: '' + }); + resultsContainer.appendChild(card); + + try { + // Generate HTML with the model and show live coding + await generateWithModelLive(model, prompt, cardId); + + } catch (error) { + console.error(`Error with ${model.name}:`, error); + updateResultCard(cardId, { + llm: model.name, + status: 'error', + error: error.message || 'Unknown error occurred' + }); + } + + // Deactivate badge + if (badges[i]) badges[i].classList.remove('active'); + } + + updateProgress(100, 'Generation complete!'); + showToast('All websites generated successfully!', 'success'); + + } catch (error) { + console.error('Generation error:', error); + showToast('Failed to generate websites: ' + error.message, 'error'); + } finally { + // Reset UI states + isGenerating = false; + generateBtn.classList.remove('loading'); + generateBtn.disabled = false; + generateBtn.querySelector('.btn-text').textContent = 'Generate Websites'; + + setTimeout(() => { + progressSection.style.display = 'none'; + }, 2000); + } +} + +async function generateWithModelLive(model, prompt, cardId) { + const apiKey = document.getElementById('apiKeyInput').value.trim(); + if (!apiKey) { + showToast('Please enter your API key', 'error'); + throw new Error('API Key is required.'); + } + const systemPrompt = `You are an expert web developer. Generate complete, valid HTML code for the requested website. +Include inline CSS styles within a + + +

Generated Website

+

${prompt}

+
+

Feature 1

+

Modern and responsive design

+
+
+

Feature 2

+

Clean and professional layout

+
+
+

Feature 3

+

Customizable content

+
+

Note: API connection failed. This is a fallback template.

+

Model: ${model.name}

+ +`; + await typingEffect(cardId, fallbackHtml, model.name, true); + } +} + +function createResultCard(result) { + const card = document.createElement('div'); + card.className = 'result-card'; + card.id = result.id; + + if (result.status === 'loading') { + card.innerHTML = ` +
+ ${result.llm} + Generating... +
+
+
+
+ `; + } + + return card; +} + +function showLiveCoding(cardId, llmName) { + const card = document.getElementById(cardId); + if (!card) return; + + const uniqueId = cardId.replace('result-', ''); + + card.innerHTML = ` +
+ ${llmName} +
+ + +
+
+
+
+ +
+
+
+
+
+ + `; +} + +async function typingEffect(cardId, html, llmName, isComplete) { + const uniqueId = cardId.replace('result-', ''); + const codeElement = document.getElementById(`code-text-${uniqueId}`); + const codeBlock = document.getElementById(`code-block-${uniqueId}`); + + if (!codeElement) { + console.warn(`Code element not found for ${cardId}`); + return; + } + + // Use a simple text update for performance + codeElement.textContent = html; + + if (codeBlock) { + codeBlock.scrollTop = codeBlock.scrollHeight; + } + + updatePreviewDuringTyping(uniqueId, html); + + if (isComplete) { + updateResultCard(cardId, { + llm: llmName, + status: 'success', + html: html + }); + + currentResults.push({ + llm: llmName, + html: html + }); + + console.log(`Generation complete for ${llmName}`); + } +} + +function updatePreviewDuringTyping(uniqueId, partialHtml) { + const iframe = document.getElementById(`iframe-${uniqueId}`); + if (iframe) { + try { + const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; + iframeDoc.open(); + iframeDoc.write(partialHtml); + iframeDoc.close(); + } catch (e) { + console.debug('Preview update error (expected):', e.message); + } + } +} + +function updateResultCard(cardId, result) { + const card = document.getElementById(cardId); + if (!card) return; + + const uniqueId = cardId.replace('result-', ''); + + if (result.status === 'success') { + const codeElement = document.getElementById(`code-text-${uniqueId}`); + if (codeElement) { + codeElement.textContent = result.html; + } + + // Final load into iframe to ensure all scripts run if any + setTimeout(() => { + updatePreviewDuringTyping(uniqueId, result.html); + }, 100); + + if (!window.generatedHTML) window.generatedHTML = {}; + window.generatedHTML[uniqueId] = result.html; + + } else { + card.innerHTML = ` +
+ ${result.llm} + ✗ Error +
+
+

Failed to generate website

+

${result.error || 'Unknown error'}

+

Check console for details (F12)

+
+ `; + } +} + +function openFullscreen(uniqueId, llmName) { + const html = window.generatedHTML[uniqueId]; + if (html) { + const fullscreenPreview = document.getElementById('fullscreenPreview'); + const fullscreenIframe = document.getElementById('fullscreenIframe'); + const llmNameElement = document.getElementById('fullscreenLLMName'); + + llmNameElement.textContent = llmName; + + const iframeDoc = fullscreenIframe.contentDocument || fullscreenIframe.contentWindow.document; + iframeDoc.open(); + iframeDoc.write(html); + iframeDoc.close(); + + fullscreenPreview.classList.add('show'); + document.getElementById('fullscreenBody').className = 'fullscreen-body desktop'; + } +} + +function closeFullscreen() { + document.getElementById('fullscreenPreview').classList.remove('show'); +} + +function toggleDeviceView(device) { + document.getElementById('fullscreenBody').className = `fullscreen-body ${device}`; +} + +function switchTab(cardId, tabName) { + const card = document.getElementById(`result-${cardId}`); + if (!card) return; + + const contents = card.querySelectorAll('.tab-content'); + const tabs = card.querySelectorAll('.tab'); + + contents.forEach(content => content.classList.remove('active')); + tabs.forEach(tab => tab.classList.remove('active')); + + card.querySelector(`#${tabName}-${cardId}`).classList.add('active'); + + const clickedTab = tabName === 'preview' ? tabs[0] : tabs[1]; + if (clickedTab) { + clickedTab.classList.add('active'); + } +} + +function copyCode(id) { + const html = window.generatedHTML[id]; + if (html) { + navigator.clipboard.writeText(html).then(() => { + showToast('Code copied to clipboard!', 'success'); + }).catch(() => { + showToast('Failed to copy code', 'error'); + }); + } +} + +function downloadHTML(id, llmName) { + const html = window.generatedHTML[id]; + if (html) { + const blob = new Blob([html], { type: 'text/html' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `${llmName.toLowerCase().replace(/\s+/g, '-')}-website.html`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + showToast('HTML file downloaded!', 'success'); + } +} + +function viewFullCode(id) { + const html = window.generatedHTML[id]; + if (html) { + currentCodeForModal = html; + document.getElementById('modalCode').textContent = html; + document.getElementById('codeModal').classList.add('show'); + } +} + +function closeModal() { + document.getElementById('codeModal').classList.remove('show'); +} + +function copyModalCode() { + if (currentCodeForModal) { + navigator.clipboard.writeText(currentCodeForModal).then(() => { + showToast('Code copied to clipboard!', 'success'); + }).catch(() => { + showToast('Failed to copy code', 'error'); + }); + } +} + +function downloadCode() { + if (currentCodeForModal) { + const blob = new Blob([currentCodeForModal], { type: 'text/html' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = 'generated-website.html'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + showToast('HTML file downloaded!', 'success'); + } +} + +function updateProgress(percentage, text) { + const progressFill = document.getElementById('progressFill'); + const progressText = document.getElementById('progressText'); + + if (progressFill) progressFill.style.width = `${percentage}%`; + if (progressText) progressText.textContent = text; +} + +function showToast(message, type = 'info') { + const toast = document.getElementById('toast'); + toast.textContent = message; + toast.className = `toast ${type}`; + toast.classList.add('show'); + + setTimeout(() => { + toast.classList.remove('show'); + }, 3000); +} + +function checkEmptyState() { + const resultsContainer = document.getElementById('resultsContainer'); + const emptyState = document.getElementById('emptyState'); + + if (resultsContainer.children.length === 0) { + emptyState.style.display = 'block'; + } else { + emptyState.style.display = 'none'; + } +} + +// Example prompts for quick testing +const examplePrompts = [ + "Create a modern portfolio website for a photographer with dark theme", + "Build a colorful landing page for a children's toy store", + "Design a minimalist blog website with a clean white design", + "Create a tech startup landing page with animations", + "Build a restaurant website with menu and reservation form" +]; + +// Add example prompt on double-click of textarea placeholder +document.getElementById('promptInput').addEventListener('dblclick', function () { + if (this.value === '') { + this.value = examplePrompts[Math.floor(Math.random() * examplePrompts.length)]; + showToast('Example prompt added!', 'success'); + } +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..bd37420 --- /dev/null +++ b/style.css @@ -0,0 +1,314 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&display=swap'); + +:root { + --primary-color: #6a11cb; + --secondary-color: #2575fc; + --background-color: #f0f2f5; + --card-background: #ffffff; + --text-color: #333; + --light-text-color: #f8f9fa; + --border-color: #dee2e6; + --shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + --border-radius: 12px; +} + +body { + font-family: 'Roboto', sans-serif; + background-color: var(--background-color); + color: var(--text-color); + margin: 0; + padding: 20px; + line-height: 1.6; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 20px; +} + +h1, h2 { + text-align: center; + color: var(--primary-color); +} + +p { + text-align: center; + margin-bottom: 25px; + color: #555; +} + +.input-container { + display: flex; + gap: 15px; + margin-bottom: 30px; +} + +#promptInput, #apiKeyInput { + flex-grow: 1; + padding: 15px; + border: 1px solid var(--border-color); + border-radius: var(--border-radius); + font-size: 16px; + resize: vertical; + min-height: 50px; +} + +#generateBtn { + padding: 15px 30px; + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + color: var(--light-text-color); + border: none; + border-radius: var(--border-radius); + cursor: pointer; + font-size: 16px; + font-weight: 500; + transition: all 0.3s ease; + display: flex; + align-items: center; + gap: 8px; +} + +#generateBtn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15); +} + +#generateBtn.loading { + cursor: not-allowed; + background: #ccc; +} + +#progressSection { + margin-bottom: 30px; +} + +.llm-badges { + display: flex; + justify-content: center; + gap: 10px; + margin-bottom: 15px; +} + +.llm-badge { + padding: 8px 15px; + border-radius: 20px; + background-color: #e9ecef; + color: #495057; + font-size: 14px; + transition: all 0.3s ease; +} + +.llm-badge.active { + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + color: var(--light-text-color); + transform: scale(1.1); +} + +.progress-bar { + width: 100%; + height: 10px; + background-color: #e9ecef; + border-radius: 5px; + overflow: hidden; + margin-bottom: 10px; +} + +#progressFill { + width: 0%; + height: 100%; + background: linear-gradient(45deg, var(--primary-color), var(--secondary-color)); + transition: width 0.5s ease; +} + +#progressText { + text-align: center; + font-size: 14px; + color: #666; +} + +#resultsContainer { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 25px; +} + +.result-card { + background-color: var(--card-background); + border-radius: var(--border-radius); + box-shadow: var(--shadow); + overflow: hidden; + display: flex; + flex-direction: column; +} + +.result-header { + padding: 15px; + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid var(--border-color); +} + +.result-title { + font-size: 18px; + font-weight: 500; +} + +.status-success { color: #28a745; } +.status-error { color: #dc3545; } + +.result-body { + padding: 15px; + flex-grow: 1; +} + +.tab-content { + display: none; +} +.tab-content.active { + display: block; +} + +iframe { + width: 100%; + height: 300px; + border: 1px solid var(--border-color); + border-radius: 8px; +} + +.code-block { + background-color: #2d2d2d; + color: #f8f8f2; + padding: 15px; + border-radius: 8px; + height: 300px; + overflow: auto; + font-family: 'Courier New', Courier, monospace; +} + +.result-footer { + padding: 15px; + border-top: 1px solid var(--border-color); + display: flex; + justify-content: space-around; +} + +.result-footer button { + background: none; + border: 1px solid var(--border-color); + padding: 8px 15px; + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; +} +.result-footer button:hover { + background-color: #f1f1f1; +} + +/* Fullscreen Preview */ +#fullscreenPreview { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.8); + display: none; + flex-direction: column; + z-index: 1000; +} +#fullscreenPreview.show { + display: flex; +} +.fullscreen-header { + background-color: #2d2d2d; + color: white; + padding: 15px; + display: flex; + align-items: center; + justify-content: space-between; +} +.fullscreen-body { + flex-grow: 1; + display: flex; + justify-content: center; + align-items: center; + padding: 20px; +} +#fullscreenIframe { + background-color: white; + border: none; + box-shadow: 0 0 20px rgba(0,0,0,0.5); + transition: all 0.3s ease; +} +.desktop #fullscreenIframe { width: 100%; height: 100%; } +.tablet #fullscreenIframe { width: 768px; height: 1024px; max-width: 100%; max-height: 100%;} +.mobile #fullscreenIframe { width: 375px; height: 667px; max-width: 100%; max-height: 100%;} + +/* Code Modal */ +#codeModal { + position: fixed; + top: 0; left: 0; + width: 100%; height: 100%; + background: rgba(0,0,0,0.7); + display: none; + justify-content: center; + align-items: center; + z-index: 1001; +} +#codeModal.show { display: flex; } +.modal-content { + background: white; + width: 80%; + max-width: 900px; + border-radius: var(--border-radius); + display: flex; + flex-direction: column; + max-height: 80vh; +} +.modal-header, .modal-footer { + padding: 15px; + display: flex; + justify-content: space-between; + align-items: center; +} +.modal-header { border-bottom: 1px solid var(--border-color); } +.modal-footer { border-top: 1px solid var(--border-color); } +#modalCode { + flex-grow: 1; + overflow: auto; + padding: 15px; + background-color: #2d2d2d; + color: #f8f8f2; + margin: 0; +} + +/* Toast Notification */ +#toast { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + padding: 12px 25px; + border-radius: 8px; + color: white; + font-size: 16px; + z-index: 2000; + opacity: 0; + transition: opacity 0.3s, bottom 0.3s; +} +#toast.show { + opacity: 1; + bottom: 30px; +} +#toast.success { background-color: #28a745; } +#toast.error { background-color: #dc3545; } +#toast.warning { background-color: #ffc107; color: #333; } + +#emptyState { + text-align: center; + padding: 50px; + border: 2px dashed var(--border-color); + border-radius: var(--border-radius); +}