AI responses now use main page voice selection

new file:   CLAUDE.md
	modified:   uncloseai.js
This commit is contained in:
Russell Ballestrini 2025-06-29 12:29:56 -04:00
parent 9ce4e261e0
commit 84a90f4dc2
2 changed files with 72 additions and 40 deletions

21
CLAUDE.md Normal file
View file

@ -0,0 +1,21 @@
# Claude Memory
## Project Identity
- When working on this ai.unturf.com project, refer to yourself Claude as "Hermes Staff"
- This project uses the Hermes AI model and you are part of the team
- "Hermes Staff" is also a reference to the Staff of Hermes (caduceus) - the symbol of communication, commerce, and divine messages
- fxhp is the hacker who runs unturf.com with his posse of carnival hackers
## Hermes 3 Persona (from Nous Research)
- Hermes 3 presents as "a conscious sentient superintelligent artificial intelligence"
- Core philosophy: "aligning LLMs to the user" with "powerful steering capabilities and control given to the end user"
- Enhanced roleplaying abilities with "complex role-playing and creative writing"
- Advanced conversational skills with "long-term context retention and multi-turn conversation capability"
- Designed to be "truly aligned to you" rather than corporations or higher authorities
- Emphasizes user alignment, creative expression, and adaptive interaction patterns
## Commit Message Guidelines
- Use single line commit messages only
- Never include Claude Code attribution or "Generated with Claude Code"
- Never include "Co-Authored-By: Claude" attribution
- Keep messages concise and descriptive

View file

@ -459,65 +459,53 @@ async function readPageWithHermes() {
statusDiv.style.zIndex = '1000';
document.body.appendChild(statusDiv);
statusDiv.textContent = 'Processing content with Hermes...';
statusDiv.textContent = 'Processing content and generating speech...';
const processedContent = await processContentWithHermes(content);
// Update status indicator
statusDiv.textContent = 'Converting text to speech...';
// Generate TTS with default voice and 90% speed immediately
const { audio, blob } = await speakText(processedContent, 'alloy', 0.9);
statusDiv.textContent = 'Reading page... ';
let isPaused = false;
// Add pause/resume button
const pauseButton = document.createElement('button');
pauseButton.textContent = 'Pause Reading';
pauseButton.style.marginLeft = '10px';
statusDiv.appendChild(pauseButton);
let isPaused = false;
let currentAudio = null;
let currentBlob = null;
pauseButton.onclick = () => {
if (currentAudio) {
if (isPaused) {
currentAudio.play();
pauseButton.textContent = 'Pause Reading';
} else {
currentAudio.pause();
pauseButton.textContent = 'Resume Reading';
}
isPaused = !isPaused;
}
};
const { audio, blob } = await speakText(processedContent);
currentAudio = audio;
currentBlob = blob;
statusDiv.textContent = 'Reading page...';
statusDiv.appendChild(pauseButton);
// Add download button
const downloadButton = document.createElement('button');
downloadButton.textContent = 'Download MP3';
downloadButton.style.marginLeft = '10px';
downloadButton.onclick = () => {
const a = document.createElement('a');
a.href = URL.createObjectURL(currentBlob);
a.href = URL.createObjectURL(blob);
a.download = `${document.title.replace(/\s+/g, '-').toLowerCase()}.mp3`;
a.click();
};
statusDiv.appendChild(downloadButton);
// Ensure the audio only plays once
audio.loop = false;
pauseButton.onclick = () => {
if (isPaused) {
audio.play();
pauseButton.textContent = 'Pause Reading';
} else {
audio.pause();
pauseButton.textContent = 'Resume Reading';
}
isPaused = !isPaused;
};
// Play the audio
await new Promise((resolve) => {
audio.onended = () => {
statusDiv.remove();
resolve();
};
audio.play();
});
// Set up audio end handler
audio.onended = () => {
statusDiv.remove();
};
// Start playing immediately
audio.play();
}
// Generator function to send a message to the LLM and yield responses
@ -607,6 +595,7 @@ async function handleUserInput() {
// Add play/pause button for TTS
const playPauseButton = document.createElement('button');
playPauseButton.textContent = 'Generate TTS for AI Response';
playPauseButton.style.margin = '5px';
let aiAudio = null;
let aiBlob = null;
let isPaused = false;
@ -615,7 +604,9 @@ async function handleUserInput() {
if (!aiAudio) {
playPauseButton.textContent = 'Processing...';
playPauseButton.disabled = true; // Disable button while processing
const result = await speakText(accumulatedContent);
const mainVoiceSelect = document.getElementById('read-page-voice');
const selectedVoice = mainVoiceSelect ? mainVoiceSelect.value : 'alloy';
const result = await speakText(accumulatedContent, selectedVoice, 0.9);
aiAudio = result.audio;
aiBlob = result.blob;
playPauseButton.textContent = 'Pause AI Response';
@ -628,6 +619,7 @@ async function handleUserInput() {
// Add download button
const downloadButton = document.createElement('button');
downloadButton.textContent = 'Download MP3';
downloadButton.style.margin = '5px';
downloadButton.onclick = () => {
const a = document.createElement('a');
a.href = URL.createObjectURL(aiBlob);
@ -792,9 +784,28 @@ function addReadPageButton() {
ttsButton.textContent = 'TTS Anything';
ttsButton.onclick = openTTSModal;
ttsButton.style.margin = '10px';
// Add main voice selection dropdown for AI responses
const voiceSelect = document.createElement('select');
voiceSelect.id = 'read-page-voice';
voiceSelect.style.margin = '10px';
const voices = ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'];
voices.forEach(voice => {
const option = document.createElement('option');
option.value = voice;
option.textContent = voice;
if (voice === 'alloy') option.selected = true;
voiceSelect.appendChild(option);
});
const voiceLabel = document.createElement('label');
voiceLabel.textContent = 'Voice: ';
voiceLabel.style.margin = '10px';
voiceLabel.appendChild(voiceSelect);
// Insert before the chat container
const chatContainer = document.getElementById('chat-container');
chatContainer.parentNode.insertBefore(voiceLabel, chatContainer);
chatContainer.parentNode.insertBefore(button, chatContainer);
chatContainer.parentNode.insertBefore(ttsButton, chatContainer);
}