make this code really cool and designe like cool loading for modern with chat with my ai pls



import os
import datetime
import json
import asyncio
from dateutil.parser import parse as parse_date
import dateparser
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from email.mime.text import MIMEText
import base64

from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect
from starlette.websockets import WebSocketState
from fastapi.templating import Jinja2Templates
from fastapi.responses import HTMLResponse
from fastapi.middleware.cors import CORSMiddleware

# Import the Groq client
from groq import Groq

# Import dotenv to manage environment variables
from dotenv import load_dotenv

load_dotenv()

# Import GoogleAuthRequest to avoid conflicts with FastAPI's Request
from google.auth.transport.requests import Request as GoogleAuthRequest

# Set the scopes for Google API permissions
SCOPES = [
    'https://www.googleapis.com/auth/documents',
    'https://www.googleapis.com/auth/calendar',
    'https://www.googleapis.com/auth/drive',
    'https://www.googleapis.com/auth/gmail.send',
    'https://www.googleapis.com/auth/gmail.readonly'
]

app = FastAPI()

templates = Jinja2Templates(directory="templates")

# Add CORS middleware (if necessary)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # Adjust according to your needs
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

def authenticate_google_services():
    creds = None
    # Check if credentials already exist
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # If no valid credentials, initiate the authentication flow
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(GoogleAuthRequest())
        else:
            if not os.path.exists('credentials.json'):
                print("The 'credentials.json' file is missing.")
                return None, None, None, None
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.json', 'w') as token:
            token.write(creds.to_json())
    # Create Google Docs, Calendar, Drive, and Gmail services
    try:
        docs_service = build('docs', 'v1', credentials=creds)
        calendar_service = build('calendar', 'v3', credentials=creds)
        drive_service = build('drive', 'v3', credentials=creds)
        gmail_service = build('gmail', 'v1', credentials=creds)
        return docs_service, calendar_service, drive_service, gmail_service
    except Exception as e:
        print(f"An error occurred while creating Google services: {e}")
        return None, None, None, None

# Initialize Google services
docs_service, calendar_service, drive_service, gmail_service = authenticate_google_services()
if not docs_service or not calendar_service or not drive_service or not gmail_service:
    print("Authentication failed.")

# Get the Groq API key from environment variables
groq_api_key = os.getenv("GROQ_API_KEY")
if not groq_api_key:
    raise Exception("Please set the 'GROQ_API_KEY' environment variable with your Groq API key.")

# Initialize the Groq client
groq_client = Groq(api_key=groq_api_key)

