Checkpoint current interactive fiction state
This commit is contained in:
+312
-157
@@ -7,7 +7,7 @@ class UIControllerModule extends BaseModule {
|
||||
|
||||
// Remove 'tts' from direct dependencies to break circular dependency
|
||||
// UI Controller will access TTS through the Game Loop instead
|
||||
this.dependencies = ['animation-queue', 'ui-display-handler', 'ui-input-handler', 'ui-effects', 'text-buffer', 'socket-client'];
|
||||
this.dependencies = ['animation-queue', 'ui-display-handler', 'ui-input-handler', 'ui-effects', 'text-buffer', 'socket-client', 'sentence-queue', 'playback-coordinator'];
|
||||
|
||||
// References to sub-modules
|
||||
this.displayHandler = null;
|
||||
@@ -43,6 +43,12 @@ class UIControllerModule extends BaseModule {
|
||||
'applyBookSizing',
|
||||
'setupEventListeners',
|
||||
'setupMainUI',
|
||||
'bindTopControls',
|
||||
'syncTopControls',
|
||||
'getStoredTtsPreference',
|
||||
'setStoredTtsPreference',
|
||||
'sliderValueFromSpeed',
|
||||
'speedFromSliderValue',
|
||||
'initializeTextBuffer',
|
||||
'showUI',
|
||||
'hideUI',
|
||||
@@ -103,15 +109,25 @@ class UIControllerModule extends BaseModule {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.reportProgress(50, 'Setting up event listeners');
|
||||
|
||||
// Set up event listeners between components
|
||||
this.setupEventListeners();
|
||||
|
||||
this.reportProgress(70, 'Setting up main UI');
|
||||
this.reportProgress(50, 'Setting up main UI');
|
||||
|
||||
// Initialize main UI container
|
||||
await this.setupMainUI();
|
||||
|
||||
this.reportProgress(70, 'Setting up event listeners');
|
||||
|
||||
// Set up event listeners after the display handler has created controls
|
||||
this.setupEventListeners();
|
||||
this.bindTopControls();
|
||||
this.syncTopControls();
|
||||
requestAnimationFrame(() => {
|
||||
this.bindTopControls();
|
||||
this.syncTopControls();
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.bindTopControls();
|
||||
this.syncTopControls();
|
||||
}, 250);
|
||||
|
||||
this.reportProgress(80, 'Initializing text buffer');
|
||||
|
||||
@@ -146,22 +162,45 @@ class UIControllerModule extends BaseModule {
|
||||
this.applyBookSizing();
|
||||
|
||||
// Set up window resize handler
|
||||
window.addEventListener('resize', () => this.applyBookSizing());
|
||||
const handleViewportResize = () => this.applyBookSizing();
|
||||
window.addEventListener('resize', handleViewportResize);
|
||||
if (window.visualViewport) {
|
||||
window.visualViewport.addEventListener('resize', handleViewportResize);
|
||||
}
|
||||
if (window.ResizeObserver && document.body) {
|
||||
this.bodyResizeObserver = new ResizeObserver(handleViewportResize);
|
||||
this.bodyResizeObserver.observe(document.body);
|
||||
}
|
||||
}
|
||||
|
||||
applyBookSizing() {
|
||||
// Apply book sizing based on viewport dimensions
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const aspectRatio = viewportWidth / viewportHeight;
|
||||
const viewportAspectRatio = viewportWidth / viewportHeight;
|
||||
const bookWidth = 2727;
|
||||
const bookHeight = 1691;
|
||||
const bookScale = Math.min(viewportWidth / bookWidth, viewportHeight / bookHeight);
|
||||
|
||||
document.documentElement.style.setProperty('--viewport-aspect-ratio', aspectRatio);
|
||||
|
||||
const maxBookHeight = viewportHeight * 0.9;
|
||||
document.documentElement.style.setProperty('--book-height', `${maxBookHeight}px`);
|
||||
|
||||
const bookWidth = maxBookHeight * Math.min(aspectRatio, 1.613);
|
||||
document.documentElement.style.setProperty('--book-width', `${bookWidth}px`);
|
||||
document.documentElement.style.setProperty('--book-height', `${bookHeight}px`);
|
||||
document.documentElement.style.setProperty('--book-scale', bookScale);
|
||||
document.documentElement.style.setProperty('--viewport-aspect-ratio', viewportAspectRatio);
|
||||
document.documentElement.style.setProperty(
|
||||
'--viewport-dimension',
|
||||
viewportWidth / viewportHeight > bookWidth / bookHeight ? 'vw' : 'vh'
|
||||
);
|
||||
|
||||
document.dispatchEvent(new CustomEvent('book:scaled', {
|
||||
detail: {
|
||||
bookWidth,
|
||||
bookHeight,
|
||||
bookScale,
|
||||
displayWidth: bookWidth * bookScale,
|
||||
displayHeight: bookHeight * bookScale,
|
||||
viewportWidth,
|
||||
viewportHeight
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
@@ -169,62 +208,46 @@ class UIControllerModule extends BaseModule {
|
||||
const saveButton = document.getElementById('save');
|
||||
const loadButton = document.getElementById('reload');
|
||||
const restartButton = document.getElementById('rewind');
|
||||
const speechToggle = document.getElementById('speech');
|
||||
const optionsButton = document.getElementById('options');
|
||||
|
||||
// Get persistence manager module
|
||||
const persistenceManager = this.getModule('persistence-manager');
|
||||
const ttsFactory = this.getModule('tts-factory');
|
||||
|
||||
// Set up save button
|
||||
if (saveButton) {
|
||||
saveButton.addEventListener('click', () => {
|
||||
saveButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
if (saveButton.getAttribute('disabled') === 'disabled') return;
|
||||
document.dispatchEvent(new CustomEvent('ui:game:save'));
|
||||
});
|
||||
}
|
||||
|
||||
// Set up load button
|
||||
if (loadButton) {
|
||||
loadButton.addEventListener('click', () => {
|
||||
loadButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
if (loadButton.getAttribute('disabled') === 'disabled') return;
|
||||
document.dispatchEvent(new CustomEvent('ui:game:load'));
|
||||
});
|
||||
}
|
||||
|
||||
// Set up restart button
|
||||
if (restartButton) {
|
||||
restartButton.addEventListener('click', () => {
|
||||
restartButton.addEventListener('click', (event) => {
|
||||
event.preventDefault();
|
||||
event.__newGameHandled = true;
|
||||
document.dispatchEvent(new CustomEvent('ui:game:restart'));
|
||||
});
|
||||
}
|
||||
|
||||
// Set up speech toggle button
|
||||
if (speechToggle) {
|
||||
// Initialize ttsEnabled from persistence manager
|
||||
if (persistenceManager) {
|
||||
const prefs = persistenceManager.getAllPreferences();
|
||||
this.ttsEnabled = prefs.tts?.enabled ?? false;
|
||||
|
||||
// Update button state
|
||||
this.updateButtonStates();
|
||||
|
||||
this.addEventListener(document, 'click', (event) => {
|
||||
if (event.target && event.target.closest && event.target.closest('#rewind')) {
|
||||
event.preventDefault();
|
||||
if (event.__newGameHandled) return;
|
||||
document.dispatchEvent(new CustomEvent('ui:game:restart'));
|
||||
}
|
||||
|
||||
speechToggle.addEventListener('click', () => {
|
||||
// Toggle TTS state
|
||||
this.ttsEnabled = !this.ttsEnabled;
|
||||
|
||||
// Update UI
|
||||
this.updateButtonStates();
|
||||
|
||||
// Save preference
|
||||
if (persistenceManager) {
|
||||
persistenceManager.updatePreference('tts', 'enabled', this.ttsEnabled);
|
||||
}
|
||||
|
||||
// Notify other components
|
||||
document.dispatchEvent(new CustomEvent('ui:tts:toggle', {
|
||||
detail: { enabled: this.ttsEnabled }
|
||||
}));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Set up options button
|
||||
if (optionsButton) {
|
||||
@@ -232,9 +255,31 @@ class UIControllerModule extends BaseModule {
|
||||
document.dispatchEvent(new CustomEvent('ui:options:toggle'));
|
||||
});
|
||||
}
|
||||
|
||||
this.addEventListener(document, 'ui:command', (event) => {
|
||||
if (!event.detail || event.detail.moduleId === this.id) return;
|
||||
this.handleCommand(event.detail);
|
||||
});
|
||||
|
||||
this.addEventListener(document, 'click', (event) => {
|
||||
if (event.target && event.target.closest && event.target.closest('#options-modal, #controls, #player_input, #command_input')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const playbackCoordinator = this.getModule('playback-coordinator');
|
||||
if (playbackCoordinator && playbackCoordinator.isPlaying) {
|
||||
this.handleCommand({ type: 'continue', source: 'book-click' });
|
||||
}
|
||||
|
||||
if (this.inputHandler && typeof this.inputHandler.focusInput === 'function') {
|
||||
this.inputHandler.focusInput();
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for book events
|
||||
document.addEventListener('book:ready', () => {
|
||||
this.bindTopControls();
|
||||
this.syncTopControls();
|
||||
this.updateButtonStates({
|
||||
canSave: true,
|
||||
canLoad: true,
|
||||
@@ -275,8 +320,9 @@ class UIControllerModule extends BaseModule {
|
||||
this.updateButtonStates();
|
||||
|
||||
// Ensure persistence is updated
|
||||
if (persistenceManager) {
|
||||
persistenceManager.updatePreference('tts', 'enabled', this.ttsEnabled);
|
||||
const currentPersistenceManager = this.getModule('persistence-manager');
|
||||
if (currentPersistenceManager) {
|
||||
currentPersistenceManager.updatePreference('tts', 'enabled', this.ttsEnabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -294,64 +340,26 @@ class UIControllerModule extends BaseModule {
|
||||
this.updateButtonStates();
|
||||
|
||||
// Ensure persistence is updated
|
||||
if (persistenceManager) {
|
||||
persistenceManager.updatePreference('tts', 'enabled', this.ttsEnabled);
|
||||
const currentPersistenceManager = this.getModule('persistence-manager');
|
||||
if (currentPersistenceManager) {
|
||||
currentPersistenceManager.updatePreference('tts', 'enabled', this.ttsEnabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Set up speed slider in main UI
|
||||
const speedSlider = document.getElementById('speed');
|
||||
const speedReset = document.getElementById('speed_reset');
|
||||
|
||||
if (speedSlider) {
|
||||
// Initialize speed from persistence manager
|
||||
if (persistenceManager) {
|
||||
const prefs = persistenceManager.getAllPreferences();
|
||||
// Get the unified speed value (0-1 range)
|
||||
const speed = prefs.tts?.speed ?? 0.5;
|
||||
// Convert to slider range (0-100)
|
||||
speedSlider.value = Math.round(speed * 100);
|
||||
|
||||
document.addEventListener('preference-updated', (event) => {
|
||||
const { category, key, value } = event.detail || {};
|
||||
if (category !== 'tts') {
|
||||
return;
|
||||
}
|
||||
|
||||
speedSlider.addEventListener('input', (e) => {
|
||||
// Convert slider value (0-100) to normalized speed (0-1)
|
||||
const speed = parseInt(e.target.value) / 100;
|
||||
|
||||
// Scale for different TTS engines
|
||||
// This value is used for real-time preview only
|
||||
const rate = this.ttsEnabled ? speed * 2 : 1;
|
||||
|
||||
// Update animation speed
|
||||
document.dispatchEvent(new CustomEvent('animation:speed:change', {
|
||||
detail: { speed: rate }
|
||||
}));
|
||||
|
||||
// Update TTS speed
|
||||
document.dispatchEvent(new CustomEvent('tts:speed:change', {
|
||||
detail: { speed: speed }
|
||||
}));
|
||||
|
||||
// Save preference
|
||||
if (persistenceManager) {
|
||||
persistenceManager.updatePreference('tts', 'speed', speed);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (speedReset) {
|
||||
speedReset.addEventListener('click', () => {
|
||||
// Reset to default speed (0.5)
|
||||
if (speedSlider) {
|
||||
// Default value is 0.5 in normalized form (0-1),
|
||||
// which is 50 in slider range (0-100)
|
||||
speedSlider.value = 50;
|
||||
|
||||
// Trigger the input event to update all components
|
||||
speedSlider.dispatchEvent(new Event('input'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (key === 'enabled') {
|
||||
this.ttsEnabled = value === true;
|
||||
this.syncTopControls();
|
||||
} else if (key === 'speed') {
|
||||
this.syncTopControls();
|
||||
}
|
||||
});
|
||||
|
||||
// Listen for speed change events from other components
|
||||
document.addEventListener('tts:speed:change', (event) => {
|
||||
@@ -359,17 +367,157 @@ class UIControllerModule extends BaseModule {
|
||||
// Update the main UI speed slider
|
||||
const speedSlider = document.getElementById('speed');
|
||||
if (speedSlider) {
|
||||
// Convert normalized speed (0-1) to slider range (0-100)
|
||||
speedSlider.value = Math.round(event.detail.speed * 100);
|
||||
speedSlider.value = this.sliderValueFromSpeed(event.detail.speed);
|
||||
}
|
||||
|
||||
// Save to persistence manager
|
||||
if (persistenceManager) {
|
||||
persistenceManager.updatePreference('tts', 'speed', event.detail.speed);
|
||||
const currentPersistenceManager = this.getModule('persistence-manager');
|
||||
if (currentPersistenceManager) {
|
||||
currentPersistenceManager.updatePreference('tts', 'speed', event.detail.speed);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sliderValueFromSpeed(speed) {
|
||||
const value = Number.isFinite(Number(speed)) ? Number(speed) : 1;
|
||||
return Math.round((Math.max(0.5, Math.min(2.0, value)) * 50) + 50);
|
||||
}
|
||||
|
||||
speedFromSliderValue(value) {
|
||||
const sliderValue = Number.isFinite(Number(value)) ? Number(value) : 50;
|
||||
return Math.max(0.5, Math.min(2.0, (sliderValue - 50) / 50));
|
||||
}
|
||||
|
||||
bindTopControls() {
|
||||
const speechToggle = document.getElementById('speech');
|
||||
const speedSlider = document.getElementById('speed');
|
||||
const speedReset = document.getElementById('speed_reset');
|
||||
|
||||
if (speechToggle && speechToggle.dataset.uiControllerBound !== 'true') {
|
||||
speechToggle.dataset.uiControllerBound = 'true';
|
||||
speechToggle.removeAttribute('disabled');
|
||||
speechToggle.addEventListener('click', async (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const persistenceManager = this.getModule('persistence-manager');
|
||||
const ttsFactory = this.getModule('tts-factory');
|
||||
const currentEnabled = this.getStoredTtsPreference('enabled', this.ttsEnabled);
|
||||
const nextEnabled = !currentEnabled;
|
||||
this.ttsEnabled = nextEnabled;
|
||||
console.log(`UIController: Top speech toggle set to ${nextEnabled ? 'enabled' : 'disabled'}`);
|
||||
|
||||
this.setStoredTtsPreference('enabled', nextEnabled);
|
||||
|
||||
if (ttsFactory) {
|
||||
if (nextEnabled) {
|
||||
const preferredHandler = persistenceManager?.getPreference('tts', 'preferred_handler', 'none') || 'none';
|
||||
if (preferredHandler !== 'none') {
|
||||
await ttsFactory.setActiveHandler(preferredHandler);
|
||||
}
|
||||
} else {
|
||||
await ttsFactory.disableAfterCurrentPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
this.syncTopControls();
|
||||
document.dispatchEvent(new CustomEvent('tts:enabled:change', {
|
||||
detail: { enabled: nextEnabled, source: 'topbar' }
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
if (speedSlider && speedSlider.dataset.uiControllerBound !== 'true') {
|
||||
speedSlider.dataset.uiControllerBound = 'true';
|
||||
speedSlider.min = speedSlider.min || '50';
|
||||
speedSlider.max = speedSlider.max || '150';
|
||||
speedSlider.addEventListener('input', (event) => {
|
||||
const persistenceManager = this.getModule('persistence-manager');
|
||||
const speed = this.speedFromSliderValue(event.target.value);
|
||||
|
||||
document.dispatchEvent(new CustomEvent('animation:speed:change', {
|
||||
detail: { speed: 1 }
|
||||
}));
|
||||
|
||||
document.dispatchEvent(new CustomEvent('tts:speed:change', {
|
||||
detail: { speed }
|
||||
}));
|
||||
|
||||
this.setStoredTtsPreference('speed', speed);
|
||||
});
|
||||
}
|
||||
|
||||
if (speedReset && speedReset.dataset.uiControllerBound !== 'true') {
|
||||
speedReset.dataset.uiControllerBound = 'true';
|
||||
speedReset.addEventListener('click', () => {
|
||||
const slider = document.getElementById('speed');
|
||||
if (slider) {
|
||||
slider.value = this.sliderValueFromSpeed(1);
|
||||
slider.dispatchEvent(new Event('input'));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
syncTopControls() {
|
||||
this.bindTopControls();
|
||||
|
||||
this.ttsEnabled = this.getStoredTtsPreference('enabled', this.ttsEnabled) === true;
|
||||
|
||||
const speedSlider = document.getElementById('speed');
|
||||
if (speedSlider) {
|
||||
const speed = this.getStoredTtsPreference('speed', 1);
|
||||
const value = String(this.sliderValueFromSpeed(speed));
|
||||
if (speedSlider.value !== value) {
|
||||
speedSlider.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
this.updateButtonStates();
|
||||
}
|
||||
|
||||
getStoredTtsPreference(key, defaultValue) {
|
||||
const persistenceManager = this.getModule('persistence-manager');
|
||||
if (persistenceManager && typeof persistenceManager.getPreference === 'function') {
|
||||
const value = persistenceManager.getPreference('tts', key, undefined);
|
||||
if (typeof value !== 'undefined' && value !== null) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const raw = localStorage.getItem('ai-interactive-fiction-preferences');
|
||||
if (raw) {
|
||||
const prefs = JSON.parse(raw);
|
||||
if (prefs && prefs.tts && Object.prototype.hasOwnProperty.call(prefs.tts, key)) {
|
||||
return prefs.tts[key];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('UIController: Failed to read TTS preference fallback:', error);
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
setStoredTtsPreference(key, value) {
|
||||
const persistenceManager = this.getModule('persistence-manager');
|
||||
if (persistenceManager && typeof persistenceManager.updatePreference === 'function') {
|
||||
persistenceManager.updatePreference('tts', key, value);
|
||||
}
|
||||
|
||||
try {
|
||||
const storageKey = 'ai-interactive-fiction-preferences';
|
||||
const raw = localStorage.getItem(storageKey);
|
||||
const prefs = raw ? JSON.parse(raw) : {};
|
||||
prefs.tts = prefs.tts || {};
|
||||
prefs.tts[key] = value;
|
||||
localStorage.setItem(storageKey, JSON.stringify(prefs));
|
||||
} catch (error) {
|
||||
console.warn('UIController: Failed to write TTS preference fallback:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async setupMainUI() {
|
||||
// Ensure all UI components exist
|
||||
@@ -383,36 +531,45 @@ class UIControllerModule extends BaseModule {
|
||||
this.rightPage = document.getElementById('page_right');
|
||||
this.storyElement = document.getElementById('story');
|
||||
}
|
||||
|
||||
if (this.inputHandler && typeof this.inputHandler.focusInput === 'function') {
|
||||
requestAnimationFrame(() => this.inputHandler.focusInput());
|
||||
}
|
||||
}
|
||||
|
||||
initializeTextBuffer() {
|
||||
// Initialize text buffer handling
|
||||
if (this.textBuffer) {
|
||||
console.log('UIController: Setting up text buffer callback');
|
||||
this.textBuffer.setOnSentenceReady((text, callback) => {
|
||||
console.log('UIController: Received sentence from text buffer, displaying');
|
||||
|
||||
// Use the display handler to show text with proper formatting and TTS
|
||||
this.displayText(text)
|
||||
.then(() => {
|
||||
console.log('UIController: Display of sentence completed, continuing...');
|
||||
|
||||
// Signal that we're ready to process the next sentence
|
||||
if (typeof callback === 'function') {
|
||||
// Use a small timeout to prevent potential stack overflow with many sentences
|
||||
setTimeout(() => callback(), 10);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('UIController: Error displaying text:', error);
|
||||
// Continue anyway to prevent blocking
|
||||
if (typeof callback === 'function') callback();
|
||||
});
|
||||
});
|
||||
console.log('UIController: Text buffer callback set up');
|
||||
} else {
|
||||
console.warn('UIController: Text buffer module not found');
|
||||
// Connect SentenceQueue to UIDisplayHandler
|
||||
const sentenceQueue = this.getModule('sentence-queue');
|
||||
const displayHandler = this.getModule('ui-display-handler');
|
||||
|
||||
if (!sentenceQueue || !displayHandler) {
|
||||
console.error('UIController: Required modules not found (sentence-queue or ui-display-handler)');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('UIController: Setting up SentenceQueue → UIDisplayHandler pipeline');
|
||||
|
||||
// Set up callback for when sentences are ready to display
|
||||
sentenceQueue.setOnSentenceReady(async (sentence, callback) => {
|
||||
try {
|
||||
console.log(`UIController: Rendering sentence ${sentence.id}`);
|
||||
await displayHandler.renderSentence(sentence);
|
||||
console.log(`UIController: Sentence ${sentence.id} rendered successfully`);
|
||||
|
||||
// Signal completion to process next sentence
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('UIController: Error rendering sentence:', error);
|
||||
// Still proceed to prevent blocking
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log('UIController: SentenceQueue pipeline configured');
|
||||
}
|
||||
|
||||
handleCommand(command) {
|
||||
@@ -425,8 +582,13 @@ class UIControllerModule extends BaseModule {
|
||||
this.effects.processCommand(command);
|
||||
break;
|
||||
case 'continue':
|
||||
if (this.animationQueue) {
|
||||
this.animationQueue.fastForward();
|
||||
{
|
||||
const playbackCoordinator = this.getModule('playback-coordinator');
|
||||
if (playbackCoordinator && playbackCoordinator.isPlaying) {
|
||||
playbackCoordinator.fastForward();
|
||||
} else if (this.animationQueue) {
|
||||
this.animationQueue.fastForward();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'input':
|
||||
@@ -473,7 +635,7 @@ class UIControllerModule extends BaseModule {
|
||||
const speechToggle = document.getElementById('speech');
|
||||
|
||||
// Update save button state
|
||||
if (saveButton) {
|
||||
if (saveButton && typeof canSave === 'boolean') {
|
||||
if (canSave) {
|
||||
saveButton.removeAttribute('disabled');
|
||||
} else {
|
||||
@@ -482,7 +644,7 @@ class UIControllerModule extends BaseModule {
|
||||
}
|
||||
|
||||
// Update load button state
|
||||
if (loadButton) {
|
||||
if (loadButton && typeof canLoad === 'boolean') {
|
||||
if (canLoad) {
|
||||
loadButton.removeAttribute('disabled');
|
||||
} else {
|
||||
@@ -491,35 +653,28 @@ class UIControllerModule extends BaseModule {
|
||||
}
|
||||
|
||||
// Update restart button state
|
||||
if (restartButton) {
|
||||
if (restartButton && typeof canRestart === 'boolean') {
|
||||
if (canRestart) {
|
||||
restartButton.removeAttribute('disabled');
|
||||
} else {
|
||||
restartButton.setAttribute('disabled', 'disabled');
|
||||
}
|
||||
}
|
||||
|
||||
document.body.dataset.gameRunning = state.gameStarted ? 'true' : 'false';
|
||||
|
||||
// Update speech toggle button state
|
||||
if (speechToggle) {
|
||||
// Update the button appearance based on TTS state using existing styles
|
||||
if (!this.ttsAvailable) {
|
||||
// TTS is not available, disable the button
|
||||
speechToggle.setAttribute('disabled', 'disabled');
|
||||
speechToggle.title = 'Text-to-speech is not available';
|
||||
speechToggle.removeAttribute('disabled');
|
||||
if (this.ttsEnabled) {
|
||||
speechToggle.style.fontWeight = 'bold';
|
||||
speechToggle.style.color = '#000';
|
||||
speechToggle.title = this.ttsAvailable ? 'Disable speech' : 'Speech enabled, selected provider is not ready';
|
||||
} else {
|
||||
// TTS is available, remove disabled attribute
|
||||
speechToggle.removeAttribute('disabled');
|
||||
|
||||
// Update based on whether TTS is enabled
|
||||
if (this.ttsEnabled) {
|
||||
speechToggle.style.fontWeight = 'bold';
|
||||
speechToggle.style.color = '#000';
|
||||
speechToggle.title = 'Disable speech';
|
||||
} else {
|
||||
speechToggle.style.fontWeight = 'normal';
|
||||
speechToggle.style.color = '#999';
|
||||
speechToggle.title = 'Enable speech';
|
||||
}
|
||||
speechToggle.style.fontWeight = 'normal';
|
||||
speechToggle.style.color = '#999';
|
||||
speechToggle.title = 'Enable speech';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user