DeepSeek R1 Chat
Hi! I'm Deepseek R1. How can I help you today?
Write a browser-side app that uses google's gemini pro api to chat in a standalone HTML. Requirements are listed as follows: (1) Save my Gemini key in a local storage. (2) Support the conversation history and save it a local storage. Add a button to clear the conversation history so that gemini is aware of the context of previous messages. Allow the user to set the token usage threshold, compare it with tokens used in the metadata of gemini pro api response and alert the user if beyond the thrshold. (3) Use marked.js to display Gemini messages. (4) Use the textarea element for user to paste multi-line text. (5) Use highlight.js to check the code language automatically and highlight its code block if any. (6) Add one feature that uses free Google Translate API (https://translate.googleapis.com/translate_a/single) to translate the Gemini message to any language and append the translated message to the "normal" Gemini message. Provide an HTML element for the user to enable/disable the translation feature. Use select and option elements for the user to select output language which defaults to Simplified Chinese. Languages also support Traditional Chinese, Japanese, English and Spanish. Note that the translated text may not have the same format as the input text and marked.js may not handle it well. (7) Maximize the display window. Maximize the message window and minimize control/config buttons except the send message button.

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:

  1. Local storage for API key and conversation history
  2. Token usage tracking and alerts
  3. Markdown rendering with marked.js
  4. Syntax highlighting with highlight.js
  5. Translation feature using Google Translate API
  6. Responsive layout with maximized chat area
  7. Auto-scroll to bottom for new messages

To use:

  1. Get a Gemini API key from Google AI Studio
  2. Paste your key in the input field and click "Save Key"
  3. Set a token threshold if desired
  4. Use the textarea to input messages and click Send
  5. Toggle translation and select language as needed

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.

Tokens used: 3934