@app.get("/", response_class=HTMLResponse)
async def get_index(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    try:
        # Initialize the conversation history
        conversation_history = []
        while True:
            # Check if WebSocket is still connected
            if websocket.client_state != WebSocketState.CONNECTED:
                break

            # Receive the user's message
            data = await websocket.receive_text()
            request_data = json.loads(data)
            user_message = request_data.get('message')

            if not user_message:
                await websocket.send_text("The 'message' field is required.")
                continue  # Continue to listen for the next messages

            # Add the user's message to the history
            conversation_history.append({"role": "user", "content": user_message})

            # Process the user's request in a separate thread to avoid blocking
            assistant_response = await asyncio.to_thread(process_user_request, user_message, conversation_history)

            # Add the assistant's response to the history
            conversation_history.append({"role": "assistant", "content": assistant_response})

            # Send the response to the user
            await websocket.send_text(assistant_response)

    except WebSocketDisconnect:
        print("Client disconnected")
    except Exception as e:
        await websocket.send_text(f"Server error: {e}")

def generate_with_groq(conversation_history, temperature=0.7, max_tokens=512):
    response = groq_client.chat.completions.create(
        messages=conversation_history,
        model="llama3-8b-8192",
        temperature=temperature,
        max_tokens=max_tokens
    )
    return response.choices[0].message.content.strip()

def generate_simple_response(prompt, temperature=0.0, max_tokens=150):
    response = groq_client.chat.completions.create(
        messages=[{"role": "user", "content": prompt}],
        model="llama3-8b-8192",
        temperature=temperature,
        max_tokens=max_tokens
    )
    return response.choices[0].message.content.strip()

def process_user_request(user_message, conversation_history):
    # Use the LLM to detect the action to perform
    detection_prompt = f"""You are an assistant that analyzes the user's message to determine if there is an action to perform among the following:
- create_document: to create a Google Docs document.
- read_calendar: to read calendar events.
- create_calendar_event: to create an event in the calendar.
- share_document: to share a document with someone.
- send_email: to send an email.
- read_emails: to read emails in Gmail.

For each action, extract the necessary parameters as a JSON object. If no action is required, simply respond with: {{"action": "chat"}}

User's message: "{user_message}"

Respond in JSON with the format:
{{
  "action": "name_of_action",
  "parameters": {{
    "parameter1": "value1",
    "parameter2": "value2",
    ...
  }}
}}
"""

    # Get the LLM's response for the action to perform
    action_response = generate_simple_response(detection_prompt)

    try:
        action_data = json.loads(action_response)
        action = action_data.get("action", "chat")
    except json.JSONDecodeError:
        # If the LLM did not return valid JSON, we assume it's a normal chat
        action = "chat"

    if action == "chat":
        # Normal chat response
        assistant_response = generate_with_groq(conversation_history)
        return assistant_response
    elif action == 'create_document':
        return create_document_from_parameters(action_data.get("parameters", {}), conversation_history)
    elif action == 'read_calendar':
        return read_calendar_from_parameters(action_data.get("parameters", {}))
    elif action == 'create_calendar_event':
        return create_calendar_event_from_parameters(action_data.get("parameters", {}))
    elif action == 'share_document':
        return share_document_from_parameters(action_data.get("parameters", {}))
    elif action == 'send_email':
        return send_email_from_parameters(action_data.get("parameters", {}))
    elif action == 'read_emails':
        return read_emails_from_parameters(action_data.get("parameters", {}))
    else:
        # Unrecognized action, we respond with normal chat
        assistant_response = generate_with_groq(conversation_history)
        return assistant_response

# Specific action functions

def create_document_from_parameters(parameters, conversation_history):
    if not docs_service:
        return "Google Docs service is not available."
    try:
        # Generate the title if not provided
        if 'title' not in parameters or not parameters['title']:
            title_prompt = f"""Generate a concise and relevant title for a document based on the following user input:
{parameters.get('content', '')}"""
            title = generate_simple_response(title_prompt)
        else:
            title = parameters['title']

        # Generate the content if not provided
        if 'content' not in parameters or not parameters['content']:
            content_prompt = f"""Generate detailed content for a document titled '{title}' based on the user's input."""
            content = generate_simple_response(content_prompt, temperature=0.7, max_tokens=1024)
        else:
            content = parameters['content']

        if not content.strip():
            return "The document content is empty."

        # Create the document
        document = {'title': title}
        doc = docs_service.documents().create(body=document).execute()
        doc_id = doc.get('documentId')

        # Insert the content
        requests_body = [
            {
                'insertText': {
                    'location': {'index': 1},
                    'text': content
                }
            }
        ]

        docs_service.documents().batchUpdate(
            documentId=doc_id,
            body={'requests': requests_body}
        ).execute()

        doc_url = f"https://docs.google.com/document/d/{doc_id}"
        return f"Document '{title}' created successfully: {doc_url}"
    except Exception as e:
        return f"An error occurred while creating the document: {e}"

def read_calendar_from_parameters(parameters):
    if not calendar_service:
        return "Google Calendar service is not available."
    try:
        start_date_str = parameters.get('start_date')
        end_date_str = parameters.get('end_date')

        # Convert dates
        if not start_date_str:
            start_date = datetime.datetime.utcnow()
            start_date_str = start_date.strftime('%Y-%m-%d')
        else:
            start_date = parse_date(start_date_str)

        if not end_date_str:
            end_date = start_date + datetime.timedelta(days=7)
            end_date_str = end_date.strftime('%Y-%m-%d')
        else:
            end_date = parse_date(end_date_str)

        # Format ISO dates
        start_date_iso = start_date.isoformat() + 'Z'
        end_date_iso = end_date.isoformat() + 'Z'

        # Get events
        events_result = calendar_service.events().list(
            calendarId='primary', timeMin=start_date_iso, timeMax=end_date_iso,
            singleEvents=True, orderBy='startTime').execute()
        events = events_result.get('items', [])

        if not events:
            return "No events found for the specified period."

        # Prepare the list of events
        event_list = []
        for event in events:
            start_event = event['start'].get('dateTime', event['start'].get('date'))
            summary = event.get('summary', 'No title')
            event_list.append(f"{start_event} - {summary}")

        # Generate a response
        events_str = "\n".join(event_list)
        return f"Here are your events between {start_date_str} and {end_date_str}:\n{events_str}"

    except Exception as e:
        return f"An error occurred while reading the calendar: {e}"

def create_calendar_event_from_parameters(parameters):
    if not calendar_service:
        return "Google Calendar service is not available."
    try:
        summary = parameters.get('summary', 'New Event')
        description = parameters.get('description', '')
        location = parameters.get('location', '')
        start_datetime_str = parameters.get('start_datetime')
        end_datetime_str = parameters.get('end_datetime')
        time_zone = parameters.get('time_zone', 'UTC')

        if not start_datetime_str or not end_datetime_str:
            return "Date and time information is missing."

        # Parse dates
        start_datetime = dateparser.parse(start_datetime_str)
        end_datetime = dateparser.parse(end_datetime_str)

        if not start_datetime or not end_datetime:
            return "Unable to parse the provided dates."

        # Format ISO dates
        start_datetime_iso = start_datetime.isoformat()
        end_datetime_iso = end_datetime.isoformat()

        # Create the event
        event = {
            'summary': summary,
            'location': location,
            'description': description,
            'start': {
                'dateTime': start_datetime_iso,
                'timeZone': time_zone,
            },
            'end': {
                'dateTime': end_datetime_iso,
                'timeZone': time_zone,
            },
        }

        event = calendar_service.events().insert(calendarId='primary', body=event).execute()
        event_link = event.get('htmlLink')
        return f"Event '{summary}' created successfully: {event_link}"
    except Exception as e:
        return f"Unable to create the event: {e}"

def share_document_from_parameters(parameters):
    if not drive_service:
        return "Google Drive service is not available."
    try:
        file_name = parameters.get('file_name')
        user_email = parameters.get('user_email')

        if not file_name or not user_email:
            return "The 'file_name' or 'user_email' information is missing."

        # Search for the file by name
        search_query = f"name = '{file_name}'"
        response = drive_service.files().list(q=search_query, spaces='drive').execute()
        files = response.get('files', [])

        if not files:
            return f"No file found with the name '{file_name}'."

        file_id = files[0].get('id')

        # Share the file
        permission = {
            'type': 'user',
            'role': 'writer',
            'emailAddress': user_email
        }
        drive_service.permissions().create(
            fileId=file_id,
            body=permission,
            fields='id',
            sendNotificationEmail=True,
        ).execute()
        return f"The file '{file_name}' has been shared with {user_email}."
    except Exception as e:
        return f"Unable to share the document: {e}"

def send_email_from_parameters(parameters):
    if not gmail_service:
        return "Gmail service is not available."
    try:
        to = parameters.get('to')
        subject = parameters.get('subject', '')
        message_text = parameters.get('message', '')

        if not to or not message_text:
            return "The 'to' or 'message' information is missing."

        # Send the email
        message = MIMEText(message_text)
        message['to'] = to
        message['from'] = 'your_email_address@example.com'  # Replace with your email address
        message['subject'] = subject
        raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode()
        body = {'raw': raw_message}
        gmail_service.users().messages().send(userId='me', body=body).execute()
        return f"Email sent to {to}."
    except Exception as e:
        return f"Unable to send the email: {e}"

def read_emails_from_parameters(parameters):
    if not gmail_service:
        return "Gmail service is not available."
    try:
        query = parameters.get('query', 'is:unread')

        # Read emails matching the query
        response = gmail_service.users().messages().list(userId='me', q=query).execute()
        messages = response.get('messages', [])

        if not messages:
            return "No emails found."

        emails = []
        for message in messages[:5]:  # Limit to 5 emails to avoid a long response
            msg = gmail_service.users().messages().get(userId='me', id=message['id'], format='full').execute()
            headers = msg.get('payload', {}).get('headers', [])
            subject = "No subject"
            from_email = "Unknown"
            for header in headers:
                if header['name'] == 'Subject':
                    subject = header['value']
                elif header['name'] == 'From':
                    from_email = header['value']
            snippet = msg.get('snippet', '')
            emails.append(f"From: {from_email}\nSubject: {subject}\nPreview: {snippet}\n")

        email_list = "\n---\n".join(emails)
        return f"Here are the emails found:\n{email_list}"
    except Exception as e:
        return f"Unable to read emails: {e}"<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>AI Assistant with Google Integration</title>
    <link href="https://fonts.googleapis.com/css2?family=Google+Sans:wght@400;700&display=swap" rel="stylesheet">
    <style>
        :root {
            --primary-color: #1a73e8;
            --secondary-color: #185abc;
            --success-color: #34a853;
            --error-color: #ea4335;
            --background-color: #f8f9fa;
            --chat-bg: #ffffff;
            --user-message-bg: #e3f2fd;
            --assistant-message-bg: #f5f5f5;
            --border-radius: 12px;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Google Sans', Arial, sans-serif;
            background-color: var(--background-color);
            color: #202124;
            line-height: 1.6;
        }

        .container {
            max-width: 1200px;
            height: 100vh;
            margin: 0 auto;
            padding: 20px;
            display: flex;
            flex-direction: column;
        }

        .header {
            text-align: center;
            padding: 20px 0;
            margin-bottom: 20px;
        }

        .header h1 {
            font-size: 2.5rem;
            color: var(--primary-color);
            margin-bottom: 10px;
        }

        .header p {
            color: #5f6368;
            font-size: 1.1rem;
        }

        .chat-interface {
            flex: 1;
            background: var(--chat-bg);
            border-radius: var(--border-radius);
            box-shadow: 0 2px 6px rgba(0,0,0,0.1);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        #chat-container {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
        }

        .message {
            max-width: 80%;
            margin: 10px 0;
            padding: 15px;
            border-radius: var(--border-radius);
            animation: messageAppear 0.3s ease-out;
        }

        .user-message {
            background: var(--user-message-bg);
            margin-left: auto;
            color: #1967d2;
        }

        .assistant-message {
            background: var(--assistant-message-bg);
            margin-right: auto;
            color: #202124;
        }

        .message-content {
            word-wrap: break-word;
        }

        .input-container {
            padding: 20px;
            background: white;
            border-top: 1px solid #e0e0e0;
        }

        #assistant-form {
            display: flex;
            gap: 10px;
        }

        #user_request {
            flex: 1;
            padding: 12px 16px;
            border: 2px solid #e0e0e0;
            border-radius: var(--border-radius);
            font-size: 16px;
            transition: border-color 0.3s ease;
        }

        #user_request:focus {
            outline: none;
            border-color: var(--primary-color);
        }

        #send-button {
            padding: 12px 24px;
            background: var(--primary-color);
            color: white;
            border: none;
            border-radius: var(--border-radius);
            font-size: 16px;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }

        #send-button:hover {
            background: var(--secondary-color);
        }

        .typing-indicator {
            padding: 15px;
            background: var(--assistant-message-bg);
            border-radius: var(--border-radius);
            margin-right: auto;
            display: none;
        }

        .typing-dots {
            display: flex;
            gap: 4px;
        }

        .dot {
            width: 8px;
            height: 8px;
            background: var(--primary-color);
            border-radius: 50%;
            animation: typing 1.4s infinite;
        }

        .dot:nth-child(2) { animation-delay: 0.2s; }
        .dot:nth-child(3) { animation-delay: 0.4s; }

        .action-list {
            list-style-type: none;
            padding: 0;
            margin: 10px 0 0;
        }

        .action-list li {
            margin: 5px 0;
            font-size: 16px;
            color: #4caf50;
        }

        @keyframes messageAppear {
            from {
                opacity: 0;
                transform: translateY(10px);
            }
            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        @keyframes typing {
            0%, 60%, 100% { transform: translateY(0); }
            30% { transform: translateY(-10px); }
        }

        @media (max-width: 768px) {
            .container {
                padding: 10px;
            }

            .message {
                max-width: 90%;
            }

            .header h1 {
                font-size: 2rem;
            }
        }

        .loading-container {
            position: fixed;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            display: none; /* Hidden by default */
            z-index: 1000;
            text-align: center;
        }

        .loading-circle {
            width: 60px;
            height: 60px;
            border: 8px solid #ddd;
            border-top: 8px solid #4caf50;
            border-radius: 50%;
            margin: 20px auto;
            animation: spin 2s linear infinite;
        }

        @keyframes spin {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>AI Assistant</h1>
            <p>Powered by Google Services Integration</p>
        </div>

        <div class="chat-interface">
            <div id="chat-container"></div>

            <div class="typing-indicator">
                <div class="typing-dots">
                    <div class="dot"></div>
                    <div class="dot"></div>
                    <div class="dot"></div>
                </div>
            </div>

            <div class="input-container">
                <form id="assistant-form">
                    <input type="text"
                           id="user_request"
                           name=""
                           placeholder="Ask me anything or request an action..."
                           autocomplete="off"
                           required />
                    <button type="submit" id="send-button">Send</button>
                </form>
            </div>
        </div>
    </div>

    <div class="loading-container" id="loading-container">
        <div class="loading-circle"></div>
        <ul class="action-list" id="action-list">
            <!-- Actions will be dynamically inserted here -->
        </ul>
    </div>

    <script>
        const chatContainer = document.getElementById('chat-container');
        const form = document.getElementById('assistant-form');
        const userRequestInput = document.getElementById('user_request');
        const typingIndicator = document.querySelector('.typing-indicator');

        function showTypingIndicator() {
            typingIndicator.style.display = 'block';
        }

        function hideTypingIndicator() {
            typingIndicator.style.display = 'none';
        }

        function showLoadingIndicator() {
            document.getElementById('loading-container').style.display = 'block';
        }

        function hideLoadingIndicator() {
            document.getElementById('loading-container').style.display = 'none';
        }

        function addActionToList(action) {
            const actionList = document.getElementById('action-list');
            const listItem = document.createElement('li');
            listItem.textContent = action;
            actionList.appendChild(listItem);
        }

        function clearActionList() {
            const actionList = document.getElementById('action-list');
            actionList.innerHTML = '';
        }

        function addMessage(content, type, actions = []) {
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${type}-message`;
            messageDiv.innerHTML = `<div class="message-content">${formatMessage(content)}</div>`;

            if (actions.length > 0) {
                const actionList = document.createElement('ul');
                actionList.className = 'action-list';
                actions.forEach(action => {
                    const listItem = document.createElement('li');
                    listItem.textContent = action;
                    actionList.appendChild(listItem);
                });
                messageDiv.appendChild(actionList);
            }

            // Add timestamp
            const timestamp = document.createElement('div');
            timestamp.className = 'message-timestamp';
            timestamp.textContent = new Date().toLocaleTimeString();
            messageDiv.appendChild(timestamp);

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

            // Save message to history
            saveMessageToHistory(content, type);
        }

        function formatMessage(message) {
            // Convert URLs to clickable links
            message = message.replace(
                /(https?:\/\/[^\s]+)/g,
                '<a href="$1" target="_blank" rel="noopener noreferrer">$1</a>'
            );

            // Convert newlines to <br> tags
            message = message.replace(/\n/g, '<br>');

            return message;
        }

        function identifyAction(userRequest) {
            // Logic to identify action based on userRequest
            if (userRequest.includes('create document')) {
                return [
                    'Understanding Google Doc...',
                    'Generating Title...',
                    'Generating Content...',
                    'Posting Google Doc to Google...',
                    'Waiting for validation...'
                ];
            } else if (userRequest.includes('read calendar')) {
                return [
                    'Reading Calendar Events...',
                    'Preparing Event List...'
                ];
            } else if (userRequest.includes('create event')) {
                return [
                    'Understanding Event Details...',
                    'Creating Calendar Event...',
                    'Sending Confirmation...'
                ];
            } else if (userRequest.includes('share document')) {
                return [
                    'Finding Document...',
                    'Sharing Document...'
                ];
            } else if (userRequest.includes('send email')) {
                return [
                    'Composing Email...',
                    'Sending Email...'
                ];
            } else if (userRequest.includes('read emails')) {
                return [
                    'Reading Emails...',
                    'Preparing Email List...'
                ];
            } else {
                return [];
            }
        }

        // Add support for keyboard shortcuts
        userRequestInput.addEventListener('keydown', (e) => {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                form.dispatchEvent(new Event('submit'));
            }
        });

        // Add support for mobile devices
        if ('ontouchstart' in window) {
            document.body.classList.add('touch-device');
        }

        // Add welcome message
        window.addEventListener('load', () => {
            const welcomeMessage = "Hello! I'm your AI assistant. I can help you with various tasks including:\n" +
                "• Creating Google Docs\n" +
                "• Reading calendar events\n" +
                "• Creating calendar events\n" +
                "• Sharing documents\n" +
                "• Sending emails\n" +
                "• Reading emails\n\n" +
                "How can I help you today?";

            addMessage(welcomeMessage, 'assistant');
            loadMessageHistory();
        });

        // Add error handling for network issues
        window.addEventListener('offline', () => {
            addMessage('You are currently offline. Please check your internet connection.', 'system');
        });

        // Add support for message history
        let messageHistory = [];
        const MAX_HISTORY = 100;

        function saveMessageToHistory(content, type) {
            messageHistory.push({
                content,
                type,
                timestamp: new Date().toISOString()
            });

            // Keep history within limit
            if (messageHistory.length > MAX_HISTORY) {
                messageHistory.shift();
            }

            // Optionally save to localStorage
            localStorage.setItem('chatHistory', JSON.stringify(messageHistory));
        }

        function loadMessageHistory() {
            const savedHistory = localStorage.getItem('chatHistory');
            if (savedHistory) {
                messageHistory = JSON.parse(savedHistory);
                messageHistory.forEach(msg => {
                    addMessage(msg.content, msg.type);
                });
            }
        }

        function clearHistory() {
            messageHistory = [];
            localStorage.removeItem('chatHistory');
            chatContainer.innerHTML = '';
            addMessage('Chat history cleared.', 'system');
        }

        // Example usage in your form submit handler
        form.addEventListener('submit', async function(event) {
            event.preventDefault();
            const userRequest = userRequestInput.value.trim();
            if (!userRequest) return;

            // Display user message
            addMessage(userRequest, 'user');
            userRequestInput.value = '';

            // Identify actions
            const actions = identifyAction(userRequest);

            // Show loading indicator
            showLoadingIndicator();
            clearActionList();

            try {
                if (actions.length > 0) {
                    for (const action of actions) {
                        addActionToList(action);
                        await new Promise(resolve => setTimeout(resolve, 1000)); // Simulate delay
                    }
                }

                const response = await sendMessage(userRequest);
                hideLoadingIndicator();
                addMessage(response, 'assistant', actions);
            } catch (error) {
                hideLoadingIndicator();
                addMessage('An error occurred. Please try again.', 'assistant');
            }
        });

        async function sendMessage(message) {
            const protocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
            const ws = new WebSocket(`${protocol}://${window.location.host}/ws`);

            return new Promise((resolve, reject) => {
                ws.onopen = () => {
                    ws.send(JSON.stringify({ message }));
                };

                ws.onmessage = (event) => {
                    resolve(event.data);
                    ws.close();
                };

                ws.onerror = (error) => {
                    console.error('WebSocket Error:', error);
                    reject(error);
                };

                ws.onclose = () => {
                    console.log('WebSocket connection closed');
                };

                // Add timeout to prevent hanging
                setTimeout(() => {
                    if (ws.readyState === WebSocket.OPEN) {
                        ws.close();
                        reject(new Error('Connection timeout'));
                    }
                }, 30000); // 30 seconds timeout
            });
        }
    </script>
</body>
</html>