UNPKG

@lakshmiprasanth/react-voice-to-text

Version:

A modern React package for voice-to-text conversion with real-time speech recognition and file upload support

2,208 lines â€ĸ 85.6 kB
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var jsxRuntime = require('react/jsx-runtime');
var react = require('react');

/**
 * Simple EventEmitter implementation for browser compatibility
 */
class EventEmitter {
    constructor() {
        this.events = {};
    }
    on(event, listener) {
        if (!this.events[event]) {
            this.events[event] = [];
        }
        this.events[event].push(listener);
        return this;
    }
    once(event, listener) {
        const onceWrapper = (...args) => {
            this.off(event, onceWrapper);
            listener.apply(this, args);
        };
        return this.on(event, onceWrapper);
    }
    off(event, listener) {
        if (!this.events[event])
            return this;
        this.events[event] = this.events[event].filter(l => l !== listener);
        return this;
    }
    emit(event, ...args) {
        if (!this.events[event])
            return false;
        this.events[event].forEach(listener => {
            try {
                listener.apply(this, args);
            }
            catch (error) {
                console.error('Error in event listener:', error);
            }
        });
        return true;
    }
    removeAllListeners(event) {
        if (event) {
            delete this.events[event];
        }
        else {
            this.events = {};
        }
        return this;
    }
}
/**
 * Error types that can occur during speech recognition
 */
exports.SpeechRecognitionErrorType = void 0;
(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";
})(exports.SpeechRecognitionErrorType || (exports.SpeechRecognitionErrorType = {}));
/**
 * Speech recognition error
 */
class SpeechRecognitionError extends Error {
    constructor(type, message, originalError) {
        super(message);
        this.type = type;
        this.originalError = originalError;
        this.name = 'SpeechRecognitionError';
    }
}

/**
 * Base class for speech recognition engines
 */
class BaseSpeechRecognitionEngine extends EventEmitter {
    constructor() {
        super();
        this.isRecording = false;
        this.config = {};
    }
    /**
     * Validate configuration
     */
    validateConfig(config) {
        if (config.confidenceThreshold && (config.confidenceThreshold < 0 || config.confidenceThreshold > 1)) {
            throw new SpeechRecognitionError(exports.SpeechRecognitionErrorType.INVALID_CONFIG, 'Confidence threshold must be between 0 and 1');
        }
        if (config.maxAlternatives && config.maxAlternatives < 1) {
            throw new SpeechRecognitionError(exports.SpeechRecognitionErrorType.INVALID_CONFIG, 'Max alternatives must be at least 1');
        }
        if (config.sampleRate && config.sampleRate < 8000) {
            throw new SpeechRecognitionError(exports.SpeechRecognitionErrorType.INVALID_CONFIG, 'Sample rate must be at least 8000 Hz');
        }
    }
    /**
     * Validate audio input configuration
     */
    validateAudioConfig(audioConfig) {
        if (!audioConfig.source) {
            throw new SpeechRecognitionError(exports.SpeechRecognitionErrorType.INVALID_CONFIG, 'Audio source must be specified');
        }
        if (audioConfig.source === 'file' && !audioConfig.filePath) {
            throw new SpeechRecognitionError(exports.SpeechRecognitionErrorType.INVALID_CONFIG, 'File path must be specified for file source');
        }
        if (audioConfig.source === 'stream' && !audioConfig.audioStream) {
            throw new SpeechRecognitionError(exports.SpeechRecognitionErrorType.INVALID_CONFIG, 'Audio stream must be specified for stream source');
        }
    }
    /**
     * Emit error event with proper error handling
     */
    emitError(type, message, originalError) {
        const error = new SpeechRecognitionError(type, message, originalError);
        this.emit('error', error);
    }
    /**
     * Emit result event with validation
     */
    emitResult(result) {
        // Validate result
        if (!result.transcript) {
            return; // Skip empty results
        }
        // Apply confidence threshold if configured
        if (this.config.confidenceThreshold && result.confidence < this.config.confidenceThreshold) {
            return; // Skip low-confidence results
        }
        this.emit('result', result);
    }
    /**
     * Get default configuration merged with provided config
     */
    getConfig(config) {
        return {
            language: 'en-US',
            sampleRate: 16000,
            continuous: false,
            interimResults: false,
            maxAlternatives: 1,
            confidenceThreshold: 0.0,
            encoding: 'LINEAR16',
            ...this.config,
            ...config
        };
    }
    /**
     * Check if currently recording
     */
    get isActive() {
        return this.isRecording;
    }
    /**
     * Set recording state
     */
    setRecordingState(recording) {
        this.isRecording = recording;
    }
    /**
     * Clean up resources
     */
    cleanup() {
        this.removeAllListeners();
        this.isRecording = false;
    }
    /**
     * Type-safe event emitter methods
     */
    emit(event, ...args) {
        return super.emit(event, ...args);
    }
    on(event, listener) {
        return super.on(event, listener);
    }
    once(event, listener) {
        return super.once(event, listener);
    }
    off(event, listener) {
        return super.off(event, listener);
    }
}

/**
 * Web Speech API engine for browser environments
 */
