sp/src/pages/web-tools/annyang-js.astro

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>