Fix TTS module initialization and dependency issues. Update module IDs for consistency, improve circular dependency detection, and fix UI Controller event handling.
This commit is contained in:
+272
-91
@@ -5,53 +5,54 @@
|
||||
*/
|
||||
import { BaseModule } from './base-module.js';
|
||||
import { moduleRegistry } from './module-registry.js';
|
||||
import { ModuleEvent } from './base-module.js'; // Add this import
|
||||
|
||||
class AnimationQueueModule extends BaseModule {
|
||||
constructor() {
|
||||
super('animation-queue', 'Animation Queue');
|
||||
|
||||
// Queue of scheduled animations/functions
|
||||
this.queue = [];
|
||||
|
||||
// Animation timing properties
|
||||
this.speed = 0.05; // Base animation speed (seconds per character)
|
||||
this.delay = 0; // Current accumulated delay
|
||||
|
||||
// Module dependencies
|
||||
this.dependencies = ['tts'];
|
||||
this.tts = null; // TTS module reference
|
||||
this.dependencies = [];
|
||||
|
||||
// Fast-forwarding state
|
||||
this.isFastForwarding = false;
|
||||
// Queue of scheduled animations/functions
|
||||
this.timeoutQueue = [];
|
||||
|
||||
// Bind methods
|
||||
this.schedule = this.schedule.bind(this);
|
||||
this.fastForward = this.fastForward.bind(this);
|
||||
this.clearAll = this.clearAll.bind(this);
|
||||
this.setSpeed = this.setSpeed.bind(this);
|
||||
}
|
||||
|
||||
async waitForDependencies() {
|
||||
try {
|
||||
// Wait for TTS module to be available
|
||||
this.tts = moduleRegistry.getModule('tts');
|
||||
|
||||
if (!this.tts) {
|
||||
console.warn("TTS module not ready, Animation Queue will have limited functionality");
|
||||
return true; // Continue anyway
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error waiting for Animation Queue dependencies:", error);
|
||||
return false;
|
||||
}
|
||||
// Animation timing properties - use parent's config system
|
||||
this.updateConfig({
|
||||
speed: 0.05, // Base animation speed (seconds per character)
|
||||
fastForwardEnabled: false
|
||||
});
|
||||
|
||||
this.delay = 0; // Current accumulated delay
|
||||
this.tts = null; // TTS module reference
|
||||
|
||||
// Bind methods using parent's bindMethods utility
|
||||
this.bindMethods([
|
||||
'schedule',
|
||||
'fastForward',
|
||||
'clearAll',
|
||||
'setSpeed',
|
||||
'beginFastForward',
|
||||
'endFastForward',
|
||||
'emitAnimationComplete',
|
||||
'cleanupStaleTasks',
|
||||
'isAnyTtsSpeaking'
|
||||
]);
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
try {
|
||||
// Nothing special to initialize here
|
||||
this.reportProgress(20, "Initializing Animation Queue");
|
||||
|
||||
// We'll try to get the TTS module, but it's not a hard dependency
|
||||
// We'll check for it again at runtime when needed
|
||||
setTimeout(() => {
|
||||
// Try to get TTS module after a delay to allow it to initialize
|
||||
this.tts = this.getModule('tts-player');
|
||||
if (!this.tts) {
|
||||
console.log("Animation Queue: TTS Player module not found yet, will try again when needed");
|
||||
}
|
||||
}, 500);
|
||||
|
||||
this.reportProgress(100, "Animation Queue ready");
|
||||
return true;
|
||||
} catch (error) {
|
||||
@@ -61,107 +62,273 @@ class AnimationQueueModule extends BaseModule {
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a function to execute after a delay
|
||||
* Schedule a function to execute after a delay, with optional TTS synchronization
|
||||
* @param {Function} func - Function to execute
|
||||
* @param {number} delay - Delay in milliseconds
|
||||
* @param {...any} args - Arguments to pass to the function
|
||||
* @returns {Object} - Timeout object that can be used to cancel
|
||||
* @param {Object} options - Optional parameters including TTS text
|
||||
* @returns {number} - Timeout ID for cancellation
|
||||
*/
|
||||
schedule(func, delay, ...args) {
|
||||
schedule(func, delay, options = {}) {
|
||||
if (typeof func !== 'function') {
|
||||
console.error('Animation Queue: Not a function passed to schedule');
|
||||
return null;
|
||||
console.error('AnimationQueue: Invalid function provided to schedule');
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Create timeout object with execute method
|
||||
// Adjust delay based on fast-forward or speed settings
|
||||
const actualDelay = this.config.fastForwardEnabled ? 0 : Math.max(0, delay * this.config.speed);
|
||||
|
||||
// Record the delay for tracking
|
||||
this.delay = Math.max(this.delay, delay);
|
||||
|
||||
// If we don't have a reference to the TTS module yet, try to get it
|
||||
if (!this.tts) {
|
||||
this.tts = this.getModule('tts-player');
|
||||
}
|
||||
|
||||
// Handle TTS if text is provided and TTS is available and enabled
|
||||
let ttsSpeaking = false;
|
||||
if (options.text && this.tts && typeof this.tts.isEnabled === 'function' && this.tts.isEnabled()) {
|
||||
// If we're fast forwarding, don't speak
|
||||
if (!this.config.fastForwardEnabled) {
|
||||
ttsSpeaking = true;
|
||||
// Request TTS to speak the text
|
||||
this.tts.speak(options.text, (result) => {
|
||||
ttsSpeaking = false;
|
||||
|
||||
// Check if this was keeping the queue busy
|
||||
if (this.timeoutQueue.length === 0) {
|
||||
this.emitAnimationComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Create a timeout object
|
||||
const timeoutObject = {
|
||||
execute: () => {
|
||||
try {
|
||||
func(...args);
|
||||
} catch (error) {
|
||||
console.error('Error executing scheduled function:', error);
|
||||
}
|
||||
},
|
||||
func: func,
|
||||
delay: actualDelay,
|
||||
timeoutId: null,
|
||||
createdAt: Date.now(),
|
||||
delay: delay
|
||||
executed: false,
|
||||
startTime: Date.now(),
|
||||
ttsSpeaking: ttsSpeaking,
|
||||
|
||||
// Add an execute method that marks the timeout as executed
|
||||
execute: function() {
|
||||
if (!this.executed) {
|
||||
this.func();
|
||||
this.executed = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Apply speed factor to the delay
|
||||
const adjustedDelay = delay * this.speed;
|
||||
// Add to queue
|
||||
this.timeoutQueue.push(timeoutObject);
|
||||
|
||||
// Schedule execution
|
||||
// If we're fast forwarding, execute immediately
|
||||
if (this.config.fastForwardEnabled) {
|
||||
timeoutObject.execute();
|
||||
return -1; // No timeout ID since it executed immediately
|
||||
}
|
||||
|
||||
// Schedule the timeout
|
||||
timeoutObject.timeoutId = setTimeout(() => {
|
||||
// Execute the function
|
||||
timeoutObject.execute();
|
||||
|
||||
// Remove from queue
|
||||
const index = this.queue.indexOf(timeoutObject);
|
||||
const index = this.timeoutQueue.indexOf(timeoutObject);
|
||||
if (index !== -1) {
|
||||
this.queue.splice(index, 1);
|
||||
this.timeoutQueue.splice(index, 1);
|
||||
}
|
||||
}, adjustedDelay);
|
||||
|
||||
// If queue is empty and no TTS is speaking, emit animation complete
|
||||
if (this.timeoutQueue.length === 0 && !this.isAnyTtsSpeaking()) {
|
||||
this.emitAnimationComplete();
|
||||
}
|
||||
|
||||
}, actualDelay);
|
||||
|
||||
// Add to queue
|
||||
this.queue.push(timeoutObject);
|
||||
|
||||
// Update current total delay
|
||||
this.delay = adjustedDelay + delay;
|
||||
|
||||
return timeoutObject;
|
||||
return timeoutObject.timeoutId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast-forward all pending animations
|
||||
* Emit an animation complete event
|
||||
*/
|
||||
emitAnimationComplete() {
|
||||
// Only emit if queue is empty and no TTS is speaking
|
||||
if (this.timeoutQueue.length === 0 && !this.isAnyTtsSpeaking()) {
|
||||
// Use parent's dispatchEvent method
|
||||
this.dispatchEvent('ui:animation:complete', {
|
||||
timestamp: Date.now()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up any animation tasks that might have been missed
|
||||
* (e.g. due to browser tab being inactive)
|
||||
*/
|
||||
cleanupStaleTasks() {
|
||||
const now = Date.now();
|
||||
const staleTasks = [];
|
||||
|
||||
// Find stale tasks
|
||||
this.timeoutQueue.forEach(task => {
|
||||
// If task has been running for more than 10 seconds, consider it stale
|
||||
if (now - task.startTime > 10000 && !task.executed) {
|
||||
staleTasks.push(task);
|
||||
}
|
||||
});
|
||||
|
||||
// Execute and remove stale tasks
|
||||
staleTasks.forEach(task => {
|
||||
console.log('AnimationQueue: Cleaning up stale task');
|
||||
|
||||
// Clear the timeout
|
||||
if (task.timeoutId !== null) {
|
||||
clearTimeout(task.timeoutId);
|
||||
}
|
||||
|
||||
// Execute the task
|
||||
task.execute();
|
||||
|
||||
// Remove from queue
|
||||
const index = this.timeoutQueue.indexOf(task);
|
||||
if (index !== -1) {
|
||||
this.timeoutQueue.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if any TTS is currently speaking
|
||||
* @returns {boolean} - True if TTS is speaking
|
||||
*/
|
||||
isAnyTtsSpeaking() {
|
||||
// If we don't have a reference to the TTS module yet, try to get it
|
||||
if (!this.tts) {
|
||||
this.tts = this.getModule('tts-player');
|
||||
}
|
||||
|
||||
// Check if TTS is speaking
|
||||
if (this.tts && typeof this.tts.isSpeaking === 'function') {
|
||||
return this.tts.isSpeaking();
|
||||
}
|
||||
|
||||
// Default to false if TTS module is not available
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast forward all pending animations and stop TTS
|
||||
*/
|
||||
fastForward() {
|
||||
console.log(`Animation Queue: Fast-forwarding ${this.queue.length} pending items`);
|
||||
if (this.timeoutQueue.length === 0) {
|
||||
console.log('AnimationQueue: No animations to fast forward');
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop TTS if playing
|
||||
if (this.tts) {
|
||||
console.log(`AnimationQueue: Fast forwarding ${this.timeoutQueue.length} pending items`);
|
||||
|
||||
// If we don't have a reference to the TTS module yet, try to get it
|
||||
if (!this.tts) {
|
||||
this.tts = this.getModule('tts-player');
|
||||
}
|
||||
|
||||
// Stop any active TTS
|
||||
if (this.tts && typeof this.tts.stop === 'function') {
|
||||
this.tts.stop();
|
||||
}
|
||||
|
||||
// Execute and clear all timeouts
|
||||
const queueCopy = [...this.queue]; // Make a copy to avoid modification during iteration
|
||||
|
||||
queueCopy.forEach(timeoutObject => {
|
||||
// Clear timeout
|
||||
if (timeoutObject.timeoutId !== null) {
|
||||
clearTimeout(timeoutObject.timeoutId);
|
||||
// Execute all pending animations immediately
|
||||
this.timeoutQueue.forEach(timeout => {
|
||||
// Clear the timeout
|
||||
if (timeout.timeoutId !== null) {
|
||||
clearTimeout(timeout.timeoutId);
|
||||
timeout.timeoutId = null;
|
||||
}
|
||||
|
||||
// Execute immediately
|
||||
timeoutObject.execute();
|
||||
// Clear TTS flag
|
||||
timeout.ttsSpeaking = false;
|
||||
|
||||
// Execute the function immediately
|
||||
timeout.execute();
|
||||
});
|
||||
|
||||
// Clear queue
|
||||
this.queue = [];
|
||||
// Clear the queue
|
||||
this.timeoutQueue = [];
|
||||
|
||||
// Reset delay
|
||||
this.delay = 0;
|
||||
|
||||
// Use direct DOM event dispatch instead of this.dispatchEvent
|
||||
document.dispatchEvent(new CustomEvent('animations:fastForwarded', {
|
||||
detail: { moduleId: this.id }
|
||||
}));
|
||||
// Update config using parent's updateConfig method
|
||||
this.updateConfig({ fastForwardEnabled: false });
|
||||
|
||||
// Emit animation complete event
|
||||
this.emitAnimationComplete();
|
||||
|
||||
// Log the fastforward completion
|
||||
console.log('AnimationQueue: Fast forward complete');
|
||||
|
||||
// Use parent's dispatchEvent method
|
||||
this.dispatchEvent('ui:animation:fastforward', { state: false });
|
||||
}
|
||||
|
||||
/**
|
||||
* Begin fast forwarding mode
|
||||
*/
|
||||
beginFastForward() {
|
||||
if (this.config.fastForwardEnabled) return;
|
||||
|
||||
// Update config using parent's updateConfig method
|
||||
this.updateConfig({ fastForwardEnabled: true });
|
||||
|
||||
// If we don't have a reference to the TTS module yet, try to get it
|
||||
if (!this.tts) {
|
||||
this.tts = this.getModule('tts-player');
|
||||
}
|
||||
|
||||
// Stop any active TTS
|
||||
if (this.tts && typeof this.tts.stop === 'function') {
|
||||
this.tts.stop();
|
||||
}
|
||||
|
||||
// Use parent's dispatchEvent method
|
||||
this.dispatchEvent('ui:animation:fastforward', { state: true });
|
||||
|
||||
console.log('AnimationQueue: Fast forward mode activated');
|
||||
}
|
||||
|
||||
/**
|
||||
* End fast forwarding mode
|
||||
*/
|
||||
endFastForward() {
|
||||
if (!this.config.fastForwardEnabled) return;
|
||||
|
||||
// Update config using parent's updateConfig method
|
||||
this.updateConfig({ fastForwardEnabled: false });
|
||||
|
||||
// Use parent's dispatchEvent method
|
||||
this.dispatchEvent('ui:animation:fastforward', { state: false });
|
||||
|
||||
console.log('AnimationQueue: Fast forward mode deactivated');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all scheduled animations without executing them
|
||||
*/
|
||||
clearAll() {
|
||||
console.log(`Animation Queue: Clearing ${this.queue.length} pending items`);
|
||||
console.log(`Animation Queue: Clearing ${this.timeoutQueue.length} pending items`);
|
||||
|
||||
// Clear all timeouts
|
||||
this.queue.forEach(timeoutObject => {
|
||||
this.timeoutQueue.forEach(timeoutObject => {
|
||||
if (timeoutObject.timeoutId !== null) {
|
||||
clearTimeout(timeoutObject.timeoutId);
|
||||
}
|
||||
});
|
||||
|
||||
// Clear queue
|
||||
this.queue = [];
|
||||
this.timeoutQueue = [];
|
||||
|
||||
// Reset delay
|
||||
this.delay = 0;
|
||||
@@ -177,16 +344,33 @@ class AnimationQueueModule extends BaseModule {
|
||||
return;
|
||||
}
|
||||
|
||||
this.speed = speed;
|
||||
// Update config using parent's updateConfig method
|
||||
this.updateConfig({ speed });
|
||||
console.log(`Animation Queue: Speed set to ${speed}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current animation speed
|
||||
* @returns {number} - Current animation speed factor
|
||||
*/
|
||||
getSpeed() {
|
||||
return this.config.speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if fast forwarding is active
|
||||
* @returns {boolean} - Whether fast forwarding is active
|
||||
*/
|
||||
isFastForwarding() {
|
||||
return this.config.fastForwardEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current queue length
|
||||
* @returns {number} - Number of items in the queue
|
||||
*/
|
||||
getQueueLength() {
|
||||
return this.queue.length;
|
||||
return this.timeoutQueue.length;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,6 +390,3 @@ moduleRegistry.register(AnimationQueue);
|
||||
|
||||
// Export the module
|
||||
export { AnimationQueue };
|
||||
|
||||
// Keep a reference in window for loader system
|
||||
window.AnimationQueue = AnimationQueue;
|
||||
|
||||
Reference in New Issue
Block a user