Skip to content

Commit f38df11

Browse files
authored
Update chat-part3.js (messages, memory, session history)
better build messages function. memory improvements. sessions now have their own message history or should at least.
1 parent a1be61a commit f38df11

File tree

1 file changed

+115
-14
lines changed

1 file changed

+115
-14
lines changed

ai/chat-part3.js

Lines changed: 115 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
document.addEventListener("DOMContentLoaded", () => {
2-
const { chatBox, chatInput, clearChatBtn, voiceToggleBtn, modelSelect, currentSession, synth, autoSpeakEnabled, speakMessage, stopSpeaking, showToast, toggleSpeechRecognition, initSpeechRecognition } = window._chatInternals;
2+
const { chatBox, chatInput, clearChatBtn, voiceToggleBtn, modelSelect, synth, autoSpeakEnabled, speakMessage, stopSpeaking, showToast, toggleSpeechRecognition, initSpeechRecognition } = window._chatInternals;
3+
4+
// No static currentSession; we'll fetch it fresh each time
35
function randomSeed() {
46
return Math.floor(Math.random() * 1000000).toString();
57
}
8+
69
function generateSessionTitle(messages) {
710
let title = "";
811
for (let i = 0; i < messages.length; i++) {
@@ -15,19 +18,22 @@ document.addEventListener("DOMContentLoaded", () => {
1518
if (title.length > 50) title = title.substring(0, 50) + "...";
1619
return title;
1720
}
21+
1822
function checkAndUpdateSessionTitle() {
19-
const current = Storage.getCurrentSession();
20-
if (!current.name || current.name === "New Chat") {
21-
const newTitle = generateSessionTitle(current.messages);
22-
if (newTitle && newTitle !== current.name) {
23-
Storage.renameSession(current.id, newTitle);
23+
const currentSession = Storage.getCurrentSession();
24+
if (!currentSession.name || currentSession.name === "New Chat") {
25+
const newTitle = generateSessionTitle(currentSession.messages);
26+
if (newTitle && newTitle !== currentSession.name) {
27+
Storage.renameSession(currentSession.id, newTitle);
2428
}
2529
}
2630
}
31+
2732
function waitForPrism(callback) {
2833
if (window.Prism) callback();
2934
else setTimeout(() => waitForPrism(callback), 100);
3035
}
36+
3137
function appendMessage({ role, content, index }) {
3238
const container = document.createElement("div");
3339
container.classList.add("message");
@@ -127,14 +133,58 @@ document.addEventListener("DOMContentLoaded", () => {
127133
}
128134
chatBox.appendChild(container);
129135
waitForPrism(() => {
130-
container.querySelectorAll("pre code").forEach((block) => Prism.highlightElement(block));
136+
const codeBlocks = container.querySelectorAll("pre code");
137+
codeBlocks.forEach((block) => {
138+
Prism.highlightElement(block);
139+
const buttonContainer = document.createElement("div");
140+
buttonContainer.style.display = "flex";
141+
buttonContainer.style.gap = "5px";
142+
buttonContainer.style.marginTop = "5px";
143+
const codeContent = block.textContent.trim();
144+
const language = block.className.match(/language-(\w+)/)?.[1] || "text";
145+
const copyCodeBtn = document.createElement("button");
146+
copyCodeBtn.className = "message-action-btn";
147+
copyCodeBtn.textContent = "Copy Code";
148+
copyCodeBtn.style.fontSize = "12px";
149+
copyCodeBtn.addEventListener("click", () => {
150+
navigator.clipboard.writeText(codeContent).then(() => {
151+
showToast("Code copied to clipboard");
152+
}).catch(() => {
153+
showToast("Failed to copy code");
154+
});
155+
});
156+
buttonContainer.appendChild(copyCodeBtn);
157+
const downloadCodeBtn = document.createElement("button");
158+
downloadCodeBtn.className = "message-action-btn";
159+
downloadCodeBtn.textContent = "Download";
160+
downloadCodeBtn.style.fontSize = "12px";
161+
downloadCodeBtn.addEventListener("click", () => {
162+
downloadCodeAsTxt(codeContent, language);
163+
});
164+
buttonContainer.appendChild(downloadCodeBtn);
165+
block.parentNode.parentNode.insertBefore(buttonContainer, block.parentNode.nextSibling);
166+
});
131167
});
132168
chatBox.scrollTop = chatBox.scrollHeight;
133169
if (autoSpeakEnabled && role === "ai") {
134170
stopSpeaking();
135171
speakMessage(content);
136172
}
137173
}
174+
175+
function downloadCodeAsTxt(codeContent, language) {
176+
const blob = new Blob([codeContent], { type: "text/plain" });
177+
const url = URL.createObjectURL(blob);
178+
const a = document.createElement("a");
179+
a.href = url;
180+
a.download = `code-${language}-${Date.now()}.txt`;
181+
document.body.appendChild(a);
182+
a.click();
183+
document.body.removeChild(a);
184+
URL.revokeObjectURL(url);
185+
showToast("Code downloaded as .txt");
186+
}
187+
138188
function createImageElement(url) {
139189
const imageContainer = document.createElement("div");
140190
imageContainer.className = "ai-image-container";
@@ -177,6 +227,7 @@ document.addEventListener("DOMContentLoaded", () => {
177227
imageContainer.appendChild(buttonContainer);
178228
return imageContainer;
179229
}
230+
180231
function renderMarkdown(mdText) {
181232
if (window.marked) {
182233
marked.setOptions({
@@ -186,25 +237,31 @@ document.addEventListener("DOMContentLoaded", () => {
186237
return code;
187238
}
188239
});
189-
return marked.parse(mdText.replace(/\[CODE\](.*?)\n([\s\S]*?)\[\/CODE\]/g, "```$1\n$2```"));
240+
return marked.parse(mdText.replace(/\$\$ CODE \$\$(.*?)\n([\s\S]*?)\$\$ \/CODE \$\$/g, "```$1\n$2```"));
190241
} else {
191242
return mdText.replace(/\n/g, "<br>");
192243
}
193244
}
245+
194246
function escapeHTML(html) {
195247
return html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
196248
}
249+
197250
function renderStoredMessages(messages) {
198251
chatBox.innerHTML = "";
199252
messages.forEach((msg, idx) => appendMessage({ role: msg.role, content: msg.content, index: idx }));
200253
}
254+
201255
window.addNewMessage = function ({ role, content }) {
256+
const currentSession = Storage.getCurrentSession();
202257
currentSession.messages.push({ role, content });
203258
Storage.updateSessionMessages(currentSession.id, currentSession.messages);
204259
appendMessage({ role, content, index: currentSession.messages.length - 1 });
205260
if (role === "ai") checkAndUpdateSessionTitle();
206261
};
262+
207263
function editMessage(msgIndex) {
264+
const currentSession = Storage.getCurrentSession();
208265
const oldMessage = currentSession.messages[msgIndex];
209266
if (!oldMessage) return;
210267
const newContent = prompt("Edit this message:", oldMessage.content);
@@ -237,16 +294,21 @@ document.addEventListener("DOMContentLoaded", () => {
237294
showToast("AI message updated");
238295
}
239296
}
297+
240298
function reGenerateAIResponse(aiIndex) {
299+
const currentSession = Storage.getCurrentSession();
241300
if (aiIndex < 0 || aiIndex >= currentSession.messages.length) return;
242301
let userIndex = -1;
243-
for (let i = aiIndex; i >= 0; i--) {
302+
for (let i = aiIndex - 1; i >= 0; i--) {
244303
if (currentSession.messages[i].role === "user") {
245304
userIndex = i;
246305
break;
247306
}
248307
}
249-
if (userIndex === -1) return;
308+
if (userIndex === -1) {
309+
showToast("No preceding user message found to regenerate from.");
310+
return;
311+
}
250312
currentSession.messages = currentSession.messages.slice(0, userIndex + 1);
251313
Storage.updateSessionMessages(currentSession.id, currentSession.messages);
252314
renderStoredMessages(currentSession.messages);
@@ -261,13 +323,16 @@ document.addEventListener("DOMContentLoaded", () => {
261323
loadingDiv.textContent = "Regenerating response...";
262324
chatBox.appendChild(loadingDiv);
263325
chatBox.scrollTop = chatBox.scrollHeight;
264-
const userMessage = currentSession.messages[userIndex];
326+
const userMessage = currentSession.messages[userIndex].content;
265327
sendToPollinations(() => {
266328
const loadingMsg = document.getElementById(loadingMsgId);
267329
if (loadingMsg) loadingMsg.remove();
268-
}, userMessage.content);
330+
showToast("Response regenerated successfully");
331+
}, userMessage);
269332
}
333+
270334
window.sendToPollinations = function (callback = null, overrideContent = null) {
335+
const currentSession = Storage.getCurrentSession();
271336
const loadingMsgId = "loading-" + Date.now();
272337
const loadingDiv = document.createElement("div");
273338
loadingDiv.id = loadingMsgId;
@@ -310,6 +375,28 @@ document.addEventListener("DOMContentLoaded", () => {
310375
const loadingMsg = document.getElementById(loadingMsgId);
311376
if (loadingMsg) loadingMsg.remove();
312377
let aiContent = extractAIContent(data);
378+
379+
// Check if the user's prompt is requesting an image
380+
const lastUserMsg = messages[messages.length - 1].content.toLowerCase();
381+
const isImageRequest = lastUserMsg.includes("image") || lastUserMsg.includes("picture") || lastUserMsg.includes("show me") || lastUserMsg.includes("generate an image");
382+
383+
// If the prompt suggests an image request but no image URL is in the response, generate one
384+
if (aiContent && isImageRequest && !aiContent.includes("https://image.pollinations.ai")) {
385+
let imagePrompt = lastUserMsg
386+
.replace(/show me|generate|image of|picture of|image|picture/gi, "")
387+
.trim();
388+
389+
if (imagePrompt.length < 5 && aiContent.toLowerCase().includes("image")) {
390+
imagePrompt = aiContent
391+
.toLowerCase()
392+
.replace(/here's an image of|image|to enjoy visually/gi, "")
393+
.trim();
394+
}
395+
396+
const imageUrl = `https://image.pollinations.ai/prompt/${encodeURIComponent(imagePrompt)}?width=512&height=512&seed=${randomSeed()}`;
397+
aiContent += `\n\n**Generated Image:**\n${imageUrl}`;
398+
}
399+
313400
if (aiContent) {
314401
const foundMemories = parseMemoryBlocks(aiContent);
315402
foundMemories.forEach((m) => Memory.addMemoryEntry(m));
@@ -328,6 +415,7 @@ document.addEventListener("DOMContentLoaded", () => {
328415
}
329416
});
330417
};
418+
331419
function extractAIContent(response) {
332420
if (response.choices && response.choices.length > 0) {
333421
if (response.choices[0].message && response.choices[0].message.content) return response.choices[0].message.content;
@@ -336,16 +424,19 @@ document.addEventListener("DOMContentLoaded", () => {
336424
else if (typeof response === "string") return response;
337425
return "Sorry, I couldn't process that response.";
338426
}
427+
339428
function parseMemoryBlocks(text) {
340429
const memRegex = /\[memory\]([\s\S]*?)\[\/memory\]/gi;
341430
const found = [];
342431
let match;
343432
while ((match = memRegex.exec(text)) !== null) found.push(match[1].trim());
344433
return found;
345434
}
435+
346436
function removeMemoryBlocks(text) {
347437
return text.replace(/\[memory\][\s\S]*?\[\/memory\]/gi, "");
348438
}
439+
349440
if (voiceToggleBtn) {
350441
voiceToggleBtn.addEventListener("click", window._chatInternals.toggleAutoSpeak);
351442
window._chatInternals.updateVoiceToggleUI();
@@ -364,8 +455,10 @@ document.addEventListener("DOMContentLoaded", () => {
364455
}
365456
}, 2000);
366457
}
458+
367459
if (clearChatBtn) {
368460
clearChatBtn.addEventListener("click", () => {
461+
const currentSession = Storage.getCurrentSession();
369462
if (confirm("Are you sure you want to clear this chat?")) {
370463
currentSession.messages = [];
371464
Storage.updateSessionMessages(currentSession.id, currentSession.messages);
@@ -374,6 +467,7 @@ document.addEventListener("DOMContentLoaded", () => {
374467
}
375468
});
376469
}
470+
377471
function checkFirstLaunch() {
378472
const firstLaunch = localStorage.getItem("firstLaunch") === "0";
379473
if (firstLaunch) {
@@ -404,6 +498,7 @@ document.addEventListener("DOMContentLoaded", () => {
404498
}
405499
}
406500
checkFirstLaunch();
501+
407502
function setupVoiceInputButton() {
408503
if ("webkitSpeechRecognition" in window || "SpeechRecognition" in window) {
409504
const inputButtonsContainer = document.querySelector(".input-buttons-container");
@@ -419,6 +514,7 @@ document.addEventListener("DOMContentLoaded", () => {
419514
}
420515
}
421516
setupVoiceInputButton();
517+
422518
const sendButton = document.getElementById("send-button");
423519
function handleSendMessage() {
424520
const message = chatInput.value.trim();
@@ -429,21 +525,26 @@ document.addEventListener("DOMContentLoaded", () => {
429525
window.sendToPollinations();
430526
sendButton.disabled = true;
431527
}
528+
432529
chatInput.addEventListener("input", () => {
433530
sendButton.disabled = chatInput.value.trim() === "";
434531
chatInput.style.height = "auto";
435532
chatInput.style.height = chatInput.scrollHeight + "px";
436533
});
534+
437535
chatInput.addEventListener("keydown", (e) => {
438536
if (e.key === "Enter" && !e.shiftKey) {
439537
e.preventDefault();
440538
handleSendMessage();
441539
}
442540
});
541+
443542
sendButton.addEventListener("click", () => {
444543
handleSendMessage();
445544
});
446-
if (currentSession.messages && currentSession.messages.length > 0) {
447-
renderStoredMessages(currentSession.messages);
545+
546+
const initialSession = Storage.getCurrentSession();
547+
if (initialSession.messages && initialSession.messages.length > 0) {
548+
renderStoredMessages(initialSession.messages);
448549
}
449550
});

0 commit comments

Comments
 (0)