Improve triggers
This commit is contained in:
@@ -348,43 +348,62 @@
|
|||||||
try {
|
try {
|
||||||
const isActiveProfile = get(activeProfileId) === profileId;
|
const isActiveProfile = get(activeProfileId) === profileId;
|
||||||
|
|
||||||
// Always add to output history for this profile
|
|
||||||
addToOutputHistory(text);
|
|
||||||
|
|
||||||
// Process triggers if available for this profile
|
// Process triggers if available for this profile
|
||||||
|
let processedText = text;
|
||||||
|
let isGagged = false;
|
||||||
|
let triggerMatched = false;
|
||||||
|
|
||||||
if (triggerSystem) {
|
if (triggerSystem) {
|
||||||
try {
|
try {
|
||||||
triggerSystem.processTriggers(text);
|
const result = triggerSystem.processTriggers(text);
|
||||||
|
processedText = result.processed;
|
||||||
|
isGagged = result.gagged;
|
||||||
|
triggerMatched = result.matched;
|
||||||
|
|
||||||
|
console.log(`Trigger processing result - gagged: ${isGagged}, matched: ${triggerMatched}, modified: ${processedText !== text}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error processing triggers for ${profileId}:`, error);
|
console.error(`Error processing triggers for ${profileId}:`, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle text-to-speech
|
// Add to output history if not gagged
|
||||||
console.log(`TTS check for ${profileId}: isTTS=${$accessibilitySettings.textToSpeech}, isActive=${isActiveProfile}, speakAll=${$accessibilitySettings.speakAllProfiles}`);
|
if (!isGagged) {
|
||||||
if (accessibilityManager && $accessibilitySettings.textToSpeech) {
|
addToOutputHistory(processedText);
|
||||||
// Speak if this is active profile OR speakAllProfiles is enabled
|
|
||||||
if (isActiveProfile || $accessibilitySettings.speakAllProfiles) {
|
// Handle text-to-speech for processed text
|
||||||
console.log(`TTS condition satisfied, will attempt to speak for ${profileId}`);
|
console.log(`TTS check for ${profileId}: isTTS=${$accessibilitySettings.textToSpeech}, isActive=${isActiveProfile}, speakAll=${$accessibilitySettings.speakAllProfiles}`);
|
||||||
// Use a small timeout to avoid UI blocking
|
if (accessibilityManager && $accessibilitySettings.textToSpeech) {
|
||||||
setTimeout(() => {
|
// Speak if this is active profile OR speakAllProfiles is enabled
|
||||||
try {
|
if (isActiveProfile || $accessibilitySettings.speakAllProfiles) {
|
||||||
// If not active profile, add profile name prefix for context
|
console.log(`TTS condition satisfied, will attempt to speak for ${profileId}`);
|
||||||
const speechText = isActiveProfile ? text : `From ${getProfileName(profileId)}: ${text}`;
|
// Use a small timeout to avoid UI blocking
|
||||||
console.log(`Speaking text for ${profileId}:`, speechText.substring(0, 50) + (speechText.length > 50 ? '...' : ''));
|
setTimeout(() => {
|
||||||
accessibilityManager.speak(speechText);
|
try {
|
||||||
} catch (error) {
|
// If not active profile, add profile name prefix for context
|
||||||
console.error('Error using text-to-speech:', error);
|
const speechText = isActiveProfile ? processedText : `From ${getProfileName(profileId)}: ${processedText}`;
|
||||||
}
|
console.log(`Speaking text for ${profileId}:`, speechText.substring(0, 50) + (speechText.length > 50 ? '...' : ''));
|
||||||
}, 10);
|
accessibilityManager.speak(speechText);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error using text-to-speech:', error);
|
||||||
|
}
|
||||||
|
}, 10);
|
||||||
|
} else {
|
||||||
|
console.log(`TTS skipped: profile ${profileId} is not active and speakAll is disabled`);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`TTS skipped: profile ${profileId} is not active and speakAll is disabled`);
|
console.log(`TTS not enabled for profile ${profileId} or accessibilityManager is null`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log(`TTS not enabled for profile ${profileId} or accessibilityManager is null`);
|
console.log(`Text was gagged by trigger - not adding to output history: ${text.substring(0, 50)}...`);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch('received', { text });
|
// Dispatch event with information about trigger processing
|
||||||
|
dispatch('received', {
|
||||||
|
originalText: text,
|
||||||
|
processedText: processedText,
|
||||||
|
gagged: isGagged,
|
||||||
|
triggerMatched: triggerMatched
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error handling received data for profile ${profileId}:`, error);
|
console.error(`Error handling received data for profile ${profileId}:`, error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,12 @@
|
|||||||
isRegex: false,
|
isRegex: false,
|
||||||
isEnabled: true,
|
isEnabled: true,
|
||||||
soundFile: '',
|
soundFile: '',
|
||||||
|
soundVolume: 0.7, // Default sound volume
|
||||||
sendText: '',
|
sendText: '',
|
||||||
highlightColor: '',
|
highlightColor: '',
|
||||||
priority: 0
|
priority: 0,
|
||||||
|
replaceText: '', // New text replacement option
|
||||||
|
gag: false // Option to hide matched text
|
||||||
};
|
};
|
||||||
|
|
||||||
// Available sounds
|
// Available sounds
|
||||||
@@ -135,6 +138,64 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if localTrigger.soundFile}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="soundVolume">Sound Volume</label>
|
||||||
|
<div class="range-control">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
id="soundVolume"
|
||||||
|
bind:value={localTrigger.soundVolume}
|
||||||
|
min="0"
|
||||||
|
max="1"
|
||||||
|
step="0.1"
|
||||||
|
/>
|
||||||
|
<span class="range-value">{Math.round(localTrigger.soundVolume * 100)}%</span>
|
||||||
|
</div>
|
||||||
|
<small>Trigger sound volume is multiplied by global volume setting</small>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="textAction">Text Action</label>
|
||||||
|
<select
|
||||||
|
id="textAction"
|
||||||
|
on:change={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
if (val === 'gag') {
|
||||||
|
localTrigger.gag = true;
|
||||||
|
localTrigger.replaceText = '';
|
||||||
|
} else if (val === 'replace') {
|
||||||
|
localTrigger.gag = false;
|
||||||
|
localTrigger.replaceText = localTrigger.replaceText || '';
|
||||||
|
} else {
|
||||||
|
localTrigger.gag = false;
|
||||||
|
localTrigger.replaceText = '';
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={localTrigger.gag ? 'gag' : (localTrigger.replaceText ? 'replace' : 'none')}
|
||||||
|
>
|
||||||
|
<option value="none">No change (show original text)</option>
|
||||||
|
<option value="replace">Replace text</option>
|
||||||
|
<option value="gag">Hide text (gag)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if !localTrigger.gag && localTrigger.replaceText !== undefined && localTrigger.replaceText !== null}
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="replaceText">Replacement Text</label>
|
||||||
|
<textarea
|
||||||
|
id="replaceText"
|
||||||
|
bind:value={localTrigger.replaceText}
|
||||||
|
rows="3"
|
||||||
|
placeholder="Text to replace the matched text with"
|
||||||
|
></textarea>
|
||||||
|
{#if localTrigger.isRegex}
|
||||||
|
<small>Use $1, $2, etc. to reference captured groups</small>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="sendText">Send Text</label>
|
<label for="sendText">Send Text</label>
|
||||||
<input type="text" id="sendText" bind:value={localTrigger.sendText} />
|
<input type="text" id="sendText" bind:value={localTrigger.sendText} />
|
||||||
@@ -306,6 +367,21 @@
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.range-control {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-control input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.range-value {
|
||||||
|
min-width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
.match-list {
|
.match-list {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ export class MudConnection extends EventEmitter {
|
|||||||
// Binary data
|
// Binary data
|
||||||
this.handleIncomingData(new Uint8Array(event.data));
|
this.handleIncomingData(new Uint8Array(event.data));
|
||||||
} else if (typeof event.data === 'string') {
|
} else if (typeof event.data === 'string') {
|
||||||
// Text data
|
// Text data - let listeners process it directly
|
||||||
|
// TriggerSystem will handle gagging and replacing in the component
|
||||||
this.emit('received', event.data);
|
this.emit('received', event.data);
|
||||||
} else if (event.data instanceof Blob) {
|
} else if (event.data instanceof Blob) {
|
||||||
// Blob data (sometimes WebSockets send this instead of ArrayBuffer)
|
// Blob data (sometimes WebSockets send this instead of ArrayBuffer)
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ export interface Trigger {
|
|||||||
isRegex: boolean;
|
isRegex: boolean;
|
||||||
isEnabled: boolean;
|
isEnabled: boolean;
|
||||||
soundFile?: string;
|
soundFile?: string;
|
||||||
|
soundVolume?: number; // Sound volume (0-1)
|
||||||
action?: string;
|
action?: string;
|
||||||
sendText?: string;
|
sendText?: string;
|
||||||
highlightColor?: string;
|
highlightColor?: string;
|
||||||
priority: number;
|
priority: number;
|
||||||
|
replaceText?: string; // New text to replace the matched text with
|
||||||
|
gag?: boolean; // If true, don't display the matched text at all
|
||||||
}
|
}
|
||||||
|
|
||||||
export class TriggerSystem extends EventEmitter {
|
export class TriggerSystem extends EventEmitter {
|
||||||
@@ -91,9 +94,11 @@ export class TriggerSystem extends EventEmitter {
|
|||||||
// Sort triggers by priority
|
// Sort triggers by priority
|
||||||
this.triggers.sort((a, b) => b.priority - a.priority);
|
this.triggers.sort((a, b) => b.priority - a.priority);
|
||||||
|
|
||||||
// Preload sound if specified
|
// Preload sound if specified (now uses async method but doesn't wait)
|
||||||
if (trigger.soundFile) {
|
if (trigger.soundFile) {
|
||||||
this.loadSound(trigger.id, trigger.soundFile);
|
this.loadSound(trigger.id, trigger.soundFile).catch(error => {
|
||||||
|
console.error(`Failed to preload sound for trigger ${trigger.id}:`, error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save triggers to storage
|
// Save triggers to storage
|
||||||
@@ -126,9 +131,18 @@ export class TriggerSystem extends EventEmitter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Process text for triggers
|
* Process text for triggers
|
||||||
|
* @returns Object with information about gagging and replacement
|
||||||
*/
|
*/
|
||||||
public processTriggers(text: string): void {
|
public processTriggers(text: string): {
|
||||||
// Process only enabled triggers
|
processed: string; // Text after all replacements
|
||||||
|
gagged: boolean; // Whether the text should be completely hidden
|
||||||
|
matched: boolean; // Whether any triggers matched
|
||||||
|
} {
|
||||||
|
let processedText = text;
|
||||||
|
let isGagged = false;
|
||||||
|
let anyTriggerMatched = false;
|
||||||
|
|
||||||
|
// Process only enabled triggers in priority order
|
||||||
for (const trigger of this.triggers.filter(t => t.isEnabled)) {
|
for (const trigger of this.triggers.filter(t => t.isEnabled)) {
|
||||||
let matched = false;
|
let matched = false;
|
||||||
let matches: RegExpMatchArray | null = null;
|
let matches: RegExpMatchArray | null = null;
|
||||||
@@ -136,81 +150,209 @@ export class TriggerSystem extends EventEmitter {
|
|||||||
if (trigger.isRegex) {
|
if (trigger.isRegex) {
|
||||||
try {
|
try {
|
||||||
const regex = new RegExp(trigger.pattern, 'g');
|
const regex = new RegExp(trigger.pattern, 'g');
|
||||||
matches = text.match(regex);
|
matches = processedText.match(regex);
|
||||||
matched = matches !== null;
|
matched = matches !== null && matches.length > 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Invalid regex pattern in trigger ${trigger.name}:`, error);
|
console.error(`Invalid regex pattern in trigger ${trigger.name}:`, error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Simple text matching
|
// Simple text matching
|
||||||
matched = text.includes(trigger.pattern);
|
matched = processedText.includes(trigger.pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matched) {
|
if (matched) {
|
||||||
this.executeTrigger(trigger, text, matches);
|
anyTriggerMatched = true;
|
||||||
|
|
||||||
|
// Execute the trigger actions
|
||||||
|
this.executeTrigger(trigger, processedText, matches);
|
||||||
|
|
||||||
|
// Handle gagging - if any trigger gags, the whole line is gagged
|
||||||
|
if (trigger.gag) {
|
||||||
|
isGagged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle text replacement if not gagged
|
||||||
|
if (!isGagged && trigger.replaceText) {
|
||||||
|
if (trigger.isRegex) {
|
||||||
|
try {
|
||||||
|
let replacedText = trigger.replaceText;
|
||||||
|
|
||||||
|
// If we have regex matches, replace $1, $2, etc. with capture groups
|
||||||
|
if (matches && matches.length > 0) {
|
||||||
|
// For actual text replacement, we need to use the regex directly
|
||||||
|
const regex = new RegExp(trigger.pattern, 'g');
|
||||||
|
|
||||||
|
// Use replace function to handle capture groups dynamically
|
||||||
|
processedText = processedText.replace(regex, (match, ...groups) => {
|
||||||
|
// Create a copy of the replacement template
|
||||||
|
let result = trigger.replaceText || '';
|
||||||
|
|
||||||
|
// Replace $1, $2, etc. with the actual captured values
|
||||||
|
for (let i = 0; i < groups.length - 2; i++) { // -2 to skip lastIndex and input
|
||||||
|
const captureValue = groups[i] || '';
|
||||||
|
result = result.replace(new RegExp(`\\$${i + 1}`, 'g'), captureValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error replacing text with regex in trigger ${trigger.name}:`, error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Simple string replacement for non-regex triggers
|
||||||
|
processedText = processedText.replace(
|
||||||
|
new RegExp(this.escapeRegExp(trigger.pattern), 'g'),
|
||||||
|
trigger.replaceText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
processed: processedText,
|
||||||
|
gagged: isGagged,
|
||||||
|
matched: anyTriggerMatched
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a triggered action - simplified for stability
|
* Helper to escape special regex characters in strings
|
||||||
|
*/
|
||||||
|
private escapeRegExp(string: string): string {
|
||||||
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a triggered action with support for advanced features
|
||||||
*/
|
*/
|
||||||
private executeTrigger(trigger: Trigger, text: string, matches: RegExpMatchArray | null): void {
|
private executeTrigger(trigger: Trigger, text: string, matches: RegExpMatchArray | null): void {
|
||||||
// Play sound if specified - with minimal options
|
// Get settings
|
||||||
if (trigger.soundFile && this.sounds.has(trigger.id)) {
|
const uiSettingsValue = get(uiSettings);
|
||||||
try {
|
const globalVolume = uiSettingsValue.globalVolume || 0.7;
|
||||||
const sound = this.sounds.get(trigger.id);
|
|
||||||
|
// Handle sound playback, loading on demand if needed
|
||||||
|
if (trigger.soundFile) {
|
||||||
|
// Try to get the existing sound, or load it if not already loaded
|
||||||
|
const soundPromise = this.sounds.has(trigger.id)
|
||||||
|
? Promise.resolve(this.sounds.get(trigger.id)!)
|
||||||
|
: this.loadSound(trigger.id, trigger.soundFile);
|
||||||
|
|
||||||
|
soundPromise.then(sound => {
|
||||||
if (sound) {
|
if (sound) {
|
||||||
sound.volume(0.7); // Fixed volume for stability
|
// Calculate volume based on trigger-specific volume and global volume
|
||||||
|
const triggerVolume = trigger.soundVolume !== undefined ? trigger.soundVolume : 0.7;
|
||||||
|
const finalVolume = Math.min(triggerVolume * globalVolume, 1.0); // Cap at 1.0
|
||||||
|
|
||||||
|
console.log(`Playing sound with final volume: ${finalVolume} (trigger: ${triggerVolume} * global: ${globalVolume})`);
|
||||||
|
sound.volume(finalVolume);
|
||||||
sound.play();
|
sound.play();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}).catch(error => {
|
||||||
console.error('Error playing sound:', error);
|
console.error(`Error playing sound for trigger ${trigger.id}:`, error);
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send text if specified
|
// Send text if specified - support for regex capture group substitution
|
||||||
if (trigger.sendText) {
|
if (trigger.sendText) {
|
||||||
this.emit('sendText', trigger.sendText);
|
let textToSend = trigger.sendText;
|
||||||
|
|
||||||
|
// If we have regex matches, replace $1, $2, etc. with capture groups
|
||||||
|
if (trigger.isRegex && matches && matches.length > 1) {
|
||||||
|
// matches[0] is the full match, captures start at index 1
|
||||||
|
for (let i = 1; i < matches.length; i++) {
|
||||||
|
const captureGroup = matches[i] || '';
|
||||||
|
textToSend = textToSend.replace(new RegExp(`\\$${i}`, 'g'), captureGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('sendText', textToSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip custom actions for stability
|
// Handle text replacement or gagging
|
||||||
|
if (trigger.gag) {
|
||||||
|
// If gagged, emit a special event to prevent displaying the text
|
||||||
|
this.emit('gagText', text);
|
||||||
|
} else if (trigger.replaceText) {
|
||||||
|
let replacedText = trigger.replaceText;
|
||||||
|
|
||||||
|
// If we have regex matches, replace $1, $2, etc. with capture groups
|
||||||
|
if (trigger.isRegex && matches && matches.length > 1) {
|
||||||
|
for (let i = 1; i < matches.length; i++) {
|
||||||
|
const captureGroup = matches[i] || '';
|
||||||
|
replacedText = replacedText.replace(new RegExp(`\\$${i}`, 'g'), captureGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit the replaced text instead of the original
|
||||||
|
this.emit('replaceText', { original: text, replacement: replacedText });
|
||||||
|
}
|
||||||
|
|
||||||
// Emit highlight event if color specified
|
// Handle highlight if specified
|
||||||
if (trigger.highlightColor) {
|
if (trigger.highlightColor) {
|
||||||
this.emit('highlight', text, trigger.pattern, trigger.highlightColor, trigger.isRegex);
|
this.emit('highlight', text, trigger.pattern, trigger.highlightColor, trigger.isRegex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit basic trigger fired event
|
// Emit basic trigger fired event
|
||||||
this.emit('triggerFired', trigger.id);
|
this.emit('triggerFired', trigger.id);
|
||||||
|
|
||||||
|
// Execute custom action if specified and in browser environment
|
||||||
|
if (trigger.action && typeof window !== 'undefined') {
|
||||||
|
try {
|
||||||
|
// Create a sandboxed function with limited scope
|
||||||
|
const actionFn = new Function('text', 'matches', trigger.action);
|
||||||
|
actionFn(text, matches);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error executing custom action for trigger ${trigger.id}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a sound file for a trigger - simplified version
|
* Load a sound file for a trigger with better error handling and loading status
|
||||||
|
* @returns Promise that resolves with the sound object when loaded
|
||||||
*/
|
*/
|
||||||
private loadSound(triggerId: string, soundFile: string): void {
|
private loadSound(triggerId: string, soundFile: string): Promise<Howl> {
|
||||||
try {
|
return new Promise((resolve, reject) => {
|
||||||
console.log(`Loading sound for trigger ${triggerId}: ${soundFile}`);
|
try {
|
||||||
|
console.log(`Loading sound for trigger ${triggerId}: ${soundFile}`);
|
||||||
// Build path based on whether it's a URL or local file
|
|
||||||
const soundPath = soundFile.startsWith('http') || soundFile.startsWith('/')
|
// If we already have this sound loaded, return it immediately
|
||||||
? soundFile
|
if (this.sounds.has(triggerId)) {
|
||||||
: `/sounds/${soundFile}`;
|
console.log(`Sound for trigger ${triggerId} already loaded, reusing`);
|
||||||
|
resolve(this.sounds.get(triggerId)!);
|
||||||
// Create minimal sound object
|
return;
|
||||||
const sound = new Howl({ src: [soundPath] });
|
}
|
||||||
|
|
||||||
// Set only necessary handlers
|
// Build path based on whether it's a URL or local file
|
||||||
sound.once('load', () => {
|
const soundPath = soundFile.startsWith('http') || soundFile.startsWith('/')
|
||||||
this.sounds.set(triggerId, sound);
|
? soundFile
|
||||||
});
|
: `/sounds/${soundFile}`;
|
||||||
|
|
||||||
sound.once('loaderror', () => {
|
// Create sound object with HTML5 audio to better support streaming
|
||||||
console.error(`Failed to load sound ${soundFile}`);
|
const sound = new Howl({
|
||||||
});
|
src: [soundPath],
|
||||||
} catch (error) {
|
html5: true // Better for streaming and prevents global audio lock
|
||||||
console.error('Error loading sound:', error);
|
});
|
||||||
}
|
|
||||||
|
// Set up handlers
|
||||||
|
sound.once('load', () => {
|
||||||
|
console.log(`Sound for trigger ${triggerId} loaded successfully`);
|
||||||
|
this.sounds.set(triggerId, sound);
|
||||||
|
resolve(sound);
|
||||||
|
});
|
||||||
|
|
||||||
|
sound.once('loaderror', (id, error) => {
|
||||||
|
console.error(`Failed to load sound ${soundFile}:`, error);
|
||||||
|
reject(new Error(`Failed to load sound: ${error || 'Unknown error'}`));
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error setting up sound:', error);
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -275,4 +417,4 @@ export class TriggerSystem extends EventEmitter {
|
|||||||
typeof obj.priority === 'number'
|
typeof obj.priority === 'number'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user