200 lines
8.1 KiB
Plaintext
200 lines
8.1 KiB
Plaintext
---
|
|
import Layout from "../../layouts/Layout.astro"
|
|
---
|
|
<Layout title="">
|
|
<div class=" min-h-screen flex items-center justify-center p-4">
|
|
<div class="w-full max-w-2xl bg-white rounded-lg shadow-lg overflow-hidden">
|
|
<div class="bg-blue-600 p-4 text-white">
|
|
<h1 class="text-2xl font-bold">Real-Time Speech Transcription</h1>
|
|
<p class="text-blue-100">Using Annyang.js for speech recognition</p>
|
|
</div>
|
|
<div class="p-6">
|
|
<div class="mb-6">
|
|
<div class="flex items-center justify-between mb-2">
|
|
<h2 class="text-lg font-semibold text-gray-800">Transcription</h2>
|
|
<div id="status" class="px-3 py-1 rounded-full text-sm font-medium bg-gray-200 text-gray-800">
|
|
Not Listening
|
|
</div>
|
|
</div>
|
|
<div id="transcript" class="h-64 p-4 border border-gray-300 rounded-lg overflow-y-auto bg-gray-50">
|
|
<p class="text-gray-500 italic">Your transcribed text will appear here...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-col sm:flex-row gap-4">
|
|
<button id="startBtn" class="flex-1 bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-4 rounded-lg transition">
|
|
Start Listening
|
|
</button>
|
|
<button id="stopBtn" class="flex-1 bg-red-600 hover:bg-red-700 text-white font-bold py-3 px-4 rounded-lg transition" disabled>
|
|
Stop Listening
|
|
</button>
|
|
<button id="clearBtn" class="flex-1 bg-gray-600 hover:bg-gray-700 text-white font-bold py-3 px-4 rounded-lg transition">
|
|
Clear Text
|
|
</button>
|
|
</div>
|
|
|
|
<div class="mt-6">
|
|
<label class="block text-sm font-medium text-gray-700 mb-2">Available Commands</label>
|
|
<div class="bg-gray-50 p-3 rounded-lg border border-gray-200">
|
|
<ul class="list-disc pl-5 text-sm text-gray-600">
|
|
<li>"clear transcript" - Clears the transcription</li>
|
|
<li>"new paragraph" - Inserts a new paragraph</li>
|
|
<li>"period", "comma", "question mark" - Adds punctuation</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</Layout>
|
|
<script is:inline src="https://cdnjs.cloudflare.com/ajax/libs/annyang/2.6.1/annyang.min.js"></script>
|
|
<script is:inline>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const startBtn = document.getElementById('startBtn');
|
|
const stopBtn = document.getElementById('stopBtn');
|
|
const clearBtn = document.getElementById('clearBtn');
|
|
const transcript = document.getElementById('transcript');
|
|
const status = document.getElementById('status');
|
|
|
|
// Check if browser supports speech recognition
|
|
if (!annyang) {
|
|
status.textContent = "Speech recognition not supported";
|
|
startBtn.disabled = true;
|
|
return;
|
|
}
|
|
|
|
// Add commands
|
|
const commands = {
|
|
'*text': function(phrase) {
|
|
appendToTranscript(phrase);
|
|
},
|
|
'clear transcript': function() {
|
|
clearTranscript();
|
|
},
|
|
'new paragraph': function() {
|
|
appendToTranscript("\n\n");
|
|
},
|
|
'period': function() {
|
|
appendToTranscript(". ");
|
|
},
|
|
'comma': function() {
|
|
appendToTranscript(", ");
|
|
},
|
|
'question mark': function() {
|
|
appendToTranscript("? ");
|
|
},
|
|
'exclamation mark': function() {
|
|
appendToTranscript("! ");
|
|
}
|
|
};
|
|
|
|
annyang.addCommands(commands);
|
|
|
|
// Event listeners
|
|
startBtn.addEventListener('click', function() {
|
|
annyang.start({
|
|
autoRestart: true,
|
|
continuous: true
|
|
});
|
|
status.textContent = "Listening...";
|
|
status.className = "px-3 py-1 rounded-full text-sm font-medium bg-green-200 text-green-800";
|
|
startBtn.disabled = true;
|
|
stopBtn.disabled = false;
|
|
});
|
|
|
|
stopBtn.addEventListener('click', function() {
|
|
try {
|
|
annyang.abort();
|
|
} catch (e) {
|
|
console.log("Stopping recognition", e);
|
|
}
|
|
status.textContent = "Not Listening";
|
|
status.className = "px-3 py-1 rounded-full text-sm font-medium bg-gray-200 text-gray-800";
|
|
startBtn.disabled = true;
|
|
stopBtn.disabled = true;
|
|
|
|
setTimeout(() => {
|
|
startBtn.disabled = false;
|
|
}, 500);
|
|
});
|
|
|
|
clearBtn.addEventListener('click', clearTranscript);
|
|
|
|
// Callbacks
|
|
annyang.addCallback('start', function() {
|
|
console.log("Speech recognition started");
|
|
});
|
|
|
|
annyang.addCallback('soundstart', function() {
|
|
console.log("User is speaking");
|
|
});
|
|
|
|
annyang.addCallback('end', function() {
|
|
console.log("Speech recognition ended");
|
|
});
|
|
|
|
annyang.addCallback('error', function(err) {
|
|
if (err.error === 'aborted') {
|
|
console.log("Recognition stopped manually");
|
|
return;
|
|
}
|
|
|
|
console.error("Error:", err);
|
|
status.textContent = "Error occurred";
|
|
status.className = "px-3 py-1 rounded-full text-sm font-medium bg-red-200 text-red-800";
|
|
startBtn.disabled = false;
|
|
stopBtn.disabled = true;
|
|
});
|
|
|
|
annyang.addCallback('errorNetwork', function(err) {
|
|
console.error("Network error:", err);
|
|
status.textContent = "Network error";
|
|
status.className = "px-3 py-1 rounded-full text-sm font-medium bg-red-200 text-red-800";
|
|
startBtn.disabled = false;
|
|
stopBtn.disabled = true;
|
|
});
|
|
|
|
annyang.addCallback('errorPermissionBlocked', function() {
|
|
console.error("Permission blocked");
|
|
status.textContent = "Permission blocked";
|
|
status.className = "px-3 py-1 rounded-full text-sm font-medium bg-red-200 text-red-800";
|
|
startBtn.disabled = true;
|
|
stopBtn.disabled = true;
|
|
});
|
|
|
|
annyang.addCallback('errorPermissionDenied', function() {
|
|
console.error("Permission denied");
|
|
status.textContent = "Permission denied";
|
|
status.className = "px-3 py-1 rounded-full text-sm font-medium bg-red-200 text-red-800";
|
|
startBtn.disabled = true;
|
|
stopBtn.disabled = true;
|
|
});
|
|
|
|
// Helper functions
|
|
function appendToTranscript(text) {
|
|
if (transcript.innerHTML.includes("Your transcribed text will appear here...")) {
|
|
transcript.innerHTML = '';
|
|
}
|
|
|
|
if (text === "\n\n") {
|
|
transcript.innerHTML += '<p class="mt-4"></p>';
|
|
} else {
|
|
let paragraphs = transcript.querySelectorAll('p');
|
|
let lastParagraph = paragraphs.length > 0 ? paragraphs[paragraphs.length - 1] : null;
|
|
|
|
if (!lastParagraph) {
|
|
lastParagraph = document.createElement('p');
|
|
transcript.appendChild(lastParagraph);
|
|
}
|
|
|
|
lastParagraph.textContent += text;
|
|
}
|
|
|
|
transcript.scrollTop = transcript.scrollHeight;
|
|
}
|
|
|
|
function clearTranscript() {
|
|
transcript.innerHTML = '<p class="text-gray-500 italic">Your transcribed text will appear here...</p>';
|
|
}
|
|
});
|
|
</script> |