Here's a standalone HTML file implementing all the requested features:
<!DOCTYPE html>
<html>
<head>
<title>Gemini Pro Chat</title>
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css">
<style>
body { margin: 0; padding: 10px; height: 100vh; display: flex; flex-direction: column; }
#config { margin-bottom: 10px; padding: 10px; background: #f0f0f0; border-radius: 5px; }
#chat-container { flex: 1; overflow-y: auto; border: 1px solid #ccc; margin: 10px 0; padding: 10px; }
.message { margin: 5px 0; padding: 8px; border-radius: 5px; }
.user { background: #e3f2fd; text-align: right; }
.assistant { background: #f5f5f5; }
.translated { color: #666; font-size: 0.9em; margin-top: 5px; }
#input-area { display: flex; gap: 10px; }
textarea { flex: 1; height: 80px; padding: 8px; }
button { padding: 8px 16px; }
.hidden { display: none; }
</style>
</head>
<body>
<div id="config">
<div>
<input type="password" id="apiKey" placeholder="Gemini API Key">
<button onclick="saveApiKey()">Save Key</button>
<button onclick="clearHistory()">Clear History</button>
</div>
<div>
<label>Token Threshold: <input type="number" id="tokenThreshold" value="1000"></label>
<button onclick="saveTokenThreshold()">Save Threshold</button>
</div>
<div>
<label><input type="checkbox" id="translateCheckbox"> Enable Translation</label>
<select id="languageSelect">
<option value="zh-CN">Simplified Chinese</option>
<option value="zh-TW">Traditional Chinese</option>
<option value="ja">Japanese</option>
<option value="en">English</option>
<option value="es">Spanish</option>
</select>
</div>
</div>
<div id="chat-container"></div>
<div id="input-area">
<textarea id="userInput" placeholder="Type your message..."></textarea>
<button onclick="sendMessage()">Send</button>
</div>
<script>
let conversation = [];
let totalTokens = 0;
// Load saved data
window.onload = () => {
document.getElementById('apiKey').value = localStorage.getItem('geminiKey') || '';
document.getElementById('tokenThreshold').value = localStorage.getItem('tokenThreshold') || 1000;
document.getElementById('translateCheckbox').checked = localStorage.getItem('translateEnabled') === 'true';
document.getElementById('languageSelect').value = localStorage.getItem('targetLanguage') || 'zh-CN';
loadConversation();
};
function saveApiKey() {
const key = document.getElementById('apiKey').value;
localStorage.setItem('geminiKey', key);
}
function saveTokenThreshold() {
const threshold = document.getElementById('tokenThreshold').value;
localStorage.setItem('tokenThreshold', threshold);
}
function clearHistory() {
localStorage.removeItem('conversation');
conversation = [];
totalTokens = 0;
document.getElementById('chat-container').innerHTML = '';
}
function loadConversation() {
const saved = localStorage.getItem('conversation');
if (saved) {
conversation = JSON.parse(saved);
conversation.forEach(msg => displayMessage(msg));
}
}
async function sendMessage() {
const userInput = document.getElementById('userInput').value.trim();
const apiKey = localStorage.getItem('geminiKey');
if (!apiKey) return alert('Please save your API key first');
if (!userInput) return;
// Add user message
const userMsg = { role: 'user', parts: [{ text: userInput }] };
conversation.push(userMsg);
displayMessage(userMsg);
try {
const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ contents: conversation })
});
const data = await response.json();
const geminiMsg = data.candidates[0].content;
conversation.push(geminiMsg);
localStorage.setItem('conversation', JSON.stringify(conversation));
// Check token usage
totalTokens += data.usageMetadata.totalTokenCount;
const threshold = localStorage.getItem('tokenThreshold');
if (totalTokens > threshold) {
alert(`Token usage exceeded threshold! Used: ${totalTokens} / ${threshold}`);
}
displayMessage(geminiMsg);
if (document.getElementById('translateCheckbox').checked) {
translateMessage(geminiMsg.parts[0].text);
}
} catch (error) {
alert('Error: ' + error.message);
}
document.getElementById('userInput').value = '';
}
function displayMessage(msg) {
const container = document.getElementById('chat-container');
const div = document.createElement('div');
div.className = `message ${msg.role === 'user' ? 'user' : 'assistant'}`;
div.innerHTML = marked.parse(msg.parts[0].text);
container.appendChild(div);
hljs.highlightAll();
container.scrollTop = container.scrollHeight;
}
async function translateMessage(text) {
const targetLang = document.getElementById('languageSelect').value;
try {
const response = await fetch(
`https://translate.googleapis.com/translate_a/single?client=gtx&sl=auto&tl=${targetLang}&dt=t&q=${encodeURIComponent(text)}`
);
const data = await response.json();
const translatedText = data[0].map(item => item[0]).join('');
const container = document.getElementById('chat-container');
const div = document.createElement('div');
div.className = 'translated';
div.textContent = translatedText;
container.appendChild(div);
} catch (error) {
console.error('Translation error:', error);
}
}
// Save translation settings
document.getElementById('translateCheckbox').addEventListener('change', function() {
localStorage.setItem('translateEnabled', this.checked);
});
document.getElementById('languageSelect').addEventListener('change', function() {
localStorage.setItem('targetLanguage', this.value);
});
</script>
</body>
</html>
This implementation includes:
To use:
Note: The Google Translate API used here is a public endpoint that doesn't require an API key but may have usage limits. For production use, consider using the official Cloud Translation API with an API key.