Checkpoint current interactive fiction state

This commit is contained in:
2026-05-14 21:17:43 +02:00
parent c745efd1d2
commit 873049f7e6
183 changed files with 13755 additions and 1459 deletions
+127 -14
View File
@@ -45,7 +45,8 @@ class OptionsUIModule extends BaseModule {
'setupInitialState',
'dispatchApiChangeEvent',
'getPreference',
'updatePreference'
'updatePreference',
'renderProviderStatuses'
]);
}
@@ -183,20 +184,21 @@ class OptionsUIModule extends BaseModule {
const speedValue = document.createElement('span');
speedValue.className = 'slider-value';
speedValue.textContent = '100%';
this.elements.ttsSpeedValue = speedValue;
speedContainer.appendChild(speedValue);
this.elements.ttsSpeed = createUIElement('input', {
type: 'range',
min: 50,
max: 200,
max: 150,
value: 100,
'data-pref-bind': 'app.speed',
'data-pref-transform': 'range:0.5,2.0'
'data-pref-bind': 'tts.speed',
'data-pref-transform': 'centered-speed'
}, null, speedContainer);
// Update displayed value when slider changes
this.elements.ttsSpeed.addEventListener('input', () => {
speedValue.textContent = `${this.elements.ttsSpeed.value}%`;
this.updateSpeedDisplay();
});
appSettingsSection.appendChild(speedContainer);
@@ -239,6 +241,11 @@ class OptionsUIModule extends BaseModule {
}, null, ttsSystemContainer);
ttsSection.appendChild(ttsSystemContainer);
const providerStatusContainer = document.createElement('div');
providerStatusContainer.className = 'provider-status-list';
this.elements.providerStatus = providerStatusContainer;
ttsSection.appendChild(providerStatusContainer);
// TTS Voice
const ttsVoiceContainer = document.createElement('div');
@@ -510,7 +517,12 @@ class OptionsUIModule extends BaseModule {
if (this.elements.ttsSystem) {
this.elements.ttsSystem.addEventListener('change', async (event) => {
this.updateApiSettingsVisibility(event.target.value);
const ttsFactory = this.getModule('tts-factory');
if (ttsFactory) {
await ttsFactory.refreshHandlerStatus(event.target.value);
}
await this.populateVoices();
this.renderProviderStatuses();
});
}
@@ -595,6 +607,7 @@ class OptionsUIModule extends BaseModule {
// Update API settings visibility
this.updateApiSettingsVisibility(this.elements.ttsSystem.value);
this.renderProviderStatuses();
}
/**
@@ -604,8 +617,10 @@ class OptionsUIModule extends BaseModule {
const ttsFactory = this.getModule('tts-factory');
if (!ttsFactory || !this.elements.ttsVoice) return;
// Get voices for current TTS system
const voices = await ttsFactory.getVoices() || [];
const selectedHandler = this.elements.ttsSystem?.value || this.getPreference('tts', 'preferred_handler', 'none');
const voices = typeof ttsFactory.getVoicesForHandler === 'function'
? await ttsFactory.getVoicesForHandler(selectedHandler) || []
: await ttsFactory.getVoices() || [];
console.log('Options UI: TTS voices:', voices);
// Populate dropdown
@@ -614,9 +629,35 @@ class OptionsUIModule extends BaseModule {
voices,
'id',
'name',
this.getPreference('tts', 'voice', '')
this.getPreference('tts', `${selectedHandler}_voice`, this.getPreference('tts', 'voice', ''))
);
}
renderProviderStatuses() {
const container = this.elements.providerStatus;
const ttsFactory = this.getModule('tts-factory');
if (!container || !ttsFactory || typeof ttsFactory.getHandlerStatuses !== 'function') {
return;
}
container.innerHTML = '';
const statuses = ttsFactory.getHandlerStatuses();
statuses.forEach(status => {
const row = document.createElement('div');
row.className = 'provider-status-row';
const name = document.createElement('span');
name.textContent = status.name;
row.appendChild(name);
const value = document.createElement('span');
value.className = 'provider-status-value';
value.textContent = `${status.ready ? 'ready' : 'not ready'} - ${status.message}`;
row.appendChild(value);
container.appendChild(row);
});
}
/**
* Populate the languages dropdown
@@ -737,6 +778,7 @@ class OptionsUIModule extends BaseModule {
document.addEventListener('tts:voices:updated', () => {
console.log('Options UI: Received tts:voices:updated event, updating voice dropdown');
this.populateVoices();
this.renderProviderStatuses();
});
// Set up language change listener
@@ -750,6 +792,36 @@ class OptionsUIModule extends BaseModule {
console.log('Options UI: Received TTS engine change event:', event.detail);
await this.populateVoices();
this.updateApiSettingsVisibility(this.elements.ttsSystem.value);
this.renderProviderStatuses();
});
document.addEventListener('tts:status:updated', () => {
this.renderProviderStatuses();
});
document.addEventListener('tts:enabled:change', async (event) => {
if (!event.detail || typeof event.detail.enabled !== 'boolean') {
return;
}
if (this.elements.ttsEnabled) {
this.elements.ttsEnabled.checked = event.detail.enabled;
}
const ttsFactory = this.getModule('tts-factory');
if (!ttsFactory) {
return;
}
if (event.detail.enabled) {
const preferredHandler = this.getPreference('tts', 'preferred_handler', 'none');
if (preferredHandler !== 'none') {
await ttsFactory.setActiveHandler(preferredHandler);
}
} else {
await ttsFactory.disableAfterCurrentPlayback();
}
this.renderProviderStatuses();
});
}
@@ -766,6 +838,7 @@ class OptionsUIModule extends BaseModule {
// Setup all bindings in the modal
this.bindings = persistenceManager.setupBindings('#options-modal');
console.log('Options UI: Preference bindings set up', this.bindings.length);
this.updateSpeedDisplay();
// Add event listeners for side effects when preferences change
document.addEventListener('preference-updated', (event) => {
@@ -793,16 +866,46 @@ class OptionsUIModule extends BaseModule {
if (!ttsFactory) return;
if (key === 'preferred_handler') {
this.populateVoices();
const enabled = this.getPreference('tts', 'enabled', false);
const activation = enabled && value !== 'none'
? ttsFactory.setActiveHandler(value)
: Promise.resolve(ttsFactory.disableAfterCurrentPlayback());
activation.then(() => {
this.populateVoices();
this.renderProviderStatuses();
});
this.updateApiSettingsVisibility(value);
} else if (key === 'voice') {
ttsFactory.configure({ voice: value });
} else if (key === 'speed') {
ttsFactory.configure({ speed: value });
} else if (key === 'language') {
ttsFactory.configure({ language: value });
} else if (key === 'enabled') {
ttsFactory.configure({ enabled: value });
}
if (!value) {
ttsFactory.disableAfterCurrentPlayback();
} else {
const preferredHandler = this.getPreference('tts', 'preferred_handler', 'none');
if (preferredHandler !== 'none') {
ttsFactory.setActiveHandler(preferredHandler);
}
}
document.dispatchEvent(new CustomEvent('tts:enabled:change', {
detail: { enabled: value }
}));
} else if (key.endsWith('_api_key')) {
const provider = key.replace('_api_key', '');
this.dispatchApiChangeEvent('api:keyChanged', provider, 'key', value);
ttsFactory.refreshHandlerStatus(provider).then(() => this.renderProviderStatuses());
} else if (key.endsWith('_api_url')) {
const provider = key.replace('_api_url', '');
this.dispatchApiChangeEvent('api:urlChanged', provider, 'url', value);
ttsFactory.refreshHandlerStatus(provider).then(() => this.renderProviderStatuses());
}
if (key === 'speed' && this.elements.ttsSpeed) {
this.updateSpeedDisplay();
}
}
// Handle locale changes
if (category === 'app' && key === 'locale') {
@@ -810,16 +913,26 @@ class OptionsUIModule extends BaseModule {
if (localization) {
localization.setLocale(value);
}
const ttsFactory = this.getModule('tts-factory');
if (ttsFactory) {
ttsFactory.configure({ language: value });
}
this.updatePreference('tts', 'language', value);
}
});
}
updateSpeedDisplay() {
if (!this.elements.ttsSpeed || !this.elements.ttsSpeedValue) {
return;
}
this.elements.ttsSpeedValue.textContent = `${this.elements.ttsSpeed.value}%`;
}
}
// Create the singleton instance
const OptionsUI = new OptionsUIModule();
// Register with the module registry
moduleRegistry.register(OptionsUI);
// Export the module
export { OptionsUI };