feat/frontend-phase1 #1

Merged
stwhite merged 34 commits from feat/frontend-phase1 into main 2025-08-14 15:44:25 +00:00
3 changed files with 296 additions and 3 deletions
Showing only changes of commit f9e952286d - Show all commits

View File

@ -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;
}

View File

@ -101,6 +101,40 @@
</div>
</footer>
<!-- TTS Settings Modal -->
<div id="tts-settings-modal" class="modal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h3>TTS Settings</h3>
<button class="modal-close" id="tts-modal-close">&times;</button>
</div>
<div class="modal-body">
<div class="settings-group">
<label for="tts-exaggeration">Exaggeration:</label>
<input type="range" id="tts-exaggeration" min="0" max="2" step="0.1" value="0.5">
<span id="tts-exaggeration-value">0.5</span>
<small>Controls expressiveness. Higher values = more exaggerated speech.</small>
</div>
<div class="settings-group">
<label for="tts-cfg-weight">CFG Weight:</label>
<input type="range" id="tts-cfg-weight" min="0" max="2" step="0.1" value="0.5">
<span id="tts-cfg-weight-value">0.5</span>
<small>Alignment with prompt. Higher values = more aligned with speaker characteristics.</small>
</div>
<div class="settings-group">
<label for="tts-temperature">Temperature:</label>
<input type="range" id="tts-temperature" min="0" max="2" step="0.1" value="0.8">
<span id="tts-temperature-value">0.8</span>
<small>Randomness. Lower values = more deterministic, higher = more varied.</small>
</div>
</div>
<div class="modal-footer">
<button id="tts-settings-save" class="btn-primary">Save Settings</button>
<button id="tts-settings-cancel" class="btn-secondary">Cancel</button>
</div>
</div>
</div>
<script src="js/api.js" type="module"></script>
<script src="js/app.js" type="module" defer></script>
</body>

View File

@ -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 --- //