Fixed kokoro loading process.
This commit is contained in:
@@ -31,8 +31,12 @@ class PersistenceManagerModule extends BaseModule {
|
||||
this.defaultPreferences = {
|
||||
tts: {
|
||||
enabled: false,
|
||||
provider: 'none',
|
||||
preferred_handler: 'none',
|
||||
voice: '',
|
||||
'elevenlabs-tts_api_key': '',
|
||||
'elevenlabs-tts_api_url': 'https://api.elevenlabs.io/v1',
|
||||
'openai-tts_api_key': '',
|
||||
'openai-tts_api_url': 'https://api.openai.com/v1'
|
||||
},
|
||||
audio: {
|
||||
masterVolume: 1.0,
|
||||
@@ -58,7 +62,11 @@ class PersistenceManagerModule extends BaseModule {
|
||||
'createSaveSlot',
|
||||
'loadSaveSlot',
|
||||
'deleteSaveSlot',
|
||||
'getAllSaveSlots'
|
||||
'getAllSaveSlots',
|
||||
'createBinding',
|
||||
'updateElementFromPreference',
|
||||
'updatePreferenceFromElement',
|
||||
'setupBindings'
|
||||
]);
|
||||
|
||||
// Remove circular dependency
|
||||
@@ -246,19 +254,14 @@ class PersistenceManagerModule extends BaseModule {
|
||||
* @returns {boolean} - Success status
|
||||
*/
|
||||
updatePreference(category, setting, value) {
|
||||
if (!category || !setting) return false;
|
||||
if (!this.preferences) return false;
|
||||
|
||||
// Ensure preferences are loaded
|
||||
if (!this.preferences) {
|
||||
this.loadPreferences();
|
||||
}
|
||||
|
||||
// Create category if it doesn't exist
|
||||
// Ensure category exists
|
||||
if (!this.preferences[category]) {
|
||||
this.preferences[category] = {};
|
||||
}
|
||||
|
||||
// Update preference
|
||||
// Store value
|
||||
this.preferences[category][setting] = value;
|
||||
|
||||
// Save preferences
|
||||
@@ -268,7 +271,7 @@ class PersistenceManagerModule extends BaseModule {
|
||||
// Dispatch event
|
||||
this.dispatchEvent('preference-updated', {
|
||||
category,
|
||||
setting,
|
||||
key: setting,
|
||||
value,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
@@ -469,6 +472,201 @@ class PersistenceManagerModule extends BaseModule {
|
||||
return this.saveSlots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a binding between a DOM element and a preference
|
||||
* @param {HTMLElement} element - Element to bind to
|
||||
* @param {string} category - Preference category
|
||||
* @param {string} key - Preference key
|
||||
* @param {Object} options - Additional options (transformers, etc)
|
||||
* @returns {Object} - Binding control object
|
||||
*/
|
||||
createBinding(element, category, key, options = {}) {
|
||||
if (!element) return null;
|
||||
|
||||
const transformer = options.transformer || {
|
||||
toElement: (value) => value,
|
||||
toPreference: (value) => value
|
||||
};
|
||||
|
||||
// Store binding info on the element
|
||||
element._prefBinding = { category, key, transformer };
|
||||
|
||||
// Set initial value
|
||||
this.updateElementFromPreference(element);
|
||||
|
||||
// Set up event listeners
|
||||
const eventHandler = () => this.updatePreferenceFromElement(element);
|
||||
|
||||
// Choose appropriate events based on element type
|
||||
let events = ['change'];
|
||||
if (element.type !== 'checkbox' && element.type !== 'radio' && element.tagName !== 'SELECT') {
|
||||
events.push('input');
|
||||
}
|
||||
|
||||
// Attach event listeners
|
||||
events.forEach(event => {
|
||||
element.addEventListener(event, eventHandler);
|
||||
});
|
||||
|
||||
// Return control object
|
||||
return {
|
||||
update: () => this.updateElementFromPreference(element),
|
||||
destroy: () => {
|
||||
events.forEach(event => {
|
||||
element.removeEventListener(event, eventHandler);
|
||||
});
|
||||
delete element._prefBinding;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an element value from its bound preference
|
||||
* @param {HTMLElement} element - The bound element
|
||||
*/
|
||||
updateElementFromPreference(element) {
|
||||
if (!element || !element._prefBinding) return;
|
||||
|
||||
const { category, key, transformer } = element._prefBinding;
|
||||
const value = this.getPreference(category, key);
|
||||
const transformedValue = transformer.toElement(value);
|
||||
|
||||
// Set element value based on its type
|
||||
if (element.type === 'checkbox') {
|
||||
element.checked = !!transformedValue;
|
||||
} else if (element.type === 'radio') {
|
||||
element.checked = element.value === String(transformedValue);
|
||||
} else if (element.tagName === 'SELECT') {
|
||||
element.value = transformedValue;
|
||||
} else {
|
||||
element.value = transformedValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a preference from its bound element
|
||||
* @param {HTMLElement} element - The bound element
|
||||
*/
|
||||
updatePreferenceFromElement(element) {
|
||||
if (!element || !element._prefBinding) return;
|
||||
|
||||
const { category, key, transformer } = element._prefBinding;
|
||||
let value;
|
||||
|
||||
// Get element value based on its type
|
||||
if (element.type === 'checkbox') {
|
||||
value = element.checked;
|
||||
} else if (element.type === 'radio') {
|
||||
value = element.checked ? element.value : null;
|
||||
} else {
|
||||
value = element.value;
|
||||
}
|
||||
|
||||
const transformedValue = transformer.toPreference(value);
|
||||
this.updatePreference(category, key, transformedValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up bindings for all elements with data-pref-bind attributes
|
||||
* @param {string} rootSelector - Root selector to search within
|
||||
* @returns {Array} - Array of binding control objects
|
||||
*/
|
||||
setupBindings(rootSelector = 'body') {
|
||||
const root = document.querySelector(rootSelector);
|
||||
if (!root) return [];
|
||||
|
||||
const bindings = [];
|
||||
const elements = root.querySelectorAll('[data-pref-bind]');
|
||||
|
||||
elements.forEach(element => {
|
||||
const bindingStr = element.dataset.prefBind;
|
||||
if (!bindingStr) return;
|
||||
|
||||
const [category, key] = bindingStr.split('.');
|
||||
if (!category || !key) return;
|
||||
|
||||
// Parse transformer if specified
|
||||
let transformer = {
|
||||
toElement: (value) => value,
|
||||
toPreference: (value) => value
|
||||
};
|
||||
|
||||
// Handle range transformations
|
||||
if (element.type === 'range' && element.hasAttribute('min') && element.hasAttribute('max')) {
|
||||
const min = parseInt(element.getAttribute('min'), 10) || 0;
|
||||
const max = parseInt(element.getAttribute('max'), 10) || 100;
|
||||
|
||||
transformer = {
|
||||
toElement: (value) => {
|
||||
// Convert from 0-1 to min-max
|
||||
return Math.round(value * (max - min) + min);
|
||||
},
|
||||
toPreference: (value) => {
|
||||
// Convert from min-max to 0-1
|
||||
return (parseInt(value, 10) - min) / (max - min);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Custom transformer via data attribute
|
||||
if (element.dataset.prefTransform) {
|
||||
try {
|
||||
// Check if it's a range transformer in format 'range:min,max'
|
||||
if (element.dataset.prefTransform.startsWith('range:')) {
|
||||
const rangeValues = element.dataset.prefTransform.substring(6).split(',');
|
||||
if (rangeValues.length === 2) {
|
||||
const min = parseFloat(rangeValues[0]);
|
||||
const max = parseFloat(rangeValues[1]);
|
||||
|
||||
if (!isNaN(min) && !isNaN(max)) {
|
||||
transformer = {
|
||||
toElement: (value) => {
|
||||
// Convert from min-max to 0-100 for the slider
|
||||
return Math.round(((value - min) / (max - min)) * 100);
|
||||
},
|
||||
toPreference: (value) => {
|
||||
// Convert from 0-100 to min-max
|
||||
return min + (parseInt(value, 10) / 100) * (max - min);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Try to parse as JSON for backward compatibility
|
||||
const customTransformer = JSON.parse(element.dataset.prefTransform);
|
||||
if (customTransformer && typeof customTransformer === 'object') {
|
||||
transformer = customTransformer;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Invalid transformer data attribute', e);
|
||||
}
|
||||
}
|
||||
|
||||
const binding = this.createBinding(element, category, key, { transformer });
|
||||
if (binding) {
|
||||
bindings.push(binding);
|
||||
}
|
||||
});
|
||||
|
||||
// Set up event listener for preference changes from other sources
|
||||
document.addEventListener('preference-updated', (event) => {
|
||||
const { category, key } = event.detail;
|
||||
|
||||
// Update any matching elements
|
||||
elements.forEach(element => {
|
||||
if (!element._prefBinding) return;
|
||||
|
||||
if (element._prefBinding.category === category &&
|
||||
element._prefBinding.key === key) {
|
||||
this.updateElementFromPreference(element);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up when module is disposed
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user