Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 40 additions & 14 deletions chat-storage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,32 @@
document.addEventListener("DOMContentLoaded", () => {
const { chatBox, chatInput, clearChatBtn, voiceToggleBtn, modelSelect, synth, autoSpeakEnabled, speakMessage, stopSpeaking, showToast, toggleSpeechRecognition, initSpeechRecognition, handleVoiceCommand, speakSentences } = window._chatInternals;
const imagePatterns = window.imagePatterns;
function generateSessionTitle(messages) {

function openImageModal(imageUrl) {
window.open(imageUrl, "_blank");
}

function addImageToGallery(imageUrl) {
const gallery = document.getElementById('past-image-gallery');
if (!gallery) return;
if ([...gallery.querySelectorAll('img.thumbnail')].some(img => img.src === imageUrl)) return;
const img = document.createElement('img');
img.src = imageUrl;
img.className = 'thumbnail';
img.addEventListener('click', () => {
openImageModal(imageUrl);
});
gallery.appendChild(img);
if (window.Memory && typeof window.Memory.saveImage === 'function') {
window.Memory.saveImage(imageUrl);
}
}

if (window.Memory && typeof window.Memory.loadPastImages === 'function') {
window.Memory.loadPastImages(addImageToGallery);
}

function generateSessionTitle(messages) {
let title = "";
for (let i = 0; i < messages.length; i++) {
if (messages[i].role === "ai") {
Expand Down Expand Up @@ -205,19 +230,20 @@ document.addEventListener("DOMContentLoaded", () => {
img.style.display = "block";
attachImageButtons(img, imageId);
};
img.onerror = () => {
loadingDiv.innerHTML = "⚠️ Failed to load image";
loadingDiv.style.display = "flex";
loadingDiv.style.justifyContent = "center";
loadingDiv.style.alignItems = "center";
};
imageContainer.appendChild(img);
const imgButtonContainer = document.createElement("div");
imgButtonContainer.className = "image-button-container";
imgButtonContainer.dataset.imageId = imageId;
imageContainer.appendChild(imgButtonContainer);
return imageContainer;
}
img.onerror = () => {
loadingDiv.innerHTML = "⚠️ Failed to load image";
loadingDiv.style.display = "flex";
loadingDiv.style.justifyContent = "center";
loadingDiv.style.alignItems = "center";
};
imageContainer.appendChild(img);
addImageToGallery(url);
const imgButtonContainer = document.createElement("div");
imgButtonContainer.className = "image-button-container";
imgButtonContainer.dataset.imageId = imageId;
imageContainer.appendChild(imgButtonContainer);
return imageContainer;
}
function attachImageButtons(img, imageId) {
const imgButtonContainer = document.querySelector(`.image-button-container[data-image-id="${imageId}"]`);
if (!imgButtonContainer) {
Expand Down
53 changes: 29 additions & 24 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -126,30 +126,35 @@ <h2>Sessions</h2>
</button>
</div>
</div>
<div class="chat-controls">
<div>
<button id="voice-toggle" class="control-btn" title="Toggle automatic voice responses">
<i class="fas fa-volume-mute"></i> Voice Off
</button>
<button id="open-voice-settings-modal" class="control-btn" title="Open voice settings">
<i class="fas fa-sliders-h"></i> Voice Settings
</button>
<button id="open-voice-chat-modal" class="control-btn" title="Start voice chat with image slideshow">
<i class="fas fa-headset"></i> Voice Chat
</button>
<button id="shut-up-btn" class="control-btn" title="Instantly stop all voice output" onclick="window._chatInternals.shutUpTTS()">
<i class="fas fa-volume-mute"></i> Shut Up
</button>
</div>
<div>
<button id="clear-chat" class="control-btn" title="Clear current chat conversation">
<i class="fas fa-trash-alt"></i> Clear Chat
</button>
</div>
</div>
</div>
</div>
</div>
<div class="chat-controls">
<div>
<button id="voice-toggle" class="control-btn" title="Toggle automatic voice responses">
<i class="fas fa-volume-mute"></i> Voice Off
</button>
<button id="open-voice-settings-modal" class="control-btn" title="Open voice settings">
<i class="fas fa-sliders-h"></i> Voice Settings
</button>
<button id="open-voice-chat-modal" class="control-btn" title="Start voice chat with image slideshow">
<i class="fas fa-headset"></i> Voice Chat
</button>
<button id="shut-up-btn" class="control-btn" title="Instantly stop all voice output" onclick="window._chatInternals.shutUpTTS()">
<i class="fas fa-volume-mute"></i> Shut Up
</button>
</div>
<div>
<button id="clear-chat" class="control-btn" title="Clear current chat conversation">
<i class="fas fa-trash-alt"></i> Clear Chat
</button>
</div>
</div>
<div class="gallery-wrapper">
<div id="past-image-gallery" class="screensaver-thumbnails">
<!-- JS will populate thumbnails here -->
</div>
</div>
</div>
</div>
</div>
<div id="donation-modal" class="modal-backdrop hidden">
<div class="modal-container">
<div class="modal-header">
Expand Down
61 changes: 43 additions & 18 deletions memory-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,22 +97,47 @@ document.addEventListener("DOMContentLoaded", () => {
}
},

updateOrAddMemory: function(pattern, newText) {
const memories = this.getMemories();
const index = memories.findIndex(mem => mem.includes(pattern));

if (index !== -1) {
this.removeMemoryEntry(index);
}
return this.addMemoryEntry(newText);
},

setVoicePreference: function(enabled) {
const text = `Voice Preference: User prefers AI responses to be ${enabled ? 'spoken aloud' : 'not spoken'}.`;
return this.updateOrAddMemory("Voice Preference:", text);
}
};

console.log("Memory API loaded and linked to Storage-based memory system.");
updateOrAddMemory: function(pattern, newText) {
const memories = this.getMemories();
const index = memories.findIndex(mem => mem.includes(pattern));

if (index !== -1) {
this.removeMemoryEntry(index);
}
return this.addMemoryEntry(newText);
},

setVoicePreference: function(enabled) {
const text = `Voice Preference: User prefers AI responses to be ${enabled ? 'spoken aloud' : 'not spoken'}.`;
return this.updateOrAddMemory("Voice Preference:", text);
},

loadPastImages: function(callback) {
let images = [];
try {
images = JSON.parse(localStorage.getItem('pastImages')) || [];
} catch (err) {
console.warn('Failed to load past images:', err);
images = [];
}
if (Array.isArray(images) && typeof callback === 'function') {
images.forEach(url => callback(url));
}
return images;
},

saveImage: function(url) {
if (!url) return;
try {
const images = JSON.parse(localStorage.getItem('pastImages')) || [];
images.push(url);
localStorage.setItem('pastImages', JSON.stringify(images));
} catch (err) {
console.error('Error saving image:', err);
}
}
};

});
console.log("Memory API loaded and linked to Storage-based memory system.");

});
54 changes: 47 additions & 7 deletions styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -954,10 +954,50 @@ body {
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}

.screensaver canvas {
position: absolute;
top: 0;
left: 0;
z-index: 1;
pointer-events: none;
}
.screensaver canvas {
position: absolute;
top: 0;
left: 0;
z-index: 1;
pointer-events: none;
}

/* Bottom gallery for past screensaver images */
.gallery-wrapper {
position: fixed;
bottom: 0;
left: 260px;
right: 0;
background: rgba(0,0,0,0.7);
padding: 8px;
border-top: 2px solid #505050;
z-index: 50;
}

#past-image-gallery {
display: flex;
flex-direction: row;
gap: 10px;
overflow-x: auto;
scrollbar-width: thin;
}

#past-image-gallery img.thumbnail {
width: 100px;
height: 70px;
object-fit: cover;
cursor: pointer;
border: 2px solid transparent;
border-radius: 6px;
transition: border 0.2s, transform 0.2s;
flex-shrink: 0;
}

#past-image-gallery img.thumbnail:hover {
border: 2px solid #00ffcc;
transform: scale(1.05);
}

#past-image-gallery img.thumbnail.selected {
border: 2px solid #ffcc00;
}