all right I see that I can now click and put my cursor inside the transcript box so that looks promising and it lets me edit so that's perfect that's exactly what I wanted thank you I think you misunderstood me on the clear button I didn't mean the clear but shit I didn't mean the clear button above the transcript I was talking about the hot key for the clear button can we make that something that's further away from the hotkey for the start and stop recording please also I just realized that the transcript boxes good that I can click in it now but it's height is confined to only about the top inch of the trans of the actual like outline of the transcript box so I have like two lines of text and then a super small scroll bar to scroll through it and I'm just seeing like two lines of text at a time so if we could expand that to fill the transcript box like it was doing before that would be great and really those are the main those are the main things left yeah other than that it'll just be like a little refinements like if it's possible for me to like click on the hotkey buttons that are down there and select my own new combination or something but yeah that's also something that's not necessary it would be cool but it's not absolutely needed the main things that are top priority are expand the transcript to actually take up the full space of the transcript border and outline and hopefully at the very least change the position for the clear transcript hotkey but if you're able to do the suggestion that I said just then like if I can click on the hot Keys down there under the transcription and then select my own hot key for it on the Fly then I can just take care of that that would be great but yeah if that's a pain then just did the very least if you could please move it move the clear transcript hotkey to a further away position from the start and stop recording hotkey - Initial Deployment
0a0172a
verified
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Continuous Speech Transcriber</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: '#6366f1', | |
| secondary: '#8b5cf6', | |
| dark: '#1e293b', | |
| light: '#f8fafc' | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background: linear-gradient(135deg, #0f172a, #1e293b); | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| } | |
| .pulse { | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7); } | |
| 70% { box-shadow: 0 0 0 15px rgba(99, 102, 241, 0); } | |
| 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0); } | |
| } | |
| .wave { | |
| position: relative; | |
| } | |
| .wave::after { | |
| content: ""; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: radial-gradient(circle, rgba(139, 92, 246, 0.2) 0%, transparent 70%); | |
| border-radius: 50%; | |
| animation: wave 1.5s infinite; | |
| z-index: -1; | |
| } | |
| @keyframes wave { | |
| 0% { transform: scale(1); opacity: 1; } | |
| 100% { transform: scale(1.5); opacity: 0; } | |
| } | |
| .typing-cursor::after { | |
| content: "|"; | |
| animation: blink 1s infinite; | |
| } | |
| @keyframes blink { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0; } | |
| } | |
| .transcript-container { | |
| transition: all 0.3s ease; | |
| } | |
| .transcript-container:focus-within { | |
| box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.5); | |
| } | |
| .slide-in { | |
| animation: slideIn 0.3s ease-out forwards; | |
| } | |
| @keyframes slideIn { | |
| from { transform: translateY(20px); opacity: 0; } | |
| to { transform: translateY(0); opacity: 1; } | |
| } | |
| </style> | |
| </head> | |
| <body class="text-light flex flex-col min-h-screen"> | |
| <header class="py-6 px-4 md:px-8"> | |
| <div class="max-w-6xl mx-auto flex justify-between items-center"> | |
| <h1 class="text-2xl md:text-3xl font-bold flex items-center"> | |
| <i class="fas fa-microphone-alt mr-3 text-secondary"></i> | |
| Continuous Speech Transcriber | |
| </h1> | |
| <div class="flex items-center space-x-4"> | |
| <button id="help-btn" class="p-2 rounded-full hover:bg-slate-700 transition-colors"> | |
| <i class="fas fa-question-circle text-xl"></i> | |
| </button> | |
| <button id="theme-toggle" class="p-2 rounded-full hover:bg-slate-700 transition-colors"> | |
| <i class="fas fa-moon text-xl"></i> | |
| </button> | |
| </div> | |
| </div> | |
| </header> | |
| <main class="flex-grow px-4 pb-12"> | |
| <div class="max-w-4xl mx-auto"> | |
| <div class="bg-slate-800/50 backdrop-blur-lg rounded-2xl p-6 mb-8 shadow-xl"> | |
| <div class="flex flex-col items-center"> | |
| <div class="relative mb-8"> | |
| <button id="record-btn" class="wave w-24 h-24 rounded-full bg-gradient-to-br from-primary to-secondary flex items-center justify-center text-white text-3xl transition-all hover:scale-105"> | |
| <i class="fas fa-microphone"></i> | |
| </button> | |
| </div> | |
| <div class="w-full mb-6"> | |
| <div class="flex justify-between items-center mb-2"> | |
| <h2 class="text-lg font-semibold">Transcript</h2> | |
| <div class="flex space-x-4"> | |
| <button id="copy-btn" class="px-3 py-1 bg-slate-700 rounded-lg hover:bg-slate-600 transition-colors"> | |
| <i class="fas fa-copy mr-1"></i> Copy | |
| </button> | |
| <button id="clear-btn" class="px-3 py-1 bg-red-700/50 rounded-lg hover:bg-red-600/50 transition-colors ml-4"> | |
| <i class="fas fa-trash mr-1"></i> Clear | |
| </button> | |
| </div> | |
| </div> | |
| <div class="transcript-container bg-slate-800 rounded-xl p-4 min-h-[300px] overflow-y-auto"> | |
| <textarea id="transcript" class="w-full h-[300px] bg-transparent text-slate-200 resize-none outline-none" placeholder="Your transcript will appear here..."></textarea> | |
| </div> | |
| </div> | |
| <div class="w-full"> | |
| <button id="save-btn" class="slide-in bg-gradient-to-r from-cyan-600 to-blue-600 py-3 px-6 rounded-xl font-medium flex items-center justify-center space-x-2 hover:opacity-90 transition-opacity mx-auto"> | |
| <i class="fas fa-save"></i> | |
| <span>Save Transcript</span> | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6"> | |
| <div class="bg-slate-800/50 backdrop-blur-lg rounded-2xl p-6 shadow-xl"> | |
| <div class="flex items-center mb-4"> | |
| <div class="w-10 h-10 rounded-full bg-indigo-500 flex items-center justify-center mr-3"> | |
| <i class="fas fa-keyboard"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold">Keyboard Shortcuts</h3> | |
| </div> | |
| <ul class="space-y-2"> | |
| <li class="flex justify-between"> | |
| <span>Start/Stop Recording</span> | |
| <kbd class="px-2 py-1 bg-slate-700 rounded-md">Num +</kbd> | |
| </li> | |
| <li class="flex justify-between"> | |
| <span>Clear Transcript</span> | |
| <div class="flex space-x-1"> | |
| <kbd class="px-2 py-1 bg-slate-700 rounded-md">Ctrl</kbd> | |
| <kbd class="px-2 py-1 bg-slate-700 rounded-md">Shift</kbd> | |
| <kbd class="px-2 py-1 bg-slate-700 rounded-md">Backspace</kbd> | |
| </div> | |
| </li> | |
| </ul> | |
| </div> | |
| <div class="bg-slate-800/50 backdrop-blur-lg rounded-2xl p-6 shadow-xl"> | |
| <div class="flex items-center mb-4"> | |
| <div class="w-10 h-10 rounded-full bg-purple-500 flex items-center justify-center mr-3"> | |
| <i class="fas fa-info-circle"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold">Status</h3> | |
| </div> | |
| <div class="space-y-3"> | |
| <div class="flex justify-between"> | |
| <span>Recording Status:</span> | |
| <span id="status" class="px-2 py-1 bg-slate-700 rounded-md">Ready</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span>Speech Recognition:</span> | |
| <span id="recognition-support" class="px-2 py-1 bg-green-500/20 text-green-400 rounded-md">Supported</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span>Transcript Length:</span> | |
| <span id="transcript-length" class="px-2 py-1 bg-slate-700 rounded-md">0 words</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="bg-slate-800/50 backdrop-blur-lg rounded-2xl p-6 shadow-xl"> | |
| <div class="flex items-center mb-4"> | |
| <div class="w-10 h-10 rounded-full bg-cyan-500 flex items-center justify-center mr-3"> | |
| <i class="fas fa-cog"></i> | |
| </div> | |
| <h3 class="text-lg font-semibold">Settings</h3> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm mb-1">Language</label> | |
| <select class="w-full bg-slate-800 border border-slate-600 rounded-lg p-2"> | |
| <option>English (US)</option> | |
| <option>English (UK)</option> | |
| <option>Spanish</option> | |
| <option>French</option> | |
| <option>German</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" class="rounded bg-slate-800 border-slate-600"> | |
| <span>Auto punctuation</span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <div id="help-modal" class="fixed inset-0 bg-black/70 z-50 flex items-center justify-center hidden"> | |
| <div class="bg-slate-800 rounded-2xl w-full max-w-2xl mx-4 p-6"> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-bold">How to Use</h2> | |
| <button id="close-help" class="text-2xl hover:text-primary">×</button> | |
| </div> | |
| <div class="space-y-4"> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2">Continuous Speech Recognition</h3> | |
| <p>This app solves the problem of speech recognition cutting off during pauses:</p> | |
| <ul class="list-disc pl-5 mt-2 space-y-1"> | |
| <li>Click the microphone button or press <kbd>Ctrl+Space</kbd> to start recording</li> | |
| <li>Speak naturally - the app will continue listening even during long pauses</li> | |
| <li>Click the microphone button again or press <kbd>Ctrl+Space</kbd> to stop</li> | |
| <li>Your transcript will appear in the text area</li> | |
| </ul> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-semibold mb-2">Gemini Integration</h3> | |
| <p>To send your transcript to Gemini:</p> | |
| <ul class="list-disc pl-5 mt-2 space-y-1"> | |
| <li>After stopping the recording, click "Send to Gemini" or press <kbd>Ctrl+Enter</kbd></li> | |
| <li>The app will copy your transcript to the clipboard</li> | |
| <li>Paste it into your Gemini command line interface</li> | |
| </ul> | |
| </div> | |
| <div class="pt-4"> | |
| <p class="text-sm text-slate-400">Note: This app runs entirely in your browser - no data is sent to any servers.</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Elements | |
| const recordBtn = document.getElementById('record-btn'); | |
| const geminiBtn = document.getElementById('gemini-btn'); | |
| const clearBtn = document.getElementById('clear-btn'); | |
| const copyBtn = document.getElementById('copy-btn'); | |
| const saveBtn = document.getElementById('save-btn'); | |
| const transcriptEl = document.getElementById('transcript'); | |
| const placeholderText = document.getElementById('placeholder-text'); | |
| const statusEl = document.getElementById('status'); | |
| const transcriptLength = document.getElementById('transcript-length'); | |
| const helpBtn = document.getElementById('help-btn'); | |
| const helpModal = document.getElementById('help-modal'); | |
| const closeHelp = document.getElementById('close-help'); | |
| const themeToggle = document.getElementById('theme-toggle'); | |
| // Speech recognition setup | |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| let recognition = null; | |
| let isRecording = false; | |
| let finalTranscript = ''; | |
| // Check for speech recognition support | |
| if (SpeechRecognition) { | |
| recognition = new SpeechRecognition(); | |
| recognition.continuous = true; | |
| recognition.interimResults = true; | |
| recognition.lang = 'en-US'; | |
| recognition.onresult = function(event) { | |
| let interimTranscript = ''; | |
| for (let i = event.resultIndex; i < event.results.length; i++) { | |
| const transcript = event.results[i][0].transcript; | |
| if (event.results[i].isFinal) { | |
| finalTranscript += transcript + ' '; | |
| } else { | |
| interimTranscript += transcript; | |
| } | |
| } | |
| updateTranscript(finalTranscript, interimTranscript); | |
| }; | |
| recognition.onerror = function(event) { | |
| console.error('Speech recognition error', event.error); | |
| stopRecording(); | |
| statusEl.textContent = 'Error: ' + event.error; | |
| }; | |
| recognition.onend = function() { | |
| if (isRecording) { | |
| recognition.start(); | |
| } | |
| }; | |
| } else { | |
| document.getElementById('recognition-support').textContent = 'Not Supported'; | |
| document.getElementById('recognition-support').className = 'px-2 py-1 bg-red-500/20 text-red-400 rounded-md'; | |
| recordBtn.disabled = true; | |
| statusEl.textContent = 'Speech API not supported'; | |
| } | |
| // Update transcript display | |
| function updateTranscript(final, interim) { | |
| transcriptEl.value = final; | |
| if (interim) { | |
| transcriptEl.value += interim; | |
| } | |
| // Update word count | |
| const wordCount = final.trim() === '' ? 0 : final.trim().split(/\s+/).length; | |
| transcriptLength.textContent = `${wordCount} words`; | |
| } | |
| // Start recording | |
| function startRecording() { | |
| if (!recognition) return; | |
| // Get current transcript value | |
| finalTranscript = transcriptEl.value; | |
| interimTranscript = ''; | |
| try { | |
| recognition.start(); | |
| isRecording = true; | |
| recordBtn.classList.add('pulse'); | |
| recordBtn.innerHTML = '<i class="fas fa-stop"></i>'; | |
| statusEl.textContent = 'Listening...'; | |
| statusEl.className = 'px-2 py-1 bg-green-500/20 text-green-400 rounded-md'; | |
| } catch (error) { | |
| console.error('Error starting recognition:', error); | |
| statusEl.textContent = 'Error starting'; | |
| statusEl.className = 'px-2 py-1 bg-red-500/20 text-red-400 rounded-md'; | |
| } | |
| } | |
| // Stop recording | |
| function stopRecording() { | |
| if (!recognition) return; | |
| recognition.stop(); | |
| isRecording = false; | |
| recordBtn.classList.remove('pulse'); | |
| recordBtn.innerHTML = '<i class="fas fa-microphone"></i>'; | |
| statusEl.textContent = 'Ready'; | |
| statusEl.className = 'px-2 py-1 bg-slate-700 rounded-md'; | |
| } | |
| // Toggle recording | |
| function toggleRecording() { | |
| if (isRecording) { | |
| stopRecording(); | |
| } else { | |
| startRecording(); | |
| } | |
| } | |
| // Copy transcript to clipboard | |
| function copyTranscript() { | |
| navigator.clipboard.writeText(transcriptEl.value).then(() => { | |
| // Show feedback | |
| const originalText = copyBtn.innerHTML; | |
| copyBtn.innerHTML = '<i class="fas fa-check"></i> Copied!'; | |
| setTimeout(() => { | |
| copyBtn.innerHTML = originalText; | |
| }, 2000); | |
| }); | |
| } | |
| // Save transcript as file | |
| function saveTranscript() { | |
| const blob = new Blob([finalTranscript], { type: 'text/plain' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'transcript.txt'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| setTimeout(() => { | |
| document.body.removeChild(a); | |
| URL.revokeObjectURL(url); | |
| }, 100); | |
| } | |
| // Clear transcript | |
| function clearTranscript() { | |
| if (confirm('Are you sure you want to clear the transcript?')) { | |
| finalTranscript = ''; | |
| transcriptEl.value = ''; | |
| } | |
| } | |
| // Keyboard shortcuts | |
| document.addEventListener('keydown', function(e) { | |
| // Numpad + to toggle recording | |
| if (e.code === 'NumpadAdd') { | |
| e.preventDefault(); | |
| toggleRecording(); | |
| } | |
| // Ctrl+Shift+Backspace to clear transcript | |
| if (e.ctrlKey && e.shiftKey && e.code === 'Backspace') { | |
| e.preventDefault(); | |
| clearTranscript(); | |
| } | |
| }); | |
| // Event listeners | |
| recordBtn.addEventListener('click', toggleRecording); | |
| geminiBtn.addEventListener('click', sendToGemini); | |
| clearBtn.addEventListener('click', clearTranscript); | |
| copyBtn.addEventListener('click', copyTranscript); | |
| saveBtn.addEventListener('click', saveTranscript); | |
| helpBtn.addEventListener('click', () => { | |
| helpModal.classList.remove('hidden'); | |
| }); | |
| closeHelp.addEventListener('click', () => { | |
| helpModal.classList.add('hidden'); | |
| }); | |
| helpModal.addEventListener('click', (e) => { | |
| if (e.target === helpModal) { | |
| helpModal.classList.add('hidden'); | |
| } | |
| }); | |
| // Theme toggle | |
| themeToggle.addEventListener('click', () => { | |
| const icon = themeToggle.querySelector('i'); | |
| if (document.documentElement.classList.contains('dark')) { | |
| document.documentElement.classList.remove('dark'); | |
| document.documentElement.classList.add('light'); | |
| icon.classList.replace('fa-moon', 'fa-sun'); | |
| } else { | |
| document.documentElement.classList.remove('light'); | |
| document.documentElement.classList.add('dark'); | |
| icon.classList.replace('fa-sun', 'fa-moon'); | |
| } | |
| }); | |
| // Initialize | |
| updateTranscript('', ''); | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Bosovond/continuous-speech-transcriber-tool" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |