diff --git a/frontend/css/style.css b/frontend/css/style.css index ec111f2..b56396b 100644 --- a/frontend/css/style.css +++ b/frontend/css/style.css @@ -147,7 +147,7 @@ main { width: 110px; min-width: 90px; max-width: 130px; - text-align: center; + text-align: left; padding-left: 0; padding-right: 0; } @@ -185,7 +185,7 @@ main { } #dialog-items-table td.actions { - text-align: center; + text-align: left; min-width: 110px; white-space: nowrap; } @@ -514,3 +514,159 @@ footer { outline: 2px solid var(--primary-blue); outline-offset: 2px; } + +/* TTS Settings Modal */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; +} + +.modal-content { + background: var(--bg-white); + border-radius: 8px; + box-shadow: var(--shadow-strong); + max-width: 500px; + width: 90%; + max-height: 80vh; + overflow-y: auto; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 24px 16px; + border-bottom: 1px solid var(--border-light); +} + +.modal-header h3 { + margin: 0; + color: var(--text-primary); + font-size: 1.25rem; +} + +.modal-close { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: var(--text-secondary); + padding: 4px; + border-radius: 4px; + transition: background-color 0.2s; +} + +.modal-close:hover { + background-color: var(--bg-light); +} + +.modal-body { + padding: 20px 24px; +} + +.settings-group { + margin-bottom: 20px; +} + +.settings-group label { + display: block; + margin-bottom: 8px; + font-weight: 500; + color: var(--text-primary); +} + +.settings-group input[type="range"] { + width: 100%; + margin-bottom: 4px; +} + +.settings-group span { + display: inline-block; + min-width: 40px; + font-weight: 500; + color: var(--primary-blue); + margin-left: 8px; +} + +.settings-group small { + display: block; + color: var(--text-secondary); + font-size: 0.875rem; + margin-top: 4px; + line-height: 1.3; +} + +.modal-footer { + display: flex; + gap: 12px; + justify-content: flex-end; + padding: 16px 24px 20px; + border-top: 1px solid var(--border-light); +} + +.btn-primary, .btn-secondary { + padding: 8px 16px; + border-radius: 4px; + border: none; + cursor: pointer; + font-size: 0.875rem; + font-weight: 500; + transition: all 0.2s; +} + +.btn-primary { + background-color: var(--primary-blue); + color: var(--text-white); +} + +.btn-primary:hover { + background-color: var(--primary-blue-dark); +} + +.btn-secondary { + background-color: var(--bg-light); + color: var(--text-secondary); + border: 1px solid var(--border-medium); +} + +.btn-secondary:hover { + background-color: var(--border-light); +} + +/* Settings button styling */ +.settings-line-btn { + width: 32px; + height: 32px; + border-radius: 50%; + border: none; + background-color: var(--bg-light); + color: var(--text-secondary); + cursor: pointer; + font-size: 14px; + margin: 0 2px; + transition: all 0.2s; + display: inline-flex; + align-items: center; + justify-content: center; + vertical-align: middle; +} + +.settings-line-btn:hover { + background-color: var(--primary-blue); + color: var(--text-white); + transform: scale(1.05); +} + +.settings-line-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} diff --git a/frontend/index.html b/frontend/index.html index 2549897..b307869 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -101,6 +101,40 @@ + + + diff --git a/frontend/js/app.js b/frontend/js/app.js index 689a2b7..213a3b9 100644 --- a/frontend/js/app.js +++ b/frontend/js/app.js @@ -111,12 +111,21 @@ let availableSpeakersCache = []; // To populate speaker dropdown // Utility: ensure each dialog item has audioUrl, isGenerating, error function normalizeDialogItem(item) { - return { + const normalized = { ...item, audioUrl: item.audioUrl || null, isGenerating: item.isGenerating || false, error: item.error || null }; + + // Add TTS settings for speech items with defaults + if (item.type === 'speech') { + normalized.exaggeration = item.exaggeration ?? 0.5; + normalized.cfg_weight = item.cfg_weight ?? 0.5; + normalized.temperature = item.temperature ?? 0.8; + } + + return normalized; } async function initializeDialogEditor() { @@ -332,6 +341,18 @@ async function initializeDialogEditor() { }; actionsTd.appendChild(playBtn); + // --- NEW: Settings button for speech items --- + if (item.type === 'speech') { + const settingsBtn = document.createElement('button'); + settingsBtn.innerHTML = '⚙️'; + settingsBtn.title = 'TTS Settings'; + settingsBtn.className = 'settings-line-btn'; + settingsBtn.onclick = () => { + showTTSSettingsModal(item, index); + }; + actionsTd.appendChild(settingsBtn); + } + // Show error if present if (item.error) { const errorSpan = document.createElement('span'); @@ -709,6 +730,88 @@ async function initializeDialogEditor() { console.log('Dialog Editor Initialized'); renderDialogItems(); // Initial render (empty) + + // Add a function to show TTS settings modal + function showTTSSettingsModal(item, index) { + const modal = document.getElementById('tts-settings-modal'); + const exaggerationSlider = document.getElementById('tts-exaggeration'); + const exaggerationValue = document.getElementById('tts-exaggeration-value'); + const cfgWeightSlider = document.getElementById('tts-cfg-weight'); + const cfgWeightValue = document.getElementById('tts-cfg-weight-value'); + const temperatureSlider = document.getElementById('tts-temperature'); + const temperatureValue = document.getElementById('tts-temperature-value'); + const saveBtn = document.getElementById('tts-settings-save'); + const cancelBtn = document.getElementById('tts-settings-cancel'); + const closeBtn = document.getElementById('tts-modal-close'); + + // Set current values + exaggerationSlider.value = item.exaggeration || 0.5; + exaggerationValue.textContent = exaggerationSlider.value; + cfgWeightSlider.value = item.cfg_weight || 0.5; + cfgWeightValue.textContent = cfgWeightSlider.value; + temperatureSlider.value = item.temperature || 0.8; + temperatureValue.textContent = temperatureSlider.value; + + // Update value displays when sliders change + const updateValueDisplay = (slider, display) => { + display.textContent = slider.value; + }; + + exaggerationSlider.oninput = () => updateValueDisplay(exaggerationSlider, exaggerationValue); + cfgWeightSlider.oninput = () => updateValueDisplay(cfgWeightSlider, cfgWeightValue); + temperatureSlider.oninput = () => updateValueDisplay(temperatureSlider, temperatureValue); + + // Show modal + modal.style.display = 'flex'; + + // Save settings + const saveSettings = () => { + dialogItems[index].exaggeration = parseFloat(exaggerationSlider.value); + dialogItems[index].cfg_weight = parseFloat(cfgWeightSlider.value); + dialogItems[index].temperature = parseFloat(temperatureSlider.value); + + // Clear any existing audio since settings changed + dialogItems[index].audioUrl = null; + + closeModal(); + renderDialogItems(); // Re-render to reflect changes + console.log('TTS settings updated for item:', dialogItems[index]); + }; + + // Close modal + const closeModal = () => { + modal.style.display = 'none'; + // Clean up event listeners + exaggerationSlider.oninput = null; + cfgWeightSlider.oninput = null; + temperatureSlider.oninput = null; + saveBtn.onclick = null; + cancelBtn.onclick = null; + closeBtn.onclick = null; + modal.onclick = null; + }; + + // Event listeners + saveBtn.onclick = saveSettings; + cancelBtn.onclick = closeModal; + closeBtn.onclick = closeModal; + + // Close modal when clicking outside + modal.onclick = (e) => { + if (e.target === modal) { + closeModal(); + } + }; + + // Close modal on Escape key + const handleEscape = (e) => { + if (e.key === 'Escape') { + closeModal(); + document.removeEventListener('keydown', handleEscape); + } + }; + document.addEventListener('keydown', handleEscape); + } } // --- Results Display --- //