class WebSpeechEngine extends BaseSpeechRecognitionEngine {
    constructor() {
        super();
        this.recognition = null;
        this.mediaRecorder = null;
        this.audioChunks = [];
    }
    /**
     * Check if Web Speech API is available
     */
    isAvailable() {
        if (typeof window === 'undefined') {
            return false; // Not in browser environment
        }
        return !!(window.SpeechRecognition || window.webkitSpeechRecognition);
    }
    /**
     * Get supported languages (common languages supported by most browsers)
     */
    getSupportedLanguages() {
        return [
            'en-US', 'en-GB', 'en-AU', 'en-CA', 'en-IN', 'en-NZ', 'en-ZA',
            'es-ES', 'es-MX', 'es-AR', 'es-CO', 'es-CL', 'es-PE', 'es-VE',
            'fr-FR', 'fr-CA', 'fr-CH', 'fr-BE',
            'de-DE', 'de-AT', 'de-CH',
            'it-IT', 'it-CH',
            'pt-BR', 'pt-PT',
            'ru-RU',
            'ja-JP',
            'ko-KR',
            'zh-CN', 'zh-TW', 'zh-HK',
            'ar-SA', 'ar-EG',
            'hi-IN',
            'th-TH',
            'tr-TR',
            'pl-PL',
            'nl-NL', 'nl-BE',
            'sv-SE',
            'da-DK',
            'no-NO',
            'fi-FI'
        ];
    }
    /**
     * Initialize the speech recognition engine
     */
    async initialize(_config) {
        if (!this.isAvailable()) {
            throw new Error('Web Speech API is not available in this environment');
        }
        this.config = this.getConfig(_config);
        this.validateConfig(this.config);
        try {
            await this.initializeRecognition();
            this.emit('initialized');
        }
        catch (error) {
            this.emitError(exports.SpeechRecognitionErrorType.ENGINE_ERROR, `Failed to initialize Web Speech recognition: ${error instanceof Error ? error.message : 'Unknown error'}`, error instanceof Error ? error : undefined);
        }
    }
    /**
     * Start listening for speech
     */
    async startListening(config) {
        if (!this.recognition) {
            throw new Error('Speech recognition not initialized. Call initialize() first.');
        }
        if (this.isRecording) {
            await this.stopListening();
        }
        try {
            this.recognition.start();
            this.isRecording = true;
            this.emit('started');
        }
        catch (error) {
            this.emitError(exports.SpeechRecognitionErrorType.ENGINE_ERROR, `Failed to start listening: ${error instanceof Error ? error.message : 'Unknown error'}`, error instanceof Error ? error : undefined);
        }
    }
    /**
     * Stop listening for speech
     */
    async stopListening() {
        if (!this.recognition) {
            return;
        }
        try {
            this.recognition.stop();
            this.isRecording = false;
            this.emit('stopped');
        }
        catch (error) {
            this.emitError(exports.SpeechRecognitionErrorType.ENGINE_ERROR, `Failed to stop listening: ${error instanceof Error ? error.message : 'Unknown error'}`, error instanceof Error ? error : undefined);
        }
    }
    /**
     * Start speech recognition
     */
    async start(audioConfig, recognitionConfig) {
        if (!this.isAvailable()) {
            throw new Error('Web Speech API is not available in this environment');
        }
        this.validateAudioConfig(audioConfig);
        this.config = this.getConfig(recognitionConfig);
        this.validateConfig(this.config);
        if (this.isRecording) {
            await this.stop();
        }
        try {
            await this.initializeRecognition();
            if (audioConfig.source === 'microphone') {
                await this.startMicrophoneRecognition(audioConfig);
            }
            else if (audioConfig.source === 'file') {
                await this.processAudioFile(audioConfig.filePath);
            }
            else {
                throw new Error('Web Speech API only supports microphone and file input');
            }
        }
        catch (error) {
            this.emitError(exports.SpeechRecognitionErrorType.ENGINE_ERROR, `Failed to start Web Speech recognition: ${error instanceof Error ? error.message : 'Unknown error'}`, error instanceof Error ? error : undefined);
        }
    }
    /**
     * Initialize speech recognition instance
     */
    async initializeRecognition() {
        const SpeechRecognitionClass = window.SpeechRecognition || window.webkitSpeechRecognition;
        this.recognition = new SpeechRecognitionClass();
        // Configure recognition
        this.recognition.continuous = this.config.continuous || false;
        this.recognition.interimResults = this.config.interimResults || false;
        this.recognition.lang = this.config.language || 'en-US';
        this.recognition.maxAlternatives = this.config.maxAlternatives || 1;
        // Set up event handlers
        this.recognition.onstart = () => {
            this.setRecordingState(true);
            this.emit('start');
            this.emit('audiostart');
        };
        this.recognition.onend = () => {
            this.setRecordingState(false);
            this.emit('audioend');
            this.emit('end');
        };
        this.recognition.onresult = (event) => {
            for (let i = event.resultIndex; i < event.results.length; i++) {
                const result = event.results[i];
                const alternative = result[0];
                const speechResult = {
                    transcript: alternative.transcript,
                    confidence: alternative.confidence,
                    isFinal: result.isFinal,
                    alternatives: Array.from(result).map((alt) => ({
                        transcript: alt.transcript,
                        confidence: alt.confidence
                    }))
                };
                this.emitResult(speechResult);
            }
        };
        this.recognition.onerror = (event) => {
            let errorType;
            switch (event.error) {
                case 'no-speech':
                    errorType = exports.SpeechRecognitionErrorType.NO_SPEECH;
                    break;
                case 'aborted':
                    errorType = exports.SpeechRecognitionErrorType.ABORTED;
                    break;
                case 'audio-capture':
                    errorType = exports.SpeechRecognitionErrorType.AUDIO_CAPTURE;
                    break;
                case 'network':
                    errorType = exports.SpeechRecognitionErrorType.NETWORK;
                    break;
                case 'not-allowed':
                    errorType = exports.SpeechRecognitionErrorType.NOT_ALLOWED;
                    break;
                case 'service-not-allowed':
                    errorType = exports.SpeechRecognitionErrorType.SERVICE_NOT_ALLOWED;
                    break;
                case 'bad-grammar':
                    errorType = exports.SpeechRecognitionErrorType.BAD_GRAMMAR;
                    break;
                case 'language-not-supported':
                    errorType = exports.SpeechRecognitionErrorType.LANGUAGE_NOT_SUPPORTED;
                    break;
                default:
                    errorType = exports.SpeechRecognitionErrorType.ENGINE_ERROR;
            }
            this.emitError(errorType, `Web Speech API error: ${event.error}`);
        };
        this.recognition.onspeechstart = () => this.emit('speechstart');
        this.recognition.onspeechend = () => this.emit('speechend');
        this.recognition.onsoundstart = () => this.emit('soundstart');
        this.recognition.onsoundend = () => this.emit('soundend');
    }
    /**
     * Start microphone recognition
     */
    async startMicrophoneRecognition(audioConfig) {
        if (!this.recognition) {
            throw new Error('Recognition not initialized');
        }
        try {
            // Request microphone permission and start recognition
            const stream = await navigator.mediaDevices.getUserMedia({
                audio: {
                    deviceId: audioConfig.deviceId ? { exact: audioConfig.deviceId } : undefined,
                    sampleRate: this.config.sampleRate || 16000
                }
            });
            // Start recognition
            this.recognition.start();
            // If duration is specified, stop after that time
            if (audioConfig.duration) {
                setTimeout(() => {
                    this.stop().catch(console.error);
                }, audioConfig.duration);
            }
            // Clean up stream when recognition ends
            this.recognition.onend = () => {
                stream.getTracks().forEach(track => track.stop());
                this.setRecordingState(false);
                this.emit('audioend');
                this.emit('end');
            };
        }
        catch (error) {
            this.emitError(exports.SpeechRecognitionErrorType.AUDIO_CAPTURE, `Failed to access microphone: ${error instanceof Error ? error.message : 'Unknown error'}`, error instanceof Error ? error : undefined);
        }
    }
    /**
     * Process audio file (requires MediaRecorder API)
     */
    async processAudioFile(_filePath) {
        // Note: Direct file processing with Web Speech API is limited
        // This is a simplified implementation that would need additional audio processing
        throw new Error('File processing with Web Speech API requires additional audio processing libraries');
    }
    /**
     * Stop speech recognition
     */
    async stop() {
        if (this.recognition && this.isRecording) {
            this.recognition.stop();
        }
        if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {
            this.mediaRecorder.stop();
        }
        this.setRecordingState(false);
    }
    /**
     * Abort speech recognition
     */
    async abort() {
        if (this.recognition) {
            this.recognition.abort();
        }
        if (this.mediaRecorder && this.mediaRecorder.state === 'recording') {
            this.mediaRecorder.stop();
        }
        this.setRecordingState(false);
    }
    /**
     * Process audio file directly (not supported by Web Speech API)
     */
    async processFile(_file, _config) {
        throw new Error('Direct file processing is not supported by Web Speech API. Use start() with file source instead.');
    }
    /**
     * Process audio stream (not supported by Web Speech API)
     */
    async processStream(_stream, _config) {
        throw new Error('Direct stream processing is not supported by Web Speech API. Use start() with stream source instead.');
    }
    /**
     * Get available audio input devices
     */
    static async getAudioDevices() {
        if (typeof navigator === 'undefined' || !navigator.mediaDevices) {
            return [];
        }
        try {
            const devices = await navigator.mediaDevices.enumerateDevices();
            return devices.filter(device => device.kind === 'audioinput');
        }
        catch (error) {
            console.error('Failed to enumerate audio devices:', error);
            return [];
        }
    }
    /**
     * Check browser compatibility
     */
    static getBrowserSupport() {
        const features = [];
        let supported = false;
        if (typeof window !== 'undefined') {
            if (window.SpeechRecognition || window.webkitSpeechRecognition) {
                features.push('SpeechRecognition');
                supported = true;
            }
            if (navigator.mediaDevices && typeof navigator.mediaDevices.getUserMedia === 'function') {
                features.push('getUserMedia');
            }
            if (window.MediaRecorder) {
                features.push('MediaRecorder');
            }
        }
        return { supported, features };
    }
}

/**
 * Check browser support for speech recognition and related APIs
 */
function getBrowserSupport() {
    if (typeof window === 'undefined') {
        return {
            webSpeechAPI: false,
            mediaRecorder: false,
            getUserMedia: false,
            browser: {
                name: 'unknown',
                version: 'unknown'
            }
        };
    }
    const browserInfo = getBrowserInfo();
    return {
        webSpeechAPI: 'SpeechRecognition' in window || 'webkitSpeechRecognition' in window,
        mediaRecorder: 'MediaRecorder' in window,
        getUserMedia: 'mediaDevices' in navigator && 'getUserMedia' in navigator.mediaDevices,
        browser: browserInfo
    };
}
/**
 * Get the SpeechRecognition constructor for the current browser
 */
function getSpeechRecognition() {
    if (typeof window === 'undefined') {
        return null;
    }
    return (window.SpeechRecognition ||
        window.webkitSpeechRecognition ||
        null);
}
/**
 * Check if speech recognition is supported in the current browser
 */
function isSpeechRecognitionSupported() {
    const support = getBrowserSupport();
    return support.webSpeechAPI;
}
/**
 * Get available audio input devices
 */
