add message regeneration for bot messages

<!DOCTYPE html>
<html>
<head>
<title>Google</title>
<style>
body {
font-family: sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
background-color: #f0f0f0;
}

.container {
display: flex;
flex-direction: column;
width: 90%;
max-width: 600px;
height: 80vh;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
overflow: hidden;
}

.header {
padding: 10px 20px;
background-color: #f9f9f9;
border-bottom: 1px solid #ddd;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
}

.toggle-container {
display: flex;
align-items: center;
margin: 5px 0;
}

.toggle-container label {
margin-right: 10px;
font-size: 14px;
}

.switch {
position: relative;
display: inline-block;
width: 50px;
height: 28px;
margin-left: 10px;
}

.switch input {
opacity: 0;
width: 0;
height: 0;
}

.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: 0.4s;
border-radius: 28px;
}

.slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
}

input:checked + .slider {
background-color: #2196F3;
}

input:checked + .slider:before {
transform: translateX(22px);
}

#chat-container {
flex: 1;
padding: 15px;
overflow-y: auto;
background-color: #fafafa;
display: flex;
flex-direction: column;
}

.message {
padding: 10px 15px;
margin-bottom: 10px;
border-radius: 18px;
max-width: 80%;
word-wrap: break-word;
position: relative;
clear: both;
white-space: pre-line;
}

.user-message {
background-color: #e0f7fa;
align-self: flex-end;
margin-left: auto;
text-align: right;
}

.bot-message {
background-color: #f1f8e9;
align-self: flex-start;
margin-right: auto;
}

#input-area {
display: flex;
padding: 10px 20px;
border-top: 1px solid #ddd;
background-color: #f9f9f9;
align-items: center;
}

#input-box {
flex-grow: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 20px;
resize: none;
font-size: 14px;
outline: none;
}

#send-button {
padding: 10px 20px;
margin-left: 10px;
background-color: #007bff;
color: #fff;
border: none;
border-radius: 20px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}

#send-button:hover {
background-color: #0056b3;
}

#file-input {
margin-left: 10px;
}

#typing-indicator {
padding: 5px 20px;
font-style: italic;
color: #999;
background-color: #f9f9f9;
border-top: 1px solid #ddd;
display: none;
}

@media (max-width: 768px) {
.container {
width: 95%;
height: 90vh;
}
}

@media (max-width: 480px) {
.header {
flex-direction: column;
align-items: flex-start;
}

.toggle-container {
width: 100%;
justify-content: space-between;
}

#send-button {
padding: 10px;
margin-left: 5px;
}
}
/* Style for the changelog button */
#changelog-button {
padding: 8px 15px;
margin-left: 10px;
background-color: #4CAF50; /* Green */
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}

#changelog-button:hover {
background-color: #45a049;
}

/* Style for the changelog modal */
.modal {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgb(0,0,0);
background-color: rgba(0,0,0,0.4);
}
#clear-chat-button {
    padding: 8px 15px;
    margin-left: 10px; /* Space between changelog and clear buttons */
    background-color: #f44336; /* Red */
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    transition: background-color 0.3s;
}

#clear-chat-button:hover {
    background-color: #d32f2f; /* Darker red on hover */
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
}

.close {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}

.close:hover,
.close:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
#last-updated {
position: fixed;
top: 10px;
right: 10px;
font-size: 12px;
color: #999;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> </head>
<body>
<div class="container">
    <div class="header">
        <div style="display:flex; align-items:center;">
            <div class="toggle-container">
                <label for="modelToggle" style="margin-right: 5px;">Fast Mode:</label>
                <label class="switch">
                    <input type="checkbox" id="modelToggle">
                    <span class="slider"></span>
                </label>
            </div>
        </div>
        <div>
            <button id="changelog-button">Changelog</button>
            <button id="clear-chat-button">Clear Chat</button>
        </div>
    </div>
    <div id="chat-container"></div>
    <div id="typing-indicator">Gemini is typing...</div>
    <div id="input-area">
        <input type="file" id="file-input" accept="image/*,application/pdf,audio/*" style="display:none;"> <label for="file-input" style="padding: 10px; margin-right: 5px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer;">Choose File</label>
        <textarea id="input-box" placeholder="Enter your message..." rows="1"></textarea>
        <button id="send-button">Send</button>
    </div>
    <p style="font-size: 10px; text-align: center; color: #888;">
        contact <PRESIDIO_ANONYMIZED_EMAIL_ADDRESS> for bug reports, feature requests, or an overpriced haircut
    </p>
