Refactored modules and updated loader.

This commit is contained in:
2025-04-06 18:35:04 +00:00
parent fc693ae695
commit 0ab639fd25
37 changed files with 3530 additions and 5989 deletions
+385
View File
@@ -0,0 +1,385 @@
/**
* Animation Queue Module
* Handles scheduling and executing animations with proper resource management
* and synchronization with TTS
*/
import { BaseModule } from './base-module.js';
class AnimationQueueModule extends BaseModule {
constructor() {
super('animation-queue', 'Animation Queue');
// Module dependencies
this.dependencies = ['tts-player'];
// Queue of scheduled animations/functions
this.timeoutQueue = [];
// 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 {
this.reportProgress(20, "Initializing Animation Queue");
// Try to get the TTS module, but it's not a hard dependency
// We'll check for it again at runtime when needed
this.tts = this.getModule('tts-player');
if (!this.tts) {
console.log("Animation Queue: TTS Player module not found yet, will try again when needed");
}
this.reportProgress(100, "Animation Queue ready");
return true;
} catch (error) {
console.error("Error initializing Animation Queue:", error);
return false;
}
}
/**
* 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 {Object} options - Optional parameters including TTS text
* @returns {number} - Timeout ID for cancellation
*/
schedule(func, delay, options = {}) {
if (typeof func !== 'function') {
console.error('AnimationQueue: Invalid function provided to schedule');
return -1;
}
// 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 = {
func: func,
delay: actualDelay,
timeoutId: null,
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;
}
}
};
// Add to queue
this.timeoutQueue.push(timeoutObject);
// 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.timeoutQueue.indexOf(timeoutObject);
if (index !== -1) {
this.timeoutQueue.splice(index, 1);
}
// If queue is empty and no TTS is speaking, emit animation complete
if (this.timeoutQueue.length === 0 && !this.isAnyTtsSpeaking()) {
this.emitAnimationComplete();
}
}, actualDelay);
return timeoutObject.timeoutId;
}
/**
* 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() {
if (this.timeoutQueue.length === 0) {
console.log('AnimationQueue: No animations to fast forward');
return;
}
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 all pending animations immediately
this.timeoutQueue.forEach(timeout => {
// Clear the timeout
if (timeout.timeoutId !== null) {
clearTimeout(timeout.timeoutId);
timeout.timeoutId = null;
}
// Clear TTS flag
timeout.ttsSpeaking = false;
// Execute the function immediately
timeout.execute();
});
// Clear the queue
this.timeoutQueue = [];
// Reset delay
this.delay = 0;
// 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.timeoutQueue.length} pending items`);
// Clear all timeouts
this.timeoutQueue.forEach(timeoutObject => {
if (timeoutObject.timeoutId !== null) {
clearTimeout(timeoutObject.timeoutId);
}
});
// Clear queue
this.timeoutQueue = [];
// Reset delay
this.delay = 0;
}
/**
* Set the animation speed
* @param {number} speed - Animation speed factor (lower is faster)
*/
setSpeed(speed) {
if (typeof speed !== 'number' || speed <= 0) {
console.error('Animation Queue: Invalid speed value');
return;
}
// 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.timeoutQueue.length;
}
/**
* Get current accumulated delay
* @returns {number} - Current delay in milliseconds
*/
getCurrentDelay() {
return this.delay;
}
}
// Create the singleton instance
const AnimationQueue = new AnimationQueueModule();
// Export the module
export { AnimationQueue };