Made with DeepSite LogoDeepSite - 🧬 Remix

`; } function getBaseCSS() { return `body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f5f5f5; } #app { max-width: 1200px; margin: 0 auto; padding: 20px; }`; } function getBaseJS(appType, prompt) { let jsContent = `// ${appType} app - ${prompt}\n\n`; if (appType === 'chatbot') { jsContent += `document.getElementById('app').innerHTML = \`

Chatbot App

\`;`; } else if (appType === 'image') { jsContent += `document.getElementById('app').innerHTML = \`

Image Generator

\`;`; } else if (appType === 'video') { jsContent += `document.getElementById('app').innerHTML = \`

Video Generator

\`;`; } return jsContent; } const description = appDescription.value.trim(); if (!description) { addSystemMessage("Please describe the app you want to build."); return; } if (isGenerating) { addSystemMessage("I'm already working on your request. Please wait..."); return; } isGenerating = true; generateBtn.disabled = true; generateBtn.textContent = "Generating..."; addSystemMessage("Starting to generate your app: " + description); try { // First, get the app structure const structurePrompt = `I want to build this app: ${description}. Please generate a complete file structure for a full-stack application. Include all necessary files for frontend (HTML, CSS, JS, React, etc.), backend (Node.js, Express, etc.), and any other required files. Respond with a JSON object where keys are file paths and values are brief descriptions of what each file should contain.`; const structureResponse = await queryAI(structurePrompt); const fileStructure = parseFileStructure(structureResponse); // Then generate each file for (const [filePath, description] of Object.entries(fileStructure)) { await generateFile(filePath, description, description); } // Set the first file as active const firstFile = Object.keys(currentFiles)[0]; if (firstFile) { setActiveFile(firstFile); } updatePreview(); addSystemMessage("App generation complete! You can now preview your app and modify the code as needed."); } catch (error) { console.error("Error generating app:", error); addSystemMessage("Sorry, there was an error generating your app. Please try again."); } finally { isGenerating = false; generateBtn.disabled = false; generateBtn.textContent = "Generate App"; } } function parseFileStructure(response) { try { // Try to find JSON in the response const jsonStart = response.indexOf('{'); const jsonEnd = response.lastIndexOf('}') + 1; const jsonStr = response.slice(jsonStart, jsonEnd); const structure = JSON.parse(jsonStr); // Validate structure if (typeof structure !== 'object' || structure === null) { throw new Error("Invalid structure format"); } return structure; } catch (e) { console.error("Error parsing file structure:", e); // Fallback: create a basic structure return { "package.json": "Node.js package configuration", "server.js": "Express server entry point", "client/index.html": "Frontend HTML file", "client/styles.css": "Frontend styles", "client/app.js": "Frontend JavaScript", "README.md": "Project documentation" }; } } async function generateFile(filePath, description, context) { showFileGenerationStatus(filePath); const prompt = `Based on this app description: "${appDescription.value.trim()}" and this file context: "${context}", please generate complete code for the file "${filePath}" which should contain: "${description}". Respond with only the code (no explanations or markdown formatting) that should be in this file.`; const code = await queryAI(prompt); // Store the file currentFiles[filePath] = { path: filePath, content: code, description: description }; // Add file tab addFileTab(filePath); updateStatusMessage(`Generated ${filePath}`); } function showFileGenerationStatus(filePath) { const messageDiv = document.createElement('div'); messageDiv.className = 'bg-blue-50 p-3 rounded-md text-sm'; const typingDiv = document.createElement('div'); typingDiv.className = 'typing-animation text-blue-600'; typingDiv.textContent = `Generating ${filePath}...`; messageDiv.appendChild(typingDiv); chatMessages.appendChild(messageDiv); // Smooth scroll to bottom chatMessages.scrollTo({ top: chatMessages.scrollHeight, behavior: 'smooth' }); } function addFileTab(filePath) { // Check if tab already exists if (document.querySelector(`[data-file="${filePath}"]`)) { return; } const fileName = filePath.split('/').pop(); const tab = document.createElement('button'); tab.className = 'px-4 py-2 font-medium border-r border-gray-200 text-sm flex items-center'; tab.dataset.file = filePath; tab.innerHTML = ` ${fileName} `; tab.addEventListener('click', () => setActiveFile(filePath)); const closeBtn = tab.querySelector('.close-tab'); closeBtn.addEventListener('click', (e) => { e.stopPropagation(); closeFileTab(filePath); }); fileTabs.appendChild(tab); } function setActiveFile(filePath) { if (!currentFiles[filePath]) return; // Update UI document.querySelectorAll('[data-file]').forEach(tab => { tab.classList.remove('active'); }); const activeTab = document.querySelector(`[data-file="${filePath}"]`); if (activeTab) { activeTab.classList.add('active'); } // Set editor content aceEditor.setValue(currentFiles[filePath].content); // Set mode based on file extension const mode = getEditorMode(filePath); aceEditor.session.setMode(`ace/mode/${mode}`); // Update file info fileInfo.textContent = filePath; activeFile = filePath; } function getEditorMode(filePath) { const extension = filePath.split('.').pop().toLowerCase(); switch(extension) { case 'js': return 'javascript'; case 'jsx': return 'jsx'; case 'ts': return 'typescript'; case 'tsx': return 'tsx'; case 'html': return 'html'; case 'css': return 'css'; case 'scss': return 'scss'; case 'json': return 'json'; case 'md': return 'markdown'; case 'py': return 'python'; case 'java': return 'java'; case 'php': return 'php'; case 'rb': return 'ruby'; case 'go': return 'golang'; case 'sh': return 'sh'; default: return 'text'; } } function closeFileTab(filePath) { // Remove from UI const tab = document.querySelector(`[data-file="${filePath}"]`); if (tab) tab.remove(); // Remove from currentFiles delete currentFiles[filePath]; // If this was the active file, set a new active file if (activeFile === filePath) { const remainingFiles = Object.keys(currentFiles); if (remainingFiles.length > 0) { setActiveFile(remainingFiles[0]); } else { activeFile = null; aceEditor.setValue(''); fileInfo.textContent = ''; } } } function updatePreview() { if (!activeFile) return; // For HTML files, show directly if (activeFile.endsWith('.html')) { appPreview.srcdoc = currentFiles[activeFile].content; } // For other files, we'd need a more sophisticated preview system // This is a simplified version } async function sendUserMessage() { const message = userMessage.value.trim(); if (!message) return; addUserMessage(message); userMessage.value = ''; try { const response = await queryAI(message); addSystemMessage(response); // Check if the AI is trying to modify or create files if (response.includes('I\'ll create') || response.includes('Here\'s the code for')) { // Extract file paths and code from the response // This would need more sophisticated parsing in a real implementation const fileMatches = response.match(/```(?:[a-z]+\n)?([^\s]+)\n([\s\S]*?)```/g); if (fileMatches) { fileMatches.forEach(match => { const lines = match.split('\n'); const filePath = lines[1].trim(); const code = lines.slice(2, -1).join('\n'); if (filePath && code) { // Store or update the file currentFiles[filePath] = { path: filePath, content: code, description: `Modified by user request: ${message}` }; addFileTab(filePath); updateStatusMessage(`Updated ${filePath} based on your request`); } }); } } } catch (error) { console.error("Error processing user message:", error); addSystemMessage("Sorry, I encountered an error processing your request. Please try again."); } } async function queryAI(prompt, model = EDITOR_MODEL) { // Add to chat history chatHistory.push({ role: 'user', content: prompt }); const response = await fetch('https://openrouter.ai/api/v1/chat/completions', { method: 'POST', headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json', 'HTTP-Referer': window.location.href, 'X-Title': 'AI App Builder' }, body: JSON.stringify({ model: model, messages: chatHistory, temperature: 0.7 }) }); if (!response.ok) { throw new Error(`API request failed with status ${response.status}`); } const data = await response.json(); const aiResponse = data.choices[0].message.content; // Add to chat history chatHistory.push({ role: 'assistant', content: aiResponse }); return aiResponse; } function addSystemMessage(message) { const messageDiv = document.createElement('div'); messageDiv.className = 'flex'; messageDiv.innerHTML = `
${marked.parse(message)}
`; chatMessages.appendChild(messageDiv); // Smooth scroll to bottom chatMessages.scrollTo({ top: chatMessages.scrollHeight, behavior: 'smooth' }); } function addUserMessage(message) { const messageDiv = document.createElement('div'); messageDiv.className = 'flex justify-end'; messageDiv.innerHTML = `
${message}
`; chatMessages.appendChild(messageDiv); chatMessages.scrollTop = chatMessages.scrollHeight; } function updateStatusMessage(message) { statusMessage.textContent = message; setTimeout(() => { statusMessage.textContent = ''; }, 3000); } function downloadProject() { if (Object.keys(currentFiles).length === 0) { addSystemMessage("No files to download. Please generate an app first."); return; } // In a real implementation, this would create a zip file with all files // For this demo, we'll just download the active file if (activeFile) { const blob = new Blob([currentFiles[activeFile].content], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = activeFile.split('/').pop(); a.click(); URL.revokeObjectURL(url); addSystemMessage(`Downloaded ${activeFile}`); } } // Initialize the app init();