Full Code of Max-Eee/NeoPass for AI

main 079ea379f5db cached
25 files
617.9 KB
130.7k tokens
116 symbols
1 requests
Download .txt
Showing preview only (637K chars total). Download the full file or copy to clipboard to get everything.
Repository: Max-Eee/NeoPass
Branch: main
Commit: 079ea379f5db
Files: 25
Total size: 617.9 KB

Directory structure:
gitextract_mqhp625o/

├── README.md
├── contentScript.js
├── data/
│   ├── inject/
│   │   ├── anti-anti-debug.js
│   │   ├── chatbot.js
│   │   ├── content.js
│   │   ├── copyOverride.js
│   │   ├── customPaste.js
│   │   ├── exam.js
│   │   ├── isolated.js
│   │   ├── main.js
│   │   ├── mock_code/
│   │   │   ├── minifiedBackground.js
│   │   │   ├── minifiedContent-script.js
│   │   │   ├── mock_manifest.json
│   │   │   └── rules.json
│   │   ├── mock_code.js
│   │   ├── rightclickmenu.js
│   │   └── screenshare.js
│   └── nptel.json
├── devtools.js
├── manifest.json
├── metadata.json
├── nptel.txt
├── popup.html
├── popup.js
└── worker.js

================================================
FILE CONTENTS
================================================

================================================
FILE: README.md
================================================
<img width="1500" height="500" alt="NeoPass Banner" src="https://github.com/user-attachments/assets/7369dd86-838d-4fdc-abdd-6b41a9b14aed" />

# <i>**`Free`** NeoPass Extension</i>

