about summary refs log blame commit diff stats
path: root/html/matt-chat/index.html
blob: 2bc81190863b38a40d8b28f1e237f050e0563cc6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11




                                                                          

                                                                                                                



                                                                 
                                                                                                       


                                           
                            


                                      
                             





                                   




                                    
                         
                           
                    
                             

                              
                                    

                     
                        




                                   
                                   








                                      
                        



                                      












                                           


                                  
                         

                                      

                            
         
 



                                      
                              
         
 


                                      

                               
         


                                   
                                  

             



































                                      


                            


                               

         
                                


                                           

         
                                  


























































































                                                  
 
                         




















































































































                                                                                       
 
                               

                                      
                                                           




















































































































































                                                              










































































































































































                                                             



            
                                        
                                           




                                                                                    



                                          

                                                                                                            


                                                                                              


                                                                                

            








                                                                                            



                             


                                                                              



                                                                                                                             

          









                                                                                                                             



                                   

          
                                                        
 
                                                          
 

                                                     
                                                                         
                                                          


                                                                                       

          




                                       



                                                          
                                              


                                                                        
                                                        

                                
                 
                                                                    
                                                                            
 
                                                   
                                                   
 
                                               

                                                                        
                                                
                                                      
                                                        
                                                
                       
                                                        
                        
                                                                                                 

                             
                                                                       
                       
                                        
                                          
                                                                                                                                


                                                                      
             

         
                                                             
                                  
                                                                        



                                                                     


                                                                         

                                                                                       
           
 





                                                                                                        
                                                                                
                                                                                                                 
                                                                                       

         
                                                                
                                           

                                                                        
                                                 




                                                                                  

         
                                     















                                                                                           




                                                                               
                                                                                                                                                                


                                                                                                           
 





                                                                    
                                       
                                                                                                     

                                                                  
                                                  


                       


                                                                  
                                                  





                                                                  
                                                  


                       
                                                                                                   





                                                                  




                                                                                                                                                
 


                                                                               
                                                                               










                                                                                                                      

















                                                                                     
                                            
                                                                          
            



                                                                    





                                                                                    
                             



                                                                                

                                                                   


                                                                            


                                                                                                                    
                                                                          
 
                                                                          





                                                           
                                                 






                                                                           
                                                   
                                                   
 

                                                                        
                    


                                                     

                                                             
                                                   
                                                                                                  
 

                                                            



                                                                                   
                                                                                       

                                     





                                                                                            

                                                                                     
                 








                                                                                        


                                                     
                                      





                                                                  

                                                                                      





                                                                                         
 




























                                                                            
 

                                                                            





                                         

         

                                 
                     
                                                            



                                                    

                                                                                                    

                                          
              
                                           
         














                                                                                       
                                                                          




































                                                                                      
                                                                               



                                                                      
                                       




                                                                                           
                                                                            
























                                                                                                                                                                                                   
                                             









                                                                                









                                                                            
 



                                                                            
 








                                                                 






                                                                                      


                                                                          


                                                                          


                                                                          






                                                                          
                                                                         

                                                                                   


                                           


             
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="Chatty chat chat chat. A super simple chat interface for the Ollama API.">
    <title>matt chat is not a cat</title>
    <meta name="theme-color" content="#007BFF">
    <link rel="icon" href="cat.png" type="image/x-icon">
    <link rel="shortcut icon" href="cat.png" type="image/x-icon">
    <link rel="apple-touch-icon" href="cat.png">  
    <link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: Arial, sans-serif;
            font-size: 22px;
            margin: 0;
            padding: 20px;
            background-color: #f7f7f7;
            max-width: 800px;
            margin: 0 auto;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100vh;
            overflow: hidden;
        }
        #chat-container {
            background-color: white;
            border: 1px solid #ccc;
            border-radius: 8px;
            padding: 1em;
            margin: 0 auto;
            flex: 1;
            overflow-y: auto;
            width: 100%;
            max-height: 400px;
            scroll-behavior: smooth;
        }
        #user-input {
            width: 100%;
            padding: 10px;
            border-radius: 4px;
            border: 1px solid #ddd;
            font-size: 16px;
            margin-top: 10px;
            box-sizing: border-box;
        }
        #send-button {
            padding: 10px 15px;
            border-radius: 4px;
            background-color: #007BFF;
            color: white;
            border: none;
            cursor: pointer;
            margin-top: 10px;
            width: 100%;
        }
        #send-button:hover {
            background-color: #0056b3;
        }

        .model-select-container {
            align-self: flex-start;
            width: 100%;
            display: flex;
            justify-content: space-between;
            padding: 1em;
        }

        .model-select-container label {
            margin-left: 10px;
        }

        .message {
            white-space: pre-wrap;
            margin-bottom: 10px;
            padding: 1em;
            border-radius: 8px;
            background-color: #f1f1f1;
            display: block;
            max-width: 100%;
        }

        .user-message {
            background-color: #007BFF;
            color: white;
            text-align: right;
            margin-left: 20px;
        }

        .bot-message {
            background-color: #f0f0f0;
            color: #333;
            text-align: left;
            margin-right: 20px;
        }

        @media (max-width: 600px) {
            #chat-container {
                max-height: 300px;
            }
        }

        body.dark-mode {
            background-color: #333;
            color: #f7f7f7;
        }

        #chat-container.dark-mode {
            background-color: #444;
            border: 1px solid #555;
        }

        #user-input.dark-mode {
            background-color: #555;
            color: #f7f7f7;
            border: 1px solid #666;
        }

        #send-button.dark-mode {
            background-color: #007BFF;
            color: white;
        }

        .message.dark-mode {
            background-color: #555;
            color: #f7f7f7;
        }

        .user-message.dark-mode {
            background-color: #007BFF;
            color: white;
        }

        .bot-message.dark-mode {
            background-color: #666;
            color: #f7f7f7;
        }

        .bot-time {
            margin: 0.5em 0;
            font-size: 0.9em;
            color: #888;
            text-align: center;
        }
        
        /* Professional theme */
        body.theme-professional {
            font-family: Arial, sans-serif;
            font-size: 22px;
        }

        /* Molly Millions theme */
        body.theme-molly-millions {
            font-family: "Courier New", monospace;
            font-size: 22px;
            margin: 0;
            padding: 20px;
            background-color: #0a0a0a;
            color: #00ff00;
            max-width: 800px;
            margin: 0 auto;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100vh;
            overflow: hidden;
        }

        .theme-molly-millions #chat-container {
            background-color: #000000;
            border: 2px solid #00ff00;
            border-radius: 0;
            padding: 1em;
            margin: 0 auto;
            flex: 1;
            overflow-y: auto;
            width: 100%;
            max-height: 400px;
            scroll-behavior: smooth;
            box-shadow: 0 0 10px #00ff00;
        }

        .theme-molly-millions #user-input {
            width: 100%;
            padding: 10px;
            border-radius: 0;
            border: 2px solid #00ff00;
            background-color: #000000;
            color: #00ff00;
            font-family: "Courier New", monospace;
            font-size: 16px;
            margin-top: 10px;
            box-sizing: border-box;
        }

        .theme-molly-millions #send-button {
            padding: 10px 15px;
            border-radius: 0;
            background-color: #000000;
            color: #00ff00;
            border: 2px solid #00ff00;
            cursor: pointer;
            margin-top: 10px;
            width: 100%;
            font-family: "Courier New", monospace;
            text-transform: uppercase;
        }

        .theme-molly-millions #send-button:hover {
            background-color: #00ff00;
            color: #000000;
        }

        .theme-molly-millions .message {
            white-space: pre-wrap;
            margin-bottom: 10px;
            padding: 1em;
            border-radius: 0;
            border: 1px solid #00ff00;
            background-color: #0a0a0a;
            display: block;
            max-width: 100%;
        }

        .theme-molly-millions .user-message {
            background-color: #001100;
            color: #00ff00;
            border: 1px solid #00ff00;
            text-align: right;
            margin-left: 20px;
        }

        .theme-molly-millions .bot-message {
            background-color: #000000;
            color: #00ff00;
            border: 1px solid #00ff00;
            text-align: left;
            margin-right: 20px;
        }

        .theme-molly-millions .bot-time {
            color: #005500;
        }

        /* Cloud theme */
        body.theme-cloud {
            font-family: "Press Start 2P", "Courier New", monospace;
            font-size: 18px;
            margin: 0;
            padding: 20px;
            background: linear-gradient(135deg, #1a1b4b 0%, #162057 50%, #1a1b4b 100%);
            color: #ffffff;
            max-width: 800px;
            margin: 0 auto;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100vh;
            overflow: hidden;
        }

        .theme-cloud #chat-container {
            background: rgba(0, 0, 32, 0.75);
            border: 3px solid #4080ff;
            border-radius: 3px;
            padding: 1em;
            margin: 0 auto;
            flex: 1;
            overflow-y: auto;
            width: 100%;
            max-height: 400px;
            scroll-behavior: smooth;
            box-shadow: 0 0 15px rgba(64, 128, 255, 0.3);
        }

        .theme-cloud #user-input {
            width: 100%;
            padding: 10px;
            border: 2px solid #4080ff;
            background: rgba(0, 0, 32, 0.75);
            color: #ffffff;
            font-family: "Press Start 2P", "Courier New", monospace;
            font-size: 14px;
            margin-top: 10px;
            box-sizing: border-box;
        }

        .theme-cloud #send-button {
            padding: 10px 15px;
            background: linear-gradient(to bottom, #4080ff 0%, #2048c0 100%);
            color: white;
            border: 2px solid #2048c0;
            cursor: pointer;
            margin-top: 10px;
            width: 100%;
            font-family: "Press Start 2P", "Courier New", monospace;
            font-size: 14px;
            text-transform: uppercase;
            text-shadow: 2px 2px #000000;
        }

        .theme-cloud #send-button:hover {
            background: linear-gradient(to bottom, #50a0ff 0%, #3060e0 100%);
        }

        .theme-cloud .message {
            white-space: pre-wrap;
            margin-bottom: 10px;
            padding: 1em;
            border: 2px solid #4080ff;
            background: rgba(0, 0, 32, 0.5);
            display: block;
            max-width: 100%;
            font-size: 14px;
        }

        .theme-cloud .user-message {
            background: rgba(64, 128, 255, 0.2);
            color: #ffffff;
            border: 2px solid #4080ff;
            text-align: right;
            margin-left: 20px;
            text-shadow: 1px 1px #000000;
        }

        .theme-cloud .bot-message {
            background: rgba(32, 64, 128, 0.2);
            color: #ffffff;
            border: 2px solid #4080ff;
            text-align: left;
            margin-right: 20px;
            text-shadow: 1px 1px #000000;
        }

        .theme-cloud .bot-time {
            color: #80c0ff;
            font-size: 12px;
            text-shadow: 1px 1px #000000;
        }

        .theme-cloud #counter {
            color: #80c0ff !important;
            text-shadow: 1px 1px #000000;
        }

        .theme-cloud .model-select-container {
            background: rgba(0, 0, 32, 0.75);
            border: 2px solid #4080ff;
            padding: 10px;
            margin-bottom: 10px;
            width: 100%;
            box-sizing: border-box;
        }

        .theme-cloud #model-select {
            background: rgba(0, 0, 32, 0.75);
            color: #ffffff;
            border: 1px solid #4080ff;
            padding: 5px;
            font-family: "Press Start 2P", "Courier New", monospace;
            font-size: 12px;
        }

        /* Classic Mac theme */
        @font-face {
            font-family: 'ChicagoFLF';
            src: url('/ChicagoFLF.ttf') format('truetype');
        }

        body.theme-classic {
            font-family: 'ChicagoFLF', 'Monaco', monospace;
            font-size: 14px;
            margin: 0;
            padding: 20px;
            background-color: #DDDDDD;
            color: #000000;
            max-width: 800px;
            margin: 0 auto;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100vh;
            overflow: hidden;
            image-rendering: pixelated;
        }

        .theme-classic #chat-container {
            background-color: #FFFFFF;
            border: 2px solid #000000;
            border-radius: 2px;
            padding: 1em;
            margin: 0 auto;
            flex: 1;
            overflow-y: auto;
            width: 100%;
            max-height: 400px;
            scroll-behavior: smooth;
            box-shadow: 2px 2px 0px #000000;
        }

        .theme-classic #user-input {
            width: 100%;
            padding: 8px;
            border: 2px solid #000000;
            background-color: #FFFFFF;
            color: #000000;
            font-family: 'ChicagoFLF', 'Monaco', monospace;
            font-size: 14px;
            margin-top: 10px;
            box-sizing: border-box;
            border-radius: 2px;
        }

        .theme-classic #send-button {
            padding: 4px 15px;
            background-color: #FFFFFF;
            color: #000000;
            border: 2px solid #000000;
            border-radius: 2px;
            cursor: pointer;
            margin-top: 10px;
            width: 100%;
            font-family: 'ChicagoFLF', 'Monaco', monospace;
            font-size: 14px;
            box-shadow: 2px 2px 0px #000000;
        }

        .theme-classic #send-button:hover {
            background-color: #000000;
            color: #FFFFFF;
        }

        .theme-classic #send-button:active {
            box-shadow: 1px 1px 0px #000000;
            transform: translate(1px, 1px);
        }

        .theme-classic .message {
            white-space: pre-wrap;
            margin-bottom: 10px;
            padding: 8px;
            border: 2px solid #000000;
            background-color: #FFFFFF;
            display: block;
            max-width: 100%;
            font-size: 14px;
            border-radius: 2px;
        }

        .theme-classic .user-message {
            background-color: #FFFFFF;
            color: #000000;
            text-align: right;
            margin-left: 20px;
            box-shadow: 2px 2px 0px #000000;
        }

        .theme-classic .bot-message {
            background-color: #FFFFFF;
            color: #000000;
            text-align: left;
            margin-right: 20px;
            box-shadow: 2px 2px 0px #000000;
        }

        .theme-classic .bot-time {
            color: #666666;
            font-size: 12px;
            text-align: center;
            margin: 4px 0;
        }

        .theme-classic #counter {
            color: #000000 !important;
        }

        .theme-classic .model-select-container {
            background-color: #FFFFFF;
            border: 2px solid #000000;
            padding: 8px;
            margin-bottom: 10px;
            width: 100%;
            box-sizing: border-box;
            border-radius: 2px;
            box-shadow: 2px 2px 0px #000000;
        }

        .theme-classic #model-select {
            background-color: #FFFFFF;
            color: #000000;
            border: 2px solid #000000;
            padding: 2px;
            font-family: 'ChicagoFLF', 'Monaco', monospace;
            font-size: 14px;
            border-radius: 2px;
        }

        .theme-classic input[type="checkbox"] {
            appearance: none;
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            border: 2px solid #000000;
            background-color: #FFFFFF;
            position: relative;
            vertical-align: middle;
            margin-right: 5px;
        }

        .theme-classic input[type="checkbox"]:checked::after {
            content: '✓';
            position: absolute;
            left: 1px;
            top: -2px;
            font-size: 14px;
        }

        /* LCARS Theme */
        body.theme-lcars {
            font-family: "Helvetica Neue", Arial, sans-serif;
            font-size: 18px;
            margin: 0;
            padding: 20px;
            background-color: #000;
            color: #FF9966;
            max-width: 800px;
            margin: 0 auto;
            display: flex;
            flex-direction: column;
            align-items: center;
            height: 100vh;
            overflow: hidden;
        }

        .theme-lcars #chat-container {
            background-color: #000;
            border: none;
            border-radius: 0;
            padding: 1em;
            margin: 0 auto;
            flex: 1;
            overflow-y: auto;
            width: 100%;
            max-height: 400px;
            scroll-behavior: smooth;
            position: relative;
        }

        .theme-lcars #chat-container::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 2em;
            background: #CC6699;
            border-radius: 20px 20px 0 0;
        }

        .theme-lcars #user-input {
            width: 100%;
            padding: 10px;
            border: none;
            background-color: #000;
            color: #FF9966;
            font-family: "Helvetica Neue", Arial, sans-serif;
            font-size: 16px;
            margin-top: 10px;
            box-sizing: border-box;
            border-left: 2em solid #CC6699;
        }

        .theme-lcars #send-button {
            padding: 10px 15px;
            background-color: #CC6699;
            color: #000;
            border: none;
            cursor: pointer;
            margin-top: 10px;
            width: 100%;
            font-family: "Helvetica Neue", Arial, sans-serif;
            font-weight: bold;
            font-size: 16px;
            text-transform: uppercase;
            border-radius: 0 0 20px 20px;
        }

        .theme-lcars #send-button:hover {
            background-color: #FF9966;
        }

        .theme-lcars .message {
            white-space: pre-wrap;
            margin-bottom: 10px;
            padding: 1em;
            border: none;
            display: block;
            max-width: 100%;
            position: relative;
        }

        .theme-lcars .user-message {
            background-color: #000;
            color: #FF9966;
            text-align: right;
            margin-left: 20px;
            border-right: 1em solid #CC6699;
        }

        .theme-lcars .bot-message {
            background-color: #000;
            color: #99CCFF;
            text-align: left;
            margin-right: 20px;
            border-left: 1em solid #9999CC;
        }

        .theme-lcars .bot-time {
            color: #CC6699;
            font-size: 0.8em;
            text-align: center;
            margin: 4px 0;
        }

        .theme-lcars #counter {
            color: #99CCFF !important;
        }

        .theme-lcars .model-select-container {
            background-color: #000;
            border: none;
            padding: 10px;
            margin-bottom: 10px;
            width: 100%;
            box-sizing: border-box;
            display: flex;
            align-items: center;
            border-radius: 20px;
            position: relative;
            overflow: hidden;
        }

        .theme-lcars .model-select-container::before {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            bottom: 0;
            width: 2em;
            background: #9999CC;
            border-radius: 20px 0 0 20px;
        }

        .theme-lcars #model-select {
            background-color: #000;
            color: #FF9966;
            border: none;
            padding: 5px;
            margin-left: 3em;
            font-family: "Helvetica Neue", Arial, sans-serif;
            font-size: 16px;
        }

        .theme-lcars input[type="checkbox"] {
            appearance: none;
            -webkit-appearance: none;
            width: 16px;
            height: 16px;
            border: 2px solid #CC6699;
            background-color: #000;
            position: relative;
            vertical-align: middle;
            margin-right: 5px;
        }

        .theme-lcars input[type="checkbox"]:checked {
            background-color: #CC6699;
        }

        .theme-lcars input[type="checkbox"]:checked::after {
            content: '✓';
            position: absolute;
            left: 2px;
            top: -2px;
            color: #000;
            font-size: 14px;
        }
    </style>
