commit 636de138195e4f11c4adbac98deb794653801e74
Author: root <root@hub.scroll.pub> Date: 2025-01-01 07:18:20 +0000 Subject: Initial commit diff --git a/body.html b/body.html new file mode 100644 index 0000000..bfdd6ef --- /dev/null +++ b/body.html @@ -0,0 +1,50 @@ +<header> + <nav> + <div class="logo">SpeakAI</div> + <div class="nav-links"> + <a href="#features">Features</a> + <a href="#languages">Languages</a> + <a href="#start">Start Practice</a> + </div> + </nav> +</header> + +<main> + <section id="hero"> + <h1>Practice Speaking Any Language with AI</h1> + <p>Have natural conversations with an AI language partner available 24/7</p> + </section> + + <section id="chat-interface"> + <div class="language-select"> + <label for="language">Choose Language:</label> + <select id="language"> + <option value="spanish">Spanish</option> + <option value="french">French</option> + <option value="german">German</option> + <option value="japanese">Japanese</option> + </select> + </div> + + <div class="chat-container"> + <div id="chat-messages"></div> + <div class="controls"> + <button id="start-recording" aria-label="Start Recording"> + <span class="mic-icon">🎤</span> + </button> + <button id="stop-recording" aria-label="Stop Recording" disabled> + <span class="stop-icon">⬛</span> + </button> + <p id="status">Click microphone to start speaking</p> + </div> + </div> + </section> +</main> + +<footer> + <nav> + <a href="/privacy">Privacy Policy</a> + <a href="/terms">Terms of Use</a> + <a href="/contact">Contact</a> + </nav> +</footer> diff --git a/index.scroll b/index.scroll new file mode 100644 index 0000000..60b3123 --- /dev/null +++ b/index.scroll @@ -0,0 +1,8 @@ +buildHtml +baseUrl https://speakai.scroll.pub +metaTags +editButton /edit.html +title SpeakAI - Practice Languages with AI Voice Chat +style.css +body.html +script.js diff --git a/prompt.json b/prompt.json new file mode 100644 index 0000000..a0e804c --- /dev/null +++ b/prompt.json @@ -0,0 +1,20 @@ +{ + "id": "msg_01Vm3iX16278aPKnJUc7Q1LJ", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet-20241022", + "content": [ + { + "type": "text", + "text": "Here's a language practice web app with AI voice chat capabilities:\n\n---domain---\nspeakai.scroll.pub\n\n---index.scroll---\nbuildHtml\nbaseUrl https://speakai.scroll.pub\nmetaTags\neditButton /edit.html\ntitle SpeakAI - Practice Languages with AI Voice Chat\nstyle.css\nbody.html\nscript.js\n\n---body.html---\n<header>\n <nav>\n <div class=\"logo\">SpeakAI</div>\n <div class=\"nav-links\">\n <a href=\"#features\">Features</a>\n <a href=\"#languages\">Languages</a>\n <a href=\"#start\">Start Practice</a>\n </div>\n </nav>\n</header>\n\n<main>\n <section id=\"hero\">\n <h1>Practice Speaking Any Language with AI</h1>\n <p>Have natural conversations with an AI language partner available 24/7</p>\n </section>\n\n <section id=\"chat-interface\">\n <div class=\"language-select\">\n <label for=\"language\">Choose Language:</label>\n <select id=\"language\">\n <option value=\"spanish\">Spanish</option>\n <option value=\"french\">French</option>\n <option value=\"german\">German</option>\n <option value=\"japanese\">Japanese</option>\n </select>\n </div>\n\n <div class=\"chat-container\">\n <div id=\"chat-messages\"></div>\n <div class=\"controls\">\n <button id=\"start-recording\" aria-label=\"Start Recording\">\n <span class=\"mic-icon\">🎤</span>\n </button>\n <button id=\"stop-recording\" aria-label=\"Stop Recording\" disabled>\n <span class=\"stop-icon\">⬛</span>\n </button>\n <p id=\"status\">Click microphone to start speaking</p>\n </div>\n </div>\n </section>\n</main>\n\n<footer>\n <nav>\n <a href=\"/privacy\">Privacy Policy</a>\n <a href=\"/terms\">Terms of Use</a>\n <a href=\"/contact\">Contact</a>\n </nav>\n</footer>\n\n---style.css---\n:root {\n --primary: #4a90e2;\n --secondary: #2c3e50;\n --accent: #e74c3c;\n --background: #f5f6fa;\n --text: #2c3e50;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n line-height: 1.6;\n color: var(--text);\n background: var(--background);\n}\n\nheader {\n background: white;\n box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n position: fixed;\n width: 100%;\n top: 0;\n z-index: 100;\n}\n\nnav {\n max-width: 1200px;\n margin: 0 auto;\n padding: 1rem;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.logo {\n font-size: 1.5rem;\n font-weight: bold;\n color: var(--primary);\n}\n\n.nav-links a {\n color: var(--secondary);\n text-decoration: none;\n margin-left: 2rem;\n transition: color 0.3s ease;\n}\n\n.nav-links a:hover {\n color: var(--primary);\n}\n\nmain {\n margin-top: 80px;\n padding: 2rem;\n}\n\n#hero {\n text-align: center;\n padding: 4rem 1rem;\n}\n\nh1 {\n font-size: 2.5rem;\n margin-bottom: 1rem;\n color: var(--secondary);\n}\n\n.chat-container {\n max-width: 800px;\n margin: 2rem auto;\n background: white;\n border-radius: 12px;\n box-shadow: 0 4px 20px rgba(0,0,0,0.1);\n padding: 2rem;\n}\n\n#chat-messages {\n min-height: 300px;\n margin-bottom: 1rem;\n padding: 1rem;\n border-radius: 8px;\n background: var(--background);\n}\n\n.controls {\n display: flex;\n align-items: center;\n gap: 1rem;\n}\n\nbutton {\n border: none;\n background: var(--primary);\n color: white;\n padding: 1rem;\n border-radius: 50%;\n cursor: pointer;\n transition: transform 0.2s ease;\n}\n\nbutton:hover {\n transform: scale(1.1);\n}\n\nbutton:disabled {\n background: #ccc;\n cursor: not-allowed;\n}\n\n#status {\n color: var(--secondary);\n}\n\n.language-select {\n margin-bottom: 2rem;\n text-align: center;\n}\n\nselect {\n padding: 0.5rem 1rem;\n font-size: 1rem;\n border-radius: 4px;\n border: 1px solid var(--primary);\n}\n\nfooter {\n background: var(--secondary);\n color: white;\n padding: 2rem;\n margin-top: 4rem;\n}\n\nfooter nav {\n justify-content: center;\n gap: 2rem;\n}\n\nfooter a {\n color: white;\n text-decoration: none;\n}\n\n@media (max-width: 768px) {\n .nav-links {\n display: none;\n }\n \n h1 {\n font-size: 2rem;\n }\n \n .chat-container {\n padding: 1rem;\n }\n}\n\n---script.js---\ndocument.addEventListener('DOMContentLoaded', () => {\n const startButton = document.getElementById('start-recording');\n const stopButton = document.getElementById('stop-recording');\n const statusElement = document.getElementById('status');\n const chatMessages = document.getElementById('chat-messages');\n let mediaRecorder;\n let audioChunks = [];\n\n // Check for browser support\n if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {\n statusElement.textContent = 'Voice recording not supported in this browser';\n startButton.disabled = true;\n return;\n }\n\n startButton.addEventListener('click', async () => {\n try {\n const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n mediaRecorder = new MediaRecorder(stream);\n \n mediaRecorder.ondataavailable = (event) => {\n audioChunks.push(event.data);\n };\n\n mediaRecorder.onstop = async () => {\n const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });\n audioChunks = [];\n \n // Add user message to chat\n addMessageToChat('user', 'User audio recorded');\n \n // Simulate AI response (in real app, would send to backend)\n setTimeout(() => {\n addMessageToChat('ai', 'AI response would go here');\n }, 1000);\n };\n\n mediaRecorder.start();\n startButton.disabled = true;\n stopButton.disabled = false;\n statusElement.textContent = 'Recording... Click stop when finished.';\n } catch (err) {\n console.error('Error accessing microphone:', err);\n statusElement.textContent = 'Error accessing microphone';\n }\n });\n\n stopButton.addEventListener('click', () => {\n if (mediaRecorder && mediaRecorder.state !== 'inactive') {\n mediaRecorder.stop();\n startButton.disabled = false;\n stopButton.disabled = true;\n statusElement.textContent = 'Click microphone to start speaking';\n }\n });\n\n function addMessageToChat(sender, text) {\n const messageDiv = document.createElement('div');\n messageDiv.className = `message ${sender}`;\n messageDiv.textContent = `${sender === 'user' ? '👤' : '🤖'} ${text}`;\n chatMessages.appendChild(messageDiv);\n chatMessages.scrollTop = chatMessages.scrollHeight;\n }\n});\n\n---end---\n\nThis implementation creates a sleek, modern web app for language learning with voice chat capabilities. It features:\n\n1. Clean, semantic HTML structure\n2. Modern, responsive design with smooth animations\n3. Voice recording functionality\n4. Simulated AI chat interface\n5. Language selection dropdown\n6. Accessible controls and ARIA labels\n7. Mobile-responsive layout\n8. Professional styling with CSS variables\n9. Error handling for browser compatibility\n10. Clear user feedback and status messages\n\nNote that this is a frontend-only implementation. In a real application, you'd need to:\n1. Implement actual speech-to-text processing\n2. Add AI language model integration\n3. Handle audio streaming\n4. Add user authentication\n5. Implement proper error handling\n6. Add more language options\n7. Include actual AI responses\n\nThe design is modern and professional while remaining simple and intuitive to use." + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 294, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2348 + } +} \ No newline at end of file diff --git a/readme.scroll b/readme.scroll new file mode 100644 index 0000000..536ad16 --- /dev/null +++ b/readme.scroll @@ -0,0 +1,2 @@ +# speakai.scroll.pub +Website generated by Claude from prompt: create an app for language learner to practice speaking their target language with an ai partner. It should have a voice capability \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..85a79cb --- /dev/null +++ b/script.js @@ -0,0 +1,64 @@ +document.addEventListener('DOMContentLoaded', () => { + const startButton = document.getElementById('start-recording'); + const stopButton = document.getElementById('stop-recording'); + const statusElement = document.getElementById('status'); + const chatMessages = document.getElementById('chat-messages'); + let mediaRecorder; + let audioChunks = []; + + // Check for browser support + if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { + statusElement.textContent = 'Voice recording not supported in this browser'; + startButton.disabled = true; + return; + } + + startButton.addEventListener('click', async () => { + try { + const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + mediaRecorder = new MediaRecorder(stream); + + mediaRecorder.ondataavailable = (event) => { + audioChunks.push(event.data); + }; + + mediaRecorder.onstop = async () => { + const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); + audioChunks = []; + + // Add user message to chat + addMessageToChat('user', 'User audio recorded'); + + // Simulate AI response (in real app, would send to backend) + setTimeout(() => { + addMessageToChat('ai', 'AI response would go here'); + }, 1000); + }; + + mediaRecorder.start(); + startButton.disabled = true; + stopButton.disabled = false; + statusElement.textContent = 'Recording... Click stop when finished.'; + } catch (err) { + console.error('Error accessing microphone:', err); + statusElement.textContent = 'Error accessing microphone'; + } + }); + + stopButton.addEventListener('click', () => { + if (mediaRecorder && mediaRecorder.state !== 'inactive') { + mediaRecorder.stop(); + startButton.disabled = false; + stopButton.disabled = true; + statusElement.textContent = 'Click microphone to start speaking'; + } + }); + + function addMessageToChat(sender, text) { + const messageDiv = document.createElement('div'); + messageDiv.className = `message ${sender}`; + messageDiv.textContent = `${sender === 'user' ? '👤' : '🤖'} ${text}`; + chatMessages.appendChild(messageDiv); + chatMessages.scrollTop = chatMessages.scrollHeight; + } +}); diff --git a/style.css b/style.css new file mode 100644 index 0000000..54c9f03 --- /dev/null +++ b/style.css @@ -0,0 +1,160 @@ +:root { + --primary: #4a90e2; + --secondary: #2c3e50; + --accent: #e74c3c; + --background: #f5f6fa; + --text: #2c3e50; +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + line-height: 1.6; + color: var(--text); + background: var(--background); +} + +header { + background: white; + box-shadow: 0 2px 10px rgba(0,0,0,0.1); + position: fixed; + width: 100%; + top: 0; + z-index: 100; +} + +nav { + max-width: 1200px; + margin: 0 auto; + padding: 1rem; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 1.5rem; + font-weight: bold; + color: var(--primary); +} + +.nav-links a { + color: var(--secondary); + text-decoration: none; + margin-left: 2rem; + transition: color 0.3s ease; +} + +.nav-links a:hover { + color: var(--primary); +} + +main { + margin-top: 80px; + padding: 2rem; +} + +#hero { + text-align: center; + padding: 4rem 1rem; +} + +h1 { + font-size: 2.5rem; + margin-bottom: 1rem; + color: var(--secondary); +} + +.chat-container { + max-width: 800px; + margin: 2rem auto; + background: white; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0,0,0,0.1); + padding: 2rem; +} + +#chat-messages { + min-height: 300px; + margin-bottom: 1rem; + padding: 1rem; + border-radius: 8px; + background: var(--background); +} + +.controls { + display: flex; + align-items: center; + gap: 1rem; +} + +button { + border: none; + background: var(--primary); + color: white; + padding: 1rem; + border-radius: 50%; + cursor: pointer; + transition: transform 0.2s ease; +} + +button:hover { + transform: scale(1.1); +} + +button:disabled { + background: #ccc; + cursor: not-allowed; +} + +#status { + color: var(--secondary); +} + +.language-select { + margin-bottom: 2rem; + text-align: center; +} + +select { + padding: 0.5rem 1rem; + font-size: 1rem; + border-radius: 4px; + border: 1px solid var(--primary); +} + +footer { + background: var(--secondary); + color: white; + padding: 2rem; + margin-top: 4rem; +} + +footer nav { + justify-content: center; + gap: 2rem; +} + +footer a { + color: white; + text-decoration: none; +} + +@media (max-width: 768px) { + .nav-links { + display: none; + } + + h1 { + font-size: 2rem; + } + + .chat-container { + padding: 1rem; + } +}