async function getAudioDevices() {
    if (typeof navigator === 'undefined' || !navigator.mediaDevices) {
        return [];
    }
    try {
        const devices = await navigator.mediaDevices.enumerateDevices();
        return devices.filter(device => device.kind === 'audioinput');
    }
    catch (error) {
        console.error('Failed to enumerate audio devices:', error);
        return [];
    }
}
/**
 * Request microphone permission
 */
async function requestMicrophonePermission() {
    if (typeof navigator === 'undefined' || !navigator.mediaDevices) {
        return false;
    }
    try {
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        // Stop the stream immediately as we only needed permission
        stream.getTracks().forEach(track => track.stop());
        return true;
    }
    catch (error) {
        console.error('Microphone permission denied:', error);
        return false;
    }
}
/**
 * Check if the current environment is a browser
 */
function isBrowser() {
    return typeof window !== 'undefined' && typeof document !== 'undefined';
}
/**
 * Check if the current environment is a secure context (HTTPS)
 */
function isSecureContext() {
    if (typeof window === 'undefined') {
        return false;
    }
    return window.isSecureContext || false;
}
/**
 * Get browser name and version
 */
function getBrowserInfo() {
    if (typeof navigator === 'undefined') {
        return { name: 'unknown', version: 'unknown' };
    }
    const userAgent = navigator.userAgent;
    let name = 'unknown';
    let version = 'unknown';
    // Chrome
    if (userAgent.includes('Chrome') && !userAgent.includes('Edg')) {
        name = 'Chrome';
        const match = userAgent.match(/Chrome\/(\d+)/);
        if (match)
            version = match[1];
    }
    // Safari
    else if (userAgent.includes('Safari') && !userAgent.includes('Chrome')) {
        name = 'Safari';
        const match = userAgent.match(/Version\/(\d+)/);
        if (match)
            version = match[1];
    }
    // Firefox
    else if (userAgent.includes('Firefox')) {
        name = 'Firefox';
        const match = userAgent.match(/Firefox\/(\d+)/);
        if (match)
            version = match[1];
    }
    // Edge
    else if (userAgent.includes('Edg')) {
        name = 'Edge';
        const match = userAgent.match(/Edg\/(\d+)/);
        if (match)
            version = match[1];
    }
    return { name, version };
}

const VoiceToTextContext = react.createContext(null);
const VoiceToTextProvider = ({ children, options = {}, onResult, onError, onStart, onStop }) => {
    const [isInitialized, setIsInitialized] = react.useState(false);
    const [isRecording, setIsRecording] = react.useState(false);
    const [isProcessing, setIsProcessing] = react.useState(false);
    const [results, setResults] = react.useState([]);
    const [error, setError] = react.useState(null);
    const [browserSupport, setBrowserSupport] = react.useState(null);
    const [config, setConfig] = react.useState({
        defaultRecognitionConfig: {
            language: 'en-US',
            continuous: true,
            interimResults: true
        },
        debug: false,
        ...options
    });
    const [engine, setEngine] = react.useState(null);
    react.useEffect(() => {
        const initialize = async () => {
            try {
                // Check browser support
                const support = getBrowserSupport();
                setBrowserSupport(support);
                if (!support.webSpeechAPI) {
                    throw new Error('Speech recognition is not supported in this browser');
                }
                // Initialize Web Speech engine
                const speechEngine = new WebSpeechEngine();
                await speechEngine.initialize(config.defaultRecognitionConfig);
                // Set up event listeners
                speechEngine.on('result', (result) => {
                    setResults(prev => [...prev, result]);
                    onResult?.(result);
                });
                speechEngine.on('error', (error) => {
                    const errorMessage = error.message || 'Speech recognition error';
                    setError(errorMessage);
                    onError?.(errorMessage);
                });
                speechEngine.on('start', () => {
                    setIsRecording(true);
                    onStart?.();
                });
                speechEngine.on('end', () => {
                    setIsRecording(false);
                    onStop?.();
                });
                setEngine(speechEngine);
                setIsInitialized(true);
            }
            catch (err) {
                const errorMessage = err instanceof Error ? err.message : 'Failed to initialize voice recognition';
                setError(errorMessage);
                onError?.(errorMessage);
            }
        };
        initialize();
        // Cleanup
        return () => {
            if (engine) {
                engine.cleanup();
            }
        };
    }, []);
    const startRecording = async (recordingOptions) => {
        if (!engine || !isInitialized) {
            throw new Error('Voice recognition not initialized');
        }
        try {
            setIsProcessing(true);
            setError(null);
            setResults([]);
            const finalConfig = {
                ...config.defaultRecognitionConfig,
                ...recordingOptions
            };
            await engine.startListening(finalConfig);
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : 'Failed to start recording';
            setError(errorMessage);
            onError?.(errorMessage);
            throw err;
        }
        finally {
            setIsProcessing(false);
        }
    };
    const stopRecording = async () => {
        if (!engine) {
            throw new Error('Voice recognition not initialized');
        }
        try {
            setIsProcessing(true);
            await engine.stopListening();
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : 'Failed to stop recording';
            setError(errorMessage);
            onError?.(errorMessage);
            throw err;
        }
        finally {
            setIsProcessing(false);
        }
    };
    const clearResults = () => {
        setResults([]);
    };
    const clearError = () => {
        setError(null);
    };
    const updateConfig = (newConfig) => {
        setConfig(prev => ({ ...prev, ...newConfig }));
    };
    const getConfig = () => config;
    const contextValue = {
        isInitialized,
        isRecording,
        isProcessing,
        results,
        error,
        browserSupport,
        startRecording,
        stopRecording,
        clearResults,
        clearError,
        updateConfig,
        getConfig
    };
    return (jsxRuntime.jsx(VoiceToTextContext.Provider, { value: contextValue, children: children }));
};
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 }))] })] }));
};

const FileUpload = ({ className, onFileSelect, onConvert, onError, acceptedFormats = ['audio/*'], maxFileSize = 50 * 1024 * 1024, // 50MB
showLanguageSelector = true, language = 'en-US', dragAndDrop = true, multiple = false, disabled = false, children, ...props }) => {
    const [selectedFile, setSelectedFile] = react.useState(null);
    const [isConverting, setIsConverting] = react.useState(false);
    const [isDragOver, setIsDragOver] = react.useState(false);
    const [selectedLanguage, setSelectedLanguage] = react.useState(language);
    const fileInputRef = react.useRef(null);
    const handleFileSelect = (file) => {
        // Validate file size
        if (file.size > maxFileSize) {
            const error = `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds maximum allowed size (${(maxFileSize / 1024 / 1024).toFixed(2)}MB)`;
            onError?.(error);
            return;
        }
        // Validate file type
        const isValidType = acceptedFormats.some(format => {
            if (format === 'audio/*') {
                return file.type.startsWith('audio/');
            }
            return file.type === format;
        });
        if (!isValidType) {
            const error = `File type ${file.type} is not supported. Please select an audio file.`;
            onError?.(error);
            return;
        }
        setSelectedFile(file);
        onFileSelect?.(file);
    };
    const handleFileInputChange = (event) => {
        const files = event.target.files;
        if (files && files.length > 0) {
            handleFileSelect(files[0]);
        }
    };
    const handleDragOver = (event) => {
        event.preventDefault();
        setIsDragOver(true);
    };
    const handleDragLeave = (event) => {
        event.preventDefault();
        setIsDragOver(false);
    };
    const handleDrop = (event) => {
        event.preventDefault();
        setIsDragOver(false);
        const files = event.dataTransfer.files;
        if (files && files.length > 0) {
            handleFileSelect(files[0]);
        }
    };
    const handleConvert = async () => {
        if (!selectedFile)
            return;
        setIsConverting(true);
        try {
            await onConvert?.(selectedFile, selectedLanguage);
        }
        catch (error) {
            onError?.(error instanceof Error ? error.message : 'Failed to convert file');
        }
        finally {
            setIsConverting(false);
        }
    };
    const handleClearFile = () => {
        setSelectedFile(null);
        if (fileInputRef.current) {
            fileInputRef.current.value = '';
        }
    };
    const handleLanguageChange = (newLanguage) => {
        setSelectedLanguage(newLanguage);
    };
    const openFileDialog = () => {
        fileInputRef.current?.click();
    };
    const formatFileSize = (bytes) => {
        if (bytes === 0)
            return '0 Bytes';
        const k = 1024;
        const sizes = ['Bytes', 'KB', 'MB', 'GB'];
        const i = Math.floor(Math.log(bytes) / Math.log(k));
        return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    };
    return (jsxRuntime.jsxs("div", { className: classNames('file-upload', className), ...props, children: [jsxRuntime.jsxs("div", { className: "file-upload__header", children: [jsxRuntime.jsx("h3", { children: "\uD83D\uDCC1 File Upload & Convert" }), jsxRuntime.jsx("p", { children: "Upload an audio file to convert speech to text" })] }), jsxRuntime.jsxs("div", { className: "file-upload__content", children: [showLanguageSelector && (jsxRuntime.jsx("div", { className: "file-upload__language", children: jsxRuntime.jsx(LanguageSelector, { value: selectedLanguage, onChange: handleLanguageChange, disabled: disabled || isConverting }) })), jsxRuntime.jsxs("div", { className: "file-upload__upload-area", children: [dragAndDrop ? (jsxRuntime.jsx("div", { className: classNames('file-upload__drop-zone', {
                                    'file-upload__drop-zone--drag-over': isDragOver,
                                    'file-upload__drop-zone--disabled': disabled
                                }), onDragOver: handleDragOver, onDragLeave: handleDragLeave, onDrop: handleDrop, onClick: !disabled ? openFileDialog : undefined, children: jsxRuntime.jsxs("div", { className: "file-upload__drop-zone-content", children: [jsxRuntime.jsx("div", { className: "file-upload__icon", children: "\uD83D\uDCC1" }), jsxRuntime.jsx("p", { className: "file-upload__drop-text", children: isDragOver ? 'Drop your audio file here' : 'Click to select or drag & drop audio file' }), jsxRuntime.jsx("p", { className: "file-upload__drop-hint", children: "Supported formats: MP3, WAV, OGG, FLAC, M4A" }), jsxRuntime.jsxs("p", { className: "file-upload__drop-hint", children: ["Max size: ", formatFileSize(maxFileSize)] })] }) })) : (jsxRuntime.jsxs("div", { className: "file-upload__input-wrapper", children: [jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", accept: acceptedFormats.join(','), onChange: handleFileInputChange, disabled: disabled, multiple: multiple, className: "file-upload__input" }), jsxRuntime.jsx("button", { onClick: openFileDialog, disabled: disabled, className: "file-upload__select-button", children: "Select Audio File" })] })), jsxRuntime.jsx("input", { ref: fileInputRef, type: "file", accept: acceptedFormats.join(','), onChange: handleFileInputChange, disabled: disabled, multiple: multiple, className: "file-upload__hidden-input" })] }), selectedFile && (jsxRuntime.jsxs("div", { className: "file-upload__file-info", children: [jsxRuntime.jsxs("div", { className: "file-upload__file-details", children: [jsxRuntime.jsx("div", { className: "file-upload__file-name", children: selectedFile.name }), jsxRuntime.jsx("div", { className: "file-upload__file-size", children: formatFileSize(selectedFile.size) })] }), jsxRuntime.jsx("button", { onClick: handleClearFile, disabled: isConverting, className: "file-upload__clear-button", children: "\u2715" })] })), jsxRuntime.jsx("div", { className: "file-upload__controls", children: jsxRuntime.jsx("button", { onClick: handleConvert, disabled: !selectedFile || disabled || isConverting, className: "file-upload__convert-button", children: isConverting ? '🔄 Converting...' : '🔄 Convert File' }) }), children && (jsxRuntime.jsx("div", { className: "file-upload__children", children: children }))] })] }));
};

const VoiceToTextConverter = ({ className, showFileUpload = true, showVoiceRecorder = true, showResults = true, defaultLanguage = 'en-US', onResult, onError, onStart, onStop, children }) => {
    const [activeTab, setActiveTab] = react.useState('recorder');
    const [fileResults, setFileResults] = react.useState([]);
    const handleFileConvert = async (file, _language) => {
        try {
            // For file conversion, we'll use a simple approach with Web Speech API
            // In a real implementation, you might want to use a different service
            // const audio = new Audio(URL.createObjectURL(file));
            // Note: This is a simplified implementation
            // Real file conversion would require a backend service or different approach
            console.log('File conversion not fully implemented in this demo');
            // For demo purposes, we'll create a mock result
            const mockResult = {
                transcript: `Converted audio file: ${file.name}`,
                confidence: 0.95,
                isFinal: true,
                timestamp: { start: Date.now(), end: Date.now() }
            };
            setFileResults([mockResult]);
            onResult?.(mockResult);
        }
        catch (error) {
            const errorMessage = error instanceof Error ? error.message : 'Failed to convert file';
            onError?.(errorMessage);
        }
    };
    const handleFileError = (error) => {
        onError?.(error);
    };
    return (jsxRuntime.jsx(VoiceToTextProvider, { options: {
            defaultRecognitionConfig: {
                language: defaultLanguage,
                continuous: true,
                interimResults: true
            }
        }, onResult: onResult, onError: onError, onStart: onStart, onStop: onStop, children: jsxRuntime.jsxs("div", { className: classNames('voice-to-text-converter', className), children: [jsxRuntime.jsxs("div", { className: "voice-to-text-converter__header", children: [jsxRuntime.jsx("h1", { children: "\uD83C\uDFA4 Voice-to-Text Converter" }), jsxRuntime.jsx("p", { children: "Convert speech to text in real-time or from audio files" })] }), jsxRuntime.jsxs("div", { className: "voice-to-text-converter__content", children: [showFileUpload && showVoiceRecorder && (jsxRuntime.jsxs("div", { className: "voice-to-text-converter__tabs", children: [jsxRuntime.jsx("button", { onClick: () => setActiveTab('recorder'), className: classNames('voice-to-text-converter__tab', {
                                        'voice-to-text-converter__tab--active': activeTab === 'recorder'
                                    }), children: "\uD83C\uDF99\uFE0F Voice Recorder" }), jsxRuntime.jsx("button", { onClick: () => setActiveTab('upload'), className: classNames('voice-to-text-converter__tab', {
                                        'voice-to-text-converter__tab--active': activeTab === 'upload'
                                    }), children: "\uD83D\uDCC1 File Upload" })] })), jsxRuntime.jsxs("div", { className: "voice-to-text-converter__main", children: [showVoiceRecorder && (activeTab === 'recorder' || !showFileUpload) && (jsxRuntime.jsx(VoiceRecorder, { showResults: false, onResult: onResult, onError: onError, onStart: onStart, onStop: onStop })), showFileUpload && (activeTab === 'upload' || !showVoiceRecorder) && (jsxRuntime.jsx(FileUpload, { onConvert: handleFileConvert, onError: handleFileError, language: defaultLanguage }))] }), showResults && (jsxRuntime.jsx("div", { className: "voice-to-text-converter__results", children: jsxRuntime.jsx(ResultsDisplay, { results: fileResults, showClearButton: true, onClear: () => setFileResults([]) }) })), children && (jsxRuntime.jsx("div", { className: "voice-to-text-converter__children", children: children }))] })] }) }));
};

const useVoiceToText = (options = {}) => {
    const [isInitialized, setIsInitialized] = react.useState(false);
    const [isRecording, setIsRecording] = react.useState(false);
    const [isProcessing, setIsProcessing] = react.useState(false);
    const [results, setResults] = react.useState([]);
    const [error, setError] = react.useState(null);
    const [browserSupport, setBrowserSupport] = react.useState(null);
    const [config, setConfig] = react.useState({
        defaultRecognitionConfig: {
            language: 'en-US',
            continuous: true,
            interimResults: true
        },
        debug: false,
        ...options
    });
    const [engine, setEngine] = react.useState(null);
    // Initialize the engine
    react.useEffect(() => {
        const initialize = async () => {
            try {
                // Check browser support
                const support = getBrowserSupport();
                setBrowserSupport(support);
                if (!support.webSpeechAPI) {
                    throw new Error('Speech recognition is not supported in this browser');
                }
                // Initialize Web Speech engine
                const speechEngine = new WebSpeechEngine();
                await speechEngine.initialize(config.defaultRecognitionConfig);
                // Set up event listeners
                speechEngine.on('result', (result) => {
                    setResults(prev => [...prev, result]);
                    options.onResult?.(result);
                });
                speechEngine.on('error', (error) => {
                    const errorMessage = error.message || 'Speech recognition error';
                    setError(errorMessage);
                    options.onError?.(errorMessage);
                });
                speechEngine.on('start', () => {
                    setIsRecording(true);
                    options.onStart?.();
                });
                speechEngine.on('end', () => {
                    setIsRecording(false);
                    options.onStop?.();
                });
                setEngine(speechEngine);
                setIsInitialized(true);
            }
            catch (err) {
                const errorMessage = err instanceof Error ? err.message : 'Failed to initialize voice recognition';
                setError(errorMessage);
                options.onError?.(errorMessage);
            }
        };
        initialize();
        // Cleanup
        return () => {
            if (engine) {
                engine.cleanup();
            }
        };
    }, []);
    const startRecording = react.useCallback(async (recordingOptions) => {
        if (!engine || !isInitialized) {
            throw new Error('Voice recognition not initialized');
        }
        try {
            setIsProcessing(true);
            setError(null);
            setResults([]);
            const finalConfig = {
                ...config.defaultRecognitionConfig,
                ...recordingOptions
            };
            await engine.startListening(finalConfig);
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : 'Failed to start recording';
            setError(errorMessage);
            options.onError?.(errorMessage);
            throw err;
        }
        finally {
            setIsProcessing(false);
        }
    }, [engine, isInitialized, config.defaultRecognitionConfig, options]);
    const stopRecording = react.useCallback(async () => {
        if (!engine) {
            throw new Error('Voice recognition not initialized');
        }
        try {
            setIsProcessing(true);
            await engine.stopListening();
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : 'Failed to stop recording';
            setError(errorMessage);
            options.onError?.(errorMessage);
            throw err;
        }
        finally {
            setIsProcessing(false);
        }
    }, [engine, options]);
    const clearResults = react.useCallback(() => {
        setResults([]);
    }, []);
    const clearError = react.useCallback(() => {
        setError(null);
    }, []);
    const updateConfig = react.useCallback((newConfig) => {
        setConfig(prev => ({ ...prev, ...newConfig }));
    }, []);
    const getConfig = react.useCallback(() => config, [config]);
    return {
        isInitialized,
        isRecording,
        isProcessing,
        results,
        error,
        browserSupport,
        startRecording,
        stopRecording,
        clearResults,
        clearError,
        updateConfig,
        getConfig
    };
};

const useVoiceRecorder = (options = {}) => {
    const { language = 'en-US', continuous = true, interimResults = true, onResult, onError, onStart, onStop } = options;
    const voiceToText = useVoiceToText({
        defaultRecognitionConfig: {
            language,
            continuous,
            interimResults
        },
        onResult,
        onError,
        onStart,
        onStop
    });
    const startRecording = react.useCallback(async () => {
        await voiceToText.startRecording({
            language,
            continuous,
            interimResults
        });
    }, [voiceToText, language, continuous, interimResults]);
    const stopRecording = react.useCallback(async () => {
        await voiceToText.stopRecording();
    }, [voiceToText]);
    return {
        isInitialized: voiceToText.isInitialized,
        isRecording: voiceToText.isRecording,
        isProcessing: voiceToText.isProcessing,
        results: voiceToText.results,
        error: voiceToText.error,
        startRecording,
        stopRecording,
        clearResults: voiceToText.clearResults,
        clearError: voiceToText.clearError
    };
};

const useFileUpload = (options = {}) => {
    const { acceptedFormats = ['audio/*'], maxFileSize = 50 * 1024 * 1024, // 50MB
    onFileSelect, onConvert, onError } = options;
    const [selectedFile, setSelectedFile] = react.useState(null);
    const [isConverting, setIsConverting] = react.useState(false);
    const [results, setResults] = react.useState([]);
    const [error, setError] = react.useState(null);
    const validateFile = react.useCallback((file) => {
        // Validate file size
        if (file.size > maxFileSize) {
            return {
                isValid: false,
                error: `File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds maximum allowed size (${(maxFileSize / 1024 / 1024).toFixed(2)}MB)`
            };
        }
        // Validate file type
        const isValidType = acceptedFormats.some(format => {
            if (format === 'audio/*') {
                return file.type.startsWith('audio/');
            }
            return file.type === format;
        });
        if (!isValidType) {
            return {
                isValid: false,
                error: `File type ${file.type} is not supported. Please select an audio file.`
            };
        }
        return { isValid: true };
    }, [acceptedFormats, maxFileSize]);
    const selectFile = react.useCallback((file) => {
        const validation = validateFile(file);
        if (!validation.isValid) {
            setError(validation.error || 'Unknown validation error');
            onError?.(validation.error);
            return;
        }
        setSelectedFile(file);
        setError(null);
        onFileSelect?.(file);
    }, [validateFile, onFileSelect, onError]);
    const convertFile = react.useCallback(async (language = 'en-US') => {
        if (!selectedFile) {
            const error = 'No file selected';
            setError(error);
            onError?.(error);
            return;
        }
        setIsConverting(true);
        setError(null);
        try {
            const result = await onConvert?.(selectedFile, language);
            if (result) {
                setResults(prev => [...prev, result]);
            }
        }
        catch (err) {
            const errorMessage = err instanceof Error ? err.message : 'Failed to convert file';
            setError(errorMessage);
            onError?.(errorMessage);
        }
        finally {
            setIsConverting(false);
        }
    }, [selectedFile, onConvert, onError]);
    const clearFile = react.useCallback(() => {
        setSelectedFile(null);
    }, []);
    const clearResults = react.useCallback(() => {
        setResults([]);
    }, []);
    const clearError = react.useCallback(() => {
        setError(null);
    }, []);
    return {
        selectedFile,
        isConverting,
        results,
        error,
        selectFile,
        convertFile,
        clearFile,
        clearResults,
        clearError,
        validateFile
    };
};

const useSpeechRecognition = (options = {}) => {
    const { language = 'en-US', continuous = true, interimResults = true, maxAlternatives = 1, onResult, onError, onStart, onEnd, onAudioStart, onAudioEnd, onSoundStart, onSoundEnd, onSpeechStart, onSpeechEnd, onNoMatch, onNomatch } = options;
    const [isSupported, setIsSupported] = react.useState(false);
    const [isListening, setIsListening] = react.useState(false);
    const [transcript, setTranscript] = react.useState('');
    const [finalTranscript, setFinalTranscript] = react.useState('');
    const [interimTranscript, setInterimTranscript] = react.useState('');
    const [error, setError] = react.useState(null);
    const recognitionRef = react.useRef(null);
    const finalTranscriptRef = react.useRef('');
    // Check browser support and initialize
    react.useEffect(() => {
        const support = getBrowserSupport();
        setIsSupported(support.webSpeechAPI);
        if (support.webSpeechAPI) {
            const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
            recognitionRef.current = new SpeechRecognition();
            const recognition = recognitionRef.current;
            // Configure recognition
            recognition.continuous = continuous;
            recognition.interimResults = interimResults;
            recognition.lang = language;
            recognition.maxAlternatives = maxAlternatives;
            // Set up event listeners
            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;
                    }
                    else {
                        interimTranscript += transcript;
                    }
                }
                finalTranscriptRef.current = finalTranscript;
                setFinalTranscript(finalTranscript);
                setInterimTranscript(interimTranscript);
                setTranscript(finalTranscript + interimTranscript);
                onResult?.(event);
            };
            recognition.onerror = (event) => {
                setError(event.error);
                setIsListening(false);
                onError?.(event);
            };
            recognition.onstart = () => {
                setIsListening(true);
                setError(null);
                onStart?.();
            };
            recognition.onend = () => {
                setIsListening(false);
                onEnd?.();
            };
            recognition.onaudiostart = onAudioStart;
            recognition.onaudioend = onAudioEnd;
            recognition.onsoundstart = onSoundStart;
            recognition.onsoundend = onSoundEnd;
            recognition.onspeechstart = onSpeechStart;
            recognition.onspeechend = onSpeechEnd;
            recognition.onnomatch = onNomatch || onNoMatch;
        }
        // Cleanup
        return () => {
            if (recognitionRef.current) {
                recognitionRef.current.abort();
            }
        };
    }, [continuous, interimResults, language, maxAlternatives, onResult, onError, onStart, onEnd, onAudioStart, onAudioEnd, onSoundStart, onSoundEnd, onSpeechStart, onSpeechEnd, onNoMatch, onNomatch]);
    const start = react.useCallback(() => {
        if (!isSupported || !recognitionRef.current) {
            setError('Speech recognition is not supported');
            return;
        }
        try {
            recognitionRef.current.start();
        }
        catch (err) {
            setError(err instanceof Error ? err.message : 'Failed to start recognition');
        }
    }, [isSupported]);
    const stop = react.useCallback(() => {
        if (recognitionRef.current) {
            recognitionRef.current.stop();
        }
    }, []);
    const abort = react.useCallback(() => {
        if (recognitionRef.current) {
            recognitionRef.current.abort();
        }
    }, []);
    const reset = react.useCallback(() => {
        setTranscript('');
        setFinalTranscript('');
        setInterimTranscript('');
        setError(null);
        finalTranscriptRef.current = '';
    }, []);
    const updateConfig = react.useCallback((config) => {
        if (recognitionRef.current) {
            if (config.language !== undefined) {
                recognitionRef.current.lang = config.language;
            }
            if (config.continuous !== undefined) {
                recognitionRef.current.continuous = config.continuous;
            }
            if (config.interimResults !== undefined) {
                recognitionRef.current.interimResults = config.interimResults;
            }
            if (config.maxAlternatives !== undefined) {
                recognitionRef.current.maxAlternatives = config.maxAlternatives;
            }
        }
    }, []);
    return {
        isSupported,
        isListening,
        transcript,
        finalTranscript,
        interimTranscript,
        error,
        start,
        stop,
        abort,
        reset,
        updateConfig
    };
};

/**
 * Supported audio file formats
 */
const SUPPORTED_AUDIO_FORMATS = ['.wav', '.mp3', '.ogg', '.flac', '.m4a'];
/**
 * Check if a file is a supported audio format
 */
function isSupportedAudioFormat(filePath) {
    const ext = getFileExtension(filePath).toLowerCase();
    return SUPPORTED_AUDIO_FORMATS.includes(ext);
}
/**
 * Get file extension from file path or name
 */
function getFileExtension(filePath) {
    const lastDotIndex = filePath.lastIndexOf('.');
    return lastDotIndex !== -1 ? filePath.substring(lastDotIndex) : '';
}
/**
 * Validate audio file format (browser version)
 */
function validateAudioFile(file) {
    if (!isSupportedAudioFormat(file.name)) {
        throw new Error(`Unsupported audio format. Supported formats: ${SUPPORTED_AUDIO_FORMATS.join(', ')}`);
    }
}
function getAudioFileInfo(file) {
    validateAudioFile(file);
    const format = getFileExtension(file.name).toLowerCase().substring(1);
    return {
        size: file.size,
        format,
        name: file.name,
        type: file.type,
        lastModified: file.lastModified
    };
}
/**
 * Convert audio buffer to WAV format (simplified implementation)
 */
function bufferToWav(buffer, sampleRate = 16000, channels = 1) {
    const length = buffer.byteLength;
    const arrayBuffer = new ArrayBuffer(44 + length);
    const view = new DataView(arrayBuffer);
    // WAV header
    const writeString = (offset, string) => {
        for (let i = 0; i < string.length; i++) {
            view.setUint8(offset + i, string.charCodeAt(i));
        }
    };
    writeString(0, 'RIFF');
    view.setUint32(4, 36 + length, true);
    writeString(8, 'WAVE');
    writeString(12, 'fmt ');
    view.setUint32(16, 16, true);
    view.setUint16(20, 1, true);
    view.setUint16(22, channels, true);
    view.setUint32(24, sampleRate, true);
    view.setUint32(28, sampleRate * channels * 2, true);
    view.setUint16(32, channels * 2, true);
    view.setUint16(34, 16, true);
    writeString(36, 'data');
    view.setUint32(40, length, true);
    // Copy audio data
    const uint8Array = new Uint8Array(arrayBuffer);
    uint8Array.set(new Uint8Array(buffer), 44);
    return arrayBuffer;
}
/**
 * Normalize audio volume
 */
function normalizeAudio(buffer, targetVolume = 0.8) {
    const samples = new Int16Array(buffer);
    // Find peak amplitude
    let peak = 0;
    for (let i = 0; i < samples.length; i++) {
        const abs = Math.abs(samples[i]);
        if (abs > peak)
            peak = abs;
    }
    if (peak === 0)
        return buffer;
    // Calculate normalization factor
    const factor = (targetVolume * 32767) / peak;
    // Apply normalization
    for (let i = 0; i < samples.length; i++) {
        samples[i] = Math.round(samples[i] * factor);
    }
    return samples.buffer;
}
/**
 * Detect silence in audio buffer
 */
function detectSilence(buffer, threshold = 500) {
    const samples = new Int16Array(buffer);
    let sum = 0;
    for (let i = 0; i < samples.length; i++) {
        sum += Math.abs(samples[i]);
    }
    const average = sum / samples.length;
    return average < threshold;
}
/**
 * Split audio buffer into chunks
 */
function chunkAudioBuffer(buffer, chunkSize) {
    const chunks = [];
    const uint8Array = new Uint8Array(buffer);
    for (let i = 0; i < uint8Array.length; i += chunkSize) {
        const end = Math.min(i + chunkSize, uint8Array.length);
        const chunk = uint8Array.slice(i, end);
        chunks.push(chunk.buffer);
    }
    return chunks;
}
/**
 * Merge multiple audio buffers
 */
function mergeAudioBuffers(buffers) {
    const totalLength = buffers.reduce((sum, buffer) => sum + buffer.byteLength, 0);
    const merged = new Uint8Array(totalLength);
    let offset = 0;
    for (const buffer of buffers) {
        merged.set(new Uint8Array(buffer), offset);
        offset += buffer.byteLength;
    }
    return merged.buffer;
}
/**
 * Convert sample rate (basic implementation)
 */
function resampleAudio(buffer, fromRate, toRate) {
    if (fromRate === toRate)
        return buffer;
    const samples = new Int16Array(buffer);
    const ratio = fromRate / toRate;
    const newLength = Math.floor(samples.length / ratio);
    const resampled = new Int16Array(newLength);
    for (let i = 0; i < newLength; i++) {
        const sourceIndex = Math.floor(i * ratio);
        resampled[i] = samples[sourceIndex];
    }
    return resampled.buffer;
}
/**
 * Create audio context for web audio processing
 */
function createAudioContext() {
    try {
        const AudioContextClass = window.AudioContext || window.webkitAudioContext;
        return new AudioContextClass();
    }
    catch (error) {
        console.error('Failed to create AudioContext:', error);
        return null;
    }
}
/**
 * Load audio file as ArrayBuffer
 */
function loadAudioFile(file) {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onload = () => {
            if (reader.result instanceof ArrayBuffer) {
                resolve(reader.result);
            }
            else {
                reject(new Error('Failed to read file as ArrayBuffer'));
            }
        };
        reader.onerror = () => reject(reader.error);
        reader.readAsArrayBuffer(file);
    });
}

// Recognition utility functions for voice-to-text package
/**
 * Create a new SpeechRecognition instance with the given configuration
 */
function createSpeechRecognition(config) {
    const SpeechRecognition = getSpeechRecognition();
    if (!SpeechRecognition) {
        return null;
    }
    const recognition = new SpeechRecognition();
    // Configure recognition properties
    recognition.continuous = config.continuous ?? true;
    recognition.interimResults = config.interimResults ?? true;
    recognition.lang = config.language ?? 'en-US';
    recognition.maxAlternatives = config.maxAlternatives ?? 1;
    return recognition;
}
/**
 * Check if speech recognition is available in the current environment
 */
function isSpeechRecognitionAvailable() {
    const support = getBrowserSupport();
    return support.webSpeechAPI;
}
/**
 * Validate speech recognition configuration
 */
function validateSpeechRecognitionConfig(config) {
    const errors = [];
    if (config.language && !isValidLanguageCode(config.language)) {
        errors.push(`Invalid language code: ${config.language}`);
    }
    if (config.maxAlternatives && (config.maxAlternatives < 1 || config.maxAlternatives > 10)) {
        errors.push('maxAlternatives must be between 1 and 10');
    }
    return {
        isValid: errors.length === 0,
        errors
    };
}
/**
 * Check if a language code is valid
 */
function isValidLanguageCode(languageCode) {
    // Basic validation - language codes should be in format like 'en-US', 'es-ES', etc.
    const languageCodePattern = /^[a-z]{2}(-[A-Z]{2})?$/;
    return languageCodePattern.test(languageCode);
}
/**
 * Get supported language codes
 */
function getSupportedLanguages() {
    return [
        'en-US', 'en-GB', 'en-AU', 'en-CA', 'en-IN', 'en-IE', 'en-NZ', 'en-ZA',
        'es-ES', 'es-MX', 'es-AR', 'es-CL', 'es-CO', 'es-PE', 'es-VE', 'es-EC',
        'fr-FR', 'fr-CA', 'fr-BE', 'fr-CH', 'fr-LU', 'fr-MC',
        'de-DE', 'de-AT', 'de-CH', 'de-LU', 'de-LI',
        'it-IT', 'it-CH',
        'pt-BR', 'pt-PT',
        'ru-RU', 'ru-UA', 'ru-KZ',
        'ja-JP',
        'ko-KR',
        'zh-CN', 'zh-TW', 'zh-HK',
        'ar-SA', 'ar-EG', 'ar-IL', 'ar-JO', 'ar-LB', 'ar-MA', 'ar-PS', 'ar-SY',
        'hi-IN',
        'th-TH',
        'tr-TR',
        'pl-PL',
        'nl-NL', 'nl-BE',
        'sv-SE',
        'da-DK',
        'no-NO',
        'fi-FI',
        'cs-CZ',
        'hu-HU',
        'ro-RO',
        'bg-BG',
        'hr-HR',
        'sk-SK',
        'sl-SI',
        'et-EE',
        'lv-LV',
        'lt-LT',
        'mt-MT',
        'el-GR',
        'he-IL',
        'id-ID',
        'ms-MY',
        'vi-VN',
        'uk-UA',
        'ca-ES',
        'eu-ES',
        'gl-ES',
        'cy-GB',
        'ga-IE',
        'is-IS',
        'mk-MK',
        'sq-AL',
        'sr-RS',
        'bs-BA',
        'me-ME',
        'mn-MN',
        'ka-GE',
        'hy-AM',
        'az-AZ',
        'uz-UZ',
        'kk-KZ',
        'ky-KG',
        'tg-TJ',
        'fa-IR',
        'ur-PK',
        'bn-BD',
        'si-LK',
        'my-MM',
        'km-KH',
        'lo-LA',
        'ne-NP',
        'gu-IN',
        'pa-IN',
        'te-IN',
        'kn-IN',
        'ml-IN',
        'ta-IN',
        'or-IN',
        'as-IN',
        'mr-IN',
        'sa-IN',
        'bo-CN',
        'dz-BT',
        'ti-ER',
        'so-SO',
        'sw-KE',
        'rw-RW',
        'lg-UG',
        'am-ET',
        'ha-NG',
        'yo-NG',
        'ig-NG',
        'zu-ZA',
        'af-ZA',
        'xh-ZA',
        'st-ZA',
        'tn-ZA',
        'ts-ZA',
        've-ZA',
        'nr-ZA',
        'ss-ZA',
        'sn-ZW',
        'ny-MW',
        'mg-MG',
        'co-FR',
        'br-FR',
        'oc-FR',
        'gv-GB',
        'kw-GB',
        'fo-FO',
        'sm-WS',
        'to-TO',
        'fj-FJ',
        'haw-US',
        'mi-NZ',
        'qu-PE',
        'qu-EC',
        'qu-BO',
        'ay-BO',
        'gn-PY',
        'gn-AR',
        'gn-BO',
        'gn-PE',
        'gn-EC',
        'gn-CO',
        'gn-VE',
        'gn-GY',
        'gn-SR',
        'gn-FR',
        'gn-BR',
        'gn-UY',
        'gn-AR',
        'gn-PY',
        'gn-BO',
        'gn-PE',
        'gn-EC',
        'gn-CO',
        'gn-VE',
        'gn-GY',
        'gn-SR',
        'gn-FR',
        'gn-BR',
        'gn-UY'
    ];
}
/**
 * Get language name from language code
 */
function getLanguageName(languageCode) {
    const languageNames = {
        'en-US': 'English (US)',
        'en-GB': 'English (UK)',
        'en-AU': 'English (Australia)',
        'en-CA': 'English (Canada)',
        'en-IN': 'English (India)',
        'en-IE': 'English (Ireland)',
        'en-NZ': 'English (New Zealand)',
        'en-ZA': 'English (South Africa)',
        'es-ES': 'Spanish (Spain)',
        'es-MX': 'Spanish (Mexico)',
        'es-AR': 'Spanish (Argentina)',
        'es-CL': 'Spanish (Chile)',
        'es-CO': 'Spanish (Colombia)',
        'es-PE': 'Spanish (Peru)',
        'es-VE': 'Spanish (Venezuela)',
        'es-EC': 'Spanish (Ecuador)',
        'fr-FR': 'French (France)',
        'fr-CA': 'French (Canada)',
        'fr-BE': 'French (Belgium)',
        'fr-CH': 'French (Switzerland)',
        'fr-LU': 'French (Luxembourg)',
        'fr-MC': 'French (Monaco)',
        'de-DE': 'German (Germany)',
        'de-AT': 'German (Austria)',
        'de-CH': 'German (Switzerland)',
        'de-LU': 'German (Luxembourg)',
        'de-LI': 'German (Liechtenstein)',
        'it-IT': 'Italian (Italy)',
        'it-CH': 'Italian (Switzerland)',
        'pt-BR': 'Portuguese (Brazil)',
        'pt-PT': 'Portuguese (Portugal)',
        'ru-RU': 'Russian (Russia)',
        'ru-UA': 'Russian (Ukraine)',
        'ru-KZ': 'Russian (Kazakhstan)',
        'ja-JP': 'Japanese (Japan)',
        'ko-KR': 'Korean (South Korea)',
        'zh-CN': 'Chinese (Simplified)',
        'zh-TW': 'Chinese (Traditional)',
        'zh-HK': 'Chinese (Hong Kong)',
        'ar-SA': 'Arabic (Saudi Arabia)',
        'ar-EG': 'Arabic (Egypt)',
        'ar-IL': 'Arabic (Israel)',
        'ar-JO': 'Arabic (Jordan)',
        'ar-LB': 'Arabic (Lebanon)',
        'ar-MA': 'Arabic (Morocco)',
        'ar-PS': 'Arabic (Palestine)',
        'ar-SY': 'Arabic (Syria)',
        'hi-IN': 'Hindi (India)',
        'th-TH': 'Thai (Thailand)',
        'tr-TR': 'Turkish (Turkey)',
        'pl-PL': 'Polish (Poland)',
        'nl-NL': 'Dutch (Netherlands)',
        'nl-BE': 'Dutch (Belgium)',
        'sv-SE': 'Swedish (Sweden)',
        'da-DK': 'Danish (Denmark)',
        'no-NO': 'Norwegian (Norway)',
        'fi-FI': 'Finnish (Finland)',
        'cs-CZ': 'Czech (Czech Republic)',
        'hu-HU': 'Hungarian (Hungary)',
        'ro-RO': 'Romanian (Romania)',
        'bg-BG': 'Bulgarian (Bulgaria)',
        'hr-HR': 'Croatian (Croatia)',
        'sk-SK': 'Slovak (Slovakia)',
        'sl-SI': 'Slovenian (Slovenia)',
        'et-EE': 'Estonian (Estonia)',
        'lv-LV': 'Latvian (Latvia)',
        'lt-LT': 'Lithuanian (Lithuania)',
        'mt-MT': 'Maltese (Malta)',
        'el-GR': 'Greek (Greece)',
        'he-IL': 'Hebrew (Israel)',
        'id-ID': 'Indonesian (Indonesia)',
        'ms-MY': 'Malay (Malaysia)',
        'vi-VN': 'Vietnamese (Vietnam)',
        'uk-UA': 'Ukrainian (Ukraine)',
        'ca-ES': 'Catalan (Spain)',
        'eu-ES': 'Basque (Spain)',
        'gl-ES': 'Galician (Spain)',
        'cy-GB': 'Welsh (UK)',
        'ga-IE': 'Irish (Ireland)',
        'is-IS': 'Icelandic (Iceland)',
        'mk-MK': 'Macedonian (North Macedonia)',
        'sq-AL': 'Albanian (Albania)',
        'sr-RS': 'Serbian (Serbia)',
        'bs-BA': 'Bosnian (Bosnia and Herzegovina)',
        'me-ME': 'Montenegrin (Montenegro)',
        'mn-MN': 'Mongolian (Mongolia)',
        'ka-GE': 'Georgian (Georgia)',
        'hy-AM': 'Armenian (Armenia)',
        'az-AZ': 'Azerbaijani (Azerbaijan)',
        'uz-UZ': 'Uzbek (Uzbekistan)',
        'kk-KZ': 'Kazakh (Kazakhstan)',
        'ky-KG': 'Kyrgyz (Kyrgyzstan)',
        'tg-TJ': 'Tajik (Tajikistan)',
        'fa-IR': 'Persian (Iran)',
        'ur-PK': 'Urdu (Pakistan)',
        'bn-BD': 'Bengali (Bangladesh)',
        'si-LK': 'Sinhala (Sri Lanka)',
        'my-MM': 'Burmese (Myanmar)',
        'km-KH': 'Khmer (Cambodia)',
        'lo-LA': 'Lao (Laos)',
        'ne-NP': 'Nepali (Nepal)',
        'gu-IN': 'Gujarati (India)',
        'pa-IN': 'Punjabi (India)',
        'te-IN': 'Telugu (India)',
        'kn-IN': 'Kannada (India)',
        'ml-IN': 'Malayalam (India)',
        'ta-IN': 'Tamil (India)',
        'or-IN': 'Odia (India)',
        'as-IN': 'Assamese (India)',
        'mr-IN': 'Marathi (India)',
        'sa-IN': 'Sanskrit (India)',
        'bo-CN': 'Tibetan (China)',
        'dz-BT': 'Dzongkha (Bhutan)',
        'ti-ER': 'Tigrinya (Eritrea)',
        'so-SO': 'Somali (Somalia)',
        'sw-KE': 'Swahili (Kenya)',
        'rw-RW': 'Kinyarwanda (Rwanda)',
        'lg-UG': 'Ganda (Uganda)',
        'am-ET': 'Amharic (Ethiopia)',
        'ha-NG': 'Hausa (Nigeria)',
        'yo-NG': 'Yoruba (Nigeria)',
        'ig-NG': 'Igbo (Nigeria)',
        'zu-ZA': 'Zulu (South Africa)',
        'af-ZA': 'Afrikaans (South Africa)',
        'xh-ZA': 'Xhosa (South Africa)',
        'st-ZA': 'Southern Sotho (South Africa)',
        'tn-ZA': 'Tswana (South Africa)',
        'ts-ZA': 'Tsonga (South Africa)',
        've-ZA': 'Venda (South Africa)',
        'nr-ZA': 'Southern Ndebele (South Africa)',
        'ss-ZA': 'Swati (South Africa)',
        'sn-ZW': 'Shona (Zimbabwe)',
        'ny-MW': 'Chichewa (Malawi)',
        'mg-MG': 'Malagasy (Madagascar)',
        'co-FR': 'Corsican (France)',
        'br-FR': 'Breton (France)',
        'oc-FR': 'Occitan (France)',
        'gv-GB': 'Manx (UK)',
        'kw-GB': 'Cornish (UK)',
        'fo-FO': 'Faroese (Faroe Islands)',
        'sm-WS': 'Samoan (Samoa)',
        'to-TO': 'Tongan (Tonga)',
        'fj-FJ': 'Fijian (Fiji)',
        'haw-US': 'Hawaiian (US)',
        'mi-NZ': 'Maori (New Zealand)',
        'qu-PE': 'Quechua (Peru)',
        'qu-EC': 'Quechua (Ecuador)',
        'qu-BO': 'Quechua (Bolivia)',
        'ay-BO': 'Aymara (Bolivia)',
        'gn-PY': 'Guarani (Paraguay)',
        'gn-AR': 'Guarani (Argentina)',
        'gn-BO': 'Guarani (Bolivia)',
        'gn-PE': 'Guarani (Peru)',
        'gn-EC': 'Guarani (Ecuador)',
        'gn-CO': 'Guarani (Colombia)',
        'gn-VE': 'Guarani (Venezuela)',
        'gn-GY': 'Guarani (Guyana)',
        'gn-SR': 'Guarani (Suriname)',
        'gn-FR': 'Guarani (French Guiana)',
        'gn-BR': 'Guarani (Brazil)',
        'gn-UY': 'Guarani (Uruguay)'
    };
    return languageNames[languageCode] || languageCode;
}
/**
 * Format a SpeechRecognitionResult for display
 */
function formatRecognitionResult(result) {
    const confidence = result.confidence ? ` (${(result.confidence * 100).toFixed(1)}%)` : '';
    const status = result.isFinal ? 'Final' : 'Interim';
    return `${result.transcript}${confidence} [${status}]`;
}
/**
 * Merge multiple recognition results
 */
function mergeRecognitionResults(results) {
    return results
        .filter(result => result.isFinal)
        .map(result => result.transcript)
        .join(' ');
}
/**
 * Get the best result from multiple alternatives
 */
function getBestResult(results) {
    if (results.length === 0) {
        return null;
    }
    // Return the result with the highest confidence
    return results.reduce((best, current) => {
        if (!best)
            return current;
        if (!current)
            return best;
        return (current.confidence || 0) > (best.confidence || 0) ? current : best;
    });
}

// React Voice-to-Text Package - Main Entry Point
// This package provides React components and hooks for voice-to-text functionality
// Core React components
// Version information
const VERSION = '1.0.0';

exports.EventEmitter = EventEmitter;
exports.FileUpload = FileUpload;
exports.LanguageSelector = LanguageSelector;
exports.RecordingControls = RecordingControls;
exports.ResultsDisplay = ResultsDisplay;
exports.SUPPORTED_AUDIO_FORMATS = SUPPORTED_AUDIO_FORMATS;
exports.SpeechRecognitionError = SpeechRecognitionError;
exports.VERSION = VERSION;
exports.VoiceRecorder = VoiceRecorder;
exports.VoiceToTextConverter = VoiceToTextConverter;
exports.VoiceToTextProvider = VoiceToTextProvider;
exports.WebSpeechEngine = WebSpeechEngine;
exports.bufferToWav = bufferToWav;
exports.chunkAudioBuffer = chunkAudioBuffer;
exports.createAudioContext = createAudioContext;
exports.createSpeechRecognition = createSpeechRecognition;
exports.default = VoiceToTextProvider;
exports.detectSilence = detectSilence;
exports.formatRecognitionResult = formatRecognitionResult;
exports.getAudioDevices = getAudioDevices;
exports.getAudioFileInfo = getAudioFileInfo;
exports.getBestResult = getBestResult;
exports.getBrowserInfo = getBrowserInfo;
exports.getBrowserSupport = getBrowserSupport;
exports.getLanguageName = getLanguageName;
exports.getSpeechRecognition = getSpeechRecognition;
exports.getSupportedLanguages = getSupportedLanguages;
exports.isBrowser = isBrowser;
exports.isSecureContext = isSecureContext;
exports.isSpeechRecognitionAvailable = isSpeechRecognitionAvailable;
exports.isSpeechRecognitionSupported = isSpeechRecognitionSupported;
exports.isSupportedAudioFormat = isSupportedAudioFormat;
exports.isValidLanguageCode = isValidLanguageCode;
exports.loadAudioFile = loadAudioFile;
exports.mergeAudioBuffers = mergeAudioBuffers;
exports.mergeRecognitionResults = mergeRecognitionResults;
exports.normalizeAudio = normalizeAudio;
exports.requestMicrophonePermission = requestMicrophonePermission;
exports.resampleAudio = resampleAudio;
exports.useFileUpload = useFileUpload;
exports.useSpeechRecognition = useSpeechRecognition;
exports.useVoiceRecorder = useVoiceRecorder;
exports.useVoiceToText = useVoiceToText;
exports.validateAudioFile = validateAudioFile;
exports.validateSpeechRecognitionConfig = validateSpeechRecognitionConfig;
//# sourceMappingURL=index.js.map