> **NeoPass Pro** - [Click here to see Pro features and benefits](https://neopass.tech/pro)

This chrome extension is for students taking tests on the **`Iamneo portal`**, **`HackerRank`**, **`Wildlife Ecology NPTEL`**, **`conservation-geography NPTEL`**, **`forest management NPTEL`** and `other exam portals in chrome browser` that restrict your abilities

### [**Make sure to visit our website for the best experience!**](https://freeneopass.vercel.app) 🌐

<samp>
  
> [!IMPORTANT]
> **Free Users**: No sign-up needed! Configure your own AI API key by clicking the extension icon and going to the **Settings** tab.  
> Supported providers: OpenAI, Google Gemini, Anthropic Claude, and custom endpoints.
> 
>
> **Want a hassle-free experience?** Upgrade to Pro by visiting **neopass.tech/pro** for AI managed by NeoPass (GPT-5.1), increased rate limits, and NeoBrowser with built in Exam Helper access!  

> [!WARNING]
> **Educational Purposes Only**: This extension is intended for educational purposes. Please use it responsibly and ethically.  
> We am not responsible for any actions taken, and we do not encourage or promote cheating in any way.  
> Be cautious when using the extension to maintain academic integrity.

## ✨ Features

### Free Version (Bring Your Own API Key)
- **`NPTEL Integration`** : Solve NPTEL Wildlife ecology answers
- **`NeoExamShield Bypass`** : Break free from Examly's limitations. NeoPass mimics the NeoExamShield extension
- **`Chatbot With Stealth Mode`** : Leverage AI Chatbot to enhance your search capabilities
- **`AI Search Answers/Code`** : Perform AI-powered searches, helping you find answers without switching tabs
- **`Solve MCQ`** : Quickly Search MCQ Answers by simply selecting
- **`Tab Switching Bypass`** : Prevents unwanted tab switch restrictions
- **`Pasting When Restricted`** : Quickly paste answers with ease, reducing the time spent on manual entry
- **`Multiple AI Providers`** : Support for OpenAI, Google Gemini, Anthropic Claude, and custom endpoints

### Pro Version Features
- **`Everything in free`** : All free features are included
- **`Managed AI by NeoPass`** : Powered by GPT-5.1 - no API key needed!
- **`NeoBrowser Access`** : Exclusive access to the NeoBrowser with built in Exam Helper
- **`No Network Restrictions`** : Works even if AI providers are blocked on your network
- **`Increased Rate Limits`** : Higher usage limits for intensive exam sessions
- **`Priority Support`** : Get help when you need it most
- **`Hassle-Free Experience`** : No configuration needed, just login and go!

## ⬇️ Installation

1. [Download](https://github.com/Max-Eee/NeoPass/archive/refs/heads/main.zip) the extension.
2. Open Chrome and go to the Extensions page by typing `chrome://extensions/`.
3. Enable **Developer mode** in the top right corner.
4. Click on **Load unpacked** and select the folder where the extension is located.
5. Your NeoPass extension is now installed!

### Installation Guide Video



https://github.com/user-attachments/assets/89fb986c-2edb-4252-8232-dbd10beec0cf


## 💻 Usage

### For Free Users:
1. Click the NeoPass extension icon in your browser toolbar
2. Navigate to the **Settings** tab
3. Enter your AI API key (OpenAI, Google Gemini, Anthropic, or custom endpoint)
4. Select your AI provider from the dropdown menu
5. Click "Test Connection" to verify your setup
6. Start using all NeoPass features with your own API!

> [!NOTE]
> **Network Restrictions**: If your school/organization blocks AI service providers (OpenAI, Google, etc.), the extension will not work even with a valid API key. In this case, consider using a VPN or upgrade to Pro by visiting **neopass.tech/pro**.

### For Pro Users:
1. Visit [neopass.tech/pro](https://freeneopass.vercel.app/pro) to subscribe
2. Click the extension icon and go to the **Pro** tab
3. Login with your Pro credentials you have created from the webstie
4. Enjoy hassle-free AI-powered assistance with no configuration needed!

## ⌨️ Shortcuts

### Windows/Linux Users:
- <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Q</kbd> : Solve Iam Neo MCQs/Coding Questions with 100% ACCURACY
- <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> : Solve Iam Neo MCQs/Coding Questions with using AI [Backup]
- <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>T</kbd> : Autotypes Iam Neo Coding Question Solution letter by letter
- <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>H</kbd> : Solve HackerRank Questions [BETA]
> [!NOTE]
> The following shortcuts **require text to be selected** before activation:  
> - <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>N</kbd> : Solve NPTEL MCQs from selected text
> - <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd> : Search answers and code from selected text  
> - <kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd> : Search MCQs from selected text
- <kbd>Ctrl</kbd> + <kbd>V</kbd> : Paste content when blocked
- <kbd>Alt</kbd> + <kbd>C</kbd> : Open/Close Chatbot

<details>
<summary><strong>Mac Users (Click to expand)</strong></summary>

- <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>Q</kbd> : Solve Iam Neo MCQs/Coding Questions with 100% ACCURACY
- <kbd>Option</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> : Solve Iam Neo MCQs/Coding Questions with using AI [Backup]
- <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>T</kbd> : Autotypes Iam Neo Coding Question Solution letter by letter
- <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>H</kbd> : Solve HackerRank Questions [BETA]

> [!NOTE]
> The following shortcuts **require text to be selected** before activation:  
> - <kbd>Option</kbd> + <kbd>Shift</kbd> + <kbd>N</kbd> : Solve NPTEL MCQs from selected text
> - <kbd>Option</kbd> + <kbd>Shift</kbd> + <kbd>S</kbd> : Search answers and code from selected text  
> - <kbd>Option</kbd> + <kbd>Shift</kbd> + <kbd>M</kbd> : Search MCQs from selected text

- <kbd>Cmd</kbd> + <kbd>V</kbd> : Paste content when blocked
- <kbd>Option</kbd> + <kbd>C</kbd> : Open/Close Chatbot

</details>

## 🤝 Contribute or Add NPTEL Dataset

If you want to contribute to the NPTEL question database, follow these steps:

1. Fork this repository
2. Open your NPTEL assignment page in the browser
3. Open browser developer tools (F12 or right-click > Inspect)
4. Go to the Console tab
5. Copy and paste the script from `nptel.txt` in the repository
6. Run the script by pressing Enter
7. The script will extract all questions and correct answers from the page
8. Copy the output JSON data
9. Update the `data/nptel.json` file with the new questions and answers
10. Create a pull request to contribute your additions back to the main repository

This helps expand our database and improves the accuracy of the NPTEL question solving feature!

## 💬 Feedback

We'd love to hear your thoughts! If you encounter any issues or have suggestions for improvement, please reach out. Your feedback is invaluable! 💌

📧 **Contact us at:** [freeneopass@gmail.com](mailto:freeneopass@gmail.com?subject=Issue%20Title%3A%20%5BBrief%20description%20of%20your%20issue%5D&body=Hello%20NeoPass%20Support%20Team%2C%0A%0AIssue%20Description%3A%0A%5BPlease%20describe%20your%20issue%20in%20detail%5D%0A%0AWhen%20does%20this%20occur%3A%0A%5BSpecify%20when%20the%20issue%20happens%20-%20e.g.%2C%20during%20login%2C%20while%20using%20a%20specific%20feature%2C%20etc.%5D%0A%0ASteps%20to%20Reproduce%3A%0A1.%20%5BFirst%20step%5D%0A2.%20%5BSecond%20step%5D%0A3.%20%5BThird%20step%5D%0A%0AScreenshots%2FError%20Messages%20if%20possible%3A%0A%5BPlease%20attach%20any%20relevant%20screenshots%20or%20paste%20error%20messages%20here%5D%0A%0AAdditional%20Information%3A%0A%5BAny%20other%20relevant%20details%5D%0A%0AThank%20you!)

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

</samp>


================================================
FILE: contentScript.js
================================================
// Check if the chrome object is available (for compatibility)
if (typeof chrome === "undefined") {
  // Handle the case where chrome is not defined (like in Firefox)
}

// Always inject mock_code.js interceptor to handle extension detection (even when not logged in)
(function injectMockCode() {
  const mockScript = document.createElement('script');
  mockScript.src = chrome.runtime.getURL('data/inject/mock_code.js');
  mockScript.onload = function () {
      console.log('✅ Mock code interceptor loaded');
      this.remove(); // Clean up after execution
  };
  mockScript.onerror = function() {
      console.error('❌ Failed to load mock code interceptor');
  };
  // Inject as early as possible
  (document.head || document.documentElement).prepend(mockScript);
})();

// Inject exam.js (no login required)
const script = document.createElement('script');
script.src = chrome.runtime.getURL('data/inject/exam.js');
(document.head || document.documentElement).appendChild(script);

// Login prompt and status sync removed - extension features now available to all users

// Function removed - login check no longer required for extension features

// Neo Browser Download Link - Updated
const neoBrowserDownloadLink = "https://freeneopass.vercel.app";

// Function to add our NeoPass button left of the existing Neo Browser button
function replaceNeoBrowserButton() {
  const neoButton = document.querySelector('button#neobrowser');

  if (neoButton && !neoButton.dataset.replaced) {
    // Create custom styled button/link
    const ourBtn = document.createElement('a');
    ourBtn.innerHTML = `
      <div class="container jcc btn-align">
        <div class="t-whitespace-nowrap ng-star-inserted">
          <span>Download NeoPass Launcher</span>
        </div>
      </div>
    `;
    ourBtn.href = neoBrowserDownloadLink;
    ourBtn.target = "_blank";
    ourBtn.className = neoButton.className;
    ourBtn.id = "neopass-browser-btn";
    ourBtn.tabIndex = 0;

    // Apply gradient styling
    ourBtn.style.cssText = `
      position: relative !important;
      display: inline-flex !important;
      padding: 8px 16px !important;
      font-size: 14px !important;
      font-weight: 500 !important;
      color: white !important;
      background-color: black !important;
      border-radius: 8px !important;
      text-align: center !important;
      text-decoration: none !important;
      cursor: pointer !important;
      z-index: 1 !important;
      border: 2px solid transparent !important;
      transition: all 0.3s ease !important;
    `;

    // Create gradient border effect
    const beforeStyle = document.createElement('style');
    beforeStyle.textContent = `
      a#neopass-browser-btn {
        position: relative !important;
        background: linear-gradient(black, black) padding-box,
                    linear-gradient(45deg, #3b82f6, #8b5cf6, #ec4899) border-box !important;
        border: 2px solid transparent !important;
      }
      a#neopass-browser-btn:hover {
        transform: scale(1.05) !important;
        box-shadow: 0 0 20px rgba(139, 92, 246, 0.6) !important;
      }
    `;
    if (!document.querySelector('style[data-neobrowser-style]')) {
      beforeStyle.setAttribute('data-neobrowser-style', 'true');
      document.head.appendChild(beforeStyle);
    }

    // Insert our button to the left of the existing button
    neoButton.parentNode.insertBefore(ourBtn, neoButton);

    // Make the parent (app-button) a flex row so both buttons sit side by side
    neoButton.parentNode.style.cssText += `
      display: flex !important;
      flex-direction: row !important;
      align-items: center !important;
      gap: 8px !important;
    `;

    neoButton.dataset.replaced = "true";

    console.log('✅ NeoPass NeoBrowser button added left of existing Neo Browser button');
  }
}

// Observer to detect Neo Browser button and add our button
const buttonObserver = new MutationObserver((mutations) => {
  replaceNeoBrowserButton();
});

// Start observing for button changes
buttonObserver.observe(document.body, { 
  childList: true, 
  subtree: true 
});

// Initial check for Neo Browser button (in case already loaded)
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', replaceNeoBrowserButton);
} else {
  replaceNeoBrowserButton();
}

// Listen for window messages
window.addEventListener("message", function(event) {
  // Only process messages that:
  // 1. Come from the same window
  // 2. Are targeted for the extension
  if (event.data.target === "extension") {
      // Forward the message to the extension's background script
      chrome.runtime.sendMessage(event.data.message, response => {
          // Send the response back to the window
          window.postMessage({
              source: "extension",
              response: response
          }, "*");
      });
  }
});

window.addEventListener("message", function (event) {

  if (event.source === window && event.data.target === "extension") {

    browser.runtime.sendMessage(event.data.message, (response) => {

      window.postMessage({ source: "extension", response: response }, "*");
    });
  }
});

// Listen for the 'beforeunload' event to remove any injected elements
window.addEventListener("beforeunload", removeInjectedElement);

// Function to send a message to the website
function sendMessageToWebsite(messageData) {
  removeInjectedElement(); // Clean up any previous injected elements

  // Create a new span element with a unique ID
  const injectedElement = document.createElement("span");
  injectedElement.id = "x-template-base-" + messageData.currentKey; // Set a unique ID based on currentKey

  // Append the new element to the document body
  document.body.appendChild(injectedElement);
  console.log("message", messageData); // Log the message data

  // Send the message to the website
  window.postMessage(0, messageData.url); // 0 is the targetOrigin, meaning the same origin
}

// Function to remove injected elements from the DOM
function removeInjectedElement() {
  const injectedElement = document.querySelector("[id^='x-template-base-']"); // Select elements with ID starting with "x-template-base-"
  if (injectedElement) {
      injectedElement.remove(); // Remove the element if it exists
  }
}



================================================
FILE: data/inject/anti-anti-debug.js
================================================
!(() => {
    const Proxy = window.Proxy;
    const Object = window.Object;
    const Array = window.Array;
    /**
     * Save original methods before we override them
     */
    const Originals = {
        createElement: document.createElement,
        log: console.log,
        table: console.table,
        clear: console.clear,
        functionConstructor: window.Function.prototype.constructor,
        setInterval: window.setInterval,
        createElement: document.createElement,
        toString: Function.prototype.toString,
        addEventListener: window.addEventListener
    }

    /**
     * Cutoffs for logging. After cutoff is reached, will no longer log anti debug warnings.
     */
    const cutoffs = {
        table: {
            amount: 5,
            within: 5000
        },
        clear: {
            amount: 5,
            within: 5000
        },
        redactedLog: {
            amount: 5,
            within: 5000
        },
        debugger: {
            amount: 10,
            within: 10000
        },
        debuggerThrow: {
            amount: 10,
            within: 10000
        }
    }

    /**
     * Decides if anti debug warnings should be logged
     */
    function shouldLog(type) {

            return false;
    }

    window.console.log = wrapFn((...args) => {
        // Keep track of redacted arguments
        let redactedCount = 0;

        // Filter arguments for detectors
        const newArgs = args.map((a) => {

            // Don't print functions.
            if (typeof a === 'function') {
                redactedCount++;
                return "Redacted Function";
            }

            // Passthrough if primitive
            if (typeof a !== 'object' || a === null) return a;

            // For objects, scan properties
            var props = Object.getOwnPropertyDescriptors(a)
            for (var name in props) {

                // Redact custom getters
                if (props[name].get !== undefined) {
                    redactedCount++;
                    return "Redacted Getter";
                }

                // Also block toString overrides
                if (name === 'toString') {
                    redactedCount++;
                    return "Redacted Str";
                }
            }

            // Defeat Performance Detector
            // https://github.com/theajack/disable-devtool/blob/master/src/detector/sub-detector/performance.ts
            if (Array.isArray(a) && a.length === 50 && typeof a[0] === "object") {
                redactedCount++;
                return "Redacted LargeObjArray";
            }

            return a;
        });

        // If most arguments are redacted, its probably spam
        if (redactedCount >= Math.max(args.length - 1, 1)) {
            if (!shouldLog("redactedLog")) {
                return;
            }
        }

    }, Originals.log);

    window.console.table = wrapFn((obj) => {
        if (shouldLog("table")) {
        }
    }, Originals.table);

    window.console.clear = wrapFn(() => {
        if (shouldLog("table")) {
        }
    }, Originals.clear);

    let debugCount = 0;
    window.Function.prototype.constructor = wrapFn((...args) => {
        const originalFn = Originals.functionConstructor.apply(this, args);
        var fnContent = args[0];
        if (fnContent) {
            if (fnContent.includes('debugger')) { // An anti-debugger is attempting to stop debugging
                if (shouldLog("debugger")) {
                }
                debugCount++;
                if (debugCount > 100) {
                    if (shouldLog("debuggerThrow")) {
                    }
                    throw new Error("You bad!");
                } else {
                    setTimeout(() => {
                        debugCount--;
                    }, 1);
                }
                const newArgs = args.slice(0);
                newArgs[0] = args[0].replaceAll("debugger", ""); // remove debugger statements
                return new Proxy(Originals.functionConstructor.apply(this, newArgs),{
                    get: function (target, prop) {
                        if (prop === "toString") {
                            return originalFn.toString;
                        }
                        return target[prop];
                    }
                });
            }
        }
        return originalFn;
    }, Originals.functionConstructor);

    document.createElement = wrapFn((el, o) => {
        var string = el.toString();
        var element = Originals.createElement.apply(document, [string, o]);
        if (string.toLowerCase() === "iframe") {
            element.addEventListener("load", () => {
                try {
                    element.contentWindow.window.console = window.console;
                } catch (e) {

                }
            });
        }
        return element;
    }, Originals.createElement);

    function wrapFn(newFn, old) {
        return new Proxy(newFn, {
            get: function (target, prop) {
                const callMethods = ['apply', 'bind', 'call'];
                if (callMethods.includes(prop)) {
                    return target[prop];
                }
                return old[prop];
            }
        });
    }
})()


================================================
FILE: data/inject/chatbot.js
================================================
if (typeof chrome === "undefined") {}

if (typeof window.isMac === 'undefined') {
    window.isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0 || 
                   navigator.userAgent.toUpperCase().indexOf('MAC') >= 0;
}

(function() {
    chrome.storage.local.get(['stealth'], function(result) {
        if (window.chatOverlayInjected) {
            console.log("Chat overlay script already injected.");
            return;
        }
        window.chatOverlayInjected = true;
        
        const isStealthModeEnabled = result.stealth === true;
        console.log("Initial stealth mode state:", isStealthModeEnabled);


        function loadShowdown() {
            return new Promise((resolve, reject) => {
                if (typeof showdown !== 'undefined') {
                    resolve();
                    return;
                }

                const script = document.createElement('script');
                script.src = chrome.runtime.getURL('data/lib/showdown.min.js'); // Local path
                script.onload = resolve;
                script.onerror = reject;
                document.head.appendChild(script);
            });
        }

        function loadPrism() {
            return new Promise((resolve) => {
                // Create a lightweight inline syntax highlighter to bypass CSP
                window.SimplePrism = {
                    highlightElement: function(codeElement) {
                        const code = codeElement.textContent;
                        const language = codeElement.className.replace('language-', '');
                        
                        // Use a simpler approach to avoid overlapping replacements
                        let highlightedCode = this.simpleHighlight(code, language);
                        codeElement.innerHTML = highlightedCode;
                    },
                    
                    simpleHighlight: function(code, language) {
                        // Escape HTML first
                        let highlighted = code.replace(/&/g, '&amp;')
                                             .replace(/</g, '&lt;')
                                             .replace(/>/g, '&gt;');
                        
                        // Apply basic highlighting based on language
                        if (language === 'python') {
                            highlighted = this.highlightPython(highlighted);
                        } else if (language === 'javascript' || language === 'js') {
                            highlighted = this.highlightJavaScript(highlighted);
                        } else if (language === 'java') {
                            highlighted = this.highlightJava(highlighted);
                        } else if (language === 'css') {
                            highlighted = this.highlightCSS(highlighted);
                        } else if (language === 'html') {
                            highlighted = this.highlightHTML(highlighted);
                        } else if (language === 'sql') {
                            highlighted = this.highlightSQL(highlighted);
                        } else if (language === 'json') {
                            highlighted = this.highlightJSON(highlighted);
                        } else {
                            // Default to javascript-like highlighting
                            highlighted = this.highlightJavaScript(highlighted);
                        }
                        
                        return highlighted;
                    },
                    
                    highlightPython: function(code) {
                        // Use a token-based approach to avoid overlapping
                        let tokens = [];
                        let currentIndex = 0;
                        
                        // First, find all comments
                        let match;
                        const commentRegex = /#.*$/gm;
                        while ((match = commentRegex.exec(code)) !== null) {
                            tokens.push({
                                start: match.index,
                                end: match.index + match[0].length,
                                type: 'comment',
                                content: match[0]
                            });
                        }
                        
                        // Find strings (avoiding those inside comments)
                        const stringRegex = /(['"])((?:\\.|(?!\1)[^\\])*?)\1/g;
                        while ((match = stringRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'string',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find keywords (avoiding those inside comments and strings)
                        const keywordRegex = /\b(def|class|if|elif|else|for|while|return|import|from|try|except|finally|with|as|and|or|not|in|is)\b/g;
                        while ((match = keywordRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'keyword',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find booleans and None
                        const booleanRegex = /\b(True|False|None)\b/g;
                        while ((match = booleanRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'boolean',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find numbers
                        const numberRegex = /\b\d+(\.\d+)?\b/g;
                        while ((match = numberRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'number',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Sort tokens by position
                        tokens.sort((a, b) => a.start - b.start);
                        
                        // Build highlighted code
                        let result = '';
                        let lastIndex = 0;
                        
                        tokens.forEach(token => {
                            // Add unhighlighted text before this token
                            result += code.slice(lastIndex, token.start);
                            // Add highlighted token
                            result += `<span class="${token.type}">${token.content}</span>`;
                            lastIndex = token.end;
                        });
                        
                        // Add remaining text
                        result += code.slice(lastIndex);
                        
                        return result;
                    },
                    
                    buildHighlightedCode: function(code, tokens) {
                        // Sort tokens by their start position
                        tokens.sort((a, b) => a.start - b.start);
                        
                        let result = '';
                        let lastIndex = 0;
                        
                        for (let token of tokens) {
                            // Add text before this token
                            result += code.slice(lastIndex, token.start);
                            
                            // Add the highlighted token
                            result += `<span class="${token.type}">${token.content}</span>`;
                            
                            lastIndex = token.end;
                        }
                        
                        // Add remaining text
                        result += code.slice(lastIndex);
                        
                        return result;
                    },
                    
                    isInsideToken: function(position, tokens) {
                        return tokens.some(token => position >= token.start && position < token.end);
                    },
                    
                    highlightJavaScript: function(code) {
                        let tokens = [];
                        let match;
                        
                        // Find comments first
                        const singleLineCommentRegex = /\/\/.*$/gm;
                        while ((match = singleLineCommentRegex.exec(code)) !== null) {
                            tokens.push({
                                start: match.index,
                                end: match.index + match[0].length,
                                type: 'comment',
                                content: match[0]
                            });
                        }
                        
                        const multiLineCommentRegex = /\/\*[\s\S]*?\*\//g;
                        while ((match = multiLineCommentRegex.exec(code)) !== null) {
                            tokens.push({
                                start: match.index,
                                end: match.index + match[0].length,
                                type: 'comment',
                                content: match[0]
                            });
                        }
                        
                        // Find strings
                        const stringRegex = /(['"`])((?:\\.|(?!\1)[^\\])*?)\1/g;
                        while ((match = stringRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'string',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find keywords
                        const keywordRegex = /\b(function|const|let|var|if|else|for|while|return|import|export|class|extends|new|this|typeof|instanceof)\b/g;
                        while ((match = keywordRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'keyword',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find booleans
                        const booleanRegex = /\b(true|false|null|undefined)\b/g;
                        while ((match = booleanRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'boolean',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find numbers
                        const numberRegex = /\b\d+(\.\d+)?\b/g;
                        while ((match = numberRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'number',
                                    content: match[0]
                                });
                            }
                        }
                        
                        return this.buildHighlightedCode(code, tokens);
                    },
                    
                    highlightJava: function(code) {
                        let tokens = [];
                        let match;
                        
                        // Find comments first
                        const singleLineCommentRegex = /\/\/.*$/gm;
                        while ((match = singleLineCommentRegex.exec(code)) !== null) {
                            tokens.push({
                                start: match.index,
                                end: match.index + match[0].length,
                                type: 'comment',
                                content: match[0]
                            });
                        }
                        
                        const multiLineCommentRegex = /\/\*[\s\S]*?\*\//g;
                        while ((match = multiLineCommentRegex.exec(code)) !== null) {
                            tokens.push({
                                start: match.index,
                                end: match.index + match[0].length,
                                type: 'comment',
                                content: match[0]
                            });
                        }
                        
                        // Find strings
                        const stringRegex = /(['"])((?:\\.|(?!\1)[^\\])*?)\1/g;
                        while ((match = stringRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'string',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find keywords
                        const keywordRegex = /\b(public|private|protected|static|final|class|interface|extends|implements|if|else|for|while|return|import|package|new|this)\b/g;
                        while ((match = keywordRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'keyword',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find booleans
                        const booleanRegex = /\b(true|false|null)\b/g;
                        while ((match = booleanRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'boolean',
                                    content: match[0]
                                });
                            }
                        }
                        
                        // Find numbers
                        const numberRegex = /\b\d+(\.\d+)?[fFdDlL]?\b/g;
                        while ((match = numberRegex.exec(code)) !== null) {
                            if (!this.isInsideToken(match.index, tokens)) {
                                tokens.push({
                                    start: match.index,
                                    end: match.index + match[0].length,
                                    type: 'number',
                                    content: match[0]
                                });
                            }
                        }
                        
                        return this.buildHighlightedCode(code, tokens);
                    },
                    
                    highlightCSS: function(code) {
                        // Comments first
                        code = code.replace(/\/\*[\s\S]*?\*\//g, '<span class="comment">$&</span>');
                        // Selectors
                        code = code.replace(/([.#][a-zA-Z][a-zA-Z0-9_-]*)/g, '<span class="selector">$1</span>');
                        // Properties
                        code = code.replace(/([a-zA-Z-]+)(\s*:)/g, '<span class="property">$1</span>$2');
                        // Values
                        code = code.replace(/(#[0-9a-fA-F]+)/g, '<span class="value">$1</span>');
                        return code;
                    },
                    
                    highlightHTML: function(code) {
                        // Comments first
                        code = code.replace(/(&lt;!--[\s\S]*?--&gt;)/g, '<span class="comment">$1</span>');
                        // Tags
                        code = code.replace(/(&lt;\/?[^&gt;]+&gt;)/g, '<span class="tag">$1</span>');
                        return code;
                    },
                    
                    highlightSQL: function(code) {
                        // Comments first
                        code = code.replace(/--.*$/gm, '<span class="comment">$&</span>');
                        // Strings
                        code = code.replace(/'[^']*'/g, '<span class="string">$&</span>');
                        // Keywords
                        code = code.replace(/\b(SELECT|FROM|WHERE|INSERT|UPDATE|DELETE|CREATE|DROP|ALTER|TABLE|INDEX|PRIMARY|KEY|FOREIGN|NOT|NULL|DEFAULT|AND|OR|ORDER|BY|GROUP|HAVING|LIMIT)\b/gi, '<span class="keyword">$1</span>');
                        // Numbers
                        code = code.replace(/\b\d+(\.\d+)?\b/g, '<span class="number">$&</span>');
                        return code;
                    },
                    
                    highlightJSON: function(code) {
                        // Property keys first (before general strings)
                        code = code.replace(/"([^"]*)"(\s*:)/g, '<span class="property">"$1"</span>$2');
                        // Remaining strings
                        code = code.replace(/"([^"]*)"/g, '<span class="string">"$1"</span>');
                        // Booleans and null
                        code = code.replace(/\b(true|false|null)\b/g, '<span class="boolean">$1</span>');
                        // Numbers
                        code = code.replace(/\b\d+(\.\d+)?\b/g, '<span class="number">$&</span>');
                        return code;
                    }
                };
                
                // Add CSS for syntax highlighting with clean default theme
                // Styles will be added to shadow DOM later, not to document.head
                window._chatSyntaxHighlightCSS = `
                    .keyword { color: #0066CC; font-weight: bold; }
                    .string { color: #008000; }
                    .comment { color: #808080; font-style: italic; }
                    .number { color: #FF6600; }
                    .boolean { color: #0066CC; font-weight: bold; }
                    .property { color: #9932CC; }
                    .selector { color: #008000; font-weight: bold; }
                    .value { color: #FF6600; }
                    .tag { color: #0066CC; }
                `;
                
                resolve();
            });
        }

        // Chat icon SVG data URL (matching Crisp style)
        const CHAT_ICON_SVG_URL = 'url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2235%22%20height%3D%2230%22%20viewBox%3D%220%200%2035%2030%22%3E%3Cdefs%3E%3Cfilter%20id%3D%22c%22%20width%3D%22123.1%25%22%20height%3D%22127.9%25%22%20x%3D%22-11.5%25%22%3E%3CfeOffset%20dy%3D%221%22%20in%3D%22SourceAlpha%22%20result%3D%22shadowOffsetOuter1%22%2F%3E%3CfeGaussianBlur%20in%3D%22shadowOffsetOuter1%22%20result%3D%22shadowBlurOuter1%22%20stdDeviation%3D%221%22%2F%3E%3CfeColorMatrix%20in%3D%22shadowBlurOuter1%22%20values%3D%220%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200%200.07%200%22%2F%3E%3C%2Ffilter%3E%3Cfilter%20id%3D%22e%22%20width%3D%22129.7%25%22%20height%3D%22135.9%25%22%20x%3D%22-14.8%25%22%20y%3D%22-14%25%22%3E%3CfeMorphology%20in%3D%22SourceAlpha%22%20radius%3D%221%22%20result%3D%22shadowSpreadInner1%22%2F%3E%3CfeGaussianBlur%20in%3D%22shadowSpreadInner1%22%20result%3D%22shadowBlurInner1%22%20stdDeviation%3D%222%22%2F%3E%3CfeOffset%20in%3D%22shadowBlurInner1%22%20result%3D%22shadowOffsetInner1%22%2F%3E%3CfeComposite%20in%3D%22shadowOffsetInner1%22%20in2%3D%22SourceAlpha%22%20k2%3D%22-1%22%20k3%3D%221%22%20operator%3D%22arithmetic%22%20result%3D%22shadowInnerInner1%22%2F%3E%3CfeColorMatrix%20in%3D%22shadowInnerInner1%22%20values%3D%220%200%200%200%201%200%200%200%200%201%200%200%200%200%201%200%200%200%200.750191215%200%22%2F%3E%3C%2Ffilter%3E%3ClinearGradient%20id%3D%22d%22%20x1%3D%2246.514%25%22%20x2%3D%2256.692%25%22%20y1%3D%2215.835%25%22%20y2%3D%2275.847%25%22%3E%3Cstop%20offset%3D%220%22%20stop-color%3D%22%23fff%22%2F%3E%3Cstop%20offset%3D%221%22%20stop-color%3D%22%23fff%22%20stop-opacity%3D%22.601%22%2F%3E%3C%2FlinearGradient%3E%3Cpath%20id%3D%22a%22%20d%3D%22m40.34%2016.878.005.052%201.327%2014.35a2%202%200%200%201-1.754%202.17l-7.814.934-3.293%205.326a1%201%200%200%201-1.574.165l-4.207-4.407-8.113.969a2%202%200%200%201-2.228-1.802l-1.328-14.35a2%202%200%200%201%201.755-2.17l25-2.986a2%202%200%200%201%202.223%201.749%22%2F%3E%3C%2Fdefs%3E%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%20transform%3D%22translate%28-9%20-14%29%22%3E%3Cuse%20xlink%3Ahref%3D%22%23a%22%20fill%3D%22%23000%22%20filter%3D%22url%28%23c%29%22%2F%3E%3Cuse%20xlink%3Ahref%3D%22%23a%22%20fill%3D%22url%28%23d%29%22%2F%3E%3Cuse%20xlink%3Ahref%3D%22%23a%22%20fill%3D%22%23000%22%20filter%3D%22url%28%23e%29%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")';

        // State variables
        let isOverlayVisible = false;
        let chatHistory = [];
        let isDragging = false;
        let isResizing = false;
        let markdownConverter = null; // Will be initialized when showdown loads
        let currentStreamingDiv = null; // Tracks the current assistant message being streamed
        let chatAboutQuestionEnabled = false; // Toggle for chat about question feature
        let extractedQuestion = null; // Store extracted question

        // Helper function to access elements in shadow DOM
        function getShadowElement(id) {
            const shadowHost = document.getElementById('chat-overlay-shadow-host');
            if (!shadowHost || !shadowHost.shadowRoot) return null;
            return shadowHost.shadowRoot.getElementById(id);
        }
        
        function getShadowRoot() {
            const shadowHost = document.getElementById('chat-overlay-shadow-host');
            return shadowHost ? shadowHost.shadowRoot : null;
        }
        
        function getChatButton() {
            const buttonShadowHost = document.getElementById('chat-button-shadow-host');
            if (!buttonShadowHost || !buttonShadowHost.shadowRoot) return null;
            return buttonShadowHost.shadowRoot.getElementById('chat-button');
        }

        // Drag and resize state
        let dragOffsetX;
        let dragOffsetY;
        let initialWidth;
        let initialHeight;
        let resizeStartX;
        let resizeStartY;

        const fontLink = document.createElement('link');
        fontLink.href = 'https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap';
        fontLink.rel = 'stylesheet';
        document.head.appendChild(fontLink);

        // Question extraction functions
        function detectPlatform() {
            // Check for Examly/IamNeo
            if (document.querySelector('div[aria-labelledby="question-data"]')) {
                return 'examly';
            }
            // Check for HackerRank
            if (document.querySelector('.QuestionDetails_container__AIu0X') || 
                document.querySelector('.monaco-editor') ||
                document.querySelector('.grouped-mcq__question')) {
                return 'hackerrank';
            }
            return null;
        }

        function extractExamlyQuestion() {
            const questionElement = document.querySelector('div[aria-labelledby="question-data"]');
            if (!questionElement) return null;

            const questionText = questionElement.innerText.trim();

            // Check if it's a coding question
            const codingQuestionElement = document.querySelector('div[aria-labelledby="input-format"]');
            
            if (codingQuestionElement) {
                // Coding question
                const programmingLanguageElement = document.querySelector('span.inner-text');
                const programmingLanguage = programmingLanguageElement ? programmingLanguageElement.innerText.trim() : 'Programming language not found.';

                const inputFormatElement = document.querySelector('div[aria-labelledby="input-format"]');
                const inputFormatText = inputFormatElement ? inputFormatElement.innerText.trim() : '';

                const outputFormatElement = document.querySelector('div[aria-labelledby="output-format"]');
                const outputFormatText = outputFormatElement ? outputFormatElement.innerText.trim() : '';

                const sampleTestCaseElements = document.querySelectorAll('div[aria-labelledby="each-tc-card"]');
                let testCasesText = '';
                sampleTestCaseElements.forEach((testCase, index) => {
                    const inputElement = testCase.querySelector('div[aria-labelledby="each-tc-input-container"] pre');
                    const outputElement = testCase.querySelector('div[aria-labelledby="each-tc-output-container"] pre');

                    const inputText = inputElement ? inputElement.innerText.trim() : 'Input not found';
                    const outputText = outputElement ? outputElement.innerText.trim() : 'Output not found';

                    testCasesText += `Sample Test Case ${index + 1}:\nInput:\n${inputText}\nOutput:\n${outputText}\n\n`;
                });

                return {
                    type: 'coding',
                    language: programmingLanguage,
                    question: questionText,
                    inputFormat: inputFormatText,
                    outputFormat: outputFormatText,
                    testCases: testCasesText
                };
            } else {
                // MCQ question
                const codeLines = [];
                const codeElements = document.querySelectorAll('.ace_layer.ace_text-layer .ace_line');
                codeElements.forEach(line => {
                    codeLines.push(line.innerText.trim());
                });
                const codeText = codeLines.length > 0 ? codeLines.join('\n') : null;

                const optionsElements = document.querySelectorAll('div[aria-labelledby="each-option"]');
                const optionsText = [];
                optionsElements.forEach((option, index) => {
                    optionsText.push(`Option ${index + 1}: ${option.innerText.trim()}`);
                });

                return {
                    type: 'mcq',
                    question: questionText,
                    code: codeText,
                    options: optionsText.join('\n')
                };
            }
        }

        function extractHackerRankQuestion() {
            const getCleanText = el => el?.innerText?.trim() || "";

            // Check if it's a coding question (has Monaco editor)
            const monacoEditor = document.querySelector('.monaco-editor, .hr-monaco-editor');
            
            if (monacoEditor) {
                // Coding question
                let language = "Unknown";
                let title = "No Title Found";
                let instruction = "No Instructions Found";
                let details = "";

                const newLanguageSelector = document.querySelector('.select-language .css-3d4y2u-singleValue, .select-language .css-x7738g');
                if (newLanguageSelector) {
                    language = getCleanText(newLanguageSelector);
                } else {
                    language = getCleanText(document.querySelector('.select-language .css-x7738g')) || "Unknown";
                }

                let container = document.querySelector('.QuestionDetails_container__AIu0X');
                if (container) {
                    const titleElement = container.querySelector('.qaas-block-question-title, h2');
                    if (titleElement) {
                        const titleText = titleElement.textContent || titleElement.innerText;
                        title = titleText.replace(/Bookmark question \d+/g, '').trim();
                    }
                    
                    const instructionElement = container.querySelector('.qaas-block-question-instruction, .RichTextPreview_richText__1vKu5');
                    if (instructionElement) {
                        instruction = getCleanText(instructionElement);
                    }
                    
                    const detailsElements = container.querySelectorAll('details');
                    if (detailsElements.length > 0) {
                        details = Array.from(detailsElements).map(detail => {
                            const summary = getCleanText(detail.querySelector('summary'));
                            const content = getCleanText(detail.querySelector('.collapsable-details'));
                            return `\n${summary}\n${'-'.repeat(summary.length)}\n${content}`;
                        }).join('\n');
                    }
                } else {
                    container = document.querySelector('#main-splitpane-left');
                    if (container) {
                        title = getCleanText(container.querySelector('.question-view__title')) || "No Title Found";
                        instruction = getCleanText(container.querySelector('.question-view__instruction')) || "No Instructions Found";
                        
                        details = Array.from(container.querySelectorAll('details') || []).map(detail => {
                            const summary = getCleanText(detail.querySelector('summary'));
                            const content = getCleanText(detail.querySelector('.collapsable-details'));
                            return `\n${summary}\n${'-'.repeat(summary.length)}\n${content}`;
                        }).join('\n');
                    }
                }

                return {
                    type: 'coding',
                    language: language,
                    title: title,
                    instruction: instruction,
                    details: details
                };
            } else {
                // MCQ question
                const newLayoutQuestions = document.querySelectorAll('.QuestionDetails_container__AIu0X');
                
                if (newLayoutQuestions.length > 0) {
                    // New layout
                    const container = newLayoutQuestions[0]; // Get first question
                    let title = '';
                    let instruction = '';
                    let options = [];

                    const titleElement = container.querySelector('.qaas-block-question-title, h2');
                    if (titleElement) {
                        const titleText = titleElement.textContent || titleElement.innerText;
                        title = titleText.replace(/Bookmark question \d+/g, '').trim();
                    }

                    const instructionElement = container.querySelector('.qaas-block-question-instruction, .RichTextPreview_richText__1vKu5');
                    if (instructionElement) {
                        instruction = getCleanText(instructionElement);
                    }

                    let optionsContainer = container.nextElementSibling;
                    let attempts = 0;
                    while (optionsContainer && attempts < 5) {
                        const hasOptions = optionsContainer.querySelector('[role="checkbox"], [role="radio"]');
                        if (hasOptions) break;
                        optionsContainer = optionsContainer.nextElementSibling;
                        attempts++;
                    }

                    if (optionsContainer) {
                        let optionElements = optionsContainer.querySelectorAll('[role="radio"]');
                        if (optionElements.length === 0) {
                            optionElements = optionsContainer.querySelectorAll('[role="checkbox"]');
                        }

                        optionElements.forEach((option, index) => {
                            const labelId = option.getAttribute('aria-labelledby');
                            const labelElement = labelId ? document.getElementById(labelId) : 
                                              option.closest('.Control_optionList__vIubt, li')?.querySelector('label');
                            
                            if (labelElement) {
                                options.push(`Option ${index + 1}: ${labelElement.textContent.trim()}`);
                            }
                        });
                    }

                    return {
                        type: 'mcq',
                        title: title,
                        instruction: instruction,
                        options: options.join('\n')
                    };
                } else {
                    // Old layout
                    const oldLayoutQuestion = document.querySelector('.grouped-mcq__question');
                    if (oldLayoutQuestion) {
                        let title = '';
                        let instruction = '';
                        let options = [];

                        const titleElement = oldLayoutQuestion.querySelector('.question-view__title');
                        if (titleElement) {
                            title = titleElement.textContent.trim();
                        }

                        const instructionElement = oldLayoutQuestion.querySelector('.question-view__instruction');
                        if (instructionElement) {
                            instruction = instructionElement.textContent.trim();
                        }

                        const optionElements = oldLayoutQuestion.querySelectorAll('.ui-radio');
                        optionElements.forEach((option, index) => {
                            const labelElement = option.querySelector('.label');
                            if (labelElement) {
                                options.push(`Option ${index + 1}: ${labelElement.textContent.trim()}`);
                            }
                        });

                        return {
                            type: 'mcq',
                            title: title,
                            instruction: instruction,
                            options: options.join('\n')
                        };
                    }
                }
            }

            return null;
        }

        function extractCurrentQuestion() {
            const platform = detectPlatform();
            
            if (platform === 'examly') {
                return extractExamlyQuestion();
            } else if (platform === 'hackerrank') {
                return extractHackerRankQuestion();
            }
            
            return null;
        }

        function formatQuestionForChat(questionData) {
            if (!questionData) return null;

            let formattedQuestion = '';

            if (questionData.type === 'coding') {
                if (questionData.language) {
                    // Examly or HackerRank coding
                    formattedQuestion += `[Coding Question - ${questionData.language}]\n\n`;
                    
                    if (questionData.title) {
                        formattedQuestion += `Title: ${questionData.title}\n\n`;
                    }
                    
                    if (questionData.question) {
                        formattedQuestion += `Question:\n${questionData.question}\n\n`;
                    }
                    
                    if (questionData.instruction) {
                        formattedQuestion += `Instruction:\n${questionData.instruction}\n\n`;
                    }
                    
                    if (questionData.inputFormat) {
                        formattedQuestion += `Input Format:\n${questionData.inputFormat}\n\n`;
                    }
                    
                    if (questionData.outputFormat) {
                        formattedQuestion += `Output Format:\n${questionData.outputFormat}\n\n`;
                    }
                    
                    if (questionData.testCases) {
                        formattedQuestion += `Test Cases:\n${questionData.testCases}\n\n`;
                    }
                    
                    if (questionData.details) {
                        formattedQuestion += `Additional Details:${questionData.details}\n\n`;
                    }
                }
            } else if (questionData.type === 'mcq') {
                formattedQuestion += `[MCQ Question]\n\n`;
                
                if (questionData.title) {
                    formattedQuestion += `Title: ${questionData.title}\n\n`;
                }
                
                if (questionData.question) {
                    formattedQuestion += `Question:\n${questionData.question}\n\n`;
                }
                
                if (questionData.instruction) {
                    formattedQuestion += `${questionData.instruction}\n\n`;
                }
                
                if (questionData.code) {
                    formattedQuestion += `Code:\n${questionData.code}\n\n`;
                }
                
                if (questionData.options) {
                    formattedQuestion += `Options:\n${questionData.options}\n`;
                }
            }

            return formattedQuestion.trim();
        }

        // Create the main chat overlay UI
        function createChatOverlay() {
            // Check if shadow host already exists
            let shadowHost = document.getElementById("chat-overlay-shadow-host");
            if (shadowHost) {
                return shadowHost.shadowRoot.querySelector("#chat-overlay");
            }

            // Create shadow host element
            shadowHost = document.createElement("div");
            shadowHost.id = "chat-overlay-shadow-host";
            shadowHost.style.cssText = `
                position: fixed;
                bottom: 0;
                right: 0;
                z-index: 2147483647;
                pointer-events: none;
            `;

            // Attach shadow root
            const shadowRoot = shadowHost.attachShadow({ mode: 'open' });

            const overlay = document.createElement("div");
            overlay.id = "chat-overlay";
            overlay.style.cssText = `
                display: ${isOverlayVisible ? "flex" : "none"};
                position: fixed;
                bottom: 20px;
                right: 20px;
                width: 380px;
                height: 500px;
                background-color: #fff;
                border: none;
                border-radius: 16px;
                box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
                z-index: 2147483647;
                flex-direction: column;
                font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
                overflow: hidden;
                transition: opacity 0.3s ease;
                pointer-events: auto;
            `;

            // Create header
            const header = document.createElement("div");
            header.style.cssText = `
            padding: 16px 20px !important;
            font-weight: 500 !important;
            display: flex !important;
            justify-content: space-between !important;
            align-items: center !important;
            background-color: #fff !important;
            color: #333 !important;
            cursor: move !important;
        `;

            header.innerHTML = `
        <div style="display: flex !important; flex-direction: column !important; align-items: flex-start !important; gap: 2px !important;">
            <span style="display: flex !important; align-items: center !important; gap: 8px !important; font-size: 18px !important; font-weight: 700 !important; color: rgb(60, 84, 114) !important; opacity: 0.85 !important;">
                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
                <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
                </svg>
                Chat
            </span>
            <span style="font-size: 12px !important; font-weight: 500 !important; color: #777 !important; margin-left: 30px !important;">
                ${window.isMac ? 'Option+C' : 'Alt+C'} to toggle
            </span>
        </div>
        <div style="display: flex !important; gap: 14px !important; align-items: center !important;">
            <span id="clear-chat" style="cursor: pointer !important; font-size: 14px !important; font-weight: 600 !important; color: rgb(220, 53, 69) !important; padding: 4px 8px !important; transition: all 0.2s ease !important;" onmouseover="this.style.opacity='0.7'" onmouseout="this.style.opacity='1'">Clear</span>
            <span id="close-chat" style="cursor: pointer !important; font-size: 22px !important; line-height: 1 !important; color: #888 !important; transition: color 0.2s ease !important; font-weight: 500 !important;" onmouseover="this.style.color='#333'" onmouseout="this.style.color='#888'">×</span>
        </div>
        `;

            // Create opacity slider container (Stealth mode control)
            const sliderContainer = document.createElement("div");
            sliderContainer.style.cssText = `
                width: 100%;
                height: 2px;
                background-color: rgba(60, 84, 114, 0.1);
                position: relative;
                z-index: 10;
                display: flex;
                align-items: center;
            `;

            const opacitySlider = document.createElement("input");
            opacitySlider.type = "range";
            opacitySlider.min = "15";
            opacitySlider.max = "100";
            opacitySlider.value = "100";
            opacitySlider.id = "opacity-slider";
            opacitySlider.title = "Adjust opacity / Enable Stealth Mode";
            sliderContainer.appendChild(opacitySlider);

            // Create messages container
            const messagesContainer = document.createElement("div");
            messagesContainer.id = "chat-messages";
            messagesContainer.style.cssText = `
        padding: 20px;
        flex: 1;
        overflow-y: auto;
        background-color: #fafafa;
        color: #333;
        scroll-behavior: smooth;
        white-space: pre-wrap;
        display: flex;
        flex-direction: column;
        gap: 12px;
        `;

            // Create input area
            const inputArea = document.createElement("div");
            inputArea.style.cssText = `
        padding: 12px 16px 16px 16px;
        background-color: #fff;
        display: flex;
        flex-direction: column;
        gap: 8px;
        z-index: 10;
        `;

            // Create button container (which now acts as the pill wrapper)
            const buttonContainer = document.createElement("div");
            buttonContainer.style.cssText = `
        display: flex;
        align-items: stretch; /* Stretch children to fill height */
        background-color: #f4f6f8;
        border: 1px solid rgba(0, 0, 0, 0.08);
        border-radius: 24px;
        padding: 0; /* Remove all padding from container */
        transition: all 0.2s ease;
        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.02);
        gap: 0;
        overflow: hidden; /* Ensures inner elements don't break the pill curve */
        min-height: 44px;
        `;

            // Hover effect for the pill container
            buttonContainer.addEventListener('mouseenter', () => {
                buttonContainer.style.border = '1px solid rgba(60, 84, 114, 0.3)';
                buttonContainer.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.05)';
            });
            buttonContainer.addEventListener('mouseleave', () => {
                buttonContainer.style.border = '1px solid rgba(0, 0, 0, 0.08)';
                buttonContainer.style.boxShadow = '0 2px 6px rgba(0, 0, 0, 0.02)';
            });

            // Create input field with plain text only
            const inputField = document.createElement("div");
            inputField.contentEditable = "plaintext-only"; // Force plain text only
            inputField.placeholder = "Message...";
            inputField.style.cssText = `
        flex: 1;
        padding: 12px 12px 12px 16px; /* Put padding on the input instead */
        border: none;
        outline: none;
        background-color: transparent;
        color: #222;
        font-family: 'Poppins', sans-serif;
        font-size: 14px;
        line-height: 1.5;
        font-weight: 400;
        min-height: 45px; /* Minimum height for 1 line */
        max-height: 66px; /* Max height for exactly 2 lines (14px font * 1.5 line height * 2 + 24px padding = 66px) */
        overflow-y: auto;
        overflow-x: hidden;
        white-space: pre-wrap;
        word-wrap: break-word; /* Ensure text breaks into new lines */
        -webkit-user-modify: read-write-plaintext-only;
        display: block; /* Removed flex to allow proper text wrapping */
        `;

            // Simple paste event to ensure consistency (optional fallback)
            inputField.addEventListener('paste', async function(e) {
                e.preventDefault();
                e.stopPropagation();
                e.stopImmediatePropagation();
                
                try {
                    let clipText = '';
                    
                    // First try native clipboard (prioritize external app copies)
                    try {
                        clipText = await navigator.clipboard.readText();
                        console.log('[ChatBot Paste] Using native clipboard, length:', clipText.length);
                    } catch (err) {
                        console.log('[ChatBot Paste] Native clipboard read failed:', err.message);
                    }
                    
                    // If empty, fall back to neoPassClipboard
                    if (!clipText && window.neoPassClipboard) {
                        clipText = window.neoPassClipboard;
                        console.log('[ChatBot Paste] Using neoPassClipboard, length:', clipText.length);
                    }
                    
                    // Also try clipboardData from the paste event
                    if (!clipText && e.clipboardData) {
                        clipText = e.clipboardData.getData('text/plain');
                        console.log('[ChatBot Paste] Using event clipboardData, length:', clipText.length);
                    }
                    
                    if (clipText) {
                        console.log('[ChatBot Paste] Attempting to insert text...');
                        let inserted = false;
                        
                        // Try method 1: Use selection API
                        try {
                            const selection = window.getSelection();
                            if (selection && selection.rangeCount > 0) {
                                const range = selection.getRangeAt(0);
                                
                                // Ensure the range is within our input field
                                if (this.contains(range.commonAncestorContainer)) {
                                    range.deleteContents();
                                    const textNode = document.createTextNode(clipText);
                                    range.insertNode(textNode);
                                    range.setStartAfter(textNode);
                                    range.setEndAfter(textNode);
                                    selection.removeAllRanges();
                                    selection.addRange(range);
                                    inserted = true;
                                    console.log('[ChatBot Paste] Inserted using selection API');
                                }
                            }
                        } catch (selErr) {
                            console.log('[ChatBot Paste] Selection API failed:', selErr.message);
                        }
                        
                        // Fallback method 2: Direct textContent manipulation
                        if (!inserted) {
                            console.log('[ChatBot Paste] Using fallback: direct insertion');
                            const currentText = this.textContent || '';
                            this.textContent = currentText + clipText;
                            
                            // Move cursor to end
                            const range = document.createRange();
                            const selection = window.getSelection();
                            range.selectNodeContents(this);
                            range.collapse(false);
                            selection.removeAllRanges();
                            selection.addRange(range);
                            inserted = true;
                        }
                        
                        if (inserted) {
                            // Dispatch input event to trigger any listeners
                            this.dispatchEvent(new InputEvent('input', { 
                                bubbles: true, 
                                cancelable: true,
                                inputType: 'insertText',
                                data: clipText
                            }));
                            console.log('[ChatBot Paste] Paste successful');
                        }
                    }
                    
                    // Clean any potential HTML that might slip through
                    setTimeout(() => {
                        if (this.children.length > 0) {
                            const text = this.textContent || this.innerText;
                            this.textContent = text;
                        }
                    }, 10);
                } catch (err) {
                    console.error('[ChatBot Paste] Error:', err);
                    // Fallback: let browser handle it
                    setTimeout(() => {
                        if (this.children.length > 0) {
                            const text = this.textContent || this.innerText;
                            this.textContent = text;
                        }
                    }, 10);
                }
            }, true); // Use capture phase to intercept before document-level handlers
            
            // Add Ctrl+V / Cmd+V handler for paste
            inputField.addEventListener('keydown', async function(e) {
                const ctrlKey = e.ctrlKey || e.metaKey; // Support both Ctrl (Windows/Linux) and Cmd (macOS)
                
                // Handle Enter key for sending messages
                if (e.key === 'Enter' && !e.shiftKey) {
                    e.preventDefault();
                    sendButton.click();
                    return;
                }
                
                // Handle Ctrl+V / Cmd+V for paste
                if (ctrlKey && (e.key === 'V' || e.key === 'v')) {
                    e.preventDefault();
                    e.stopPropagation();
                    e.stopImmediatePropagation();
                    
                    try {
                        let clipText = '';
                        
                        // First try native clipboard (prioritize external app copies)
                        try {
                            clipText = await navigator.clipboard.readText();
                            console.log('[ChatBot Ctrl+V] Using native clipboard, length:', clipText.length);
                        } catch (err) {
                            console.log('[ChatBot Ctrl+V] Native clipboard read failed:', err.message);
                        }
                        
                        // If empty, fall back to neoPassClipboard
                        if (!clipText && window.neoPassClipboard) {
                            clipText = window.neoPassClipboard;
                            console.log('[ChatBot Ctrl+V] Using neoPassClipboard, length:', clipText.length);
                        }
                        
                        if (clipText) {
                            console.log('[ChatBot Ctrl+V] Attempting to insert text...');
                            let inserted = false;
                            
                            // Try method 1: Use selection API
                            try {
                                const selection = window.getSelection();
                                if (selection && selection.rangeCount > 0) {
                                    const range = selection.getRangeAt(0);
                                    
                                    // Ensure the range is within our input field
                                    if (this.contains(range.commonAncestorContainer)) {
                                        range.deleteContents();
                                        const textNode = document.createTextNode(clipText);
                                        range.insertNode(textNode);
                                        range.setStartAfter(textNode);
                                        range.setEndAfter(textNode);
                                        selection.removeAllRanges();
                                        selection.addRange(range);
                                        inserted = true;
                                        console.log('[ChatBot Ctrl+V] Inserted using selection API');
                                    }
                                }
                            } catch (selErr) {
                                console.log('[ChatBot Ctrl+V] Selection API failed:', selErr.message);
                            }
                            
                            // Fallback method 2: Direct textContent manipulation
                            if (!inserted) {
                                console.log('[ChatBot Ctrl+V] Using fallback: direct insertion');
                                const currentText = this.textContent || '';
                                this.textContent = currentText + clipText;
                                
                                // Move cursor to end
                                const range = document.createRange();
                                const selection = window.getSelection();
                                range.selectNodeContents(this);
                                range.collapse(false);
                                selection.removeAllRanges();
                                selection.addRange(range);
                                inserted = true;
                            }
                            
                            if (inserted) {
                                // Dispatch input event to trigger any listeners
                                this.dispatchEvent(new InputEvent('input', { 
                                    bubbles: true, 
                                    cancelable: true,
                                    inputType: 'insertText',
                                    data: clipText
                                }));
                                console.log('[ChatBot Ctrl+V] Paste successful');
                            }
                        } else {
                            console.log('[ChatBot Ctrl+V] No clipboard content available');
                        }
                    } catch (err) {
                        console.error('[ChatBot Ctrl+V] Error:', err);
                    }
                }
            }, true); // Use capture phase to intercept before document-level handlers

            // Create checkbox container for "Chat about question"
            const checkboxContainer = document.createElement("div");
            checkboxContainer.style.cssText = `
        display: none;
        align-items: center;
        gap: 8px;
        padding: 4px 0;
        `;

            const checkbox = document.createElement("input");
            checkbox.type = "checkbox";
            checkbox.id = "chat-about-question-checkbox";
            checkbox.style.cssText = `
        width: 16px;
        height: 16px;
        cursor: pointer;
        accent-color: rgb(60, 84, 114);
        `;

            const checkboxLabel = document.createElement("label");
            checkboxLabel.htmlFor = "chat-about-question-checkbox";
            checkboxLabel.style.cssText = `
        font-family: 'Poppins', sans-serif;
        font-size: 13px;
        color: #666;
        cursor: pointer;
        user-select: none;
        display: flex;
        align-items: center;
        gap: 6px;
        `;

            const questionIcon = `
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
            <circle cx="12" cy="12" r="10"></circle>
            <path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"></path>
            <line x1="12" y1="17" x2="12.01" y2="17"></line>
        </svg>
        `;

            checkboxLabel.innerHTML = questionIcon + '<span>Chat about question</span>';

            checkboxContainer.appendChild(checkbox);
            checkboxContainer.appendChild(checkboxLabel);

            // Store last question hash to detect question changes
            let lastQuestionHash = null;

            // Function to generate a simple hash from question data
            function getQuestionHash(questionData) {
                if (!questionData) return null;
                
                // Create a unique string from the question data
                let hashString = '';
                if (questionData.type) hashString += questionData.type;
                if (questionData.question) hashString += questionData.question;
                if (questionData.title) hashString += questionData.title;
                if (questionData.instruction) hashString += questionData.instruction;
                
                // Simple hash function
                let hash = 0;
                for (let i = 0; i < hashString.length; i++) {
                    const char = hashString.charCodeAt(i);
                    hash = ((hash << 5) - hash) + char;
                    hash = hash & hash; // Convert to 32-bit integer
                }
                return hash;
            }

            // Function to check and update checkbox visibility based on platform detection
            function updateCheckboxVisibility() {
                const platform = detectPlatform();
                if (platform) {
                    // Valid platform detected, show the checkbox
                    checkboxContainer.style.display = 'flex';
                    
                    // If checkbox is enabled, check if question has changed
                    if (chatAboutQuestionEnabled && checkbox.checked) {
                        const currentQuestionData = extractCurrentQuestion();
                        const currentQuestionHash = getQuestionHash(currentQuestionData);
                        
                        // If question hash changed, re-extract the question
                        if (currentQuestionHash !== lastQuestionHash && lastQuestionHash !== null) {
                            if (currentQuestionData) {
                                extractedQuestion = formatQuestionForChat(currentQuestionData);
                                lastQuestionHash = currentQuestionHash;
                                console.log('Question changed and re-extracted for chat');
                                
                                // Clear chat history when question changes
                                clearChatHistoryAndUI('question-switch');
                                
                                // Show notification that question was updated and chat cleared
                                addNotificationMessage('Question updated - Chat cleared');
                            } else {
                                // Question no longer available
                                checkbox.checked = false;
                                chatAboutQuestionEnabled = false;
                                extractedQuestion = null;
                                lastQuestionHash = null;
                                checkboxLabel.style.color = '#666';
                                checkboxLabel.style.fontWeight = '400';
                                addNotificationMessage('Question no longer detected');
                            }
                        }
                    }
                } else {
                    // No valid platform, hide the checkbox and reset state
                    checkboxContainer.style.display = 'none';
                    checkbox.checked = false;
                    chatAboutQuestionEnabled = false;
                    extractedQuestion = null;
                    lastQuestionHash = null;
                    checkboxLabel.style.color = '#666';
                    checkboxLabel.style.fontWeight = '400';
                }
            }

            // Initial check when overlay is created
            updateCheckboxVisibility();

            // Re-check periodically in case user navigates to a different page
            setInterval(updateCheckboxVisibility, 2000);

            // Handle checkbox change
            checkbox.addEventListener('change', function() {
                chatAboutQuestionEnabled = this.checked;
                
                if (chatAboutQuestionEnabled) {
                    // Extract question when enabled
                    const questionData = extractCurrentQuestion();
                    if (questionData) {
                        extractedQuestion = formatQuestionForChat(questionData);
                        lastQuestionHash = getQuestionHash(questionData);
                        console.log('Question extracted for chat:', extractedQuestion);
                        
                        // Update label to show question is attached
                        checkboxLabel.style.color = 'rgb(60, 84, 114)';
                        checkboxLabel.style.fontWeight = '500';
                    } else {
                        // No question found, disable checkbox
                        this.checked = false;
                        chatAboutQuestionEnabled = false;
                        extractedQuestion = null;
                        lastQuestionHash = null;
                        
                        // Show notification
                        addNotificationMessage('No question detected on this page');
                    }
                } else {
                    // Reset styles when disabled
                    checkboxLabel.style.color = '#666';
                    checkboxLabel.style.fontWeight = '400';
                    extractedQuestion = null;
                    lastQuestionHash = null;
                }
            });

            // Create send button
            const sendButton = document.createElement("button");
            sendButton.innerHTML = "Send";
            sendButton.style.cssText = `
        padding: 0 20px 0 16px; /* Wider padding for text */
        margin: 0;
        background-color: rgb(60, 84, 114);
        color: #fff;
        border: none;
        border-radius: 0; /* Let the container's overflow:hidden handle the curve */
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        font-family: 'Poppins', sans-serif;
        font-weight: 500;
        font-size: 14px;
        letter-spacing: 0.3px;
        transition: all 0.2s ease;
        flex-shrink: 0;
        height: auto; /* Stretch to fill parent height */
        box-shadow: -1px 0 3px rgba(0, 0, 0, 0.05); /* Very subtle separation */
        `;

            // Create resize handle
            const resizeHandle = document.createElement("div");
            resizeHandle.style.cssText = `
        position: absolute;
        top: 0;
        left: 0;
        width: 12px;
        height: 12px;
        background-color: rgb(60, 84, 114);
        cursor: nw-resize;
        border-radius: 12px 0 12px 0;
        opacity: 0.8;
        `;

            // Add custom scrollbar styles and Prism theme overrides
            const scrollbarStyles = document.createElement("style");
            scrollbarStyles.innerHTML = `
        ${window._chatSyntaxHighlightCSS || ''}
        
        ::-webkit-scrollbar {
            width: 6px;
            height: 6px;
        }

        #chat-overlay ::-webkit-scrollbar-thumb {
            background-color: rgba(0, 0, 0, 0.2);
            border-radius: 3px;
            transition: background-color 0.2s ease;
        }

        #chat-overlay ::-webkit-scrollbar-thumb:hover {
            background-color: rgba(0, 0, 0, 0.3);
        }

        #chat-overlay ::-webkit-scrollbar-track {
            background-color: transparent;
        }

        #chat-overlay [contenteditable]:empty:before {
            content: attr(placeholder);
            color: rgba(0, 0, 0, 0.4);
            font-weight: 300;
        }

        /* Prism theme customizations for chat overlay */
        #chat-overlay pre[class*="language-"] {
            background: #f8f9fa !important;
            border: 1px solid #e1e4e8 !important;
            border-radius: 6px !important;
            margin: 15px 0 !important;
            padding: 12px !important;
            overflow-x: auto !important;
        }

        #chat-overlay code[class*="language-"] {
            background: transparent !important;
            font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', Menlo, monospace !important;
            font-size: 13px !important;
            line-height: 1.4 !important;
            color: #24292e !important;
        }

        /* Token colors for better readability */
        #chat-overlay .token.comment,
        #chat-overlay .token.prolog,
        #chat-overlay .token.doctype,
        #chat-overlay .token.cdata {
            color: #6a737d !important;
        }

        #chat-overlay .token.punctuation {
            color: #24292e !important;
        }

        #chat-overlay .token.property,
        #chat-overlay .token.tag,
        #chat-overlay .token.boolean,
        #chat-overlay .token.number,
        #chat-overlay .token.constant,
        #chat-overlay .token.symbol,
        #chat-overlay .token.deleted {
            color: #005cc5 !important;
        }

        #chat-overlay .token.selector,
        #chat-overlay .token.attr-name,
        #chat-overlay .token.string,
        #chat-overlay .token.char,
        #chat-overlay .token.builtin,
        #chat-overlay .token.inserted {
            color: #032f62 !important;
        }

        #chat-overlay .token.operator,
        #chat-overlay .token.entity,
        #chat-overlay .token.url,
        #chat-overlay .language-css .token.string,
        #chat-overlay .style .token.string {
            color: #e36209 !important;
        }

        #chat-overlay .token.atrule,
        #chat-overlay .token.attr-value,
        #chat-overlay .token.keyword {
            color: #d73a49 !important;
        }

        #chat-overlay .token.function,
        #chat-overlay .token.class-name {
            color: #6f42c1 !important;
        }

        #chat-overlay .token.regex,
        #chat-overlay .token.important,
        #chat-overlay .token.variable {
            color: #e36209 !important;
        }

        /* Remove any pseudo-elements that might cause overlay effects */
        #chat-overlay pre[class*="language-"]:before,
        #chat-overlay pre[class*="language-"]:after,
        #chat-overlay code[class*="language-"]:before,
        #chat-overlay code[class*="language-"]:after {
            display: none !important;
        }

        /* Ensure no box-shadow or other effects */
        #chat-overlay pre[class*="language-"] {
            box-shadow: none !important;
            text-shadow: none !important;
        }

        #chat-overlay code[class*="language-"] {
            box-shadow: none !important;
            text-shadow: none !important;
        }
        `;

            // Assemble the components
            buttonContainer.appendChild(inputField);
            buttonContainer.appendChild(sendButton);
            inputArea.appendChild(checkboxContainer);
            inputArea.appendChild(buttonContainer);
            // Create comprehensive CSS reset and styles for shadow DOM
            const shadowStyles = document.createElement('style');
            shadowStyles.textContent = `
                @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap');
                
                /* CSS Reset for Shadow DOM */
                * {
                    box-sizing: border-box;
                }
                
                #opacity-slider {
                    -webkit-appearance: none;
                    width: 100%;
                    height: 2px;
                    background: transparent;
                    outline: none;
                    margin: 0;
                    padding: 0;
                }
                
                #opacity-slider::-webkit-slider-thumb {
                    -webkit-appearance: none;
                    appearance: none;
                    width: 16px;
                    height: 8px;
                    border-radius: 4px;
                    background: rgb(60, 84, 114);
                    cursor: pointer;
                    transition: transform 0.2s, background 0.2s;
                }
                
                #opacity-slider::-webkit-slider-thumb:hover {
                    transform: scale(1.2);
                    background: rgb(80, 104, 134);
                }
                
                #opacity-slider::-moz-range-thumb {
                    width: 16px;
                    height: 8px;
                    border-radius: 4px;
                    background: rgb(60, 84, 114);
                    cursor: pointer;
                    border: none;
                    transition: transform 0.2s, background 0.2s;
                }
                
                #opacity-slider::-moz-range-thumb:hover {
                    transform: scale(1.2);
                    background: rgb(80, 104, 134);
                }
                
                /* Re-apply base styles needed */
                div, span, p {
                    display: block;
                    margin: 0;
                    padding: 0;
                }
                
                button {
                    cursor: pointer;
                    border: none;
                    background: none;
                    color: inherit;
                    font-family: 'Poppins', sans-serif;
                }
                
                input[type="checkbox"] {
                    cursor: pointer;
                    width: 16px;
                    height: 16px;
                }
                
                label {
                    cursor: pointer;
                    font-family: 'Poppins', sans-serif;
                }
                
                pre {
                    display: block;
                    margin: 0;
                    padding: 0;
                    font-family: monospace;
                    white-space: pre-wrap;
                }
                
                code {
                    font-family: monospace;
                }
                
                strong, b {
                    font-weight: bold;
                }
                
                em, i {
                    font-style: italic;
                }
                
                a {
                    color: #0066cc;
                    text-decoration: underline;
                    cursor: pointer;
                }
                
                ul, ol {
                    display: block;
                    margin: 10px 0;
                    padding-left: 20px;
                }
                
                li {
                    display: list-item;
                    margin: 5px 0;
                }
                
                p {
                    margin: 10px 0;
                    line-height: 1.5;
                }
                
                h1, h2, h3, h4, h5, h6 {
                    font-weight: bold;
                    margin: 15px 0 10px 0;
                    line-height: 1.3;
                }
                
                h1 { font-size: 2em; }
                h2 { font-size: 1.5em; }
                h3 { font-size: 1.3em; }
                h4 { font-size: 1.1em; }
                h5 { font-size: 1em; }
                h6 { font-size: 0.9em; }
                
                ${scrollbarStyles.innerHTML}
            `;
            
            // Assemble the components in shadow DOM
            shadowRoot.appendChild(shadowStyles);
            overlay.appendChild(header);
            overlay.appendChild(sliderContainer);
            overlay.appendChild(messagesContainer);
            overlay.appendChild(inputArea);
            overlay.appendChild(resizeHandle);
            shadowRoot.appendChild(overlay);
            document.body.appendChild(shadowHost);
            
            // Store shadow root reference for later access
            shadowHost._shadowRoot = shadowRoot;

            // Add placeholder behavior after element is in DOM
            inputField.addEventListener('focus', function() {
                if (this.textContent.trim() === '') {
                    this.setAttribute('data-placeholder', 'Type a message...');
                }
            });

            inputField.addEventListener('blur', function() {
                if (this.textContent.trim() === '') {
                    this.removeAttribute('data-placeholder');
                }
            });

            // Add hover effect to send button
            sendButton.addEventListener('mouseenter', () => {
                sendButton.style.transform = 'translateY(-1px)';
                sendButton.style.boxShadow = '0 4px 8px rgba(60, 84, 114, 0.3)';
            });

            sendButton.addEventListener('mouseleave', () => {
                sendButton.style.transform = 'translateY(0)';
                sendButton.style.boxShadow = '0 2px 4px rgba(60, 84, 114, 0.2)';
            });

            // Add event listeners for dragging
            header.addEventListener("mousedown", (e) => {
                isDragging = true;
                dragOffsetX = e.clientX - overlay.getBoundingClientRect().left;
                dragOffsetY = e.clientY - overlay.getBoundingClientRect().top;
            });

            // Add event listeners for stealth-mode
            // Get the initial state from storage
            chrome.storage.local.get(['stealth', 'stealthOpacity'], function(result) {
                // Initialize stealth mode based on storage
                let stealthModeEnabled = result.stealth === true;
                let currentOpacity = result.stealthOpacity || (stealthModeEnabled ? 15 : 100);
                
                const slider = shadowRoot.querySelector("#opacity-slider");
                if (slider) {
                    slider.value = stealthModeEnabled ? currentOpacity : 100;
                    
                    if (stealthModeEnabled) {
                        overlay.style.opacity = currentOpacity / 100;
                    } else {
                        overlay.style.opacity = "1";
                    }
                    
                    slider.addEventListener("input", (e) => {
                        const val = parseInt(e.target.value);
                        overlay.style.opacity = val / 100;
                    });
                    
                    slider.addEventListener("change", (e) => {
                        const val = parseInt(e.target.value);
                        const isStealth = val < 100;
                        
                        // Only send notification if stealth mode STATE changed
                        if (isStealth !== stealthModeEnabled) {
                            stealthModeEnabled = isStealth;
                            const chatButton = getChatButton();
                            if (chatButton) {
                                chatButton.style.opacity = isStealth ? "0" : "1";
                            }
                            
                            if (isStealth) {
                                chrome.runtime.sendMessage({
                                    action: 'showStealthToast',
                                    message: `Hover over the area where the chat icon is located \nor press ${window.isMac ? 'Option+C' : 'Alt+C'} to access [Chatbot opacity reduced]`,
                                    stealthEnabled: true
                                });
                            } else {
                                chrome.runtime.sendMessage({
                                    action: 'showStealthToast',
                                    message: 'Chat icon is now visible',
                                    stealthEnabled: false
                                });
                            }
                        }
                        
                        chrome.storage.local.set({ 
                            stealth: isStealth,
                            stealthOpacity: val
                        });
                    });
                }
                
                // Listen for storage changes to update stealth mode state across all tabs
                chrome.storage.onChanged.addListener((changes, namespace) => {
                    if (namespace === 'local' && slider) {
                        if (changes.stealthOpacity) {
                            currentOpacity = changes.stealthOpacity.newValue;
                            if (stealthModeEnabled) {
                                slider.value = currentOpacity;
                                if (overlay) overlay.style.opacity = currentOpacity / 100;
                            }
                        }
                        
                        if (changes.stealth) {
                            const newStealthMode = changes.stealth.newValue === true;
                            stealthModeEnabled = newStealthMode;
                            
                            if (newStealthMode) {
                                slider.value = currentOpacity;
                                if (overlay) overlay.style.opacity = currentOpacity / 100;
                            } else {
                                slider.value = 100;
                                if (overlay) overlay.style.opacity = "1";
                            }
                            
                            // Update chat button visibility
                            const chatButton = getChatButton();
                            if (chatButton) {
                                chatButton.style.opacity = newStealthMode ? "0" : "1";
                                chatButton.style.pointerEvents = "auto";
                            }
                        }
                    }
                });
            });

            // Add event listeners for resizing
            // Add minimum size constants at the top with the other state variables
            const MIN_WIDTH = 250; // Minimum width in pixels
            const MIN_HEIGHT = 200; // Minimum height in pixels
            const MAX_WIDTH = window.innerWidth - 40; // Maximum width (leaving 20px padding on each side)
            const MAX_HEIGHT = window.innerHeight - 40; // Maximum height (leaving 20px padding on each side)

            // Replace the resize event listener section with this updated version
            resizeHandle.addEventListener("mousedown", (e) => {
                isResizing = true;
                resizeStartX = e.clientX;
                resizeStartY = e.clientY;
                initialWidth = overlay.offsetWidth;
                initialHeight = overlay.offsetHeight;
                e.stopPropagation(); // Prevent dragging when resizing
            });

            resizeHandle.addEventListener("mouseenter", () => {
                resizeHandle.style.opacity = "1";
            });

            resizeHandle.addEventListener("mouseleave", () => {
                resizeHandle.style.opacity = "0.8";
            });

            // Update the mousemove event listener to include size constraints
            // This should be outside the createChatOverlay function as it's document level

            // Add window resize handler to keep overlay within bounds
            window.addEventListener('resize', () => {
                const overlay = getShadowElement('chat-overlay');
                if (overlay) {
                    const rect = overlay.getBoundingClientRect();

                    // Update maximum constraints
                    const newMaxWidth = window.innerWidth - 40;
                    const newMaxHeight = window.innerHeight - 40;

                    // Adjust size if necessary
                    if (rect.width > newMaxWidth) {
                        overlay.style.width = newMaxWidth + 'px';
                    }
                    if (rect.height > newMaxHeight) {
                        overlay.style.height = newMaxHeight + 'px';
                    }

                    // Keep overlay within viewport
                    if (rect.right > window.innerWidth) {
                        overlay.style.left = (window.innerWidth - rect.width) + "px";
                    }
                    if (rect.bottom > window.innerHeight) {
                        overlay.style.top = (window.innerHeight - rect.height) + "px";
                    }
                }
            });

            // Add button event listeners
            const closeButton = header.querySelector("#close-chat");
            if (closeButton) {
                closeButton.addEventListener("click", () => {
                    isOverlayVisible = false;
                    overlay.style.display = "none";
                });
            }

            const clearChatButton = header.querySelector("#clear-chat");
            if (clearChatButton) {
                clearChatButton.addEventListener("click", () => {
                    clearChatHistoryAndUI('manual');
                });
            }

            // Handle message sending
            sendButton.addEventListener("click", async () => {
                const message = inputField.innerText.trim();
                if (message) {
                    try {
                        // Clear any error state before sending new message
                        clearErrorState();
                        
                        // Prepare the final message to send
                        let finalMessage = message;
                        
                        // If "Chat about question" is enabled, prepend the question
                        if (chatAboutQuestionEnabled && extractedQuestion) {
                            finalMessage = `Context: Below is the question I'm working on:\n\n${extractedQuestion}\n\n---\n\nMy Question: ${message}`;
                            console.log('Sending message with question context');
                        }
                        
                        chatHistory.push({
                            role: "user",
                            content: message
                        });

                        addMessageToChat(message, "user");
                        inputField.innerText = "";

                        // Add enhanced loading indicator
                        const loadingDiv = addLoadingIndicator();
                        messagesContainer.appendChild(loadingDiv);

                        // Send message and wait for response with timeout
                        const response = await new Promise((resolve, reject) => {
                            let timeoutId;
                            let resolved = false;
                            
                            // Set up timeout (30 seconds)
                            timeoutId = setTimeout(() => {
                                if (!resolved) {
                                    resolved = true;
                                    reject(new Error('Request timed out. Please try again.'));
                                }
                            }, 30000);
                            
                            // Listen for response
                            const messageListener = (message) => {
                                if (message.action === "updateChatHistory" && !resolved) {
                                    resolved = true;
                                    clearTimeout(timeoutId);
                                    chrome.runtime.onMessage.removeListener(messageListener);
                                    resolve(message);
                                }
                            };
                            
                            chrome.runtime.onMessage.addListener(messageListener);
                            
                            // Send the message (with question context if enabled)
                            // Create valid conversation context (filters errors and ensures proper role flow)
                            const validContext = createValidContext(chatHistory);
                            chrome.runtime.sendMessage({
                                action: "processChatMessage",
                                message: finalMessage, // Send the final message with or without question context
                                context: validContext
                            }).catch((error) => {
                                if (!resolved) {
                                    resolved = true;
                                    clearTimeout(timeoutId);
                                    chrome.runtime.onMessage.removeListener(messageListener);
                                    reject(error);
                                }
                            });
                        });
                        
                        // Remove loading indicator
                        const loadingMessage = getShadowElement("loading-message");
                        if (loadingMessage) {
                            loadingMessage.remove();
                        }
                        
                        // The response will be handled by the runtime message listener
                        // No need to add the message here as it will be added via "updateChatHistory"
                    }
                    catch (error) {
                        console.error("Error sending message:", error);
                        
                        // Remove loading indicator if it exists
                        const loadingMessage = getShadowElement("loading-message");
                        if (loadingMessage) {
                            loadingMessage.remove();
                        }
                        
                        // Handle different types of errors with appropriate messages
                        let errorMessage = "I encountered an error processing your message. Please try again.";
                        let isRateLimitError = false;
                        
                        if (error.message) {
                            if (error.message.includes('timeout') || error.message.includes('timed out')) {
                                errorMessage = "The request timed out. The service might be experiencing high load. Please try again in a moment.";
                            } else if (error.message.includes('rate limit') || error.message.includes('Daily request limit')) {
                                errorMessage = "You've reached your daily chat limit. Please try again tomorrow.";
                                isRateLimitError = true;
                            } else if (error.message.includes('Network') || error.message.includes('connection')) {
                                errorMessage = "Unable to connect to the chat service. Please check your internet connection and try again.";
                            } else if (error.message.includes('login') || error.message.includes('authentication')) {
                                errorMessage = "Please log in to use the chat feature. Click the extension icon to log in.";
                            } else {
                                // Use the error message if it's user-friendly
                                errorMessage = error.message;
                            }
                        }
                        
                        // Add error message to chat with special styling
                        addErrorMessageToChat(errorMessage, isRateLimitError);
                    }
                }
            });
            
            return overlay;
        }

        // Add notification message function
        function addNotificationMessage(message) {
            const messagesContainer = getShadowElement("chat-messages");
            if (!messagesContainer) return;
            
            const messageDiv = document.createElement("div");
            messageDiv.textContent = message;
            messageDiv.style.cssText = `
                margin: 12px auto;
                padding: 6px 12px;
                background-color: rgba(60, 84, 114, 0.08);
                border-radius: 12px;
                color: rgb(60, 84, 114);
                font-size: 11px;
                text-align: center;
                font-family: 'Poppins', sans-serif;
                font-weight: 500;
                letter-spacing: 0.2px;
                width: fit-content;
                box-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
            `;
            messagesContainer.appendChild(messageDiv);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }

        // Create the chat button
        function createChatButton() {
            // Check if shadow host for button already exists
            let buttonShadowHost = document.getElementById("chat-button-shadow-host");
            if (buttonShadowHost) {
                return buttonShadowHost.shadowRoot.querySelector("#chat-button");
            }

            // Create shadow host element for button
            buttonShadowHost = document.createElement("div");
            buttonShadowHost.id = "chat-button-shadow-host";
            buttonShadowHost.style.cssText = `
                position: fixed;
                bottom: 0;
                right: 0;
                z-index: 2147483647;
                pointer-events: none;
            `;

            // Attach shadow root
            const buttonShadowRoot = buttonShadowHost.attachShadow({ mode: 'open' });

            // Create comprehensive CSS reset for button shadow DOM
            const buttonStyles = document.createElement('style');
            buttonStyles.textContent = `
                @import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600&display=swap');
                
                /* CSS Reset for Button Shadow DOM */
                * {
                    box-sizing: border-box;
                }
                
                button {
                    display: block;
                    cursor: pointer;
                    border: none;
                    padding: 0;
                    margin: 0;
                    background: none;
                    outline: none;
                    font-family: 'Poppins', sans-serif;
                    position: relative;
                }
                
                .chat-icon-span {
                    display: block;
                    position: absolute;
                    top: 14px;
                    right: 10px;
                    left: 9px;
                    bottom: 10px;
                    width: 35px;
                    height: 30px;
                    background-image: ${CHAT_ICON_SVG_URL};
                    background-position: 50% 50%;
                    background-repeat: no-repeat;
                    background-size: contain;
                    pointer-events: none;
                    user-select: none;
                    z-index: 2;
                }
            `;

            const button = document.createElement("button");
            button.id = "chat-button";
            button.style.cssText = `
                display: block;
                position: fixed;
                bottom: 20px;
                right: 20px;
                width: 54px;
                height: 54px;
                background-color: rgb(60, 84, 114);
                border: none;
                border-radius: 100%;
                color: #fff;
                cursor: pointer;
                z-index: 2147483647;
                box-shadow: rgba(0, 0, 0, 0.05) 0px 4px 10px 0px;
                transition: background-color 0.1s linear, outline 0.15s ease-in-out, transform 0.15s ease-in-out;
                pointer-events: auto;
                padding: 0;
                margin: 0;
                outline: solid 0px rgba(0, 0, 0, 0);
                user-select: none;
            `;

            // Chat bubble icon as background-image on child span (matching Crisp style)
            const iconSpan = document.createElement("span");
            iconSpan.className = "chat-icon-span";
            button.appendChild(iconSpan);

            // Assemble button in shadow DOM
            buttonShadowRoot.appendChild(buttonStyles);
            buttonShadowRoot.appendChild(button);
            document.body.appendChild(buttonShadowHost);

            // Add hover effects for stealth mode
            button.addEventListener('mouseenter', () => {
                chrome.storage.local.get(['stealth'], function(result) {
                    const stealthModeEnabled = result.stealth === true;
                    if (stealthModeEnabled) {
                        button.style.opacity = "0.3"; // Show with reduced opacity on hover in stealth mode
                    }
                });
            });

            button.addEventListener('mouseleave', () => {
                chrome.storage.local.get(['stealth'], function(result) {
                    const stealthModeEnabled = result.stealth === true;
                    if (stealthModeEnabled) {
                        button.style.opacity = "0"; // Hide again when not hovering in stealth mode
                    }
                });
            });

            let dragStartX, dragStartY, initialX, initialY;
            let isDraggingButton = false;
            let hasMoved = false;

            // Handle button dragging with improved click detection
            button.addEventListener("mousedown", (e) => {
                isDraggingButton = true;
                hasMoved = false;
                dragStartX = e.clientX;
                dragStartY = e.clientY;
                initialX = button.getBoundingClientRect().left;
                initialY = button.getBoundingClientRect().top;
            });

            document.addEventListener("mousemove", (e) => {
                if (isDraggingButton) {
                    const deltaX = e.clientX - dragStartX;
                    const deltaY = e.clientY - dragStartY;

                    // Check if the button has moved more than 5 pixels in any direction
                    if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
                        hasMoved = true;
                    }

                    const newX = initialX + deltaX;
                    const newY = initialY + deltaY;

                    // Keep button within viewport bounds
                    const maxX = window.innerWidth - button.offsetWidth;
                    const maxY = window.innerHeight - button.offsetHeight;

                    button.style.left = Math.min(Math.max(0, newX), maxX) + "px";
                    button.style.top = Math.min(Math.max(0, newY), maxY) + "px";
                    button.style.bottom = "auto";
                    button.style.right = "auto";
                }
            });

            document.addEventListener("mouseup", () => {
                if (isDraggingButton) {
                    isDraggingButton = false;

                    // Only trigger click if the button hasn't moved
                    if (!hasMoved) {
                        toggleChatOverlay();
                    }
                }
            });

            // Remove double click handler and use single click with movement detection
            button.addEventListener("click", (e) => {
                // Click handling is now managed in the mouseup event
                e.preventDefault();
            });
            
            return button;
        }

        // Helper function to detect programming language from code content
        function detectLanguage(code) {
            const codeText = code.toLowerCase().trim();
            
            // TypeScript detection (check before JavaScript)
            if (codeText.includes('interface ') || codeText.includes('type ') || codeText.includes(': string') ||
                codeText.includes(': number') || codeText.includes(': boolean') || codeText.includes('export interface') ||
                codeText.includes('import type') || codeText.includes('as const') || codeText.includes('enum ')) {
                return 'typescript';
            }
            
            // JSX/TSX detection
            if (codeText.includes('<') && codeText.includes('>') && 
                (codeText.includes('return (') || codeText.includes('jsx') || codeText.includes('tsx') ||
                 codeText.includes('component') || codeText.includes('props'))) {
                return codeText.includes(': ') ? 'tsx' : 'jsx';
            }
            
            // JavaScript detection
            if (codeText.includes('function') || codeText.includes('const ') || codeText.includes('let ') ||
                codeText.includes('var ') || codeText.includes('=>') || codeText.includes('console.log') ||
                codeText.includes('document.') || codeText.includes('window.') || codeText.includes('require(') ||
                codeText.includes('import ') || codeText.includes('export ')) {
                return 'javascript';
            }
            
            // Python detection
            if (codeText.includes('def ') || codeText.includes('import ') || codeText.includes('from ') ||
                codeText.includes('print(') || codeText.includes('if __name__') || codeText.includes('self.') ||
                codeText.includes('class ') || codeText.includes('elif ') || codeText.includes('range(') ||
                codeText.includes('lambda ') || codeText.includes('yield ')) {
                return 'python';
            }
            
            // Java detection
            if (codeText.includes('public class') || codeText.includes('private ') || codeText.includes('public ') ||
                codeText.includes('import java') || codeText.includes('system.out.println') || codeText.includes('string ') ||
                codeText.includes('void main') || codeText.includes('extends ') || codeText.includes('implements ') ||
                codeText.includes('@override') || codeText.includes('new ')) {
                return 'java';
            }
            
            // C# detection
            if (codeText.includes('using system') || codeText.includes('namespace ') || codeText.includes('public static void main') ||
                codeText.includes('console.writeline') || codeText.includes('[attribute]') || codeText.includes('var ')) {
                return 'csharp';
            }
            
            // C++ detection (check before C)
            if (codeText.includes('std::') || codeText.includes('cout <<') || codeText.includes('cin >>') ||
                codeText.includes('#include <iostream>') || codeText.includes('using namespace std') ||
                codeText.includes('class ') || codeText.includes('template<')) {
                return 'cpp';
            }
            
            // C detection
            if (codeText.includes('#include') || codeText.includes('printf(') || codeText.includes('scanf(') ||
                codeText.includes('int main') || codeText.includes('malloc(') || codeText.includes('free(') ||
                codeText.includes('sizeof(')) {
                return 'c';
            }
            
            // PHP detection
            if (codeText.includes('<?php') || codeText.includes('echo ') || codeText.includes('$') ||
                codeText.includes('function ') || codeText.includes('class ') || codeText.includes('->')) {
                return 'php';
            }
            
            // Ruby detection
            if (codeText.includes('def ') || codeText.includes('end') || codeText.includes('puts ') ||
                codeText.includes('require ') || codeText.includes('class ') || codeText.includes('@')) {
                return 'ruby';
            }
            
            // Go detection
            if (codeText.includes('package ') || codeText.includes('func ') || codeText.includes('import (') ||
                codeText.includes('fmt.println') || codeText.includes('go ') || codeText.includes('defer ')) {
                return 'go';
            }
            
            // Rust detection
            if (codeText.includes('fn ') || codeText.includes('let mut') || codeText.includes('println!') ||
                codeText.includes('use ') || codeText.includes('struct ') || codeText.includes('impl ')) {
                return 'rust';
            }
            
            // Swift detection
            if (codeText.includes('import swift') || codeText.includes('var ') || codeText.includes('let ') ||
                codeText.includes('func ') || codeText.includes('class ') || codeText.includes('print(')) {
                return 'swift';
            }
            
            // Kotlin detection
            if (codeText.includes('fun ') || codeText.includes('val ') || codeText.includes('var ') ||
                codeText.includes('class ') || codeText.includes('println(') || codeText.includes('import kotlin')) {
                return 'kotlin';
            }
            
            // HTML detection
            if (codeText.includes('<!doctype') || codeText.includes('<html') || codeText.includes('<head') ||
                codeText.includes('<body') || codeText.includes('<div') || codeText.includes('<span') ||
                codeText.includes('<script') || codeText.includes('<style')) {
                return 'html';
            }
            
            // CSS/SCSS detection
            if (codeText.includes('{') && codeText.includes('}') && (codeText.includes(':') && codeText.includes(';'))) {
                if (codeText.includes('$') || codeText.includes('@mixin') || codeText.includes('@include')) {
                    return 'scss';
                }
                return 'css';
            }
            
            // SQL detection
            if (codeText.includes('select ') || codeText.includes('from ') || codeText.includes('where ') ||
                codeText.includes('insert ') || codeText.includes('update ') || codeText.includes('delete ') ||
                codeText.includes('create table') || codeText.includes('alter table') || codeText.includes('drop table')) {
                return 'sql';
            }
            
            // JSON detection
            if ((codeText.trim().startsWith('{') && codeText.trim().endsWith('}')) ||
                (codeText.trim().startsWith('[') && codeText.trim().endsWith(']'))) {
                try {
                    JSON.parse(code);
                    return 'json';
                } catch (e) {
                    // Not valid JSON, continue with other detections
                }
            }
            
            // YAML detection
            if (codeText.includes('---') || (codeText.includes(':') && !codeText.includes(';') && !codeText.includes('{')) ||
                codeText.includes('- ') || codeText.includes('version:') || codeText.includes('name:')) {
                return 'yaml';
            }
            
            // XML detection
            if (codeText.includes('<?xml') || codeText.includes('<') && codeText.includes('/>') ||
                (codeText.includes('<') && codeText.includes('>') && !codeText.includes('function'))) {
                return 'xml';
            }
            
            // Bash/Shell detection
            if (codeText.includes('#!/bin/bash') || codeText.includes('#!/bin/sh') || 
                codeText.includes('echo ') || codeText.includes('grep ') || codeText.includes('awk ') ||
                codeText.includes('sed ') || codeText.includes('chmod ') || codeText.includes('sudo ') ||
                codeText.includes('ls ') || codeText.includes('cd ') || codeText.includes('mkdir ')) {
                return 'bash';
            }
            
            // Default fallback
            return 'javascript';
        }

        // Render content (for initial or streaming updates)
        function renderChatContent(messageContainer, content) {
            try {
                // Convert markdown to HTML using showdown library
                if (typeof showdown !== 'undefined') {
                    // Initialize markdown converter if not already done
                    if (!markdownConverter) {
                        markdownConverter = new showdown.Converter();
                    }
                    const htmlContent = markdownConverter.makeHtml(content);
                    
                    // Clear and set new content
                    messageContainer.innerHTML = "";
                    const contentContainer = document.createElement("div");
                    contentContainer.innerHTML = htmlContent;
                    
                    // Style code blocks and add copy functionality
                    contentContainer.querySelectorAll("pre code").forEach(codeBlock => {
                        // Detect language from class name first (from markdown ```language)
                        let language = '';
                        const classNames = codeBlock.className.split(' ');
                        for (const className of classNames) {
                            if (className.startsWith('language-')) {
                                language = className.replace('language-', '');
                                break;
                            }
                        }
                        
                        // If no language specified in markdown, use auto-detection
                        if (!language || language === '') {
                            language = detectLanguage(codeBlock.textContent);
                        }
                        
                        // Set the language class for Prism (ensure it's set even if detected)
                        codeBlock.className = `language-${language}`;
                        
                        // Apply SimplePrism highlighting if available
                        if (typeof SimplePrism !== 'undefined') {
                            try {
                                SimplePrism.highlightElement(codeBlock);
                            } catch (error) {
                                console.warn('Failed to highlight code block:', error);
                                // Continue without highlighting
                            }
                        }
                        
                        // Style the parent <pre> element to ensure clean background
                        const preElement = codeBlock.parentNode;
                        if (preElement && preElement.tagName === 'PRE') {
                            preElement.style.cssText = `
                                background: #f8f9fa !important;
                                border: 1px solid #e1e4e8 !important;
                                border-radius: 6px !important;
                                margin: 15px 0 !important;
                                padding: 0 !important;
                                overflow: visible !important;
                                position: relative !important;
                            `;
                        }
                        
                        // Style the code block (let Prism handle syntax colors)
                        codeBlock.style.cssText = `
                            background: transparent !important;
                            border: none !important;
                            border-radius: 0 !important;
                            padding: 12px !important;
                            display: block !important;
                            margin: 0 !important;
                            overflow-x: auto !important;
                            white-space: pre !important;
                            font-family: 'SFMono-Regular', 'Consolas', 'Liberation Mono', Menlo, monospace !important;
                            font-size: 13px !important;
                            line-height: 1.4 !important;
                        `;
                
                        // Create a wrapper for the code block to handle hover events
                        const codeWrapper = document.createElement("div");
                        codeWrapper.style.cssText = `
                            position: relative;
                            background: transparent;
                            border: none;
                            margin: 0;
                            padding: 0;
                        `;
                        
                        // Move the code block into the wrapper
                        codeBlock.parentNode.insertBefore(codeWrapper, codeBlock);
                        codeWrapper.appendChild(codeBlock);
                
                        // Create copy button with new styling
                        const copyButton = document.createElement("button");
                        copyButton.innerText = "Copy";
                        copyButton.style.cssText = `
                            position: absolute;
                            right: 8px;
                            top: 8px;
                            background-color: rgb(60, 84, 114);
                            color: #fff;
                            border: none;
                            border-radius: 6px;
                            cursor: pointer;
                            padding: 6px 12px;
                            font-size: 12px;
                            font-family: 'Poppins', sans-serif;
                            opacity: 0;
                            transition: opacity 0.2s ease;
                            z-index: 10;
                        `;
                
                        // Add hover effects
                        codeWrapper.addEventListener('mouseenter', () => {
                            copyButton.style.opacity = "1";
                        });
                
                        codeWrapper.addEventListener('mouseleave', () => {
                            copyButton.style.opacity = "0";
                        });
                
                        // Add copy functionality
                        copyButton.addEventListener("click", () => {
                            navigator.clipboard.writeText(codeBlock.innerText)
                                .then(() => {
                                    copyButton.innerText = "Copied";
                                    setTimeout(() => {
                                        copyButton.innerText = "Copy";
                                    }, 5000);
                                })
                                .catch(error => {
                                    console.error("Failed to copy: ", error);
                                });
                        });
                
                        // Add the copy button to the wrapper
                        codeWrapper.appendChild(copyButton);
                    });
                    
                    // Add the content to the message container
                    messageContainer.appendChild(contentContainer);
                } else {
                    // Fallback for when showdown is not available
                    messageContainer.textContent = content;
                }
            } catch (error) {
                console.error('Error rendering chat content:', error);
                // Fallback to plain text
                messageContainer.textContent = content;
            }
        }

        // Add message to chat
        function addMessageToChat(message, role) {
            // Get the chat messages container
            const chatMessagesContainer = getShadowElement("chat-messages");
            if (!chatMessagesContainer) return;
            
            // Create a new message container
            const messageContainer = document.createElement("div");
            messageContainer.style.cssText = `
                margin-bottom: 12px;
                padding: 12px 16px;
                border-radius: 16px;
                max-width: 85%;
                width: fit-content;
                word-wrap: break-word;
                font-size: 14px;
                line-height: 1.5;
                box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
            `;
        
            // Style the message differently based on the role (user or assistant)
            if (role === "user") {
                messageContainer.style.backgroundColor = "rgb(60, 84, 114)";  // User messages use blue
                messageContainer.style.color = "#ffffff";
                messageContainer.style.alignSelf = "flex-end";
                messageContainer.style.borderBottomRightRadius = "4px";
            } else {
                messageContainer.style.backgroundColor = "#ffffff";  // Assistant messages use white/subtle grey
                messageContainer.style.color = "#333333";
                messageContainer.style.alignSelf = "flex-start";
                messageContainer.style.border = "1px solid #eaeaea";
                messageContainer.style.borderBottomLeftRadius = "4px";
            }
            
            // Add the message to the chat
            chatMessagesContainer.appendChild(messageContainer);
            
            // Render initial content
            renderChatContent(messageContainer, message);
            
            // Scroll to bottom
            chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight;
            
            return messageContainer;
        }

        // Function to clear error state and remove error messages from chat history
        function clearErrorState() {
            // Remove error messages from chat history (in case any slipped through)
            chatHistory = chatHistory.filter(msg => msg.role !== "error");
            
            // Optionally clear error messages from UI after successful response
            // This helps provide a cleaner experience when the user resolves their issue
            // We keep them for now to maintain transparency, but you could uncomment below to remove them:
            /*
            const chatMessagesContainer = document.getElementById("chat-messages");
            if (chatMessagesContainer) {
                const errorMessages = chatMessagesContainer.querySelectorAll('[style*="f8d7da"], [style*="fff3cd"]');
                errorMessages.forEach(errorMsg => errorMsg.remove());
            }
            */
        }

        // Function to create valid conversation context for the API
        function createValidContext(chatHistory) {
            // First filter out error messages
            let filteredHistory = chatHistory.filter(msg => msg.role !== "error");
            
            // Ensure valid conversation flow (alternating user/assistant roles)
            let validContext = [];
            let lastRole = null;
            
            for (const message of filteredHistory) {
                // Skip consecutive messages with the same role (except the first)
                if (lastRole === message.role) {
                    // If we have consecutive user messages, skip the earlier one
                    // If we have consecutive assistant messages, skip the earlier one
                    if (validContext.length > 0) {
                        validContext.pop(); // Remove the previous message of the same role
                    }
                }
                
                validContext.push(message);
                lastRole = message.role;
            }
            
            // Ensure the conversation doesn't end with an assistant message if we're about to add a user message
            // The API expects user -> assistant -> user flow
            if (validContext.length > 0 && validContext[validContext.length - 1].role === "assistant") {
                // This is fine, we can add a user message next
            } else if (validContext.length > 0 && validContext[validContext.length - 1].role === "user") {
                // We have a trailing user message, which is fine since we're about to send another user message
                // But we should remove the trailing user message to avoid consecutive user messages
                validContext.pop();
            }
            
            return validContext;
        }

        // Add error message to chat with special styling
        function addErrorMessageToChat(errorMessage, isRateLimitError = false) {
            const chatMessagesContainer = getShadowElement("chat-messages");
            if (!chatMessagesContainer) return;
            
            // Create error message container
            const errorContainer = document.createElement("div");
            errorContainer.style.cssText = `
                margin-bottom: 12px;
                padding: 12px 16px;
                border-radius: 8px;
                max-width: 95%;
                word-wrap: break-word;
                background-color: ${isRateLimitError ? '#fff3cd' : '#f8d7da'};
                border: 1px solid ${isRateLimitError ? '#ffeaa7' : '#f5c6cb'};
                color: ${isRateLimitError ? '#856404' : '#721c24'};
                align-self: flex-start;
                font-family: 'Poppins', sans-serif;
                position: relative;
            `;
            
            // Add error icon and message
            const errorContent = document.createElement("div");
            errorContent.style.cssText = `
                display: flex;
                align-items: flex-start;
                gap: 10px;
            `;
            
            // Error icon
            const errorIcon = document.createElement("div");
            errorIcon.innerHTML = isRateLimitError ? 
                `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>` :
                `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="15" y1="9" x2="9" y2="15"></line><line x1="9" y1="9" x2="15" y2="15"></line></svg>`;
            errorIcon.style.cssText = `
                flex-shrink: 0;
                margin-top: 2px;
                opacity: 0.8;
            `;
            
            // Error text
            const errorText = document.createElement("div");
            errorText.style.cssText = `
                flex-grow: 1;
                font-size: 14px;
                line-height: 1.4;
            `;
            errorText.textContent = errorMessage;
            
            // Add retry suggestion for certain errors
            if (!isRateLimitError && !errorMessage.includes('log in')) {
                const retryText = document.createElement("div");
                retryText.style.cssText = `
                    margin-top: 8px;
                    font-size: 12px;
                    opacity: 0.8;
                    font-style: italic;
                `;
                retryText.textContent = "You can try sending your message again.";
                errorText.appendChild(retryText);
            }
            
            errorContent.appendChild(errorIcon);
            errorContent.appendChild(errorText);
            errorContainer.appendChild(errorContent);
            
            // Note: Don't add error messages to chatHistory to prevent them from being sent as context
            // This prevents error states from persisting across requests
            
            // Add to chat and scroll
            chatMessagesContainer.appendChild(errorContainer);
            chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight;
        }

        // Add this new loading indicator function
        function addLoadingIndicator() {
            const loadingDiv = document.createElement("div");
            loadingDiv.id = "loading-message";
            loadingDiv.style.cssText = `
        margin-bottom: 16px;
        padding: 14px 16px;
        border-radius: 14px;
        background-color: #fff;
        align-self: flex-start;
        border: 1px solid rgba(0, 0, 0, 0.08);
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
        display: flex;
        align-items: center;
        gap: 8px;
        font-family: 'Poppins', sans-serif;
        font-size: 14px;
        color: rgba(0, 0, 0, 0.6);
        `;

            // Add typing animation dots
            const dotsContainer = document.createElement("div");
            dotsContainer.style.cssText = `
        display: flex;
        gap: 4px;
        margin-left: 4px;
        `;

            for (let i = 0; i < 3; i++) {
                const dot = document.createElement("div");
                dot.style.cssText = `
            width: 6px;
            height: 6px;
            background-color: rgba(0, 0, 0, 0.4);
            border-radius: 50%;
            animation: typingAnimation 1.4s infinite;
            animation-delay: ${i * 0.2}s;
        `;
                dotsContainer.appendChild(dot);
            }

            loadingDiv.textContent = "Thinking";
            loadingDiv.appendChild(dotsContainer);

            // No need to add keyframes - they're already in shadow DOM styles

            return loadingDiv;
        }

        // Function to toggle chat overlay visibility
        function toggleChatOverlay() {
            isOverlayVisible = !isOverlayVisible;
            const shadowHost = document.getElementById("chat-overlay-shadow-host");
            let chatOverlay = shadowHost ? shadowHost.shadowRoot.querySelector("#chat-overlay") : null;

            if (!chatOverlay) {
                chatOverlay = createChatOverlay(); // Creates shadow host and returns overlay
            }

            if (chatOverlay) {
                chatOverlay.style.display = isOverlayVisible ? "flex" : "none";
                
                // Focus on input field when showing overlay
                if (isOverlayVisible) {
                    setTimeout(() => {
                        const inputField = getShadowRoot()?.querySelector('[contenteditable]');
                        if (inputField) {
                            inputField.focus();
                            
                            // Place cursor at the end of existing text
                            const range = document.createRange();
                            const sel = window.getSelection();
                            
                            // If there's content, move cursor to the end
                            if (inputField.childNodes.length > 0) {
                                range.setStart(inputField.childNodes[inputField.childNodes.length - 1], 
                                    inputField.childNodes[inputField.childNodes.length - 1].length || 0);
                            } else {
                                range.setStart(inputField, 0);
                            }
                            
                            range.collapse(true);
                            sel.removeAllRanges();
                            sel.addRange(range);
                        }
                    }, 100);
                }
            }
        }

        // Function to clear chat history and UI (reusable)
        function clearChatHistoryAndUI(reason = 'manual') {
            try {
                const messagesContainer = getShadowElement("chat-messages");
                if (messagesContainer) {
                    // Clear the chat history array
                    chatHistory = [];
                    
                    // Clear the UI
                    messagesContainer.innerHTML = "";
                    
                    // Clear any error state
                    clearErrorState();
                    
                    // Send message to background script to reset context
                    chrome.runtime.sendMessage({
                        action: "resetContext"
                    });
                    
                    // Add a notification message based on the reason
                    let notificationMessage = "Chat history cleared.";
                    if (reason === 'providerChange') {
                        notificationMessage = "Chat history cleared - switched to new AI provider.";
                    }
                    
                    addNotificationMessage(notificationMessage);
                    console.log(`Chat history cleared (${reason})`);
                }
            } catch (error) {
                console.error('Error clearing chat history:', error);
            }
        }

        // Function to detect and block clashing chat elements
        function blockClashingChatElements() {
            // List of class patterns to block (updated class names)
            const blockedClassPatterns = [
                'cc-1m2mf',     // Old class
                'cc-1qbp0',     // New duplicate chatbot icon
                'cc-1o31k',     // New duplicate chatbot icon child
                'cc-otlyh',     // New duplicate chatbot icon child
                'cc-11f3x',     // New duplicate chatbot icon child
                'cc-1v4wj'      // New duplicate chatbot icon child
            ];
            
            // Function to hide elements matching any of the blocked patterns
            function hideBlockedElements() {
                blockedClassPatterns.forEach(className => {
                    // Match elements with the exact class or classes containing this pattern
                    const selector = `[class*="${className}"]`;
                    const elements = document.querySelectorAll(selector);
                    elements.forEach(element => {
                        // Only hide if it's not part of our chat overlay
                        if (!element.closest('#chat-overlay')) {
                            element.style.display = 'none';
                        }
                    });
                });
            }
            
            // Add observer to continuously check for and block the element
            const observer = new MutationObserver((mutations) => {
                hideBlockedElements();
            });
            
            // Start observing document body for changes
            observer.observe(document.body, {
                childList: true,
                subtree: true
            });
            
            // Also try to block any existing elements immediately
            hideBlockedElements();
            
            // Add CSS to ensure elements with these classes are always hidden
            const styleElement = document.createElement('style');
            const cssRules = blockedClassPatterns.map(className => `
                [class*="${className}"]:not(#chat-overlay):not(#chat-overlay *) {
                    display: none !important;
                    visibility: hidden !important;
                    opacity: 0 !important;
                    pointer-events: none !important;
                }
            `).join('\n');
            
            styleElement.textContent = cssRules;
            document.head.appendChild(styleElement);
        }

        // Set up document-level event handlers
        document.addEventListener("mousemove", (e) => {
            const shadowHost = document.getElementById("chat-overlay-shadow-host");
            if (!shadowHost) return;
            const overlay = shadowHost.shadowRoot?.querySelector("#chat-overlay");
            if (!overlay) return;
            
            if (isDragging) {
                const newLeft = e.clientX - dragOffsetX;
                const newTop = e.clientY - dragOffsetY;

                // Prevent dragging outside viewport
                const maxX = window.innerWidth - overlay.offsetWidth;
                const maxY = window.innerHeight - overlay.offsetHeight;

                overlay.style.left = Math.min(Math.max(0, newLeft), maxX) + "px";
                overlay.style.top = Math.min(Math.max(0, newTop), maxY) + "px";
                overlay.style.bottom = "auto";
                overlay.style.right = "auto";
            }

            if (isResizing) {
                const resizeHandle = overlay.querySelector("div[style*='nw-resize']");
                if (!resizeHandle) return;
                
                const MIN_WIDTH = 250;
                const MIN_HEIGHT = 200;
                const MAX_WIDTH = window.innerWidth - 40;
                const MAX_HEIGHT = window.innerHeight - 40;
                
                const dx = resizeStartX - e.clientX;
                const dy = resizeStartY - e.clientY;

                const newWidth = Math.min(Math.max(MIN_WIDTH, initialWidth + dx), MAX_WIDTH);
                const newHeight = Math.min(Math.max(MIN_HEIGHT, initialHeight + dy), MAX_HEIGHT);

                const rect = overlay.getBoundingClientRect();
                const newLeft = rect.right - newWidth;
                const newTop = rect.bottom - newHeight;

                // Ensure the overlay stays within viewport bounds
                if (newLeft >= 0 && newTop >= 0) {
                    overlay.style.width = newWidth + "px";
                    overlay.style.height = newHeight + "px";
                    overlay.style.left = newLeft + "px";
                    overlay.style.top = newTop + "px";
                }
            }
        });

        // Handle mouse up for drag and resize
        document.addEventListener("mouseup", () => {
            isDragging = false;
            isResizing = false;
        });

        // Add global keyboard event listeners
        document.addEventListener("keydown", (e) => {
            // Use Alt (Option) on all platforms including Mac
            const modifierKey = e.altKey;

            // Toggle chat with Alt/Option + C
            // Use e.code to be layout-independent (Option modifies e.key on macOS)
            if (modifierKey && e.code === "KeyC") {
                e.preventDefault(); // Prevent default browser behavior
                toggleChatOverlay();
            }

            // Close chat with Escape
            if (e.key === "Escape" && isOverlayVisible) {
                isOverlayVisible = false;
                const overlay = getShadowElement("chat-overlay");
                if (overlay) {
                    overlay.style.display = "none";
                }
            }


        });

        // Initialize everything
        async function init() {
            try {
                // Try to load showdown and our inline prism highlighter
                await Promise.all([loadShowdown(), loadPrism()]);
                console.log("Showdown and SimplePrism libraries loaded successfully");
            } catch (error) {
                console.error('Failed to load libraries:', error);
                // Continue even if libraries fail to load
            }
            
            // Block clashing chat elements
            blockClashingChatElements();
            
            // Create the chat button
            const chatButton = createChatButton();

            // Get current stealth mode state
            chrome.storage.local.get(['stealth'], function(result) {
                const stealthModeEnabled = result.stealth === true;
                
                // Hide chat button if stealth mode is enabled
                if (stealthModeEnabled && chatButton) {
                    chatButton.style.opacity = "0"; // Use opacity instead of display none
                    chatButton.style.pointerEvents = "auto"; // Keep pointer events active
                }
                
                // Create the chat overlay initially but keep it hidden
                // This ensures Alt+C (Option+C on Mac) will work right from the start
                try {
                    const overlay = createChatOverlay();
                    
                    // Set overlay opacity based on stealth mode
                    if (stealthModeEnabled && overlay) {
                        overlay.style.opacity = "0.15";
                    }
                } catch (error) {
                    console.error('Error creating chat overlay:', error);
                }
            });
        }
        
        // Start the initialization
        init();

        // Add global storage change listener for stealth mode updates across tabs
        chrome.storage.onChanged.addListener((changes, namespace) => {
            if (namespace === 'local') {
                // Clear error state when database or authentication changes occur
                if (changes.accessToken || changes.refreshToken) {
                    clearErrorState();
                    console.log("Auth state changed, cleared chat error state");
                }
                
                if (changes.stealth) {
                    const newStealthMode = changes.stealth.newValue === true;
                
                    // Update chat button visibility globally
                    const chatButton = getChatButton();
                    if (chatButton) {
                        chatButton.style.opacity = newStealthMode ? "0" : "1";
                        chatButton.style.pointerEvents = "auto"; // Keep pointer events active in both states
                        
                        // Icon is set via backgroundImage on child span, no innerHTML reset needed
                    }
                    
                    // Update overlay opacity if it exists
                    const overlay = document.getElementById("chat-overlay");
                    if (overlay) {
                        overlay.style.opacity = newStealthMode ? "0.15" : "1";
                    }
                }
            }
        });

        // Listen for messages from Chrome runtime
        chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
            if (message.action === "updateChatHistory") {
                const {
                    role,
                    content,
                    isStreaming
                } = message;
                
                // First remove loading indicator if it exists
                const loadingMessage = getShadowElement("loading-message");
                if (loadingMessage) {
                    loadingMessage.remove();
                }
                
                // Handle error responses from the background script
                if (role === "error" || content.includes("error") || content.includes("failed")) {
                    // Determine if this is a rate limit error
                    const isRateLimitError = content.includes("limit") || content.includes("exceeded") || content.includes("tomorrow");
                    addErrorMessageToChat(content, isRateLimitError);
                } else if (role === "assistant") {
                    // Clear any existing error state on successful response
                    clearErrorState();
                    
                    if (isStreaming) {
                        if (!currentStreamingDiv) {
                            // Create a new assistant message container for streaming
                            currentStreamingDiv = addMessageToChat("", "assistant");
                        }
                        // Update the content incrementally
                        renderChatContent(currentStreamingDiv, content);
                        
                        // Scroll to bottom during streaming
                        const chatMessagesContainer = getShadowElement("chat-messages");
                        if (chatMessagesContainer) {
                            chatMessagesContainer.scrollTop = chatMessagesContainer.scrollHeight;
                        }
                    } else {
                        // Stream finished or single response
                        if (currentStreamingDiv) {
                            // Final update for existing stream
                            renderChatContent(currentStreamingDiv, content);
                            currentStreamingDiv = null;
                        } else {
                            // Non-streaming assistant response
                            addMessageToChat(content, "assistant");
                        }
                        
                        // Add to local chat history for conversation context
                        chatHistory.push({
                            role: "assistant",
                            content: content
                        });
                    }
                } else {
                    // Handle other roles (like 'user' echo from server, though usually local)
                    addMessageToChat(content, role);
                }
            }
            
            // Handle clear chat history action
            if (message.action === "clearChatHistory") {
                const reason = message.reason || 'external';
                clearChatHistoryAndUI(reason);
                if (sendResponse) {
                    sendResponse({ success: true });
                }
            }
            
            // Handle direct error messages from background script
            if (message.action === "chatError") {
                // Remove loading indicator if it exists
                const loadingMessage = getShadowElement("loading-message");
                if (loadingMessage) {
                    loadingMessage.remove();
                }
                
                const { error, errorType, detailedInfo } = message;
                let errorMessage = error || "An error occurred processing your message.";
                let isRateLimitError = false;
                
                // Enhance error message based on type
                if (errorType === 'rateLimit') {
                    isRateLimitError = true;
                    if (!errorMessage.includes("tomorrow") && !errorMessage.includes("wait")) {
                        errorMessage += " Please try again later.";
                    }
                } else if (errorType === 'auth') {
                    errorMessage = "Please log in to use the chat feature. Click the extension icon to log in.";
                } else if (errorType === 'network') {
                    errorMessage += " Please check your internet connection and try again.";
                } else if (errorType === 'server') {
                    errorMessage += " The service is temporarily unavailable.";
                }
                
                addErrorMessageToChat(errorMessage, isRateLimitError);
            }
        });
    });
})();

================================================
FILE: data/inject/content.js
================================================
window.addEventListener('blur', function() {
    window.focus();
});

// Declare shared isMac variable (this will be the first to run)
window.isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0 || 
               navigator.userAgent.toUpperCase().indexOf('MAC') >= 0;

// Automatically enable text selection on all websites
(function() {
    // Function to enable text selection globally
    function enableTextSelectionGlobally() {
        // Remove CSS rules that disable text selection
        const style = document.createElement('style');
        style.id = 'force-text-selection-style';
        style.innerHTML = `
            * {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                -ms-user-select: text !important;
                user-select: text !important;
                -webkit-touch-callout: default !important;
            }
            /* Override common classes that disable text selection */
            .no-select, .noselect, .unselectable,
            .qaas-disable-text-selection,
            .qaas-disable-text-selection *,
            [data-disable-text-selection],
            [data-disable-text-selection] *,
            [unselectable="on"],
            [onselectstart],
            [ondragstart] {
                -webkit-user-select: text !important;
                -moz-user-select: text !important;
                -ms-user-select: text !important;
                user-select: text !important;
                -webkit-touch-callout: default !important;
            }
        `;
        
        // Only add if not already present
        if (!document.getElementById('force-text-selection-style')) {
            document.head.appendChild(style);
        }
        
        // Remove specific attributes and classes that disable text selection
        const disabledElements = document.querySelectorAll(`
            .no-select, .noselect, .unselectable,
            .qaas-disable-text-selection, 
            [data-disable-text-selection],
            [unselectable="on"],
            [onselectstart],
            [ondragstart]
        `);
        
        disabledElements.forEach(element => {
            // Remove classes
            element.classList.remove('no-select', 'noselect', 'unselectable', 'qaas-disable-text-selection');
            
            // Remove attributes
            element.removeAttribute('data-disable-text-selection');
            element.removeAttribute('unselectable');
            element.removeAttribute('onselectstart');
            element.removeAttribute('ondragstart');
            
            // Force styles
            element.style.userSelect = 'text';
            element.style.webkitUserSelect = 'text';
            element.style.mozUserSelect = 'text';
            element.style.msUserSelect = 'text';
            element.style.webkitTouchCallout = 'default';
        });
        
        // Override common event handlers that prevent text selection
        document.onselectstart = null;
        document.ondragstart = null;
        document.oncontextmenu = null;
        
        // Remove event listeners that might interfere with text selection
        const body = document.body;
        if (body) {
            body.onselectstart = null;
            body.ondragstart = null;
        }
    }
    
    // Apply immediately
    enableTextSelectionGlobally();
    
    // Apply when DOM is fully loaded
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', enableTextSelectionGlobally);
    }
    
    // Re-apply when new content is added (for dynamic websites)
    const observer = new MutationObserver(function(mutations) {
        let shouldReapply = false;
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                // Check if any added nodes have text selection disabled
                mutation.addedNodes.forEach(function(node) {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        const hasDisabledSelection = node.matches && node.matches(`
                            .no-select, .noselect, .unselectable,
                            .qaas-disable-text-selection,
                            [data-disable-text-selection],
                            [unselectable="on"],
                            [onselectstart],
                            [ondragstart]
                        `);
                        if (hasDisabledSelection || node.querySelector) {
                            shouldReapply = true;
                        }
                    }
                });
            }
        });
        
        if (shouldReapply) {
            enableTextSelectionGlobally();
        }
    });
    
    // Start observing
    observer.observe(document.body || document.documentElement, {
        childList: true,
        subtree: true
    });
})();

// Function to convert HTML to readable text with proper formatting
function htmlToText(element) {
    if (!element) return '';
    
    // Clone the element to avoid modifying the original
    const clone = element.cloneNode(true);
    
    // Handle superscripts - convert <sup>text</sup> to ^text
    clone.querySelectorAll('sup').forEach(sup => {
        sup.textContent = '^' + sup.textContent;
    });
    
    // Handle subscripts - convert <sub>text</sub> to _text
    clone.querySelectorAll('sub').forEach(sub => {
        sub.textContent = '_' + sub.textContent;
    });
    
    // Handle line breaks
    clone.querySelectorAll('br').forEach(br => {
        br.replaceWith('\n');
    });
    
    // Get the text content
    return clone.innerText.trim();
}

// Function to extract the question, code, and options
function extractQuestionCodeAndOptions() {
    // Extracting the question text
    const questionElement = document.querySelector('div[aria-labelledby="question-data"]');
    const questionText = questionElement ? htmlToText(questionElement) : '';

    // Extracting the code
    const codeLines = [];
    const codeElements = document.querySelectorAll('.ace_layer.ace_text-layer .ace_line');

    codeElements.forEach(line => {
        codeLines.push(line.innerText.trim());
    });

    const codeText = codeLines.length > 0 ? codeLines.join('\n') : null; // Set to null if no code is found

    // Extracting options
    const optionsElements = document.querySelectorAll('div[aria-labelledby="each-option"]'); // Update this selector as necessary
    const optionsText = [];
    optionsElements.forEach((option, index) => {
        optionsText.push(`Option ${index + 1}: ${htmlToText(option)}`);
    });

    return {
        question: questionText,
        code: codeText, // This can be null if no code is present
        options: optionsText.join('\n') // Join options with new line characters
    };
}

// Async function to handle question, code, and options extraction
async function handleQuestionExtraction() {
    const { question, code, options } = extractQuestionCodeAndOptions();

    if (!question) {
        return;
    }

    console.log('Question:', question);
    console.log('Code:\n', code ? code : 'No code available');
    console.log('Options:\n', options);

    // Send the extracted data to background.js
    // The clicking will be handled by the clickMCQOption message handler
    chrome.runtime.sendMessage({
        action: 'extractData',
        question: question,
        code: code,
        options: options,
        isMCQ: true
    });
}

// Function to extract coding question details
function extractCodingQuestion() {
    // Extract programming language
    const programmingLanguageElement = document.querySelector('span.inner-text');
    const programmingLanguage = programmingLanguageElement ? programmingLanguageElement.innerText.trim() : 'Programming language not found.';

    // Extract question components
    const questionElement = document.querySelector('div[aria-labelledby="question-data"]');
    const questionText = questionElement ? htmlToText(questionElement) : 'Question not found.';

    const inputFormatElement = document.querySelector('div[aria-labelledby="input-format"]');
    const inputFormatText = inputFormatElement ? htmlToText(inputFormatElement) : '';

    const outputFormatElement = document.querySelector('div[aria-labelledby="output-format"]');
    const outputFormatText = outputFormatElement ? htmlToText(outputFormatElement) : '';

    // Extract sample test cases with robust fallback method
    const testCases = [];
    
    // Try Method 1: Find test case containers with aria-labelledby="each-tc-card"
    let containers = document.querySelectorAll('div[aria-labelledby="each-tc-card"]');
    
    if (containers.length > 0) {
        console.log('[Test Cases] Method 1: Found', containers.length, 'test case containers');
        containers.forEach((container) => {
            const inputPre = container.querySelector('div[aria-labelledby="each-tc-input-container"] pre');
            const outputPre = container.querySelector('div[aria-labelledby="each-tc-output-container"] pre');
            
            if (inputPre && outputPre) {
                testCases.push({
                    input: inputPre.textContent.trim(),
                    output: outputPre.textContent.trim()
                });
            }
        });
    }
    
    // Try Method 2: Find by aria-labelledby="each-tc-container"
    if (testCases.length === 0) {
        console.log('[Test Cases] Method 1 failed. Trying Method 2...');
        containers = document.querySelectorAll('[aria-labelledby="each-tc-container"]');
        
        if (containers.length > 0) {
            console.log('[Test Cases] Method 2: Found', containers.length, 'test case containers');
            containers.forEach((container) => {
                const inputPre = container.querySelector('[aria-labelledby="each-tc-input"]');
                const outputPre = container.querySelector('[aria-labelledby="each-tc-output"]');
                
                if (inputPre && outputPre) {
                    testCases.push({
                        input: inputPre.textContent.trim(),
                        output: outputPre.textContent.trim()
                    });
                }
            });
        }
    }
    
    // Try Method 3: Find pre elements with Input/Output labels
    if (testCases.length === 0) {
        console.log('[Test Cases] Method 2 failed. Trying Method 3...');
        const allPres = document.querySelectorAll('pre');
        const inputs = [];
        const outputs = [];
        
        allPres.forEach(pre => {
            const text = pre.textContent.trim();
            const prevElement = pre.previousElementSibling;
            
            if (prevElement) {
                const labelText = prevElement.textContent.toLowerCase();
                if (labelText.includes('input') && !labelText.includes('output')) {
                    inputs.push(text);
                } else if (labelText.includes('output')) {
                    outputs.push(text);
                }
            }
        });
        
        console.log('[Test Cases] Method 3: Found', inputs.length, 'inputs and', outputs.length, 'outputs');
        
        // Pair inputs and outputs
        for (let i = 0; i < Math.min(inputs.length, outputs.length); i++) {
            testCases.push({
                input: inputs[i],
                output: outputs[i]
            });
        }
    }
    
    let testCasesText = '';
    if (testCases.length > 0) {
        testCases.forEach((testCase, index) => {
            testCasesText += `Sample Test Case ${index + 1}:\nInput:\n${testCase.input}\nOutput:\n${testCase.output}\n\n`;
        });
        console.log('[Test Cases] Successfully extracted', testCases.length, 'test cases');
    } else {
        console.warn('[Test Cases] All methods failed. No test cases extracted.');
        testCasesText = 'No test cases found. Please check the page structure.';
    }

    // Send data to background.js for querying
    chrome.runtime.sendMessage({
        action: 'extractData',
        programmingLanguage: programmingLanguage,
        question: questionText,
        inputFormat: inputFormatText,
        outputFormat: outputFormatText,
        testCases: testCasesText,
        isCoding: true
    }, async (response) => {
        if (response && response.success && response.response) {
            try {
                // Clean the response
                let cleanedResponse = response.response.trim()
                    .replace(/^```[a-z]*\n/, '')
                    .replace(/\n```$/, '');
                
                console.log('[AI Answer] Cleaned response length:', cleanedResponse.length);
                
                // Copy to clipboard first
                await navigator.clipboard.writeText(cleanedResponse);
                console.log('[AI Answer] Code copied to clipboard');
                
                // Dispatch custom event to page context (where ace is available)
                // This will be handled by exam.js which runs in page context
                window.dispatchEvent(new CustomEvent('NEOPASS_INSERT_CODE', {
                    detail: { code: cleanedResponse }
                }));
                
                console.log('[AI Answer] Dispatched code insertion event to page context');
            } catch (error) {
                console.error("Error processing AI response:", error);
            }
        }
    });
}    

function solveIamneoExamly(){
        // Check if this is a coding question or MCQ
        const codingQuestionElement = document.querySelector('div[aria-labelledby="input-format"]');
        if (codingQuestionElement) {
            extractCodingQuestion();
        } else {
            handleQuestionExtraction();
        }
}
document.addEventListener('keydown', (event) => {
    // Use Option (Alt) key on all platforms
    const modifierKey = event.altKey;
    
    if (modifierKey && event.shiftKey && event.code === 'KeyA') {
        solveIamneoExamly();
    }
});

// Add event listener for Option+O to toggle toast opacity
document.addEventListener('keydown', (event) => {
    // Use Option (Alt) key on all platforms
    const modifierKey = event.altKey;
    
    if (modifierKey && event.code === 'KeyO') {
        chrome.runtime.sendMessage({
            action: 'toggleToastOpacity'
        });
    }
});

// Function to extract code from snippets
function extractSnippets() {
    const headerContainer = Array.from(document.querySelectorAll('div[aria-labelledby="tt-header"]'))
        .find(container => container.innerText.includes('Header Snippet'));
    const footerContainer = Array.from(document.querySelectorAll('div[aria-labelledby="footer"]'))
        .find(container => container.innerText.includes('Footer Snippet'));

    const extractCode = container => {
        if (!container) return '';
        const codeLines = container.querySelectorAll('.ace_line');
        return Array.from(codeLines).map(line => line.textContent).join('\n');
    };

    const snippets = {
        header: extractCode(headerContainer),
        footer: extractCode(footerContainer)
    };

    // Send snippets directly to background.js
    chrome.runtime.sendMessage({
        action: 'processSnippets',
        snippets: snippets
    });
}

// Remove old listener and add new one
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === 'extractSnippets') {
        extractSnippets();
    }
    if (message.action === 'solveIamneoExamly') {
        solveIamneoExamly();
    }
});

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.action === "updateChatHistory") {
        const { role, content } = message;
        
        // Remove loading indicator if it exists
        const loadingMessage = document.getElementById("loading-message");
        if (loadingMessage) {
            loadingMessage.remove();
        }
        
        // Add the actual message
        chatHistory.push({
            role: role,
            content: content
        });
        addMessageToChat(content, role);
    }
});

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.action === 'clickMCQOption') {
        (async () => {
            try {
                // Check if this is HackerRank
                if (request.isHackerRank) {
                    let clicked = false;
                    
                    // Handle multiple choice questions (checkboxes) differently
                    if (request.isMultipleChoice) {
                    console.log('Multiple choice question detected, response:', request.response);
                    
                    // Enhanced parsing for multiple options
                    // Look for patterns like: "1. text, 3. text" or "A. text, C. text" or "1, 3" or "A, C"
                    const optionNumbers = [];
                    
                    // Pattern 1: "1. text, 3. text" or "A. text, C. text"
                    let matches = request.response.match(/([A-Z]|\d+)\.\s*[^,]+/gi);
                    if (matches) {
                        matches.forEach(match => {
                            const num = match.match(/^([A-Z]|\d+)\./);
                            if (num) {
                                let optionIndex;
                                if (isNaN(num[1])) {
                                    // Convert A,B,C to 0,1,2
                                    optionIndex = num[1].charCodeAt(0) - 'A'.charCodeAt(0);
                                } else {
                                    // Convert 1,2,3 to 0,1,2
                                    optionIndex = parseInt(num[1]) - 1;
                                }
                                if (optionIndex >= 0) {
                                    optionNumbers.push(optionIndex);
                                }
                            }
                        });
                    }
                    
                    // Pattern 2: Simple comma-separated numbers or letters: "1, 3, 5" or "A, C, E"
                    if (optionNumbers.length === 0) {
                        const simpleMatches = request.response.match(/(?:^|[,\s])([A-Z]|\d+)(?=[,\s]|$)/gi);
                        if (simpleMatches) {
                            simpleMatches.forEach(match => {
                                const cleaned = match.trim().replace(/^[,\s]+|[,\s]+$/g, '');
                                let optionIndex;
                                if (isNaN(cleaned)) {
                                    // Convert A,B,C to 0,1,2
                                    optionIndex = cleaned.charCodeAt(0) - 'A'.charCodeAt(0);
                                } else {
                                    // Convert 1,2,3 to 0,1,2
                                    optionIndex = parseInt(cleaned) - 1;
                                }
                                if (optionIndex >= 0) {
                                    optionNumbers.push(optionIndex);
                                }
                            });
                        }
                    }
                    
                    // Remove duplicates
                    const uniqueOptionNumbers = [...new Set(optionNumbers)];
                    
                    console.log('Parsed multiple choice options:', uniqueOptionNumbers.map(n => n + 1));
                    
                    // Click all the selected options for multiple choice
                    const checkboxes = document.querySelectorAll('[role="checkbox"]');
                    if (checkboxes.length > 0) {
                        console.log(`Found ${checkboxes.length} checkboxes, will click options:`, uniqueOptionNumbers.map(n => n + 1));
                        
                        // Click options with delay to ensure UI state is properly updated
                        for (let i = 0; i < uniqueOptionNumbers.length; i++) {
                            const optionNumber = uniqueOptionNumbers[i];
                            
                            if (optionNumber >= 0 && optionNumber < checkboxes.length) {
                                const checkbox = checkboxes[optionNumber];
                                
                                // Wait a bit before checking and clicking each option
                                await new Promise(resolve => setTimeout(resolve, 300));
                                
                                // Re-check the current state after delay
                                const isCurrentlyChecked = checkbox.getAttribute('aria-checked') === 'true' || 
                                                         checkbox.getAttribute('data-state') === 'checked' ||
                                                         checkbox.checked === true;
                                
                                console.log(`Option ${optionNumber + 1} current state: ${isCurrentlyChecked ? 'checked' : 'unchecked'}`);
                                
                                // Only click if not already checked
                                if (!isCurrentlyChecked) {
                                    console.log(`Clicking checkbox option ${optionNumber + 1}...`);
                                    
                                    // Try multiple click methods to ensure it works
                                    checkbox.click();
                                    
                                    // Alternative click method - dispatch events directly
                                    checkbox.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
                                    checkbox.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
                                    checkbox.dispatchEvent(new MouseEvent('click', { bubbles: true }));
                                    
                                    // Wait a bit more to let the UI update
                                    await new Promise(resolve => setTimeout(resolve, 200));
                                    
                                    // Verify the click worked
                                    const newState = checkbox.getAttribute('aria-checked') === 'true' || 
                                                   checkbox.getAttribute('data-state') === 'checked' ||
                                                   checkbox.checked === true;
                                    
                                    if (newState) {
                                        console.log(`✅ HackerRank checkbox option ${optionNumber + 1} clicked successfully`);
                                        clicked = true;
                                    } else {
                                        console.log(`⚠️ HackerRank checkbox option ${optionNumber + 1} click may have failed - retrying...`);
                                        
                                        // Retry once more
                                        checkbox.click();
                                        await new Promise(resolve => setTimeout(resolve, 100));
                                        
                                        const retryState = checkbox.getAttribute('aria-checked') === 'true' || 
                                                         checkbox.getAttribute('data-state') === 'checked' ||
                                                         checkbox.checked === true;
                                        
                                        if (retryState) {
                                            console.log(`✅ HackerRank checkbox option ${optionNumber + 1} clicked successfully on retry`);
                                            clicked = true;
                                        } else {
                                            console.log(`❌ HackerRank checkbox option ${optionNumber + 1} failed to click`);
                                        }
                                    }
                                } else {
                                    console.log(`✅ HackerRank checkbox option ${optionNumber + 1} already selected`);
                                    clicked = true; // Still count as successful
                                }
                            }
                        }
                        
                        // If no options were found, fall back to single option logic
                        if (uniqueOptionNumbers.length === 0) {
                            console.log('No multiple options found, falling back to single option logic');
                            const optionMatch = request.response.match(/(?:options?\s*)?([A-Z]|\d+)\.?/i);
                            if (optionMatch) {
                                let optionNumber;
                                if (isNaN(optionMatch[1])) {
                                    optionNumber = optionMatch[1].charCodeAt(0) - 'A'.charCodeAt(0);
                                } else {
                                    optionNumber = parseInt(optionMatch[1]) - 1;
                                }
                                
                                if (optionNumber >= 0 && optionNumber < checkboxes.length) {
                                    await new Promise(resolve => setTimeout(resolve, 200));
                                    
                                    const checkbox = checkboxes[optionNumber];
                                    const isCurrentlyChecked = checkbox.getAttribute('aria-checked') === 'true' || 
                                                             checkbox.getAttribute('data-state') === 'checked' ||
                                                             checkbox.checked === true;
                                    
                                    if (!isCurrentlyChecked) {
                                        checkbox.click();
                                        console.log(`HackerRank single checkbox option ${optionNumber + 1} clicked as fallback`);
                                        clicked = true;
                                    } else {
                                        console.log(`HackerRank single checkbox option ${optionNumber + 1} already selected`);
                                        clicked = true;
                                    }
                                }
                            }
                        }
                    }
                } else {
                    // Single choice question - use enhanced logic
                    const optionMatch = request.response.match(/(?:options?\s*)?([A-Z]|\d+)\.?/i);
                    if (optionMatch) {
                        let optionNumber;
                        if (isNaN(optionMatch[1])) {
                            // Handle letter options (A, B, C, etc.)
                            optionNumber = optionMatch[1].toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0);
                        } else {
                            // Handle number options (1, 2, 3, etc.)
                            optionNumber = parseInt(optionMatch[1]) - 1;
                        }
                        
                        console.log(`Single choice detected, clicking option: ${optionNumber + 1}`);
                        
                        // Add a small delay before clicking
                        await new Promise(resolve => setTimeout(resolve, 200));
                        
                        // Try new layout first - check for radio buttons
                        const newLayoutRadios = document.querySelectorAll('[role="radio"]');
                        if (newLayoutRadios.length > optionNumber && optionNumber >= 0) {
                            const radio = newLayoutRadios[optionNumber];
                            
                            // Check if already selected
                            const isCurrentlySelected = radio.getAttribute('aria-checked') === 'true' || 
                                                      radio.getAttribute('data-state') === 'checked' ||
                                                      radio.checked === true;
                            
                            if (!isCurrentlySelected) {
                                radio.click();
                                console.log(`HackerRank new layout radio option ${optionNumber + 1} clicked successfully`);
                                clicked = true;
                            } else {
                                console.log(`HackerRank new layout radio option ${optionNumber + 1} already selected`);
                                clicked = true;
                            }
                        } else {
                            // Try checkboxes if no radio buttons found (fallback for single checkbox)
                            const newLayoutCheckboxes = document.querySelectorAll('[role="checkbox"]');
                            if (newLayoutCheckboxes.length > optionNumber && optionNumber >= 0) {
                                const checkbox = newLayoutCheckboxes[optionNumber];
                                
                                const isCurrentlyChecked = checkbox.getAttribute('aria-checked') === 'true' || 
                                                         checkbox.getAttribute('data-state') === 'checked' ||
                                                         checkbox.checked === true;
                                
                                if (!isCurrentlyChecked) {
                                    checkbox.click();
                                    console.log(`HackerRank new layout checkbox option ${optionNumber + 1} clicked successfully`);
                                    clicked = true;
                                } else {
                                    console.log(`HackerRank new layout checkbox option ${optionNumber + 1} already selected`);
                                    clicked = true;
                                }
                            } else {
                                // Fallback to old layout (radio buttons)
                                const questionContainer = document.querySelector('.grouped-mcq__question');
                                if (questionContainer) {
                                    const radios = questionContainer.querySelectorAll('input[type="radio"]');
                                    if (radios.length > optionNumber && optionNumber >= 0) {
                                        const radio = radios[optionNumber];
                                        
                                        if (!radio.checked) {
                                            radio.click();
                                            console.log(`HackerRank old layout option ${optionNumber + 1} clicked successfully`);
                                            clicked = true;
                                        } else {
                                            console.log(`HackerRank old layout option ${optionNumber + 1} already selected`);
                                            clicked = true;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                
                if (!clicked) {
                    chrome.runtime.sendMessage({
                        action: 'showMCQToast',
                        message: request.response,
                    });
                }
            } else {
                // Original logic for other platforms (Examly)
                const optionMatch = request.response.match(/(?:options?\s*)?(\d+)\.?/i);
                if (optionMatch) {
                    const optionNumber = parseInt(optionMatch[1])-1;
                    // Use exact same selector as Alt+Shift+Q
                    const answerElement = document.querySelector(`#tt-option-${optionNumber} > label > span.checkmark1`);
                    
                    if (answerElement) {
                        answerElement.dispatchEvent(new Event("click", { bubbles: true }));
                        console.log(`Option element ${optionNumber + 1} clicked successfully`);
                    } else {
                        chrome.runtime.sendMessage({
                            action: 'showMCQToast',
                            message: request.response,
                        });
                    }
                } else {
                    chrome.runtime.sendMessage({
                        action: 'showMCQToast',
                        message: request.response,
                    });
                }
            }
        } catch (error) {
            chrome.runtime.sendMessage({
                action: 'showMCQToast',
                message: request.response,
            });
        }
        })();
    }
});

// Function to extract HackerRank MCQ data (updated for new layout)
function extractHackerRankMCQ() {
    const questions = [];
    
    // Try new layout first (2024+ layout)
    const newLayoutQuestions = document.querySelectorAll('.QuestionDetails_container__AIu0X');
    
    if (newLayoutQuestions.length > 0) {
        // New layout processing
        newLayoutQuestions.forEach((container, index) => {
            const questionData = {
                questionNumber: index + 1,
                title: '',
                instruction: '',
                options: [],
                selectedAnswer: null
            };
            
            // Extract question title from new layout
            const titleElement = container.querySelector('.qaas-block-question-title, h2');
            if (titleElement) {
                // Remove bookmark icon and get clean title
                const titleText = titleElement.textContent || titleElement.innerText;
                questionData.title = titleText.replace(/Bookmark question \d+/g, '').trim();
            }
            
            // Extract question instruction/content from new layout
            const instructionElement = container.querySelector('.qaas-block-question-instruction, .RichTextPreview_richText__1vKu5');
            if (instructionElement) {
                let instructionText = instructionElement.textContent || instructionElement.innerText;
                instructionText = instructionText.replace(/\s+/g, ' ').trim();
                questionData.instruction = instructionText;
            }
            
            // Look for options in multiple possible containers
            let optionsContainer = container.nextElementSibling;
            let attempts = 0;
            while (optionsContainer && attempts < 5) {
                // Check for both radio buttons and checkboxes
                const hasOptions = optionsContainer.querySelector('[role="checkbox"], [role="radio"], .ui-radio');
                if (hasOptions) {
                    break;
                }
                optionsContainer = optionsContainer.nextElementSibling;
                attempts++;
            }
            
            // Also check for options within the same container or nearby
            if (!optionsContainer || !optionsContainer.querySelector('[role="checkbox"], [role="radio"]')) {
                optionsContainer = container.parentElement?.querySelector('.Control_container__F35yA') ||
                                document.querySelector('.Control_container__F35yA');
            }
            
            if (optionsContainer) {
                // Try radio buttons first (new layout)
                let optionElements = optionsContainer.querySelectorAll('[role="radio"]');
                
                // If no radio butto
Download .txt
gitextract_mqhp625o/

├── README.md
├── contentScript.js
├── data/
│   ├── inject/
│   │   ├── anti-anti-debug.js
│   │   ├── chatbot.js
│   │   ├── content.js
│   │   ├── copyOverride.js
│   │   ├── customPaste.js
│   │   ├── exam.js
│   │   ├── isolated.js
│   │   ├── main.js
│   │   ├── mock_code/
│   │   │   ├── minifiedBackground.js
│   │   │   ├── minifiedContent-script.js
│   │   │   ├── mock_manifest.json
│   │   │   └── rules.json
│   │   ├── mock_code.js
│   │   ├── rightclickmenu.js
│   │   └── screenshare.js
│   └── nptel.json
├── devtools.js
├── manifest.json
├── metadata.json
├── nptel.txt
├── popup.html
├── popup.js
└── worker.js
Download .txt
SYMBOL INDEX (116 symbols across 13 files)

FILE: contentScript.js
  function replaceNeoBrowserButton (line 34) | function replaceNeoBrowserButton() {
  function sendMessageToWebsite (line 157) | function sendMessageToWebsite(messageData) {
  function removeInjectedElement (line 173) | function removeInjectedElement() {

FILE: data/inject/anti-anti-debug.js
  function shouldLog (line 49) | function shouldLog(type) {
  function wrapFn (line 164) | function wrapFn(newFn, old) {

FILE: data/inject/chatbot.js
  function loadShowdown (line 20) | function loadShowdown() {
  function loadPrism (line 35) | function loadPrism() {
  function getShadowElement (line 431) | function getShadowElement(id) {
  function getShadowRoot (line 437) | function getShadowRoot() {
  function getChatButton (line 442) | function getChatButton() {
  function detectPlatform (line 462) | function detectPlatform() {
  function extractExamlyQuestion (line 476) | function extractExamlyQuestion() {
  function extractHackerRankQuestion (line 540) | function extractHackerRankQuestion() {
  function extractCurrentQuestion (line 695) | function extractCurrentQuestion() {
  function formatQuestionForChat (line 707) | function formatQuestionForChat(questionData) {
  function createChatOverlay (line 773) | function createChatOverlay() {
  function addNotificationMessage (line 1928) | function addNotificationMessage(message) {
  function createChatButton (line 1953) | function createChatButton() {
  function detectLanguage (line 2127) | function detectLanguage(code) {
  function renderChatContent (line 2282) | function renderChatContent(messageContainer, content) {
  function addMessageToChat (line 2431) | function addMessageToChat(message, role) {
  function clearErrorState (line 2477) | function clearErrorState() {
  function createValidContext (line 2494) | function createValidContext(chatHistory) {
  function addErrorMessageToChat (line 2530) | function addErrorMessageToChat(errorMessage, isRateLimitError = false) {
  function addLoadingIndicator (line 2604) | function addLoadingIndicator() {
  function toggleChatOverlay (line 2653) | function toggleChatOverlay() {
  function clearChatHistoryAndUI (line 2694) | function clearChatHistoryAndUI(reason = 'manual') {
  function blockClashingChatElements (line 2727) | function blockClashingChatElements() {
  function init (line 2863) | async function init() {

FILE: data/inject/content.js
  function enableTextSelectionGlobally (line 12) | function enableTextSelectionGlobally() {
  function htmlToText (line 132) | function htmlToText(element) {
  function extractQuestionCodeAndOptions (line 158) | function extractQuestionCodeAndOptions() {
  function handleQuestionExtraction (line 188) | async function handleQuestionExtraction() {
  function extractCodingQuestion (line 211) | function extractCodingQuestion() {
  function solveIamneoExamly (line 348) | function solveIamneoExamly(){
  function extractSnippets (line 379) | function extractSnippets() {
  function extractHackerRankMCQ (line 715) | function extractHackerRankMCQ() {
  function extractHackerRankCoding (line 862) | function extractHackerRankCoding() {
  function normalizeCodeIndentation (line 937) | function normalizeCodeIndentation(code) {
  function insertCodeIntoMonacoEditor (line 974) | async function insertCodeIntoMonacoEditor(text) {
  function handleHackerRankMCQ (line 1101) | function handleHackerRankMCQ() {

FILE: data/inject/copyOverride.js
  function customCopy (line 64) | async function customCopy(selectedText) {
  function getSelectedText (line 107) | function getSelectedText() {

FILE: data/inject/customPaste.js
  function performPasteByTyping (line 1) | async function performPasteByTyping() {
  function performDragDropPaste (line 127) | async function performDragDropPaste() {

FILE: data/inject/exam.js
  function checkForQuestionChange (line 20) | function checkForQuestionChange() {
  function typeNextCharacter (line 50) | function typeNextCharacter() {
  function getAnswerFromAI (line 134) | async function getAnswerFromAI() {

FILE: data/inject/main.js
  method get (line 22) | get() {
  method get (line 30) | get() {
  method get (line 78) | get() {
  method get (line 86) | get() {
  method apply (line 96) | apply(target, self, args) {
  method apply (line 141) | apply(target, self, args) {
  method apply (line 157) | apply(target, self, args) {

FILE: data/inject/mock_code/minifiedBackground.js
  function handleMessage (line 1) | async function handleMessage(e,t,n){if(!t.id&&!t.url)return n({status:"E...

FILE: data/inject/screenshare.js
  function bypassRestrictions (line 43) | function bypassRestrictions() {
  function spoofScreenRecording (line 105) | function spoofScreenRecording() {
  function showPopup (line 121) | function showPopup(resolve, reject, constraints, originalGetDisplayMedia) {

FILE: devtools.js
  function isExamPage (line 2) | function isExamPage() {
  function injectAntiDebug (line 9) | function injectAntiDebug() {

FILE: popup.js
  function autoSaveAPIConfig (line 34) | function autoSaveAPIConfig() {
  function clearChatHistoryOnProviderChange (line 70) | function clearChatHistoryOnProviderChange() {
  function updateShortcutsForPlatform (line 93) | function updateShortcutsForPlatform() {
  function refreshAllTabs (line 154) | function refreshAllTabs() {
  function showError (line 163) | function showError(message, duration = 5000) {
  function showLoggedInState (line 172) | function showLoggedInState(username, isPro, accountData) {
  function displayAccountInfo (line 210) | function displayAccountInfo(account) {
  function fetchAccountInfo (line 225) | async function fetchAccountInfo() {
  function showLoggedOutState (line 281) | function showLoggedOutState() {
  function checkSessionExpiration (line 305) | function checkSessionExpiration() {
  function logoutUser (line 319) | function logoutUser() {
  function initializeOpacityLevel (line 500) | function initializeOpacityLevel() {
  function capitalizeFirstLetter (line 510) | function capitalizeFirstLetter(string) {
  function loadAPIConfiguration (line 533) | function loadAPIConfiguration() {

FILE: worker.js
  function canMakeRequest (line 13) | function canMakeRequest() {
  function blockRequests (line 17) | function blockRequests() {
  function unblockRequests (line 32) | function unblockRequests() {
  function handleMessage (line 80) | async function handleMessage(request, sender, sendResponse) {
  function checkForUpdate (line 218) | async function checkForUpdate() {
  function compareVersions (line 268) | function compareVersions(v1, v2) {
  function showUpdateToast (line 281) | function showUpdateToast(tabId, message, latestVersion) {
  function setupUpdateAlarm (line 557) | function setupUpdateAlarm() {
  function isLoggedIn (line 665) | function isLoggedIn(callback) {
  function showLoginPrompt (line 672) | function showLoginPrompt(tabId) {
  function handleNPTEL (line 901) | function handleNPTEL(result, tabId) {
  function getSelectedText (line 934) | function getSelectedText() {
  function handleQueryResponse (line 947) | function handleQueryResponse(response, tabId, isMCQ = false) {
  function handleQueryResponseForIamNeoExamly (line 989) | function handleQueryResponseForIamNeoExamly(response, tabId, isMCQ = fal...
  function queryRequest (line 1039) | async function queryRequest(text, isMCQ = false, isMultipleChoice = fals...
  function getCustomAPIConfig (line 1235) | async function getCustomAPIConfig() {
  function queryCustomAPI (line 1256) | async function queryCustomAPI(text, isMCQ, isMultipleChoice, config) {
  constant API_BASE_URL (line 1414) | const API_BASE_URL = 'https://api.neopass.tech';
  function getTokens (line 1417) | async function getTokens() {
  function makeAuthenticatedRequest (line 1424) | async function makeAuthenticatedRequest(url, method, token, body = null) {
  function handleChatMessage (line 1597) | async function handleChatMessage(message, sender) {
  function sendChatResponse (line 1757) | function sendChatResponse(tabId, content) {
  function sendChatErrorResponse (line 1766) | function sendChatErrorResponse(tabId, content) {
  function copyToClipboard (line 1786) | async function copyToClipboard(text, tabId) {
  function copyToClipboard (line 1815) | function copyToClipboard(text) {
  function checkStealthMode (line 1844) | async function checkStealthMode() {
  function removeExistingToast (line 1866) | function removeExistingToast(tabId) {
  function toggleToastOpacity (line 1898) | async function toggleToastOpacity() {
  function getToastOpacity (line 1933) | async function getToastOpacity() {
  function showOpacityLevelToast (line 1945) | function showOpacityLevelToast(tabId, message) {
  function showToast (line 2104) | async function showToast(tabId, message, isError = false, detailedInfo =...
  function showStealthToast (line 2318) | async function showStealthToast(tabId, message, stealthEnabled) {
  constant SESSION_DURATION (line 2669) | const SESSION_DURATION = 12 * 60 * 60 * 1000;
  function checkAndHandleSessionExpiration (line 2673) | async function checkAndHandleSessionExpiration() {
  function findAnswer (line 2758) | function findAnswer(query) {
  function levenshteinDistance (line 2784) | function levenshteinDistance(s1, s2) {
  function normalizeText (line 2806) | function normalizeText(text) {
  function loadNptelDataset (line 2816) | async function loadNptelDataset() {
  function showMCQToast (line 2830) | async function showMCQToast(tabId, message, detailedInfo = '') {
  function showNPTELToast (line 3074) | async function showNPTELToast(tabId, message, isError = false, detailedI...
  function showSpinnerToast (line 3287) | async function showSpinnerToast(tabId, message = 'Processing your reques...
Condensed preview — 25 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (660K chars).
[
  {
    "path": "README.md",
    "chars": 7911,
    "preview": "<img width=\"1500\" height=\"500\" alt=\"NeoPass Banner\" src=\"https://github.com/user-attachments/assets/7369dd86-838d-4fdc-a"
  },
  {
    "path": "contentScript.js",
    "chars": 6303,
    "preview": "// Check if the chrome object is available (for compatibility)\nif (typeof chrome === \"undefined\") {\n  // Handle the case"
  },
  {
    "path": "data/inject/anti-anti-debug.js",
    "chars": 5302,
    "preview": "!(() => {\n    const Proxy = window.Proxy;\n    const Object = window.Object;\n    const Array = window.Array;\n    /**\n    "
  },
  {
    "path": "data/inject/chatbot.js",
    "chars": 143595,
    "preview": "if (typeof chrome === \"undefined\") {}\n\nif (typeof window.isMac === 'undefined') {\n    window.isMac = navigator.platform."
  },
  {
    "path": "data/inject/content.js",
    "chars": 59495,
    "preview": "window.addEventListener('blur', function() {\n    window.focus();\n});\n\n// Declare shared isMac variable (this will be the"
  },
  {
    "path": "data/inject/copyOverride.js",
    "chars": 7487,
    "preview": "// Custom Ctrl+C override functionality - Prevents default copy on divs\n(function() {\n    'use strict';\n\n    // Create a"
  },
  {
    "path": "data/inject/customPaste.js",
    "chars": 19315,
    "preview": "async function performPasteByTyping() {\n    console.log('[PasteByTyping] Function called');\n    \n    const activeElement"
  },
  {
    "path": "data/inject/exam.js",
    "chars": 14819,
    "preview": "// Use shared isMac variable if it exists, otherwise declare it\nif (typeof window.isMac === 'undefined') {\n    window.is"
  },
  {
    "path": "data/inject/isolated.js",
    "chars": 1293,
    "preview": "(function() {\nvar port;\ntry {\n  port = document.getElementById('lwys-ctv-port');\n  port.remove();\n}\ncatch (e) {\n  port ="
  },
  {
    "path": "data/inject/main.js",
    "chars": 4522,
    "preview": "(function() {\n  /* port is used to communicate between chrome and page scripts */\n  var port;\n  try {\n    port = documen"
  },
  {
    "path": "data/inject/mock_code/minifiedBackground.js",
    "chars": 2073,
    "preview": "let allowedIPs=[];const getIPs=async()=>{let e=chrome.runtime.getManifest();return allowedIPs=e.metadata.ip||[],e.metada"
  },
  {
    "path": "data/inject/mock_code/minifiedContent-script.js",
    "chars": 200,
    "preview": "window.addEventListener(\"message\",function(e){e.source===window&&\"extension\"===e.data.target&&chrome.runtime.sendMessage"
  },
  {
    "path": "data/inject/mock_code/mock_manifest.json",
    "chars": 2123,
    "preview": "{\n    \"action\": {\n        \"default_icon\": {\n            \"16\": \"images/icon16.png\",\n            \"48\": \"images/icon48.png\""
  },
  {
    "path": "data/inject/mock_code/rules.json",
    "chars": 19341,
    "preview": "[\n  {\n    \"id\": 1,\n    \"priority\": 1,\n    \"action\": {\n      \"type\": \"block\"\n    },\n    \"condition\": {\n      \"urlFilter\":"
  },
  {
    "path": "data/inject/mock_code.js",
    "chars": 3260,
    "preview": "(function() {\n    // Check if we're on YouTube or a chrome:// page\n    if (window.location.href.toLowerCase().includes('"
  },
  {
    "path": "data/inject/rightclickmenu.js",
    "chars": 225,
    "preview": "(function() {\n\"use strict\";\n\nif (!window.__ENABLE_RIGHT_CLICK_SETUP) {\n  window.document.addEventListener('contextmenu',"
  },
  {
    "path": "data/inject/screenshare.js",
    "chars": 9309,
    "preview": "// Mac detection - only declare if not already declared\nlet isMac;\nif (typeof isMac === 'undefined') {\n    isMac = navig"
  },
  {
    "path": "data/nptel.json",
    "chars": 107548,
    "preview": "[\n    {\n        \"question\": \"\\\"Enquiry into plants\\\" is a book written by\",\n        \"answer\": \"Theophrastus\"\n    },\n    "
  },
  {
    "path": "devtools.js",
    "chars": 1073,
    "preview": "// Check if URL contains \"/courses\" or \"/test\"\nfunction isExamPage() {\n    return window.location.href.includes('/mycour"
  },
  {
    "path": "manifest.json",
    "chars": 4096,
    "preview": "{\n  \"manifest_version\": 3,\n  \"name\": \"NeoExamShield\",\n  \"version\": \"1.5.1\",\n  \"description\": \"To prevent malpractice, id"
  },
  {
    "path": "metadata.json",
    "chars": 95,
    "preview": "{\n    \"ip\": [\n      \"34.171.215.232\",\n      \"34.233.30.196\",\n      \"35.212.92.221\"\n    ]\n  }\n  "
  },
  {
    "path": "nptel.txt",
    "chars": 484,
    "preview": "const dataset = [];\ndocument.querySelectorAll('.qt-mc-question').forEach(questionBlock => {\n    const questionText = que"
  },
  {
    "path": "popup.html",
    "chars": 32568,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "popup.js",
    "chars": 30331,
    "preview": "document.addEventListener('DOMContentLoaded', function () {\n    const isMac = navigator.platform.toUpperCase().indexOf('"
  },
  {
    "path": "worker.js",
    "chars": 150011,
    "preview": "// Track shortcut execution state to prevent multiple requests when held down\nconst shortcutStates = {\n  'search': false"
  }
]

About this extraction

This page contains the full source code of the Max-Eee/NeoPass GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 25 files (617.9 KB), approximately 130.7k tokens, and a symbol index with 116 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!