Spaces:
Sleeping
Sleeping
| <html> | |
| <head> | |
| <title>Continuous Speech Input</title> | |
| <style> | |
| body { | |
| font-family: sans-serif; | |
| padding: 20px; | |
| max-width: 800px; | |
| margin: 0 auto; | |
| } | |
| button { | |
| padding: 10px 20px; | |
| margin: 10px 5px; | |
| font-size: 16px; | |
| border: none; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| color: white; | |
| } | |
| #start { background: #ff4b4b; } | |
| #stop { background: #4b4bff; } | |
| #clear { background: #666; } | |
| #status { | |
| margin: 10px 0; | |
| padding: 10px; | |
| background: #e8f5e9; | |
| border-radius: 4px; | |
| } | |
| #output { | |
| white-space: pre-wrap; | |
| padding: 15px; | |
| background: #f5f5f5; | |
| border-radius: 4px; | |
| margin: 10px 0; | |
| min-height: 100px; | |
| max-height: 400px; | |
| overflow-y: auto; | |
| } | |
| .controls { | |
| margin: 10px 0; | |
| display: flex; | |
| gap: 10px; | |
| } | |
| button:disabled { | |
| opacity: 0.6; | |
| cursor: not-allowed; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <input id="myinput" value="" style="display: none;" /> | |
| <div class="controls"> | |
| <button id="start">Start Listening</button> | |
| <button id="stop" disabled>Stop Listening</button> | |
| <button id="clear">Clear Text</button> | |
| </div> | |
| <div id="status">Ready to record...</div> | |
| <div id="output"></div> | |
| <input type="hidden" id="streamlit-data" value=""> | |
| <script> | |
| if (!('webkitSpeechRecognition' in window)) { | |
| document.getElementById('status').textContent = 'Speech recognition not supported in this browser'; | |
| } else { | |
| const recognition = new webkitSpeechRecognition(); | |
| const startButton = document.getElementById('start'); | |
| const stopButton = document.getElementById('stop'); | |
| const clearButton = document.getElementById('clear'); | |
| const status = document.getElementById('status'); | |
| const output = document.getElementById('output'); | |
| let fullTranscript = ''; | |
| let lastUpdateTime = Date.now(); | |
| recognition.continuous = true; | |
| recognition.interimResults = true; | |
| const startRecognition = () => { | |
| try { | |
| recognition.start(); | |
| status.textContent = 'Listening...'; | |
| startButton.disabled = true; | |
| stopButton.disabled = false; | |
| } catch (e) { | |
| console.error(e); | |
| status.textContent = 'Error: ' + e.message; | |
| } | |
| }; | |
| // Auto-start on load | |
| window.addEventListener('load', () => { | |
| setTimeout(startRecognition, 1000); | |
| }); | |
| startButton.onclick = startRecognition; | |
| stopButton.onclick = () => { | |
| recognition.stop(); | |
| status.textContent = 'Stopped'; | |
| startButton.disabled = false; | |
| stopButton.disabled = true; | |
| }; | |
| clearButton.onclick = () => { | |
| fullTranscript = ''; | |
| output.textContent = ''; | |
| document.getElementById('streamlit-data').value = ''; | |
| sendDataToPython({ | |
| value: '', | |
| dataType: "json", | |
| }); | |
| }; | |
| recognition.onresult = (event) => { | |
| let interimTranscript = ''; | |
| let finalTranscript = ''; | |
| for (let i = event.resultIndex; i < event.results.length; i++) { | |
| const transcript = event.results[i][0].transcript; | |
| if (event.results[i].isFinal) { | |
| finalTranscript += transcript + '\n'; | |
| } else { | |
| interimTranscript += transcript; | |
| } | |
| } | |
| if (finalTranscript || (Date.now() - lastUpdateTime > 5000)) { | |
| if (finalTranscript) { | |
| fullTranscript += finalTranscript; | |
| document.getElementById('streamlit-data').value = fullTranscript; | |
| } | |
| lastUpdateTime = Date.now(); | |
| } | |
| output.textContent = fullTranscript + (interimTranscript ? '... ' + interimTranscript : ''); | |
| output.scrollTop = output.scrollHeight; | |
| sendDataToPython({ | |
| value: fullTranscript, | |
| dataType: "json", | |
| }); | |
| }; | |
| recognition.onend = () => { | |
| if (!stopButton.disabled) { | |
| try { | |
| recognition.start(); | |
| console.log('Restarted recognition'); | |
| } catch (e) { | |
| console.error('Failed to restart:', e); | |
| status.textContent = 'Error restarting: ' + e.message; | |
| startButton.disabled = false; | |
| stopButton.disabled = true; | |
| } | |
| } | |
| }; | |
| recognition.onerror = (event) => { | |
| console.error('Recognition error:', event.error); | |
| status.textContent = 'Error: ' + event.error; | |
| if (event.error === 'not-allowed' || event.error === 'service-not-allowed') { | |
| startButton.disabled = false; | |
| stopButton.disabled = true; | |
| } | |
| }; | |
| } | |
| // Streamlit communication functions | |
| function sendMessageToStreamlitClient(type, data) { | |
| var outData = Object.assign({ | |
| isStreamlitMessage: true, | |
| type: type, | |
| }, data); | |
| window.parent.postMessage(outData, "*"); | |
| } | |
| function init() { | |
| sendMessageToStreamlitClient("streamlit:componentReady", {apiVersion: 1}); | |
| } | |
| function setFrameHeight(height) { | |
| sendMessageToStreamlitClient("streamlit:setFrameHeight", {height: height}); | |
| } | |
| function sendDataToPython(data) { | |
| sendMessageToStreamlitClient("streamlit:setComponentValue", data); | |
| } | |
| function onDataFromPython(event) { | |
| if (event.data.type !== "streamlit:render") return; | |
| document.getElementById("myinput").value = event.data.args.my_input_value; | |
| } | |
| window.addEventListener("message", onDataFromPython); | |
| init(); | |
| window.addEventListener("load", function() { | |
| window.setTimeout(function() { | |
| setFrameHeight(document.documentElement.clientHeight) | |
| }, 0); | |
| }); | |
| setFrameHeight(400); // Fixed height for better visibility | |
| </script> | |
| </body> | |
| </html> |