409 lines
16 KiB
JavaScript
409 lines
16 KiB
JavaScript
/**
|
|
* Settings Panel Module
|
|
* Handles user settings UI and persistence
|
|
*/
|
|
|
|
const Settings = {
|
|
// Current settings state
|
|
current: {},
|
|
|
|
// DOM elements
|
|
elements: {},
|
|
|
|
/**
|
|
* Initialize the settings module
|
|
*/
|
|
init() {
|
|
this.cacheElements();
|
|
this.bindEvents();
|
|
},
|
|
|
|
/**
|
|
* Cache DOM element references
|
|
*/
|
|
cacheElements() {
|
|
this.elements = {
|
|
panel: document.getElementById('settings-panel'),
|
|
overlay: document.getElementById('overlay'),
|
|
btnSettings: document.getElementById('btn-settings'),
|
|
btnClose: document.getElementById('btn-close-settings'),
|
|
btnSave: document.getElementById('btn-save-settings'),
|
|
btnReset: document.getElementById('btn-reset-settings'),
|
|
|
|
// Mic text settings
|
|
fontFamily: document.getElementById('font-family'),
|
|
fontSize: document.getElementById('font-size'),
|
|
fontSizeValue: document.getElementById('font-size-value'),
|
|
fontWeight: document.getElementById('font-weight'),
|
|
textColor: document.getElementById('text-color'),
|
|
textAlign: document.getElementById('text-align'),
|
|
|
|
// Mic background settings
|
|
backgroundColor: document.getElementById('background-color'),
|
|
backgroundOpacity: document.getElementById('background-opacity'),
|
|
opacityValue: document.getElementById('opacity-value'),
|
|
borderRadius: document.getElementById('border-radius'),
|
|
radiusValue: document.getElementById('radius-value'),
|
|
padding: document.getElementById('padding'),
|
|
paddingValue: document.getElementById('padding-value'),
|
|
|
|
// Mic behavior settings
|
|
maxWords: document.getElementById('max-words'),
|
|
maxWordsValue: document.getElementById('max-words-value'),
|
|
|
|
// Desktop text settings
|
|
desktopFontFamily: document.getElementById('desktop-font-family'),
|
|
desktopFontSize: document.getElementById('desktop-font-size'),
|
|
desktopFontSizeValue: document.getElementById('desktop-font-size-value'),
|
|
desktopFontWeight: document.getElementById('desktop-font-weight'),
|
|
desktopTextColor: document.getElementById('desktop-text-color'),
|
|
desktopTextAlign: document.getElementById('desktop-text-align'),
|
|
|
|
// Desktop background settings
|
|
desktopBackgroundColor: document.getElementById('desktop-background-color'),
|
|
desktopBackgroundOpacity: document.getElementById('desktop-background-opacity'),
|
|
desktopOpacityValue: document.getElementById('desktop-opacity-value'),
|
|
desktopBorderRadius: document.getElementById('desktop-border-radius'),
|
|
desktopRadiusValue: document.getElementById('desktop-radius-value'),
|
|
desktopPadding: document.getElementById('desktop-padding'),
|
|
desktopPaddingValue: document.getElementById('desktop-padding-value'),
|
|
|
|
// Desktop behavior settings
|
|
desktopMaxWords: document.getElementById('desktop-max-words'),
|
|
desktopMaxWordsValue: document.getElementById('desktop-max-words-value'),
|
|
|
|
// Caption displays
|
|
captionContainer: document.getElementById('caption-container'),
|
|
desktopCaptionContainer: document.getElementById('desktop-caption-container'),
|
|
|
|
// Section toggles
|
|
sectionToggles: document.querySelectorAll('.section-toggle'),
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Bind event listeners
|
|
*/
|
|
bindEvents() {
|
|
// Panel open/close
|
|
this.elements.btnSettings.addEventListener('click', () => this.openPanel());
|
|
this.elements.btnClose.addEventListener('click', () => this.closePanel());
|
|
this.elements.overlay.addEventListener('click', () => this.closePanel());
|
|
|
|
// Save/Reset
|
|
this.elements.btnSave.addEventListener('click', () => this.saveSettings());
|
|
this.elements.btnReset.addEventListener('click', () => this.resetSettings());
|
|
|
|
// Section toggle collapse/expand
|
|
this.elements.sectionToggles.forEach(toggle => {
|
|
toggle.addEventListener('click', () => this.toggleSection(toggle));
|
|
});
|
|
|
|
// Mic live preview on input change
|
|
const micInputs = [
|
|
'fontFamily', 'fontSize', 'fontWeight', 'textColor', 'textAlign',
|
|
'backgroundColor', 'backgroundOpacity', 'borderRadius', 'padding',
|
|
'maxWords'
|
|
];
|
|
|
|
micInputs.forEach(name => {
|
|
const element = this.elements[name];
|
|
if (element) {
|
|
element.addEventListener('input', () => this.updatePreview());
|
|
}
|
|
});
|
|
|
|
// Desktop live preview on input change
|
|
const desktopInputs = [
|
|
'desktopFontFamily', 'desktopFontSize', 'desktopFontWeight', 'desktopTextColor', 'desktopTextAlign',
|
|
'desktopBackgroundColor', 'desktopBackgroundOpacity', 'desktopBorderRadius', 'desktopPadding',
|
|
'desktopMaxWords'
|
|
];
|
|
|
|
desktopInputs.forEach(name => {
|
|
const element = this.elements[name];
|
|
if (element) {
|
|
element.addEventListener('input', () => this.updateDesktopPreview());
|
|
}
|
|
});
|
|
|
|
// Update mic value displays for range inputs
|
|
this.elements.fontSize.addEventListener('input', (e) => {
|
|
this.elements.fontSizeValue.textContent = e.target.value;
|
|
});
|
|
this.elements.backgroundOpacity.addEventListener('input', (e) => {
|
|
this.elements.opacityValue.textContent = e.target.value;
|
|
});
|
|
this.elements.borderRadius.addEventListener('input', (e) => {
|
|
this.elements.radiusValue.textContent = e.target.value;
|
|
});
|
|
this.elements.padding.addEventListener('input', (e) => {
|
|
this.elements.paddingValue.textContent = e.target.value;
|
|
});
|
|
this.elements.maxWords.addEventListener('input', (e) => {
|
|
this.elements.maxWordsValue.textContent = e.target.value;
|
|
});
|
|
|
|
// Update desktop value displays for range inputs
|
|
this.elements.desktopFontSize.addEventListener('input', (e) => {
|
|
this.elements.desktopFontSizeValue.textContent = e.target.value;
|
|
});
|
|
this.elements.desktopBackgroundOpacity.addEventListener('input', (e) => {
|
|
this.elements.desktopOpacityValue.textContent = e.target.value;
|
|
});
|
|
this.elements.desktopBorderRadius.addEventListener('input', (e) => {
|
|
this.elements.desktopRadiusValue.textContent = e.target.value;
|
|
});
|
|
this.elements.desktopPadding.addEventListener('input', (e) => {
|
|
this.elements.desktopPaddingValue.textContent = e.target.value;
|
|
});
|
|
this.elements.desktopMaxWords.addEventListener('input', (e) => {
|
|
this.elements.desktopMaxWordsValue.textContent = e.target.value;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Toggle settings section collapse/expand
|
|
*/
|
|
toggleSection(toggle) {
|
|
const sectionId = toggle.dataset.section;
|
|
const section = document.getElementById(sectionId);
|
|
const icon = toggle.querySelector('.toggle-icon');
|
|
|
|
section.classList.toggle('hidden');
|
|
toggle.classList.toggle('active');
|
|
icon.textContent = section.classList.contains('hidden') ? '\u25B6' : '\u25BC';
|
|
},
|
|
|
|
/**
|
|
* Open settings panel
|
|
*/
|
|
openPanel() {
|
|
this.elements.panel.classList.remove('hidden');
|
|
this.elements.overlay.classList.remove('hidden');
|
|
},
|
|
|
|
/**
|
|
* Close settings panel
|
|
*/
|
|
closePanel() {
|
|
this.elements.panel.classList.add('hidden');
|
|
this.elements.overlay.classList.add('hidden');
|
|
},
|
|
|
|
/**
|
|
* Apply settings to the UI
|
|
*/
|
|
applySettings(settings) {
|
|
this.current = settings;
|
|
|
|
// Update mic form values
|
|
this.elements.fontFamily.value = settings.font_family;
|
|
this.elements.fontSize.value = settings.font_size;
|
|
this.elements.fontSizeValue.textContent = settings.font_size;
|
|
this.elements.fontWeight.value = settings.font_weight;
|
|
this.elements.textColor.value = settings.text_color;
|
|
this.elements.textAlign.value = settings.text_align;
|
|
|
|
this.elements.backgroundColor.value = settings.background_color;
|
|
this.elements.backgroundOpacity.value = Math.round(settings.background_opacity * 100);
|
|
this.elements.opacityValue.textContent = Math.round(settings.background_opacity * 100);
|
|
this.elements.borderRadius.value = settings.border_radius;
|
|
this.elements.radiusValue.textContent = settings.border_radius;
|
|
this.elements.padding.value = settings.padding;
|
|
this.elements.paddingValue.textContent = settings.padding;
|
|
|
|
this.elements.maxWords.value = settings.max_words || 30;
|
|
this.elements.maxWordsValue.textContent = settings.max_words || 30;
|
|
|
|
// Apply to mic caption container
|
|
this.updatePreview();
|
|
|
|
// Apply desktop settings if present
|
|
if (settings.desktop_font_family !== undefined) {
|
|
this.applyDesktopSettings(settings);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Apply desktop settings to the UI
|
|
*/
|
|
applyDesktopSettings(settings) {
|
|
// Update desktop form values
|
|
this.elements.desktopFontFamily.value = settings.desktop_font_family;
|
|
this.elements.desktopFontSize.value = settings.desktop_font_size;
|
|
this.elements.desktopFontSizeValue.textContent = settings.desktop_font_size;
|
|
this.elements.desktopFontWeight.value = settings.desktop_font_weight;
|
|
this.elements.desktopTextColor.value = settings.desktop_text_color;
|
|
this.elements.desktopTextAlign.value = settings.desktop_text_align;
|
|
|
|
this.elements.desktopBackgroundColor.value = settings.desktop_background_color;
|
|
this.elements.desktopBackgroundOpacity.value = Math.round(settings.desktop_background_opacity * 100);
|
|
this.elements.desktopOpacityValue.textContent = Math.round(settings.desktop_background_opacity * 100);
|
|
this.elements.desktopBorderRadius.value = settings.desktop_border_radius;
|
|
this.elements.desktopRadiusValue.textContent = settings.desktop_border_radius;
|
|
this.elements.desktopPadding.value = settings.desktop_padding;
|
|
this.elements.desktopPaddingValue.textContent = settings.desktop_padding;
|
|
|
|
this.elements.desktopMaxWords.value = settings.desktop_max_words || 30;
|
|
this.elements.desktopMaxWordsValue.textContent = settings.desktop_max_words || 30;
|
|
|
|
// Store desktop max words
|
|
this.current.desktop_max_words = settings.desktop_max_words || 30;
|
|
|
|
// Apply to desktop caption container
|
|
this.updateDesktopPreview();
|
|
},
|
|
|
|
/**
|
|
* Update live preview of mic caption styling
|
|
*/
|
|
updatePreview() {
|
|
const container = this.elements.captionContainer;
|
|
const opacity = this.elements.backgroundOpacity.value / 100;
|
|
|
|
// Parse background color and apply opacity
|
|
const bgColor = this.elements.backgroundColor.value;
|
|
const r = parseInt(bgColor.slice(1, 3), 16);
|
|
const g = parseInt(bgColor.slice(3, 5), 16);
|
|
const b = parseInt(bgColor.slice(5, 7), 16);
|
|
|
|
container.style.fontFamily = this.elements.fontFamily.value;
|
|
container.style.fontSize = `${this.elements.fontSize.value}px`;
|
|
container.style.fontWeight = this.elements.fontWeight.value;
|
|
container.style.color = this.elements.textColor.value;
|
|
container.style.textAlign = this.elements.textAlign.value;
|
|
container.style.backgroundColor = `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
container.style.borderRadius = `${this.elements.borderRadius.value}px`;
|
|
container.style.padding = `${this.elements.padding.value}px`;
|
|
|
|
// Store max words for caption management
|
|
this.current.max_words = parseInt(this.elements.maxWords.value);
|
|
},
|
|
|
|
/**
|
|
* Update live preview of desktop caption styling
|
|
*/
|
|
updateDesktopPreview() {
|
|
const container = this.elements.desktopCaptionContainer;
|
|
if (!container) return;
|
|
|
|
const opacity = this.elements.desktopBackgroundOpacity.value / 100;
|
|
|
|
// Parse background color and apply opacity
|
|
const bgColor = this.elements.desktopBackgroundColor.value;
|
|
const r = parseInt(bgColor.slice(1, 3), 16);
|
|
const g = parseInt(bgColor.slice(3, 5), 16);
|
|
const b = parseInt(bgColor.slice(5, 7), 16);
|
|
|
|
container.style.fontFamily = this.elements.desktopFontFamily.value;
|
|
container.style.fontSize = `${this.elements.desktopFontSize.value}px`;
|
|
container.style.fontWeight = this.elements.desktopFontWeight.value;
|
|
container.style.color = this.elements.desktopTextColor.value;
|
|
container.style.textAlign = this.elements.desktopTextAlign.value;
|
|
container.style.backgroundColor = `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
|
container.style.borderRadius = `${this.elements.desktopBorderRadius.value}px`;
|
|
container.style.padding = `${this.elements.desktopPadding.value}px`;
|
|
|
|
// Store desktop max words for caption management
|
|
this.current.desktop_max_words = parseInt(this.elements.desktopMaxWords.value);
|
|
},
|
|
|
|
/**
|
|
* Get current form values as settings object
|
|
*/
|
|
getFormValues() {
|
|
return {
|
|
// Mic settings
|
|
font_family: this.elements.fontFamily.value,
|
|
font_size: parseInt(this.elements.fontSize.value),
|
|
font_weight: this.elements.fontWeight.value,
|
|
text_color: this.elements.textColor.value,
|
|
text_align: this.elements.textAlign.value,
|
|
background_color: this.elements.backgroundColor.value,
|
|
background_opacity: this.elements.backgroundOpacity.value / 100,
|
|
border_radius: parseInt(this.elements.borderRadius.value),
|
|
padding: parseInt(this.elements.padding.value),
|
|
max_words: parseInt(this.elements.maxWords.value),
|
|
// Desktop settings
|
|
desktop_font_family: this.elements.desktopFontFamily.value,
|
|
desktop_font_size: parseInt(this.elements.desktopFontSize.value),
|
|
desktop_font_weight: this.elements.desktopFontWeight.value,
|
|
desktop_text_color: this.elements.desktopTextColor.value,
|
|
desktop_text_align: this.elements.desktopTextAlign.value,
|
|
desktop_background_color: this.elements.desktopBackgroundColor.value,
|
|
desktop_background_opacity: this.elements.desktopBackgroundOpacity.value / 100,
|
|
desktop_border_radius: parseInt(this.elements.desktopBorderRadius.value),
|
|
desktop_padding: parseInt(this.elements.desktopPadding.value),
|
|
desktop_max_words: parseInt(this.elements.desktopMaxWords.value),
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Save settings to server
|
|
*/
|
|
async saveSettings() {
|
|
const settings = this.getFormValues();
|
|
|
|
try {
|
|
const response = await fetch('/api/settings', {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(settings),
|
|
});
|
|
|
|
if (response.ok) {
|
|
this.current = await response.json();
|
|
this.closePanel();
|
|
console.log('Settings saved');
|
|
} else {
|
|
console.error('Failed to save settings');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error saving settings:', error);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Reset settings to defaults
|
|
*/
|
|
async resetSettings() {
|
|
if (!confirm('Reset all settings to defaults?')) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/api/settings/reset', {
|
|
method: 'POST',
|
|
});
|
|
|
|
if (response.ok) {
|
|
const settings = await response.json();
|
|
this.applySettings(settings);
|
|
console.log('Settings reset to defaults');
|
|
} else {
|
|
console.error('Failed to reset settings');
|
|
}
|
|
} catch (error) {
|
|
console.error('Error resetting settings:', error);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Fetch settings from server
|
|
*/
|
|
async fetchSettings() {
|
|
try {
|
|
const response = await fetch('/api/settings');
|
|
if (response.ok) {
|
|
const settings = await response.json();
|
|
this.applySettings(settings);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching settings:', error);
|
|
}
|
|
}
|
|
};
|