</head>
<body>

    <div class="model-select-container">
        <select id="model-select"></select>
        <label>
            <input type="checkbox" id="retain-history" /> Build Context As You Chat?
        </label>
    </div>
    
    <div id="chat-container">
        <!-- Messages will appear here -->
    </div>

    <!-- New container for user input and send button -->
    <div id="input-container" style="width: 100%; display: flex; flex-direction: column; margin-top: 10px;">
        <div id="counter" style="text-align: left; font-size: 0.9em; color: #555;">
            Characters: <span id="char-count">0</span> | Words: <span id="word-count">0</span>
        </div>
        <textarea id="user-input" placeholder="Type your message..."></textarea>
        <button id="send-button">Send</button>
    </div>

    <script>
        // ==================================================
        // MATT CHAT IS NOT A CAT
        // This is a simple chat interface for the Ollama API
        // ==================================================
        //
        // This configuration object is used to define all local variables for your needs
        // Set the base url for the ollama api, and then list all the models you want to use
        // The context window size is the number of previous exchanges to keep...
        // though this is relatively naive at the moment

        const config = {}

        const localConfig = {
            apiUrl: "http://localhost:11434/v1",
            completionsEndpoint: "http://localhost:11434/v1/chat/completions",
            modelsEndpoint: "http://localhost:11434/v1/models",
            contextWindowSize: 6,
            systemMessage: "You are a helpful assistant. If you don't know something you'll let me know. Your name is Matt.",
            maxTokens: 4096,
            summarizeThreshold: 3584,
        };

        const mattConfig = {
            apiUrl: "http://100.108.91.106:11434/v1",
            completionsEndpoint: "http://100.108.91.106:11434/v1/chat/completions",
            modelsEndpoint: "http://100.108.91.106:11434/v1/models",
            contextWindowSize: 6,
            systemMessage: "You are a helpful assistant. If you don't know something you'll let me know. Your name is Matt.",
            maxTokens: 4096,
            summarizeThreshold: 3584,
        }

        let conversationHistory = {
            summary: null,
            current: [],
            full: []
        };

        let isCatMode = false; // Flag to track cat mode

        const API_MODELS_ENDPOINT = config.modelsEndpoint;

        // Add this near the top with other constants
        const AVAILABLE_THEMES = {
            'professional': 'Professional -- boring, like wearing a tie',
            'molly-millions': 'Molly Millions\' manicure',
            'cloud': 'Cloud -- it took a lot of self control not to add sound effects',
            'classic': 'Classic -- this is not a fish',
            'lcars': 'LCARS -- boldly going'
        };

        function handleError(message) {
            console.error(message);
            addMessage(message, "bot");
        }

        function showLoadingMessage() {
            return addMessage("Loading models...", "bot");
        }

        async function populateModelSelect() {
            const modelSelect = document.getElementById("model-select");
            modelSelect.innerHTML = ""; // Clear existing options

            const loadingMessage = showLoadingMessage();
            const modelIds = [];

            try {
                const response = await fetch(config.modelsEndpoint);
                if (!response.ok) throw new Error('Failed to fetch models');

                const data = await response.json();
                console.log("API Response:", data);

                if (Array.isArray(data.data)) {
                    data.data.forEach(model => {
                        const option = document.createElement("option");
                        option.value = model.id;
                        option.textContent = model.id;
                        modelSelect.appendChild(option);
                        modelIds.push(model.id);
                    });
                    console.log("Model IDs:", modelIds);
                } else {
                    handleError("Expected an array of models, but got: " + JSON.stringify(data));
                }
            } catch (error) {
                handleError("Error fetching models: " + error.message);
            } finally {
                loadingMessage.remove();
                if (modelIds.length > 0) {
                    addMessage(`Models loaded successfully! Ready to chat.\n\nAvailable models: ${modelIds.join(', ')}`, "bot");
                } else {
                    addMessage("No models available to chat.", "bot");
                }
            }
        }

        document.addEventListener("DOMContentLoaded", () => {
            populateModelSelect();
            const modelSelect = document.getElementById("model-select");
            const savedModel = localStorage.getItem("selectedModel");
            if (savedModel) {
                modelSelect.value = savedModel;
            }
            modelSelect.addEventListener("change", () => {
                localStorage.setItem("selectedModel", modelSelect.value);
            });
            const savedTheme = localStorage.getItem('selectedTheme') || 'professional';
            switchTheme(savedTheme);
        });

        function addMessage(message, sender = "user") {
            const chatContainer = document.getElementById("chat-container");
            const messageElement = document.createElement("div");
            messageElement.classList.add("message", sender === "user" ? "user-message" : "bot-message");
            messageElement.textContent = message;
            chatContainer.appendChild(messageElement);
            messageElement.scrollIntoView({ behavior: "smooth", block: "end" });
            chatContainer.scrollTop = chatContainer.scrollHeight; // Make sure the chat is scrolled to the bottom
            return messageElement; // Return the message element so it is easier to use
        }

        // Fancy format milliseconds into a more readable format
        function formatDuration(duration) {
            const minutes = Math.floor(duration / (1000 * 60));
            const seconds = Math.floor((duration % (1000 * 60)) / 1000);
            const milliseconds = duration % 1000;
            
            if (minutes > 0) {
                return `${minutes}m ${seconds}.${Math.floor(milliseconds / 10)}s`;
            }
            return `${seconds}.${Math.floor(milliseconds / 10)}s`;
        }

        // Character and word counter
        function updateCounter() {
            const userInput = document.getElementById("user-input");
            const charCount = document.getElementById("char-count");
            const wordCount = document.getElementById("word-count");

            const text = userInput.value;
            const characters = text.length;
            const words = text.trim() ? text.trim().split(/\s+/).length : 0; // Count words

            charCount.textContent = characters;
            wordCount.textContent = words;
        }

        // Event listener to update the counter on input
        document.getElementById("user-input").addEventListener("input", updateCounter);

        function toggleCatMode() {
            isCatMode = !isCatMode; // Toggle the flag
            if (isCatMode) {
                config.systemMessage += " You are a cat."; // Append the phrase
            } else {
                config.systemMessage = config.systemMessage.replace(" You are a large, fluffy cat. You are a little aloof, but kind.", ""); // Remove the phrase
            }
            addMessage(`Cat mode is now ${isCatMode ? "enabled" : "disabled"}.`, "bot"); // Inform the user
        }

        async function sendMessage() {
            const userInput = document.getElementById("user-input");
            const userMessage = userInput.value.trim();

            if (!userMessage) return;

            // Check for slash commands
            if (userMessage.toLowerCase() === '/dark' || userMessage.toLowerCase() === '/darkmode') {
                toggleDarkMode();
                userInput.value = ""; // Clear input after command
                updateCounter(); // Reset counters
                return;
            }

            if (userMessage.toLowerCase() === '/clear') {
                clearChat();
                userInput.value = ""; // Clear input after command
                updateCounter(); // Reset counters
                return;
            }

            if (userMessage.toLowerCase() === '/help') {
                displayHelp();
                userInput.value = ""; // Clear input after command
                updateCounter(); // Reset counters
                return;
            }

            if (userMessage.toLowerCase() === '/cat' || userMessage.toLowerCase() === '/catmode') {
                toggleCatMode(); // Toggle cat mode
                userInput.value = ""; // Clear input after command
                updateCounter(); // Reset counters
                return;
            }

            if (userMessage.toLowerCase() === '/context') {
                const context = viewCurrentContext();
                addMessage(`Current conversation has ${context.currentMessages} messages\nEstimated tokens: ${context.estimatedTokens}`, "bot");
                return;
            }

            if (userMessage.toLowerCase().startsWith('/theme')) {
                const requestedTheme = userMessage.toLowerCase().split(' ')[1];
                if (!requestedTheme) {
                    // If no theme is specified, lets show all available themes
                    addMessage(`Available themes: ${Object.keys(AVAILABLE_THEMES).join(', ')}`, "bot");
                } else if (AVAILABLE_THEMES[requestedTheme]) {
                    switchTheme(requestedTheme);
                } else {
                    addMessage(`Unknown theme. Available themes: ${Object.keys(AVAILABLE_THEMES).join(', ')}`, "bot");
                }
                userInput.value = "";
                updateCounter();
                return;
            }

            if (userMessage.toLowerCase() === '/matt') {
                Object.assign(config, mattConfig);
                addMessage("Switched to Matt's config", "bot");
                userInput.value = "";
                updateCounter();
                populateModelSelect(); // Refresh the model list for the new endpoint
                return;
            }

            if (userMessage.toLowerCase() === '/local') {
                Object.assign(config, localConfig);
                addMessage("Switched to local config", "bot");
                userInput.value = "";
                updateCounter();
                populateModelSelect(); // Refresh the model list for the new endpoint
                return;
            }

            addMessage(userMessage, "user");
            userInput.value = ""; // Clear input after sending the message
            
            // Reset the counter
            document.getElementById("char-count").textContent = "0";
            document.getElementById("word-count").textContent = "0";

            // Create and add loading indicator
            const loadingIndicator = document.createElement("div");
            loadingIndicator.id = "loading-indicator";
            loadingIndicator.classList.add("message", "bot-message");
            loadingIndicator.textContent = "...";
            document.getElementById("chat-container").appendChild(loadingIndicator);
            scrollToBottom();

            // Start animation for this specific indicator
            const animationInterval = animateLoadingIndicator(loadingIndicator);

            const startTime = Date.now(); // Capture the start time

            try {
                const modelSelect = document.getElementById("model-select");
                const selectedModel = modelSelect.value;
                const retainHistory = document.getElementById("retain-history").checked; // Check the checkbox state

                // Prepare the messages for the API
                const messagesToSend = await prepareMessages(userMessage);

                const response = await fetch(config.completionsEndpoint, {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                    },
                    body: JSON.stringify({
                        model: selectedModel,
                        messages: messagesToSend,
                    }),
                });

                if (!response.ok) {
                    throw new Error('Error communicating with Ollama API');
                }

                const data = await response.json();
                console.log("API Response:", data);

                if (data.choices && data.choices.length > 0) {
                    const botResponse = data.choices[0].message.content;
                    
                    // Clear loading indicator
                    clearInterval(animationInterval);
                    loadingIndicator.remove();
                    
                    // Add bot's response to chat and history
                    addMessage(botResponse, "bot");
                    conversationHistory.current.push({ role: "assistant", content: botResponse });

                    // Calculate and display duration
                    const duration = Date.now() - startTime;
                    const timeTakenMessage = formatDuration(duration);
                    const timeDisplay = document.createElement("div");
                    timeDisplay.classList.add("bot-time");
                    timeDisplay.textContent = `Response time: ${timeTakenMessage}`;
                    document.getElementById("chat-container").appendChild(timeDisplay);
                    scrollToBottom();

                } else {
                    console.error("No response from API");
                    loadingIndicator.remove();
                    addMessage("Sorry, I didn't get a response from the assistant.", "bot");
                }

                if (conversationHistory.current.length > 10) {
                    conversationHistory.current.shift(); // Remove the oldest message
                }

            } catch (error) {
                console.error("Error:", error);
                clearInterval(animationInterval);
                loadingIndicator.remove();
                addMessage("Sorry, there was an error processing your request.", "bot");
            }
        }

        function animateLoadingIndicator(indicator) {
            let dots = 0;
            return setInterval(() => {
                dots = (dots + 1) % 6;
                if (indicator && document.contains(indicator)) {
                    indicator.textContent = '.'.repeat(dots || 1);
                }
            }, 500);
        }

        document.getElementById("send-button").addEventListener("click", sendMessage);

        document.getElementById("user-input").addEventListener("keypress", function (e) {
            if (e.key === "Enter") {
                e.preventDefault(); // Prevent line break
                sendMessage();
            }
        });

        function toggleDarkMode() {
            const body = document.body;
            const chatContainer = document.getElementById("chat-container");
            const userInput = document.getElementById("user-input");
            const sendButton = document.getElementById("send-button");

            body.classList.toggle("dark-mode");
            chatContainer.classList.toggle("dark-mode");
            userInput.classList.toggle("dark-mode");
            sendButton.classList.toggle("dark-mode");

            // Update message classes
            const messages = document.querySelectorAll(".message");
            messages.forEach(message => {
                message.classList.toggle("dark-mode");
            });

            // Save preference to local storage
            const isDarkMode = body.classList.contains("dark-mode");
            localStorage.setItem("darkMode", isDarkMode);
        }

        // Load dark mode preference from local storage on page load
        document.addEventListener("DOMContentLoaded", () => {
            const darkModePreference = localStorage.getItem("darkMode");
            if (darkModePreference === "true") {
                toggleDarkMode(); // Activate dark mode if preference is set
            }
        });

        function clearChat() {
            const chatContainer = document.getElementById("chat-container");
            chatContainer.innerHTML = "";
            conversationHistory = {
                summary: null,
                current: [],
                full: []
            };
        }

        function displayHelp() {
            const helpMessage = `
Available commands:\n
  /dark - Toggle dark mode when using the professional theme
  /cat - Toggle cat mode
  /context - Show the current conversation's context
  /clear - Clear the chat history
  /help - Show this message
  /theme [theme-name] - Switch theme (available themes: ${Object.keys(AVAILABLE_THEMES).join(', ')})
      without a theme name, this will show all available themes, too
  /local - Switch to local Ollama instance
  /matt - Switch to Matt's Ollama instance
            `;
            addMessage(helpMessage, "bot");
        }

        function estimateTokens(text) {
            // Rough estimation: ~4 chars per token for English text
            return Math.ceil(text.length / 4);
        }

        function getContextSize(messages) {
            return messages.reduce((sum, msg) => sum + estimateTokens(msg.content), 0);
        }

        async function summarizeConversation(messages) {
            try {
                const modelSelect = document.getElementById("model-select");
                const selectedModel = modelSelect.value;

                const response = await fetch(config.completionsEndpoint, {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                    },
                    body: JSON.stringify({
                        model: selectedModel,
                        messages: messages,
                    }),
                });

                const data = await response.json();
                return data.choices[0].message.content;
            } catch (error) {
                console.error("Error summarizing conversation:", error);
                return null;
            }
        }

        async function prepareMessages(userMessage) {
            const messages = [];
            
            // Always start with system message
            messages.push({ role: "system", content: config.systemMessage });
            
            if (document.getElementById("retain-history").checked) {
                // If we have a summary, add it more naturally
                if (conversationHistory.summary) {
                    messages.push({
                        role: "system",
                        content: `Previous discussion: ${conversationHistory.summary}`
                    });
                }
                
                // Add current conversation segment
                messages.push(...conversationHistory.current);
            }
            
            // Add the new message to history before we check for summarization
            const newMessage = { role: "user", content: userMessage };
            conversationHistory.current.push(newMessage);
            messages.push(newMessage);
            
            // Do we need to summarize?
            const totalTokens = getContextSize(messages);
            if (totalTokens > config.summarizeThreshold) {
                // Move current messages to full history, except for the newest message
                conversationHistory.full.push(...conversationHistory.current.slice(0, -1));
                
                // Supposedly this is a more natural summarization prompt...
                const summary = await summarizeConversation([
                    {
                        role: "system",
                        content: "Summarize this conversation's key points and context that would be important for continuing the discussion naturally. Be concise but maintain essential details."
                    },
                    ...conversationHistory.full
                ]);
                
                if (summary) {
                    conversationHistory.summary = summary;
                    // Keep only the most recent messages for immediate context
                    conversationHistory.current = conversationHistory.current.slice(-4);
                    
                    // Rebuild messages array with new summary
                    return [
                        { role: "system", content: config.systemMessage },
                        { role: "system", content: `Previous discussion: ${summary}` },
                        ...conversationHistory.current
                    ];
                }
            }
            
            return messages;
        }

        // Clean up old messages periodically
        function pruneConversationHistory() {
            if (conversationHistory.full.length > 100) {
                // Keep only the last 100 messages in full history
                conversationHistory.full = conversationHistory.full.slice(-100);
            }
        }

        // Call this after successful responses
        setInterval(pruneConversationHistory, 60000); // Clean up every minute

        function viewCurrentContext() {
            const context = {
                summary: conversationHistory.summary,
                currentMessages: conversationHistory.current.length,
                fullHistoryMessages: conversationHistory.full.length,
                estimatedTokens: getContextSize(conversationHistory.current)
            };
            console.log("Current Context:", context);
            return context;
        }

        function scrollToBottom() {
            const chatContainer = document.getElementById("chat-container");
            chatContainer.scrollTop = chatContainer.scrollHeight;
        }

        function switchTheme(themeName) {
            // Remove all theme classes
            Object.keys(AVAILABLE_THEMES).forEach(theme => {
                document.body.classList.remove(`theme-${theme}`);
            });
            
            // Add the new theme class
            document.body.classList.add(`theme-${themeName}`);
            
            // Update meta theme-color
            const metaThemeColor = document.querySelector('meta[name="theme-color"]');
            if (metaThemeColor) {
                switch(themeName) {
                    case 'molly-millions':
                        metaThemeColor.setAttribute('content', '#00ff00');
                        break;
                    case 'cloud':
                        metaThemeColor.setAttribute('content', '#4080ff');
                        break;
                    case 'classic':
                        metaThemeColor.setAttribute('content', '#DDDDDD');
                        break;
                    case 'lcars':
                        metaThemeColor.setAttribute('content', '#CC6699');
                        break;
                    case 'professional':
                    default:
                        metaThemeColor.setAttribute('content', '#007BFF');
                        break;
                }
            }
            
            localStorage.setItem('selectedTheme', themeName);            
            addMessage(`Theme switched to: ${AVAILABLE_THEMES[themeName]}`, "bot");
        }

        // Initialize with localConfig
        Object.assign(config, localConfig);
    </script>
</body>
</html>