feat: add TTS buttons to user messages and fix delete persistence bug

This commit is contained in:
Russell Ballestrini 2025-07-02 14:47:50 -04:00
parent ba76f1dbcd
commit ae6e3baa35

171
src/ui.js
View file

@ -1205,6 +1205,90 @@ async function openUncloseaiEmbeddedModalNew() {
justify-content: flex-end;
`;
const userTtsBtn = document.createElement("button");
userTtsBtn.textContent = "🔊";
userTtsBtn.title = "Read aloud";
userTtsBtn.style.cssText = `
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
font-size: 12px;
color: white;
transition: all 0.2s;
`;
userTtsBtn.onmouseenter = () => {
userTtsBtn.style.background = 'rgba(255, 255, 255, 0.3)';
};
userTtsBtn.onmouseleave = () => {
userTtsBtn.style.background = 'rgba(255, 255, 255, 0.2)';
};
// Store audio for pause/resume functionality
let userAudio = null;
let isPlaying = false;
userTtsBtn.onclick = async () => {
const modalVoiceSelect = document.getElementById('hermes-voice-select');
const selectedVoice = modalVoiceSelect?.value || 'alloy';
// If audio is already playing, pause it
if (userAudio && !userAudio.paused) {
userAudio.pause();
userTtsBtn.textContent = "🔊";
isPlaying = false;
return;
}
// If audio exists and is paused, resume it
if (userAudio && userAudio.paused && userAudio.currentTime > 0) {
userAudio.play();
userTtsBtn.textContent = "⏸️";
isPlaying = true;
return;
}
// Generate new audio
const originalText = userTtsBtn.textContent;
userTtsBtn.textContent = "⏳";
userTtsBtn.disabled = true;
try {
const result = await speakChatText(message, selectedVoice, 1.0);
userAudio = result.audio;
// Set up audio event handlers
userAudio.onplay = () => {
userTtsBtn.textContent = "⏸️";
isPlaying = true;
};
userAudio.onpause = () => {
userTtsBtn.textContent = "🔊";
isPlaying = false;
};
userAudio.onended = () => {
userTtsBtn.textContent = "🔊";
isPlaying = false;
userAudio = null;
};
// Auto-play the generated audio
try {
await userAudio.play();
} catch (playError) {
userTtsBtn.textContent = "🔊";
}
} catch (error) {
alert("User TTS failed: " + error.message);
userTtsBtn.textContent = originalText;
} finally {
userTtsBtn.disabled = false;
}
};
const userDeleteBtn = document.createElement("button");
userDeleteBtn.textContent = "🗑️";
userDeleteBtn.title = "Delete message";
@ -1234,6 +1318,7 @@ async function openUncloseaiEmbeddedModalNew() {
userMsg.remove();
};
userMessageActions.appendChild(userTtsBtn);
userMessageActions.appendChild(userDeleteBtn);
userMsg.appendChild(messageText);
userMsg.appendChild(userMessageActions);
@ -1542,6 +1627,84 @@ You have complete knowledge of this page content and can reference any details,
justify-content: flex-end;
`;
const userTtsBtn = document.createElement("button");
userTtsBtn.textContent = "🔊";
userTtsBtn.title = "Read aloud";
userTtsBtn.style.cssText = `
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
font-size: 12px;
color: white;
transition: all 0.2s;
`;
userTtsBtn.onmouseenter = () => {
userTtsBtn.style.background = 'rgba(255, 255, 255, 0.3)';
};
userTtsBtn.onmouseleave = () => {
userTtsBtn.style.background = 'rgba(255, 255, 255, 0.2)';
};
// Store audio for pause/resume functionality
let historicalUserAudio = null;
userTtsBtn.onclick = async () => {
const modalVoiceSelect = document.getElementById('hermes-voice-select');
const selectedVoice = modalVoiceSelect?.value || 'alloy';
// If audio is already playing, pause it
if (historicalUserAudio && !historicalUserAudio.paused) {
historicalUserAudio.pause();
userTtsBtn.textContent = "🔊";
return;
}
// If audio exists and is paused, resume it
if (historicalUserAudio && historicalUserAudio.paused && historicalUserAudio.currentTime > 0) {
historicalUserAudio.play();
userTtsBtn.textContent = "⏸️";
return;
}
// Generate new audio
const originalText = userTtsBtn.textContent;
userTtsBtn.textContent = "⏳";
userTtsBtn.disabled = true;
try {
const result = await speakChatText(msg.content, selectedVoice, 1.0);
historicalUserAudio = result.audio;
// Set up audio event handlers
historicalUserAudio.onplay = () => {
userTtsBtn.textContent = "⏸️";
};
historicalUserAudio.onpause = () => {
userTtsBtn.textContent = "🔊";
};
historicalUserAudio.onended = () => {
userTtsBtn.textContent = "🔊";
historicalUserAudio = null;
};
// Auto-play the generated audio
try {
await historicalUserAudio.play();
} catch (playError) {
userTtsBtn.textContent = "🔊";
}
} catch (error) {
alert("User TTS failed: " + error.message);
userTtsBtn.textContent = originalText;
} finally {
userTtsBtn.disabled = false;
}
};
const userDeleteBtn = document.createElement("button");
userDeleteBtn.textContent = "🗑️";
userDeleteBtn.title = "Delete message";
@ -1561,15 +1724,16 @@ You have complete knowledge of this page content and can reference any details,
userDeleteBtn.onmouseleave = () => {
userDeleteBtn.style.background = 'rgba(255, 255, 255, 0.2)';
};
userDeleteBtn.onclick = () => {
userDeleteBtn.onclick = async () => {
// Remove from chat history by index
const currentHistory = loadConversationHistory();
if (index >= 0 && index < currentHistory.length) {
currentHistory.splice(index, 1);
saveConversationHistory(currentHistory);
// Update the chat.js module history
const systemMsg = currentHistory.find(msg => msg.role === 'system');
// Update the chat.js module history to match localStorage
const { updateChatHistory } = await import('./chat.js');
const { getSystemMessage } = await import('./config.js');
const newHistory = [
{ role: 'system', content: getSystemMessage() },
...currentHistory
@ -1580,6 +1744,7 @@ You have complete knowledge of this page content and can reference any details,
userMsg.remove();
};
userMessageActions.appendChild(userTtsBtn);
userMessageActions.appendChild(userDeleteBtn);
userMsg.appendChild(messageText);
userMsg.appendChild(userMessageActions);