@lakshmiprasanth/react-voice-to-text
Version:
A modern React package for voice-to-text conversion with real-time speech recognition and file upload support
315 lines (285 loc) âĸ 16.5 kB
JavaScript
'use strict';
var jsxRuntime = require('react/jsx-runtime');
var react = require('react');
/**
* Simple EventEmitter implementation for browser compatibility
*/
/**
* Error types that can occur during speech recognition
*/
var SpeechRecognitionErrorType;
(function (SpeechRecognitionErrorType) {
SpeechRecognitionErrorType["NO_SPEECH"] = "no-speech";
SpeechRecognitionErrorType["ABORTED"] = "aborted";
SpeechRecognitionErrorType["AUDIO_CAPTURE"] = "audio-capture";
SpeechRecognitionErrorType["NETWORK"] = "network";
SpeechRecognitionErrorType["NOT_ALLOWED"] = "not-allowed";
SpeechRecognitionErrorType["SERVICE_NOT_ALLOWED"] = "service-not-allowed";
SpeechRecognitionErrorType["BAD_GRAMMAR"] = "bad-grammar";
SpeechRecognitionErrorType["LANGUAGE_NOT_SUPPORTED"] = "language-not-supported";
SpeechRecognitionErrorType["ENGINE_ERROR"] = "engine-error";
SpeechRecognitionErrorType["INVALID_CONFIG"] = "invalid-config";
})(SpeechRecognitionErrorType || (SpeechRecognitionErrorType = {}));
const VoiceToTextContext = react.createContext(null);
const useVoiceToTextContext = () => {
const context = react.useContext(VoiceToTextContext);
if (!context) {
throw new Error('useVoiceToTextContext must be used within a VoiceToTextProvider');
}
return context;
};
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var classnames = {exports: {}};
/*!
Copyright (c) 2018 Jed Watson.
Licensed under the MIT License (MIT), see
http://jedwatson.github.io/classnames
*/
(function (module) {
/* global define */
(function () {
var hasOwn = {}.hasOwnProperty;
function classNames () {
var classes = '';
for (var i = 0; i < arguments.length; i++) {
var arg = arguments[i];
if (arg) {
classes = appendClass(classes, parseValue(arg));
}
}
return classes;
}
function parseValue (arg) {
if (typeof arg === 'string' || typeof arg === 'number') {
return arg;
}
if (typeof arg !== 'object') {
return '';
}
if (Array.isArray(arg)) {
return classNames.apply(null, arg);
}
if (arg.toString !== Object.prototype.toString && !arg.toString.toString().includes('[native code]')) {
return arg.toString();
}
var classes = '';
for (var key in arg) {
if (hasOwn.call(arg, key) && arg[key]) {
classes = appendClass(classes, key);
}
}
return classes;
}
function appendClass (value, newClass) {
if (!newClass) {
return value;
}
if (value) {
return value + ' ' + newClass;
}
return value + newClass;
}
if (module.exports) {
classNames.default = classNames;
module.exports = classNames;
} else {
window.classNames = classNames;
}
}());
} (classnames));
var classnamesExports = classnames.exports;
var classNames = /*@__PURE__*/getDefaultExportFromCjs(classnamesExports);
const SUPPORTED_LANGUAGES = [
{ code: 'en-US', name: 'English (US)' },
{ code: 'en-GB', name: 'English (UK)' },
{ code: 'es-ES', name: 'Spanish' },
{ code: 'fr-FR', name: 'French' },
{ code: 'de-DE', name: 'German' },
{ code: 'it-IT', name: 'Italian' },
{ code: 'pt-BR', name: 'Portuguese (Brazil)' },
{ code: 'ja-JP', name: 'Japanese' },
{ code: 'ko-KR', name: 'Korean' },
{ code: 'zh-CN', name: 'Chinese (Simplified)' },
{ code: 'zh-TW', name: 'Chinese (Traditional)' },
{ code: 'ru-RU', name: 'Russian' },
{ code: 'ar-SA', name: 'Arabic' },
{ code: 'hi-IN', name: 'Hindi' },
{ code: 'th-TH', name: 'Thai' },
{ code: 'vi-VN', name: 'Vietnamese' },
{ code: 'tr-TR', name: 'Turkish' },
{ code: 'pl-PL', name: 'Polish' },
{ code: 'nl-NL', name: 'Dutch' },
{ code: 'sv-SE', name: 'Swedish' },
{ code: 'da-DK', name: 'Danish' },
{ code: 'no-NO', name: 'Norwegian' },
{ code: 'fi-FI', name: 'Finnish' },
{ code: 'cs-CZ', name: 'Czech' },
{ code: 'hu-HU', name: 'Hungarian' },
{ code: 'ro-RO', name: 'Romanian' },
{ code: 'bg-BG', name: 'Bulgarian' },
{ code: 'hr-HR', name: 'Croatian' },
{ code: 'sk-SK', name: 'Slovak' },
{ code: 'sl-SI', name: 'Slovenian' },
{ code: 'et-EE', name: 'Estonian' },
{ code: 'lv-LV', name: 'Latvian' },
{ code: 'lt-LT', name: 'Lithuanian' },
{ code: 'mt-MT', name: 'Maltese' },
{ code: 'el-GR', name: 'Greek' },
{ code: 'he-IL', name: 'Hebrew' },
{ code: 'id-ID', name: 'Indonesian' },
{ code: 'ms-MY', name: 'Malay' },
{ code: 'fil-PH', name: 'Filipino' },
{ code: 'uk-UA', name: 'Ukrainian' },
{ code: 'be-BY', name: 'Belarusian' },
{ code: 'kk-KZ', name: 'Kazakh' },
{ code: 'ky-KG', name: 'Kyrgyz' },
{ code: 'uz-UZ', name: 'Uzbek' },
{ code: 'mn-MN', name: 'Mongolian' },
{ code: 'ka-GE', name: 'Georgian' },
{ code: 'hy-AM', name: 'Armenian' },
{ code: 'az-AZ', name: 'Azerbaijani' },
{ code: 'fa-IR', name: 'Persian' },
{ code: 'ur-PK', name: 'Urdu' },
{ code: 'bn-BD', name: 'Bengali' },
{ code: 'si-LK', name: 'Sinhala' },
{ code: 'my-MM', name: 'Burmese' },
{ code: 'km-KH', name: 'Khmer' },
{ code: 'lo-LA', name: 'Lao' },
{ code: 'ne-NP', name: 'Nepali' },
{ code: 'gu-IN', name: 'Gujarati' },
{ code: 'pa-IN', name: 'Punjabi' },
{ code: 'te-IN', name: 'Telugu' },
{ code: 'kn-IN', name: 'Kannada' },
{ code: 'ml-IN', name: 'Malayalam' },
{ code: 'ta-IN', name: 'Tamil' },
{ code: 'mr-IN', name: 'Marathi' },
{ code: 'or-IN', name: 'Odia' },
{ code: 'as-IN', name: 'Assamese' },
{ code: 'sa-IN', name: 'Sanskrit' },
{ code: 'am-ET', name: 'Amharic' },
{ code: 'sw-KE', name: 'Swahili' },
{ code: 'yo-NG', name: 'Yoruba' },
{ code: 'ig-NG', name: 'Igbo' },
{ code: 'ha-NG', name: 'Hausa' },
{ code: 'zu-ZA', name: 'Zulu' },
{ code: 'af-ZA', name: 'Afrikaans' },
{ code: 'xh-ZA', name: 'Xhosa' },
{ code: 'st-ZA', name: 'Southern Sotho' },
{ code: 'tn-ZA', name: 'Tswana' },
{ code: 'ss-ZA', name: 'Swati' },
{ code: 've-ZA', name: 'Venda' },
{ code: 'ts-ZA', name: 'Tsonga' },
{ code: 'nr-ZA', name: 'Southern Ndebele' },
{ code: 'nd-ZA', name: 'Northern Ndebele' }
];
const LanguageSelector = ({ value = 'en-US', onChange, disabled = false, className, showLabel = true, label = 'Language', placeholder = 'Select language...', ...props }) => {
const handleChange = (event) => {
onChange?.(event.target.value);
};
return (jsxRuntime.jsxs("div", { className: classNames('language-selector', className), ...props, children: [showLabel && (jsxRuntime.jsx("label", { className: "language-selector__label", children: label })), jsxRuntime.jsxs("select", { value: value, onChange: handleChange, disabled: disabled, className: "language-selector__select", children: [jsxRuntime.jsx("option", { value: "", disabled: true, children: placeholder }), SUPPORTED_LANGUAGES.map((language) => (jsxRuntime.jsx("option", { value: language.code, children: language.name }, language.code)))] })] }));
};
const RecordingControls = ({ isRecording, isProcessing, onStart, onStop, disabled = false, className, showStatus = true, startText = 'đ¤ Start Recording', stopText = 'âšī¸ Stop Recording', processingText = 'đ Processing...', ...props }) => {
const handleStart = () => {
if (!disabled && !isRecording && !isProcessing) {
onStart?.();
}
};
const handleStop = () => {
if (!disabled && isRecording && !isProcessing) {
onStop?.();
}
};
const getStatusText = () => {
if (isProcessing)
return processingText;
if (isRecording)
return 'đ´ Recording...';
return 'Ready to record';
};
const getStatusColor = () => {
if (isProcessing)
return '#f39c12';
if (isRecording)
return '#e74c3c';
return '#27ae60';
};
return (jsxRuntime.jsxs("div", { className: classNames('recording-controls', className), ...props, children: [jsxRuntime.jsx("div", { className: "recording-controls__buttons", children: !isRecording ? (jsxRuntime.jsx("button", { onClick: handleStart, disabled: disabled || isProcessing, className: classNames('recording-controls__button', 'recording-controls__button--start', {
'recording-controls__button--disabled': disabled || isProcessing
}), children: startText })) : (jsxRuntime.jsx("button", { onClick: handleStop, disabled: disabled || isProcessing, className: classNames('recording-controls__button', 'recording-controls__button--stop', {
'recording-controls__button--disabled': disabled || isProcessing
}), children: stopText })) }), showStatus && (jsxRuntime.jsxs("div", { className: "recording-controls__status", children: [jsxRuntime.jsx("div", { className: "recording-controls__status-indicator", style: { backgroundColor: getStatusColor() } }), jsxRuntime.jsx("span", { className: "recording-controls__status-text", children: getStatusText() })] }))] }));
};
const ResultsDisplay = ({ results, error, onClear, showClearButton = true, showConfidence = true, showStatus = true, maxResults = 50, className, emptyText = 'No results yet. Start recording or convert a file to see results.', ...props }) => {
const handleClear = () => {
onClear?.();
};
const formatConfidence = (confidence) => {
return `${(confidence * 100).toFixed(1)}%`;
};
const getStatusIcon = (isFinal) => {
return isFinal ? 'â
' : 'âŗ';
};
const getStatusText = (isFinal) => {
return isFinal ? 'Final' : 'Interim';
};
const getStatusColor = (isFinal) => {
return isFinal ? '#27ae60' : '#f39c12';
};
const displayedResults = results.slice(-maxResults);
if (error) {
return (jsxRuntime.jsxs("div", { className: classNames('results-display', 'results-display--error', className), ...props, children: [jsxRuntime.jsxs("div", { className: "results-display__header", children: [jsxRuntime.jsx("h3", { children: "\u274C Error" }), showClearButton && (jsxRuntime.jsx("button", { onClick: handleClear, className: "results-display__clear-button", children: "Clear" }))] }), jsxRuntime.jsx("div", { className: "results-display__error", children: jsxRuntime.jsx("p", { children: error }) })] }));
}
if (results.length === 0) {
return (jsxRuntime.jsx("div", { className: classNames('results-display', 'results-display--empty', className), ...props, children: jsxRuntime.jsxs("div", { className: "results-display__empty", children: [jsxRuntime.jsx("div", { className: "results-display__empty-icon", children: "\uD83D\uDCDD" }), jsxRuntime.jsx("h3", { children: "Results" }), jsxRuntime.jsx("p", { children: emptyText })] }) }));
}
return (jsxRuntime.jsxs("div", { className: classNames('results-display', className), ...props, children: [jsxRuntime.jsxs("div", { className: "results-display__header", children: [jsxRuntime.jsxs("h3", { children: ["\uD83D\uDCDD Results (", results.length, ")"] }), showClearButton && (jsxRuntime.jsx("button", { onClick: handleClear, className: "results-display__clear-button", children: "Clear All" }))] }), jsxRuntime.jsxs("div", { className: "results-display__content", children: [jsxRuntime.jsx("div", { className: "results-display__list", children: displayedResults.map((result, index) => (jsxRuntime.jsx("div", { className: classNames('results-display__item', {
'results-display__item--final': result.isFinal,
'results-display__item--interim': !result.isFinal
}), children: jsxRuntime.jsxs("div", { className: "results-display__item-content", children: [jsxRuntime.jsx("div", { className: "results-display__text", children: result.transcript }), jsxRuntime.jsxs("div", { className: "results-display__meta", children: [showStatus && (jsxRuntime.jsxs("div", { className: "results-display__status", style: { color: getStatusColor(result.isFinal) }, children: [jsxRuntime.jsx("span", { className: "results-display__status-icon", children: getStatusIcon(result.isFinal) }), jsxRuntime.jsx("span", { className: "results-display__status-text", children: getStatusText(result.isFinal) })] })), showConfidence && (jsxRuntime.jsxs("div", { className: "results-display__confidence", children: ["Confidence: ", formatConfidence(result.confidence)] })), jsxRuntime.jsxs("div", { className: "results-display__index", children: ["#", results.length - displayedResults.length + index + 1] })] })] }) }, `${result.transcript}-${index}`))) }), results.length > maxResults && (jsxRuntime.jsx("div", { className: "results-display__overflow", children: jsxRuntime.jsxs("p", { children: ["Showing last ", maxResults, " results of ", results.length, " total"] }) }))] })] }));
};
const VoiceRecorder = ({ className, showLanguageSelector = true, showResults = true, showControls = true, language = 'en-US', continuous = true, interimResults = true, onResult: _onResult, onError, onStart, onStop, children, ...props }) => {
const { isInitialized, isRecording, isProcessing, results, error, startRecording, stopRecording, clearResults, clearError } = useVoiceToTextContext();
const [selectedLanguage, setSelectedLanguage] = react.useState(language);
const handleStartRecording = async () => {
try {
await startRecording({
language: selectedLanguage,
continuous,
interimResults
});
onStart?.();
}
catch (err) {
onError?.(err instanceof Error ? err.message : 'Failed to start recording');
}
};
const handleStopRecording = async () => {
try {
await stopRecording();
onStop?.();
}
catch (err) {
onError?.(err instanceof Error ? err.message : 'Failed to stop recording');
}
};
const handleLanguageChange = (newLanguage) => {
setSelectedLanguage(newLanguage);
};
const handleClearResults = () => {
clearResults();
};
const handleClearError = () => {
clearError();
};
if (!isInitialized) {
return (jsxRuntime.jsx("div", { className: classNames('voice-recorder', 'voice-recorder--loading', className), ...props, children: jsxRuntime.jsxs("div", { className: "voice-recorder__status", children: [jsxRuntime.jsx("div", { className: "voice-recorder__loading-spinner" }), jsxRuntime.jsx("p", { children: "Initializing voice recognition..." })] }) }));
}
if (error) {
return (jsxRuntime.jsx("div", { className: classNames('voice-recorder', 'voice-recorder--error', className), ...props, children: jsxRuntime.jsxs("div", { className: "voice-recorder__error", children: [jsxRuntime.jsx("h3", { children: "\u274C Error" }), jsxRuntime.jsx("p", { children: error }), jsxRuntime.jsx("button", { onClick: handleClearError, className: "voice-recorder__error-button", children: "Try Again" })] }) }));
}
return (jsxRuntime.jsxs("div", { className: classNames('voice-recorder', className), ...props, children: [jsxRuntime.jsxs("div", { className: "voice-recorder__header", children: [jsxRuntime.jsx("h2", { children: "\uD83C\uDFA4 Voice Recorder" }), jsxRuntime.jsx("p", { children: "Record your voice and see real-time transcription" })] }), jsxRuntime.jsxs("div", { className: "voice-recorder__content", children: [showLanguageSelector && (jsxRuntime.jsx("div", { className: "voice-recorder__language", children: jsxRuntime.jsx(LanguageSelector, { value: selectedLanguage, onChange: handleLanguageChange, disabled: isRecording || isProcessing }) })), showControls && (jsxRuntime.jsx("div", { className: "voice-recorder__controls", children: jsxRuntime.jsx(RecordingControls, { isRecording: isRecording, isProcessing: isProcessing, onStart: handleStartRecording, onStop: handleStopRecording, disabled: !isInitialized }) })), showResults && (jsxRuntime.jsx("div", { className: "voice-recorder__results", children: jsxRuntime.jsx(ResultsDisplay, { results: results, error: error, onClear: handleClearResults, showClearButton: results.length > 0 }) })), children && (jsxRuntime.jsx("div", { className: "voice-recorder__children", children: children }))] })] }));
};
exports.VoiceRecorder = VoiceRecorder;
//# sourceMappingURL=VoiceRecorder.js.map