Spaces:
Sleeping
Sleeping
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>App Creator AI</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/core.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/javascript.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/html.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/lib/languages/css.min.js"></script> | |
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.7.0/styles/github.css"> | |
| <style> | |
| /* Custom scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #5D5CDE; | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #4a49b0; | |
| } | |
| /* Dark mode adjustments */ | |
| .dark .hljs { | |
| background: #2d2d2d; | |
| color: #ccc; | |
| } | |
| .dark .hljs-tag, | |
| .dark .hljs-keyword { | |
| color: #e78c45; | |
| } | |
| .dark .hljs-title, | |
| .dark .hljs-class, | |
| .dark .hljs-section { | |
| color: #e7c547; | |
| } | |
| .dark .hljs-string { | |
| color: #b9ca4a; | |
| } | |
| .dark .hljs-attr { | |
| color: #70c0b1; | |
| } | |
| </style> | |
| <script> | |
| tailwind.config = { | |
| darkMode: 'class', | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: '#5D5CDE', | |
| 'primary-dark': '#4a49b0', | |
| 'primary-light': '#7a79e6', | |
| }, | |
| } | |
| } | |
| } | |
| </script> | |
| </head> | |
| <body class="min-h-screen bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 transition-colors duration-200"> | |
| <div class="container mx-auto px-4 py-6 max-w-6xl"> | |
| <header class="mb-8 text-center"> | |
| <h1 class="text-3xl font-bold text-primary dark:text-primary-light mb-2">App Creator AI</h1> | |
| <p class="text-gray-600 dark:text-gray-400">Describe your app and I'll generate the code for you</p> | |
| </header> | |
| <main class="space-y-8"> | |
| <!-- App Description Input --> | |
| <section class="space-y-4"> | |
| <div class="flex justify-between items-center"> | |
| <h2 class="text-xl font-semibold">1. Describe Your App</h2> | |
| <div class="flex items-center space-x-2"> | |
| <label for="botSelect" class="text-sm">AI Model:</label> | |
| <select id="botSelect" class="bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md px-3 py-1 text-base focus:outline-none focus:ring-2 focus:ring-primary"> | |
| <option value="Claude-3.7-Sonnet" selected>Claude-3.7-Sonnet</option> | |
| <option value="GPT-4o">GPT-4o</option> | |
| <option value="GPT-4o-mini">GPT-4o-mini</option> | |
| </select> | |
| </div> | |
| </div> | |
| <textarea id="appDescription" rows="5" placeholder="Describe the app you want to create in detail. For example: 'Create a to-do list app with the ability to add, remove, and mark tasks as complete. The app should have a clean design with a dark mode option.'" class="w-full p-3 text-base border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-primary resize-y"></textarea> | |
| <button id="generateBtn" class="w-full md:w-auto px-6 py-2 bg-primary hover:bg-primary-dark text-white font-medium rounded-md transition-colors duration-200 flex items-center justify-center"> | |
| <span id="generateBtnText">Generate App</span> | |
| <svg id="generateBtnSpinner" class="animate-spin ml-2 h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | |
| <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> | |
| <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | |
| </svg> | |
| </button> | |
| </section> | |
| <!-- Results Section (hidden initially) --> | |
| <section id="resultsSection" class="hidden space-y-6"> | |
| <div class="border-t border-gray-300 dark:border-gray-700 pt-6"> | |
| <h2 class="text-xl font-semibold mb-4">2. Generated App</h2> | |
| <!-- Tabs --> | |
| <div class="flex border-b border-gray-300 dark:border-gray-700 mb-4"> | |
| <button id="previewTab" class="px-4 py-2 font-medium border-b-2 border-primary text-primary dark:text-primary-light">Preview</button> | |
| <button id="codeTab" class="px-4 py-2 font-medium text-gray-600 dark:text-gray-400 hover:text-primary dark:hover:text-primary-light">Code</button> | |
| </div> | |
| <!-- Preview panel --> | |
| <div id="previewPanel" class="bg-gray-100 dark:bg-gray-800 rounded-md p-4 min-h-[300px]"> | |
| <div class="bg-white dark:bg-gray-900 rounded-md w-full h-full min-h-[300px] border border-gray-300 dark:border-gray-700 overflow-hidden"> | |
| <iframe id="previewFrame" class="w-full h-[500px] border-none"></iframe> | |
| </div> | |
| </div> | |
| <!-- Code panel --> | |
| <div id="codePanel" class="hidden"> | |
| <div class="bg-gray-100 dark:bg-gray-800 rounded-md p-4"> | |
| <pre><code id="codeDisplay" class="language-html text-sm overflow-x-auto"></code></pre> | |
| <button id="copyCodeBtn" class="mt-4 px-4 py-2 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded-md text-sm font-medium transition-colors duration-200 flex items-center"> | |
| <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> | |
| </svg> | |
| Copy Code | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| </section> | |
| <!-- Refinement Section (hidden initially) --> | |
| <section id="refinementSection" class="hidden space-y-4 border-t border-gray-300 dark:border-gray-700 pt-6"> | |
| <h2 class="text-xl font-semibold">3. Refine Your App</h2> | |
| <textarea id="refinementInput" rows="3" placeholder="Describe the changes you want to make to the app. For example: 'Add a search feature to filter tasks' or 'Change the color scheme to blue'." class="w-full p-3 text-base border border-gray-300 dark:border-gray-700 rounded-md bg-white dark:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-primary resize-y"></textarea> | |
| <button id="refineBtn" class="w-full md:w-auto px-6 py-2 bg-primary hover:bg-primary-dark text-white font-medium rounded-md transition-colors duration-200 flex items-center justify-center"> | |
| <span id="refineBtnText">Refine App</span> | |
| <svg id="refineBtnSpinner" class="animate-spin ml-2 h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> | |
| <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> | |
| <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> | |
| </svg> | |
| </button> | |
| </section> | |
| </main> | |
| <footer class="mt-12 text-center text-gray-500 dark:text-gray-400 text-sm"> | |
| <p>Created with App Creator AI on Poe</p> | |
| </footer> | |
| </div> | |
| <script> | |
| // Dark mode detection and handling | |
| if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { | |
| document.documentElement.classList.add('dark'); | |
| } | |
| window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => { | |
| if (event.matches) { | |
| document.documentElement.classList.add('dark'); | |
| } else { | |
| document.documentElement.classList.remove('dark'); | |
| } | |
| }); | |
| // DOM Elements | |
| const generateBtn = document.getElementById('generateBtn'); | |
| const generateBtnText = document.getElementById('generateBtnText'); | |
| const generateBtnSpinner = document.getElementById('generateBtnSpinner'); | |
| const appDescription = document.getElementById('appDescription'); | |
| const botSelect = document.getElementById('botSelect'); | |
| const resultsSection = document.getElementById('resultsSection'); | |
| const refinementSection = document.getElementById('refinementSection'); | |
| const codeDisplay = document.getElementById('codeDisplay'); | |
| const previewFrame = document.getElementById('previewFrame'); | |
| const copyCodeBtn = document.getElementById('copyCodeBtn'); | |
| const previewTab = document.getElementById('previewTab'); | |
| const codeTab = document.getElementById('codeTab'); | |
| const previewPanel = document.getElementById('previewPanel'); | |
| const codePanel = document.getElementById('codePanel'); | |
| const refinementInput = document.getElementById('refinementInput'); | |
| const refineBtn = document.getElementById('refineBtn'); | |
| const refineBtnText = document.getElementById('refineBtnText'); | |
| const refineBtnSpinner = document.getElementById('refineBtnSpinner'); | |
| // State variables | |
| let currentCode = ''; | |
| // Tab switching | |
| previewTab.addEventListener('click', () => { | |
| previewTab.classList.add('border-b-2', 'border-primary', 'text-primary', 'dark:text-primary-light'); | |
| previewTab.classList.remove('text-gray-600', 'dark:text-gray-400'); | |
| codeTab.classList.remove('border-b-2', 'border-primary', 'text-primary', 'dark:text-primary-light'); | |
| codeTab.classList.add('text-gray-600', 'dark:text-gray-400'); | |
| previewPanel.classList.remove('hidden'); | |
| codePanel.classList.add('hidden'); | |
| }); | |
| codeTab.addEventListener('click', () => { | |
| codeTab.classList.add('border-b-2', 'border-primary', 'text-primary', 'dark:text-primary-light'); | |
| codeTab.classList.remove('text-gray-600', 'dark:text-gray-400'); | |
| previewTab.classList.remove('border-b-2', 'border-primary', 'text-primary', 'dark:text-primary-light'); | |
| previewTab.classList.add('text-gray-600', 'dark:text-gray-400'); | |
| codePanel.classList.remove('hidden'); | |
| previewPanel.classList.add('hidden'); | |
| }); | |
| // Copy code to clipboard | |
| copyCodeBtn.addEventListener('click', async () => { | |
| try { | |
| await navigator.clipboard.writeText(currentCode); | |
| copyCodeBtn.textContent = 'Copied!'; | |
| setTimeout(() => { | |
| copyCodeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" /> | |
| </svg>Copy Code`; | |
| }, 2000); | |
| } catch (err) { | |
| console.error('Failed to copy: ', err); | |
| } | |
| }); | |
| // Extract code from markdown response | |
| function extractCodeFromMarkdown(markdown) { | |
| const codeRegex = /```(?:html|)\n([\s\S]*?)```/; | |
| const match = markdown.match(codeRegex); | |
| return match ? match[1] : ''; | |
| } | |
| // Update preview iframe | |
| function updatePreview(code) { | |
| const blob = new Blob([code], { type: 'text/html' }); | |
| const url = URL.createObjectURL(blob); | |
| previewFrame.src = url; | |
| } | |
| // Register handler for bot responses | |
| window.Poe.registerHandler("app-creator-handler", (result, context) => { | |
| const response = result.responses[0]; | |
| if (response.status === "error") { | |
| setGenerating(false); | |
| setRefining(false); | |
| alert(`Error: ${response.statusText || 'An error occurred while generating the app'}`); | |
| return; | |
| } | |
| if (response.status === "complete") { | |
| // Extract code | |
| const extractedCode = extractCodeFromMarkdown(response.content); | |
| if (extractedCode) { | |
| currentCode = extractedCode; | |
| // Display the code with syntax highlighting | |
| codeDisplay.textContent = currentCode; | |
| hljs.highlightElement(codeDisplay); | |
| // Update the preview | |
| updatePreview(currentCode); | |
| // Show results and refinement sections | |
| resultsSection.classList.remove('hidden'); | |
| refinementSection.classList.remove('hidden'); | |
| } else { | |
| alert("No code found in the response. Please try again with a more detailed description."); | |
| } | |
| setGenerating(false); | |
| setRefining(false); | |
| } | |
| }); | |
| // Helper functions for button states | |
| function setGenerating(isGenerating) { | |
| if (isGenerating) { | |
| generateBtn.disabled = true; | |
| generateBtnText.textContent = 'Generating...'; | |
| generateBtnSpinner.classList.remove('hidden'); | |
| } else { | |
| generateBtn.disabled = false; | |
| generateBtnText.textContent = 'Generate App'; | |
| generateBtnSpinner.classList.add('hidden'); | |
| } | |
| } | |
| function setRefining(isRefining) { | |
| if (isRefining) { | |
| refineBtn.disabled = true; | |
| refineBtnText.textContent = 'Refining...'; | |
| refineBtnSpinner.classList.remove('hidden'); | |
| } else { | |
| refineBtn.disabled = false; | |
| refineBtnText.textContent = 'Refine App'; | |
| refineBtnSpinner.classList.add('hidden'); | |
| } | |
| } | |
| // Generate App button handler | |
| generateBtn.addEventListener('click', async () => { | |
| const description = appDescription.value.trim(); | |
| if (!description) { | |
| alert('Please enter a description of the app you want to create.'); | |
| return; | |
| } | |
| const selectedBot = botSelect.value; | |
| try { | |
| setGenerating(true); | |
| const prompt = `@${selectedBot} Create a canvas app based on this description: "${description}". | |
| Please provide a fully functional HTML application that can be rendered in a Poe Canvas App. Include all necessary HTML, CSS, and JavaScript in a single code block. Ensure the app is responsive and follows best practices for web development. | |
| Your response should follow this format: | |
| 1. A brief explanation of what you've created | |
| 2. A Markdown code block containing the complete HTML/CSS/JS for the app | |
| Make sure the code is clean, well-documented, and ready to deploy.`; | |
| await window.Poe.sendUserMessage(prompt, { | |
| handler: "app-creator-handler", | |
| stream: false, | |
| openChat: false | |
| }); | |
| } catch (err) { | |
| console.error('Error:', err); | |
| alert(`Error: ${err.message || 'An error occurred while generating the app'}`); | |
| setGenerating(false); | |
| } | |
| }); | |
| // Refine App button handler | |
| refineBtn.addEventListener('click', async () => { | |
| const refinement = refinementInput.value.trim(); | |
| if (!refinement) { | |
| alert('Please enter a description of the changes you want to make.'); | |
| return; | |
| } | |
| const selectedBot = botSelect.value; | |
| try { | |
| setRefining(true); | |
| const prompt = `@${selectedBot} I have a Poe Canvas App that I want to refine. Here is the current code: | |
| \`\`\`html | |
| ${currentCode} | |
| \`\`\` | |
| I want to make the following changes: | |
| "${refinement}" | |
| Please provide an updated version of the code that implements these changes. Keep the core functionality and only modify what's necessary. | |
| Your response should follow this format: | |
| 1. A brief explanation of what changes you've made | |
| 2. A Markdown code block containing the complete updated HTML/CSS/JS for the app`; | |
| await window.Poe.sendUserMessage(prompt, { | |
| handler: "app-creator-handler", | |
| stream: false, | |
| openChat: false | |
| }); | |
| } catch (err) { | |
| console.error('Error:', err); | |
| alert(`Error: ${err.message || 'An error occurred while refining the app'}`); | |
| setRefining(false); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |