function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } } function _async_to_generator(fn) { return function() { var self = this, args = arguments; return new Promise(function(resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } function _class_call_check(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperties(target, props) { for(var i = 0; i < props.length; i++){ var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _create_class(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _ts_generator(thisArg, body) { var f, y, t, g, _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function(v) { return step([ n, v ]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while(_)try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [ op[0] & 2, t.value ]; switch(op[0]){ case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [ 0 ]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [ 6, e ]; y = 0; } finally{ f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } } export var SpeechManager = /*#__PURE__*/ function() { "use strict"; function SpeechManager(onTranscript, onRecognitionActive, onCommandRecognized) { var _this = this; _class_call_check(this, SpeechManager); this.onTranscript = onTranscript; this.onRecognitionActive = onRecognitionActive; // Callback for recognition state this.onCommandRecognized = onCommandRecognized; // Callback for recognized commands this.recognition = null; this.isRecognizing = false; this.finalTranscript = ''; this.interimTranscript = ''; var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; if (SpeechRecognition) { this.recognition = new SpeechRecognition(); this.recognition.continuous = true; // Keep listening even after a pause this.recognition.interimResults = true; // Get results while speaking this.recognition.onstart = function() { _this.isRecognizing = true; console.log('Speech recognition started.'); if (_this.onRecognitionActive) _this.onRecognitionActive(true); }; this.recognition.onresult = function(event) { _this.interimTranscript = ''; for(var i = event.resultIndex; i < event.results.length; ++i){ if (event.results[i].isFinal) { // Append to finalTranscript and then clear it for the next utterance // This way, `finalTranscript` holds the *current complete* utterance. var currentFinalTranscript = event.results[i][0].transcript.trim().toLowerCase(); _this.finalTranscript += currentFinalTranscript; // Append to potentially longer session transcript if needed, though we process per utterance if (_this.onTranscript) { // Display the raw transcript before processing as command _this.onTranscript(event.results[i][0].transcript, ''); // Send final, clear interim } // Check for commands var commandMap = { 'drag': 'drag', 'rotate': 'rotate', 'rotation': 'rotate', 'scale': 'scale', 'size': 'scale', 'zoom': 'scale', 'animate': 'animate', 'anime': 'animate', 'animation': 'animate' // Alias for animate }; var spokenCommands = Object.keys(commandMap); var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined; try { for(var _iterator = spokenCommands[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){ var spokenCmd = _step.value; if (currentFinalTranscript.includes(spokenCmd)) { var actualCommand = commandMap[spokenCmd]; if (_this.onCommandRecognized) { _this.onCommandRecognized(actualCommand); } break; // Process the first command found (and its alias) } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally{ try { if (!_iteratorNormalCompletion && _iterator.return != null) { _iterator.return(); } } finally{ if (_didIteratorError) { throw _iteratorError; } } } // Reset finalTranscript for the next full utterance if you are processing utterance by utterance // If you want to accumulate, then don't reset here. // For command processing, resetting per utterance is usually best. _this.finalTranscript = ''; } else { _this.interimTranscript += event.results[i][0].transcript; if (_this.onTranscript) { _this.onTranscript(null, _this.interimTranscript); } } } // If only interim results were processed in this event batch, ensure onTranscript is called if (_this.interimTranscript && !event.results[event.results.length - 1].isFinal) { if (_this.onTranscript) { _this.onTranscript(null, _this.interimTranscript); } } }; this.recognition.onerror = function(event) { console.error('Speech recognition error:', event.error); var oldIsRecognizing = _this.isRecognizing; _this.isRecognizing = false; _this.finalTranscript = ''; // Clear transcript on error _this.interimTranscript = ''; if (_this.onTranscript) _this.onTranscript('', ''); // Clear display if (oldIsRecognizing && _this.onRecognitionActive) _this.onRecognitionActive(false); // Automatically restart if it's an 'aborted' or 'no-speech' error if (event.error === 'aborted' || event.error === 'no-speech') { console.log('Restarting speech recognition due to inactivity or abort.'); // Don't call startRecognition directly, let onend handle it if continuous } }; this.recognition.onend = function() { var oldIsRecognizing = _this.isRecognizing; _this.isRecognizing = false; console.log('Speech recognition ended.'); _this.finalTranscript = ''; // Clear transcript on end _this.interimTranscript = ''; if (_this.onTranscript) _this.onTranscript('', ''); // Clear display if (oldIsRecognizing && _this.onRecognitionActive) _this.onRecognitionActive(false); // If it ended and continuous is true, restart it. // This handles cases where the browser might stop it. if (_this.recognition.continuous) { console.log('Continuous mode: Restarting speech recognition.'); _this.startRecognition(); // startRecognition already resets transcripts } }; } else { console.warn('Web Speech API is not supported in this browser.'); } } _create_class(SpeechManager, [ { key: "startRecognition", value: function startRecognition() { var _this = this; if (this.recognition && !this.isRecognizing) { try { this.finalTranscript = ''; // Reset transcript this.interimTranscript = ''; this.recognition.start(); } catch (e) { console.error("Error starting speech recognition:", e); // This can happen if it's already started or due to permissions if (e.name === 'InvalidStateError' && this.isRecognizing) { // Already started, do nothing } else { // Attempt to restart if it fails for other reasons (e.g. after an error) setTimeout(function() { return _this.startRecognition(); }, 500); } } } } }, { key: "stopRecognition", value: function stopRecognition() { if (this.recognition && this.isRecognizing) { this.recognition.stop(); } } }, { key: "requestPermissionAndStart", value: // Call this on user interaction to request microphone permission function requestPermissionAndStart() { var _this = this; return _async_to_generator(function() { var err; return _ts_generator(this, function(_state) { switch(_state.label){ case 0: if (!_this.recognition) { console.log("Speech recognition not supported."); return [ 2 ]; } _state.label = 1; case 1: _state.trys.push([ 1, 3, , 4 ]); // Attempt to get microphone access (this might prompt the user) return [ 4, navigator.mediaDevices.getUserMedia({ audio: true }) ]; case 2: _state.sent(); console.log("Microphone permission granted."); _this.startRecognition(); return [ 3, 4 ]; case 3: err = _state.sent(); console.error("Microphone permission denied or error:", err); if (_this.onTranscript) { _this.onTranscript("Microphone access denied. Please allow microphone access in your browser settings.", ""); } return [ 3, 4 ]; case 4: return [ 2 ]; } }); })(); } } ]); return SpeechManager; }();