</div>


<!-- The Modal -->
<div id="changelogModal" class="modal">
<div class="modal-content">
<span class="close">&times;</span>
<h2>Changelog</h2>
<p>
<strong>Version 1.2 + 0.06 (November 25, 2024)</strong><br>
- I forgot to remove some debugging code (error messages in chat).<br>
</p>
<strong>Version 1.2 + 0.05 (November 22, 2024)</strong><br>
- Added A Clear Chat Button (got tired of refreshing).<br>
- General UI Improvememts
</p>
<strong>Version 1.2 (November 22, 2024)</strong><br>
- Added Text Formatting (finally).<br>
- Smarter AI Models.<br>
- Probably added more bugs too :(<br>
- Out of New Ideas Until Further Notice
</p>
<p>
<strong>Version 1.1001 (November 21, 2024)</strong><br>
- Moved the last updated label, very minor bug fixing (next update will be fire I promise).<br>
</p>
<p>
<strong>Version 1.1 (November 21, 2024)</strong><br>
- Added changelog.<br>
- Minor UI improvements.<br>
- Next update will have better text formatting and more unfun bug fixing :(
</p>
<p>
<strong>Version 1.0 (Initial Release)</strong><br>
- Made the thing, brick by brick
</p>
</div>
</div>

<script>
const API_KEYS = [
'AIzaSyBdc_A8VxExwO6-0LY1--r2-Ym73lMg-h4',
'AIzaSyBydeCpaNOIPSk794CpTGnfKrxbjCgJLu4',
'AIzaSyCQ7tBHtctxlNs61Zm2Y__7KYeDLfz5dms',
'AIzaSyAip_DFfKNC35MFIorEoNK4y6czP9P65-c'
];
const BASE_URL = 'https://generativelanguage.googleapis.com';

const chatContainer = document.getElementById('chat-container');
const inputBox = document.getElementById('input-box');
const sendButton = document.getElementById('send-button');
const typingIndicator = document.getElementById('typing-indicator');
const modelToggle = document.getElementById('modelToggle');
const fileInput = document.getElementById('file-input');

let messageHistory = [];
let currentModel = 'gemini-1.5-pro-002';

modelToggle.addEventListener('change', () => {
if (modelToggle.checked) {
currentModel = 'gemini-1.5-flash-002';
} else {
currentModel = 'gemini-1.5-pro-002';
}
console.log(`Model switched to: ${currentModel}`);
});

sendButton.addEventListener('click', sendMessage);

inputBox.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
sendMessage();
}
});
function displayError(errorMessage) {
const errorDiv = document.createElement('div');
errorDiv.classList.add('message', 'bot-message'); // Style it like a bot message
errorDiv.textContent = `Error: ${errorMessage}`;
chatContainer.appendChild(errorDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
async function fetchWithKey(apiKey, contents, model) {
const response = await fetch(`${BASE_URL}/v1beta/models/${model}:generateContent?key=${apiKey}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
contents: contents,
generationConfig: {
temperature: 0.5,
topK: 40,
topP: 0.95,
maxOutputTokens: 8192,
responseMimeType: 'text/plain'
}
})
});

if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error?.message || 'Unknown API error');
}

const data = await response.json();
return data;
}

async function sendMessage() {
const userMessageText = inputBox.value.trim();
const file = fileInput.files[0];

if (userMessageText === '' && !file) return;

const userMessage = document.createElement('div');
userMessage.classList.add('message', 'user-message');
userMessage.textContent = userMessageText || 'Sent a file';
chatContainer.appendChild(userMessage);
chatContainer.scrollTop = chatContainer.scrollHeight;

inputBox.value = '';
fileInput.value = '';

typingIndicator.style.display = 'block';

let botMessageText = '';
let success = false;
let fileData = null; // Initialize fileData outside the try block
const contents = [];

for (const msg of messageHistory) {
contents.push(msg);
}

if (userMessageText) {
contents.push({ role: 'user', parts: [{ text: userMessageText }] });
}

if (file) {
try {
fileData = await uploadFile(file); // Assign fileData here
contents.push({ role: 'user', parts: [{ file_data: { mime_type: file.type, file_uri: fileData.file.uri } }] });
} catch (error) {
console.error('File upload error:', error);
typingIndicator.style.display = 'none';
alert('Failed to upload file: ' + error.message);
return;
}
}

for (const apiKey of API_KEYS) {
try {
const data = await fetchWithKey(apiKey, contents, currentModel);
const fullResponse = data?.candidates[0]?.content?.parts[0]?.text;

if (!fullResponse) {
throw new Error('Invalid response format: Missing content.');
}

botMessageText = fullResponse.trim();

if (botMessageText) {
messageHistory.push({ role: 'model', parts: [{ text: botMessageText }] });
typingIndicator.style.display = 'none';
}

success = true;
break;
} catch (error) {
console.error(`Error with API key:`, error.message);
}
}

const botMessage = document.createElement('div');
botMessage.classList.add('message', 'bot-message');

if (success) {
// **Use marked.js to convert Markdown to HTML**
const formattedText = marked.parse(botMessageText);
botMessage.innerHTML = formattedText; // Use innerHTML to render HTML
} else {
botMessage.textContent = "I'm sorry, I'm currently unable to process your request. Please try again later.";
}

chatContainer.appendChild(botMessage);
chatContainer.scrollTop = chatContainer.scrollHeight;

if (userMessageText || file) {
messageHistory.push({ role: 'user', parts: userMessageText ? [{ text: userMessageText }] : [] });
if (file && fileData) { // Check if fileData is available
messageHistory.push({ role: 'user', parts: [{ file_data: { mime_type: file.type, file_uri: fileData.file.uri } }] });
}
}

typingIndicator.style.display = 'none';
}

async function uploadFile(file) {
return new Promise(async (resolve, reject) => {
try {
const apiKey = API_KEYS[0];
const startResponse = await fetch(`${BASE_URL}/upload/v1beta/files?key=${apiKey}`, {
method: 'POST',
headers: {
'X-Goog-Upload-Protocol': 'resumable',
'X-Goog-Upload-Command': 'start',
'X-Goog-Upload-Header-Content-Length': file.size,
'X-Goog-Upload-Header-Content-Type': file.type,
'Content-Type': 'application/json'
},
body: JSON.stringify({ file: { display_name: file.name } })
});

const uploadUrl = startResponse.headers.get('X-Goog-Upload-URL');
if (!uploadUrl) {
throw new Error('Failed to get upload URL');
}

const uploadResponse = await fetch(uploadUrl, {
method: 'POST',
headers: {
'Content-Length': file.size,
'X-Goog-Upload-Offset': '0',
'X-Goog-Upload-Command': 'upload, finalize'
},
body: file
});

if (!uploadResponse.ok) {
const errorData = await uploadResponse.json();
throw new Error(errorData.error?.message || 'Unknown upload error');
}

const fileData = await uploadResponse.json();

resolve(fileData);

} catch (error) {
reject(error);
}
});
}
// Last Updated Label
const lastUpdated = document.createElement('p');
lastUpdated.id = 'last-updated'; // Give it an ID for easy styling
lastUpdated.textContent = 'Last updated: November 25, 2024, 9:47 PM';
document.body.appendChild(lastUpdated);

// Changelog Modal
const changelogModal = document.getElementById("changelogModal");
const btn = document.getElementById("changelog-button");
const span = document.getElementsByClassName("close")[0];

btn.onclick = function() {
changelogModal.style.display = "block";
}

span.onclick = function() {
changelogModal.style.display = "none";
}

window.onclick = function(event) {
if (event.target == changelogModal) {
changelogModal.style.display = "none";
}
}
const clearChatButton = document.getElementById('clear-chat-button');
clearChatButton.addEventListener('click', () => {
    chatContainer.innerHTML = ''; // Clear the chat container
    messageHistory = [];        // Clear the message history
});
</script>
</body>
</html>