AIGPT Everywhere

Mini A.I. floating menu that can define words, answer questions, translate, and much more in a single click and with your custom prompts. Includes useful click to search on Google and copy selected text buttons, along with Rocker+Mouse Gestures and Units+Currency+Time zone Converters, all features can be easily modified or disabled.

Version au 21/05/2025. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name               AIGPT Everywhere
// @namespace          OperaBrowserGestures
// @description        Mini A.I. floating menu that can define words, answer questions, translate, and much more in a single click and with your custom prompts. Includes useful click to search on Google and copy selected text buttons, along with Rocker+Mouse Gestures and Units+Currency+Time zone Converters, all features can be easily modified or disabled.
// @version            77
// @author             hacker09
// @include            *
// @exclude            https://accounts.google.com/v3/signin/*
// @icon               https://i.imgur.com/8iw8GOm.png
// @grant              GM_registerMenuCommand
// @grant              GM_getResourceText
// @grant              GM.xmlHttpRequest
// @grant              GM_setClipboard
// @grant              GM_deleteValue
// @grant              GM_openInTab
// @grant              window.close
// @run-at             document-end
// @grant              GM_setValue
// @grant              GM_getValue
// @connect            google.com
// @connect            generativelanguage.googleapis.com
// @resource           AIMenuContent https://hacker09.glitch.me/AIMenu.html
// @require            https://update.greatest.deepsurf.us/scripts/506699/marked.js
// @require            https://update.greatest.deepsurf.us/scripts/519002/Units%20Converter.js
// ==/UserScript==

/* jshint esversion: 11 */

const toHTML = html => window.trustedTypes?.createPolicy('BypassTT', { createHTML: HTML => HTML })?.createHTML(html) || html; //Bypass Trusted Types API + create safe HTML for chromium browsers

if ((location.href === 'https://aistudio.google.com/app/apikey' && document.querySelector(".apikey-link") !== null) && GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //Set up the API Key
  window.onload = setTimeout(function() {
    document.querySelectorAll(".apikey-link")[1].click(); //Click on the API Key
    setTimeout(function() {
      GM_setValue("APIKey", document.querySelector(".apikey-text").innerText); //Store the API Key
      alert((GM_getValue("APIKey") !== undefined && GM_getValue("APIKey") !== null && GM_getValue("APIKey") !== '') ? 'API Key automatically added!' : 'Failed to add API Key automatically!');
    }, 500);
  }, 1000);
}

if (GM_getValue("SearchHighlight") === undefined) { //Set up everything on the first run
  GM_setValue("SearchHighlight", true);
  GM_setValue("MouseGestures", true);
  GM_setValue("TimeConverter", true);
  GM_setValue("UnitsConverter", true);
  GM_setValue("CurrenciesConverter", true);
}

function ToggleFeature(feature)
{
  GM_setValue(feature, GM_getValue(feature) === true ? false : true);
  location.reload();
}
//Mouse Gestures_________________________________________________________________________________________________________________________________________________________________________________
GM_registerMenuCommand(`${GM_getValue("MouseGestures") ? "Disable" : "Enable"} Mouse Gestures`, function() { ToggleFeature("MouseGestures"); });

if (GM_getValue("MouseGestures") === true) //If the MouseGestures is enabled
{
  var link;

  document.querySelectorAll('a').forEach(el => {
    el.addEventListener('mouseover', function() {
      link = this.href; //Store the hovered link
    });

    el.addEventListener('mouseout', () => {
      const previousLink = link; //Save the hovered link
      setTimeout(() => {
        if (previousLink === link) { //Check if the same link is still hovered
          link = 'about:newtab'; //Open a new tab
        }
      }, 200);
    });
  });

  const funcs = { //Store the MouseGestures functions

    'DL': function() { //Detect the Down+Left movement
      GM_openInTab(location.href, { incognito: true, });
      window.top.close();
    },

    'L': function() { //Detect the Left movement
      window.history.back();
    },

    'R': function() { //Detect the Right movement
      window.history.forward();
    },

    'D': function(e) { //Detect the Down movement
      if (e.shiftKey) {
        open(link, '_blank', 'height=' + screen.height + ',width=' + screen.width);
      }
      else {
        GM_openInTab(link, { active: true, insert: true, setParent: true });
      }
    },

    'UD': function() { //Detect the Up+Down movement
      location.reload();
    },

    'DR': function(e) { //Detect the Down+Right movement
      top.close();
      e.preventDefault();
      e.stopPropagation();
    },

    'DU': function() { //Detect the Down+Up movement
      GM_openInTab(link, { active: false, insert: true, setParent: true });
    }
  };

  //Math codes to track the mouse movement gestures
  var x, y, path;
  const TOLERANCE = 3;
  const SENSITIVITY = 3;
  const s = 1 << ((7 - SENSITIVITY) << 1);
  const t1 = Math.tan(0.15708 * TOLERANCE),t2 = 1 / t1;

  const tracer = function(e) {
    var cx = e.clientX, cy = e.clientY, deltaX = cx - x, deltaY = cy - y, distance = deltaX * deltaX + deltaY * deltaY;
    if (distance > s) {
      var slope = Math.abs(deltaY / deltaX), direction = '';
      if (slope > t1) {
        direction = deltaY > 0 ? 'D' : 'U';
      } else if (slope <= t2) {
        direction = deltaX > 0 ? 'R' : 'L';
      }
      if (path.charAt(path.length - 1) !== direction) {
        path += direction;
      }
      x = cx;
      y = cy;
    }
  };

  window.addEventListener('mousedown', function(e) {
    if (e.which === 3) {
      x = e.clientX;
      y = e.clientY;
      path = "";
      window.addEventListener('mousemove', tracer, false); //Detect the mouse position
    }
  }, false);

  window.addEventListener('contextmenu', function(e) { //When the right click BTN is released
    window.removeEventListener('mousemove', tracer, false); //Track the mouse movements
    if (path !== "") {
      e.preventDefault();
      if (funcs.hasOwnProperty(path)) {
        funcs[path](e);
      }
    }
  }, false);
}
//Rocker Mouse Gestures__________________________________________________________________________________________________________________________________________________________________________
GM_registerMenuCommand(`${GM_getValue("RockerMouseGestures") ? "Disable" : "Enable"} Rocker Gestures`, function() { ToggleFeature("RockerMouseGestures"); });

if (GM_getValue("RockerMouseGestures") === true) //If the RockerMouseGestures is enabled
{
  const mouseState = { 0: false, 2: false }; //0: Left, 2: Right

  window.addEventListener("mouseup", function(e) {
    mouseState[e.button] = false; //Update the state for the released button

    if (mouseState[0] && !mouseState[2]) { //Left clicked, Right released
      history.back();
    } else if (mouseState[2] && !mouseState[0]) { //Right clicked, Left released
      history.forward();
    }
  }, false);

  window.addEventListener("mousedown", function(e) {
    mouseState[e.button] = true; //Update the state for the pressed button
  }, false);
}
//Search HighLight + Time + Currencies + Units Converters + Search HighLight + AI menus__________________________________________________________________________________________________________
GM_registerMenuCommand(`${GM_getValue("SearchHighlight") ? "Disable" : "Enable"} Search Highlight`, function() { ToggleFeature("SearchHighlight"); });

if (GM_getValue("SearchHighlight") === true) //If the SearchHighlight is enabled
{
  var SelectedText;
  const Links = new RegExp(/\.org|\.ly|\.net|\.co|\.tv|\.me|\.biz|\.club|\.site|\.br|\.gov|\.io|\.ai|\.jp|\.edu|\.au|\.in|\.it|\.ca|\.mx|\.fr|\.tw|\.il|\.uk|\.zoom\.us|\.youtu\.be|\.com|\.us|\.de|\.cn|\.ru|\.es|\.ch|\.nl|\.se|\.no|\.dk|\.fi|\.pl|\.tr|\.xyz|\.za/i);

  document.body.addEventListener('mouseup', async function() { //When the user releases the mouse click after selecting something
    HtmlMenu.style.display = 'block'; //Display the container div
    SelectedText = getSelection().toString().trim(); //Store the selected text
    shadowRoot.querySelector("#ShowCurrencyORUnits").innerText = ''; //Remove the previous Units/Currency text

    function ShowConversion(UnitORCurrency, Type, Result) {
      shadowRoot.querySelector("#SearchBTN span")?.remove(); //Return previous HTML
      shadowRoot.querySelector("#SearchBTN").innerHTML = toHTML('<span class="GreyBar">│ </span>' + shadowRoot.querySelector("#SearchBTN").innerHTML);

      if (UnitORCurrency === 'Currencies') {
        const hasSymbol = SelectedText.match(Currencies)[2].match(CurrencySymbols) !== null;
        const currencyFormat = Intl.NumberFormat(navigator.language, {
          style: 'currency',
          currency: GM_getValue("YourLocalCurrency")
        }).format(Result);

        const displayText = hasSymbol ? (Type + ' 🠂 ' + currencyFormat) : currencyFormat;
        shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = toHTML(displayText);
      }
      else
      {
        shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = toHTML(UnitORCurrency === 'Units' ? `${Result} ${Type}` : Result); //Show the converted time results
      }

      setTimeout(() => { //Wait for Units to show up to get the right offsetWidth
        const offsetWidth = shadowRoot.querySelector("#ShowCurrencyORUnits").offsetWidth; //Store the current menu size
        shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseover = function() { //When the mouse hovers the unit/currency
          shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = toHTML(`Copy`);
          shadowRoot.querySelector("#ShowCurrencyORUnits").style.display = 'inline-flex';
          shadowRoot.querySelector("#ShowCurrencyORUnits").style.width = `${offsetWidth}px`; //Maintain the aspect ratio
        };
      }, 0);

      const htmlcode = shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML; //Save the converted unit/currency value
      shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseout = function() { //When the mouse leaves the unit/currency
        shadowRoot.querySelector("#ShowCurrencyORUnits").style.width = ''; //Return the original aspect ratio
        shadowRoot.querySelector("#ShowCurrencyORUnits").style.display = ''; //Return the original aspect ratio
        shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = toHTML(htmlcode); //Return the previous html
      };

      shadowRoot.querySelector("#ShowCurrencyORUnits").onclick = function() { //When the unit/currency is clicked
        UnitORCurrency.match(/Units|Time/) ? GM_setClipboard(`${Result} ${Type}`) : GM_setClipboard(Intl.NumberFormat(navigator.language, { style: 'currency', currency: GM_getValue("YourLocalCurrency") }).format(Result));
      };
    }

    function Get(url) { //Get the final converted time/currency value
      return new Promise(resolve => GM.xmlHttpRequest({
        method: "GET",
        url: url,
        onload: response => resolve(new DOMParser().parseFromString(response.responseText, 'text/html'))
      }));
    }
    //Time Converter_____________________________________________________________________________________________________________________________________________________________________________
    GM_registerMenuCommand(`${GM_getValue("TimeConverter") ? "Disable" : "Enable"} Time Converter`, function() { ToggleFeature("TimeConverter"); });
    const time = new RegExp(/^[ \t\xA0]*(?=.*?(\d{1,2}:\d{2}(?::\d{2})?\s?(?:[aApP]\.?[mM]\.?)?)|\d{1,2}(?::\d{2}(?::\d{2})?)?\s?(?:[aApP]\.?[mM]\.?)?)(?=.*?(PST|PDT|MST|MDT|CST|CDT|EST|EDT|AST|ADT|NST|NDT|GMT|BST|MET|CET|CEST|EET|EEST|WET|WEST|JST|KST|IST|MSK|UTC|PT))(?:\1[ \t\xA0]*\2|\2[ \t\xA0]*\1)[ \t\xA0]*$/i);

    if (GM_getValue("TimeConverter") === true && SelectedText.match(time) !== null) //If the TimeConverter is enabled and if the selected text is a time
    {
      const timeResponse = await Get(`https://www.google.com/search?q=${SelectedText.match(time)[0].replace("WET", "Western European Time")} to local time`);
      const ConvertedTime = timeResponse.querySelector(".aCAqKc")?.innerText;
      const Secs = SelectedText.match(time)[0].match(/(?:\d{1,2}:\d{2}(:\d{2})\s?[ap]\.?m)/i)?.[1] || '';
      ConvertedTime && ShowConversion('Time', '', ConvertedTime.replace(/(\d{1,2}):(\d{2})\s?([pP][mM])/, (_, h, m) => `${(h % 12 + 12) % 24}:${m}`).match(/[\d:]+/g)[0] + Secs); //Convert PM to 24-hour format if we got the final time conversion value
    }
    //Currencies Converter_______________________________________________________________________________________________________________________________________________________________________
    GM_registerMenuCommand(`${GM_getValue("CurrenciesConverter") ? "Disable" : "Enable"} Currencies Converter`, function() { ToggleFeature("CurrenciesConverter"); });
    const CurrencySymbols = new RegExp(/AU\$|HK\$|US\$|\$US|R\$|\$|¥|€|Rp|Kč|kr(?!w)|zł|£|฿|₩|лв|₪|円|₱|₽|руб|lei|Fr|krw|RON|TRY|₿|Br|₾|₴|₸|₺/i);
    const Currencies = new RegExp(/^[ \t\xA0]*\$?(?=.*?(\d+(?:.*\d+)?))(?=(?:\1[ \t\xA0]*)?(Dólares|dolares|dólares|dollars?|AU\$?D?|BGN|BRL|BCH|BTC|BYN|CAD|CHF|Fr|CNY|CZK|DKK|EUR|EGP|ETH|GBP|GEL|HKD|HUF|IDR|ILS|INR|JPY|LTC|KRW|MXN|NOK|NZD|PHP|PLN|RON|RUB|SEK|SGD|THB|TRY|USD|UAH|ZAR|KZT|YTL|\$|R\$|HK\$|US\$|\$US|¥|€|Rp|Kč|kr|krw|zł|£|฿|₩|лв|₪|円|₱|₽|руб|lei|Kč|₿|Br|₾|₴|₸|₺))(?:\1[ \t\xA0]*\2|\2[ \t\xA0]*\1)[ \t\xA0]*$/i); //https://regex101.com/r/6vTbtv/20 Davidebyzero

    if (GM_getValue("CurrenciesConverter") === true && SelectedText.match(Currencies) !== null) { //If Currencies Converter is enabled and if the selected text is a currency
      if (GM_getValue("YourLocalCurrency") === undefined) {
        const UserInput = prompt('Write your local currency.\nThe script will always use your local currency to make exchange-rate conversions.\n\n*Currency input examples:\nBRL\nCAD\nUSD\netc...\n\n*Press OK');
        GM_setValue("YourLocalCurrency", UserInput);
      }
      const currencyMap = { 'AU$': 'AUD', '$': 'USD', 'us$': 'USD', '$us': 'USD', 'r$': 'BRL', 'hk$': 'HKD', '¥': 'JPY', '€': 'EUR', 'rp': 'IDR', 'kč': 'CZK', 'kr': 'NOK', 'zł': 'PLN', '£': 'GBP', '฿': 'THB', '₩': 'KRW', 'лв': 'BGN', '₪': 'ILS', '円': 'JPY', '₱': 'PHP', '₽': 'RUB', 'руб': 'RUB', 'lei': 'RON', 'ron': 'Romanian Leu', 'krw': 'KRW', 'fr': 'CHF', '₿': 'BTC', 'Br': 'BYN', '₾': 'GEL', '₴': 'UAH', '₸': 'KZT', '₺': 'YTL', 'try': 'Turkish Lira' };
      if((currencyMap[SelectedText.match(CurrencySymbols)?.[0].toLowerCase()]||SelectedText.match(Currencies)[2]).toUpperCase() === GM_getValue("YourLocalCurrency").toUpperCase()) return; //Disable same unit conversion
      const CurrencySymbol = currencyMap[SelectedText.match(CurrencySymbols)?.[0].toUpperCase()] || SelectedText.match(Currencies)[2]; //Store the currency symbol
      const currencyResponse = await Get(`https://www.google.com/search?q=${SelectedText.replace(/[.,]/g, '').match(Currencies)[1]} ${CurrencySymbol} in ${GM_getValue("YourLocalCurrency")}`);
      const FinalCurrency = parseFloat(currencyResponse.querySelector(".SwHCTb, .pclqee").innerText.split(' ')[0].replaceAll(',', '')); //Store the FinalCurrency and erase all commas
      ShowConversion('Currencies', CurrencySymbol, FinalCurrency);
    }
    //Units Converter____________________________________________________________________________________________________________________________________________________________________________
    GM_registerMenuCommand(`${GM_getValue("UnitsConverter") ? "Disable" : "Enable"} Units Converter`, function() { ToggleFeature("UnitsConverter"); });
    const Units = new RegExp(/^[ \t\xA0]*(-?\d+(?:[., ]\d+)?)(?:[ \t\xA0]*(in|inch|inches|"|”|″|cm|cms|centimeters?|m|mt|mts|meters?|ft|kg|lbs?|pounds?|kilograms?|ounces?|g|ozs?|fl oz|fl oz \(us\)|fluid ounces?|kphs?|km\/h|kilometers per hours?|mhp|mphs?|meters per hours?|(?:°\s?|º\s?|)(?:degrees?\s+)?(?:celsius|fahrenheit|[CF])|km\/hs?|ml|milliliters?|l|liters?|litres?|gal|gallons?|yards?|yd|Millimeter|millimetre|kilometers?|mi|mm|miles?|ft|fl|feets?|grams?|kilowatts?|kws?|brake horsepower|mechanical horsepower|hps?|bhps?|miles per gallons?|mpgs?|liters per 100 kilometers?|lt?\/100km|liquid quarts?|lqs?|qt|foot-? ?pounds?|ft-?lbs?|lb fts?|newton-? ?meters?|n·?m))?(?:[ \t\xA0]*x[ \t\xA0]*(-?\d+(?:[., ]\d+)?)(?:[ \t\xA0]*(in|inch|inches|"|”|″|cm|cms|centimeters?|m|mt|mts|meters?|ft))?)?[ \t\xA0]*(?:\(\w+\)[ \t\xA0]*)?(?:[ \t\xA0]*\^(\d+\.?\d*))*$/i);

    if (GM_getValue("UnitsConverter") === true && SelectedText.match(/\^(\d+\.?\d*)/) || (SelectedText.match(Units)?.[1] && SelectedText.match(Units)?.[2] || SelectedText.match(Units)?.[3])) { //If the Units Converter option is enabled and if the selected text is a math power or an unit

      const selectedUnitType = (SelectedText.match(Units)[2]||SelectedText.match(Units)[4])?.toLowerCase();
      const SelectedUnitValue = SelectedText.match(Units)[1].replaceAll(',', '.');
      const SecondSelectedUnitValue = SelectedText.match(Units)[3]?.replaceAll(',', '.')||0;

      const convertValue = (value, unitType) => {
        const { factor, convert } = window.UConv[unitType] || {};
        return convert ? convert(value) : value * factor;
      };

      var NewUnit = window.UConv[selectedUnitType]?.unit || selectedUnitType;
      var ConvertedUnit = `${convertValue(parseFloat(SelectedUnitValue), selectedUnitType).toFixed(2)}${SecondSelectedUnitValue != 0 ? ` x ${convertValue(parseFloat(SecondSelectedUnitValue), selectedUnitType).toFixed(2)}` : ''}`;
      ConvertedUnit = SelectedText.match(/\^(\d+\.?\d*)/) ? (NewUnit = 'power', Math.pow(parseFloat(SelectedUnitValue), parseFloat(SelectedText.match(/\^(\d+\.?\d*)/)[1]))) : ConvertedUnit;
      ShowConversion('Units', NewUnit, ConvertedUnit);
    }
    //Mini Menu__________________________________________________________________________________________________________________________________________________________________________________
    if (shadowRoot.querySelector("#SearchBTN").innerText === 'Open') //If the Search BTN text is 'Open'
    {
      shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '19px'; //Increase the menu size
      shadowRoot.querySelector("#SearchBTN").innerText = 'Search'; //Display the BTN text as Search again
      shadowRoot.querySelectorAll(".AI-BG-box button").forEach(button => { button.style.marginLeft = ''; }); //Remove the margin left
      shadowRoot.querySelector("#OpenAfter").remove(); //Remove the custom Open white hover overlay
    }

    if (SelectedText.match(Links) !== null) //If the selected text is a link
    {
      shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '27px'; //Increase the menu size
      shadowRoot.querySelector("#SearchBTN").innerText = 'Open'; //Change the BTN text to Open
      shadowRoot.querySelectorAll(".AI-BG-box button").forEach(button => { button.style.marginLeft = '-2%'; }); //Add a margin left
      shadowRoot.innerHTML += toHTML(`<style id="OpenAfter"> #SearchBTN::after { width: 177% !important; transform: translate(-34%, -71%) !important; } </style> `); //Add a custom Open white hover overlay
    }

    shadowRoot.querySelector("#SearchBTN").onmousedown = function() {
      GM_openInTab(SelectedText.match(Links) ? SelectedText.replace(/^(?!https?:\/\/)(.+)$/, 'https://$1') : `https://www.google.com/search?q=${SelectedText.replaceAll('&', '%26').replace(/\s+/g, ' ')}`, { active: true, setParent: true, loadInBackground: true }); //Open link or Google and search for the selected text
      shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the menu
    };

    const menu = shadowRoot.querySelector("#highlight_menu");
    if (document.getSelection().toString().trim() !== '') { //If text has been selected
      const p = document.getSelection().getRangeAt(0).getBoundingClientRect(); //Store the selected position

      menu.classList.add('show'); //Show the menu
      menu.offsetHeight; //Trigger reflow by forcing a style calculation
      menu.style.left = p.left + (p.width / 2) - (menu.offsetWidth / 2) + 'px';
      menu.style.top = p.top - menu.offsetHeight - 18 + 'px';
      menu.classList.add('highlight_menu_animate');

      return; //Keep the menu open
    }
    menu.classList.remove('show'); //Hide the menu
    shadowRoot.querySelector("#SearchBTN span")?.remove(); //Return previous HTML
  }); //Finishes the mouseup event listener
  //AI Menu______________________________________________________________________________________________________________________________________________________________________________________
  var desiredVoice = null, isRecognizing = false;
  const HtmlMenu = document.createElement('div'); //Create a container div
  HtmlMenu.setAttribute('style', `width: 0px; height: 0px; display: none;`); //Hide the container div by default
  const shadowRoot = HtmlMenu.attachShadow({ mode: 'closed' });
  const UniqueLangs = navigator.languages.filter((l, i, arr) => !arr.slice(0, i).some(e => e.split('-')[0].toLowerCase() === l.split('-')[0].toLowerCase()) ); //Filter unique languages
  const Lang = UniqueLangs.join(' and into '); //Use 1 or more languages

  shadowRoot.innerHTML = toHTML(GM_getResourceText("AIMenuContent")); //Set the AI menu HTML+CSS

  document.addEventListener('keydown', function(e) {
    if ((e.shiftKey && e.code === 'Digit7' || e.key === '/') && (e.ctrlKey || e.metaKey)) { //Detect Ctrl/Cmd + / or Ctrl/Cmd + Shift + 7
      shadowRoot.querySelector("#prompt").value = document.activeElement.value?.trim() || ""; //If the focused element has text and isn't undefined, auto copy the texbox text into the AI prompt
      shadowRoot.querySelectorAll("#dictate, #CloseOverlay, .animated-prompt-box").forEach(el => el.classList.add('show')); //Show the AI transcript button, Close Overlay, and prompt box

      shadowRoot.querySelector("#CloseOverlay").onclick = function() {
        shadowRoot.querySelectorAll("#CloseOverlay, #AIBox, .animated-prompt-box, #AIBox.AnswerBox").forEach(el => el.classList.remove('show')); //Hide the Close Overlay, prompt and answer boxes
      };
    }
  });

  function SwitchMode() {
    if (shadowRoot.querySelector("#prompt").placeholder.match('about')) { //If the input bar contains the word "about"
      shadowRoot.querySelector("#AddContext").remove(); //Return original prompt input styles
      shadowRoot.querySelector("#context").classList.remove('show'); //Hide the context view
      shadowRoot.querySelector("#prompt").placeholder = 'Ask Gemini anything...'; //Return default placeholder
    }
    else
    {
      shadowRoot.querySelector("#context").classList.add('show'); //Show the context view
      shadowRoot.querySelector("#prompt").placeholder = `Ask about ${location.host.replace('www.','')}`; //Change placeholder
      shadowRoot.querySelector("#highlight_menu").insertAdjacentHTML('beforebegin', `<style id="AddContext"> #gemini { display: none; } #prompt { left: 14%; width: 71%; } #tabcontext { display: none; } .animated-prompt-box { --color-OrangeORLilac: #FF8051; } </style> `); //Show the context bar
    }
  }

  shadowRoot.querySelectorAll(".prompt-arrow").forEach(el => el.onclick = () => SwitchMode());

  function Generate(Prompt, button) { //Call the AI endpoint
    const IsLatin = !/\p{Script=Latin}/u.test(Prompt) ? `, add 2 headings, "Pronunciation:" and "Language:"` : '';
    const responsePrompt = Prompt.includes('?') ? 'Give me a very short, then a long, detailed answer' : 'Help me further understand/learn a term or topic from the text/word';
    const extraPromptForShortInput = Prompt.split(' ').length < 5 ? `\nAfter showing (in order) (add a heading as "${Prompt}") ${IsLatin} , a few possible "Synonyms:", "Definition:" and "Example:".` : '';
    const context = !!shadowRoot.querySelector("#context.show") ? `"${Prompt}"\n\nMainly base yourself on the text below\n\n${document.body.innerText}` : Prompt; //Add the page context if context is enabled
    const msg = button.match('translate') ? `Translate this text: "${Prompt.trim().slice(0, 215)}${Prompt.length > 215 ? '…' : ''}"` : button.match('Prompt') ? `${Prompt.trim().slice(0, 240)}${Prompt.length > 240 ? '…' : ''}` : `Help me further explore a term or topic from the text: "${Prompt.trim().slice(0, 180)}${Prompt.length > 180 ? '…' : ''}"`; //AI Box top text
    const AIPrompt = button.match('translate') ? `Translate into ${Lang} the following text:\n\n"${Prompt}"\n${extraPromptForShortInput}${UniqueLangs.length > 1 ? `\n\nYou must answer using only 1 language first, then use only the other language, don't mix both languages!\nAlso, be sure to say which language is the translated text from, if the text isn't into ${Lang}!\n\n"${Prompt}" should be translated for the other languages.\nUse ---  ${UniqueLangs.length-1}x to divide your answer into language sections.` : ''}` : button.match('Prompt') ? context : `${responsePrompt}: "${Prompt}"`;

    function handleState(state) { //Show #AIMenu + #dictate but hide #TopPause for the load/abort states. Do the opposite for the 'start' state.
      ["#AIMenu", "#dictate", "#TopPause"].forEach(el => {
        if (el === "#TopPause") {
          shadowRoot.querySelector(el).classList[state !== 'start' ? 'remove' : 'add']('show');
        } else {
          shadowRoot.querySelector(el).classList[state !== 'start' ? 'add' : 'remove']('show');
        }
      });
    }

    const request = GM.xmlHttpRequest({
      method: "POST",
      url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:streamGenerateContent?key=${GM_getValue("APIKey")}`,
      responseType: 'stream',
      headers: { "Content-Type": "application/json" },
      data: JSON.stringify({
        contents: [{
          parts: [{
            text: AIPrompt
          }]
        }],
        systemInstruction: {
          parts: [{
            text: `List of things you aren't allowed to say/do anything like:\n1 "Based on the provided text"\n2 "The text is already in"\n3 "No translation is needed"\n4 Ask for more context\n5 "You haven't provided context"\n6 Use bullet points for Synonyms`
      }]
        },
        safetySettings: [
          { category: "HARM_CATEGORY_HARASSMENT", threshold: "BLOCK_NONE" },
          { category: "HARM_CATEGORY_HATE_SPEECH", threshold: "BLOCK_NONE" },
          { category: "HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold: "BLOCK_NONE" },
          { category: "HARM_CATEGORY_DANGEROUS_CONTENT", threshold: "BLOCK_NONE" }
        ],
      }),
      onerror: function(err) {
        shadowRoot.querySelector("#msg").innerHTML = 'Error';
        shadowRoot.querySelector("#finalanswer").innerHTML = toHTML(`<br>Please copy and paste the error below:<br><a class="feedback" href="https://greatest.deepsurf.us/scripts/419825/feedback">Click here to report this bug</a><br><br> Prompt: ${Prompt}<br> Button: ${button}<br> Error: <pre>${JSON.stringify(err, null, 2)}</pre><br><br><br>`);
      },
      onload: function(response) {
        handleState('load');
      },
      onabort: function(response) {
        handleState('abort');
        shadowRoot.querySelector("#finalanswer").innerHTML = toHTML('<div>ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤResponse has been interrupted.<div>');
      },
      onloadstart: function(response) {
        handleState('start');
        shadowRoot.querySelector("#prompt").focus();
        shadowRoot.querySelector("#msg").innerHTML = msg;

        shadowRoot.querySelector("#copyAnswer").onclick = function() {
          shadowRoot.querySelector("#copyAnswer").style.display = 'none';
          shadowRoot.querySelector("#AnswerCopied").style.display = 'inline-flex';
          GM_setClipboard(shadowRoot.querySelector("#finalanswer").innerText.replace('ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ', ''));
          setTimeout(() => { //Return play BTN svg
            shadowRoot.querySelector("#copyAnswer").style.display = 'inline-flex';
            shadowRoot.querySelector("#AnswerCopied").style.display = 'none';
          }, 1000);
        };

        const reader = response.response.getReader();
        const decoder = new TextDecoder();
        var buffer = '', partialMarkdown = '';

        function readStream() {
          reader.read().then(({ value }) => {
            buffer += decoder.decode(value, { stream: true });
            var startIdx = 0;
            while (true) {
              const openBrace = buffer.indexOf('{', startIdx);
              if (openBrace === -1) break;
              var balance = 1, closeBrace = openBrace + 1;

              while (balance > 0 && closeBrace < buffer.length) {
                if (buffer[closeBrace] === '{') balance++;
                if (buffer[closeBrace] === '}') balance--;
                closeBrace++;
              }

              if (balance !== 0) break; //Incomplete JSON object

              const jsonString = buffer.substring(openBrace, closeBrace);
              const item = JSON.parse(jsonString);
              partialMarkdown += item.candidates?.[0]?.content?.parts?.[0]?.text || item.error.message; //If there's no error show the AI answer, else show the error message

              const tempDiv = document.createElement('div');
              tempDiv.innerHTML = window.marked.parse(partialMarkdown);

              shadowRoot.querySelector("#finalanswer").innerHTML = '';
              shadowRoot.querySelector("#finalanswer").appendChild(tempDiv);
              startIdx = closeBrace;
            }
            buffer = buffer.substring(startIdx);
            readStream();
          });
        }

        readStream();

        shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the mini menu on the page
        shadowRoot.querySelectorAll("#CloseOverlay, #AIBox, .animated-prompt-box, #AIBox.AnswerBox").forEach(el => el.classList.add('show')); //Show the AI Close Overlay, prompt and answer box

        let silenceTimer;
        const SILENCE_TIMEOUT = 5000;
        var SpeechRecognition = SpeechRecognition || window.webkitSpeechRecognition;
        const recognition = new SpeechRecognition();
        recognition.interimResults = true; //Show partial results
        recognition.continuous = true; //Keep listening until stopped

        var transcript = ""; //Add words
        shadowRoot.querySelector("#CloseOverlay").onclick = function() {
          [...shadowRoot.querySelector("#finalanswer div").childNodes].slice(0, -1).forEach(node => node.remove()); //Reset the text content
          shadowRoot.querySelectorAll("#CloseOverlay, #AIBox, .animated-prompt-box, #AIBox.AnswerBox").forEach(el => el.classList.remove('show')); //Hide the Close Overlay, prompt and answer boxes
          recognition.stop(); //Stop recognizing audio
          speechSynthesis.cancel(); //Stop speaking
          request.abort(); //Abort any ongoing request
          if (shadowRoot.querySelector("#gemini").style.display === 'none') {
            shadowRoot.querySelector("#AddContext").remove(); //Return original prompt input styles
            shadowRoot.querySelector("#context").classList.remove('show');
            shadowRoot.querySelector("#prompt").placeholder = 'Ask Gemini anything...'; //Return default placeholder
          }
        };

        shadowRoot.querySelector("#TopPause").onclick = function() {
          request.abort();
        };

        recognition.onend = function() {
          clearTimeout(silenceTimer); //Clear any pending timeout
          isRecognizing = false;

          shadowRoot.querySelectorAll('.state1, .state2, .state3').forEach((state, index) => { //ForEach SVG animation state
            index.toString().match(/1|2/) && (state.style.display = 'none'); //Show only the 1 state
            state.classList.remove('animate'+index); //Stop the voice recording animation
          });
          transcript ? Generate(transcript, shadowRoot.querySelector("#prompt").className) : shadowRoot.querySelector("#finalanswer").innerHTML = toHTML(`<br>No audio detected. Please try again or check your mic settings.ㅤㅤㅤㅤㅤㅤㅤㅤㅤ<br><br>`); //Call the AI API if transcript audio words were detected or show an error message
        }; //Finish the recognition end event listener

        recognition.onresult = function(event) {
          clearTimeout(silenceTimer); //Reset the silence timer on new input
          silenceTimer = setTimeout(() => {
            if (isRecognizing) {
              recognition.stop();
            }
          }, SILENCE_TIMEOUT);

          transcript = ""; // Clear the transcript at the start of the event
          for (var i = 0; i < event.results.length; i++) { //For all transcript results
            transcript += event.results[i][0].transcript + ' '; //Concatenate all intermediate transcripts
          }
          shadowRoot.querySelector("#msg").innerText = transcript.slice(0, 240) + (transcript.length > 240 ? '…' : '');
        };

        shadowRoot.querySelector("#dictate").onclick = function() {
          if (isRecognizing) {
            recognition.stop();
          } else {
            isRecognizing = true;
            recognition.start();
            shadowRoot.querySelectorAll('.state1, .state2, .state3').forEach((state, index) => { //ForEach SVG animation state
              state.style.display = 'unset'; //Show all states
              state.classList.add('animate'+index); //Start the voice recording animation
            });
          }
        };

        speechSynthesis.onvoiceschanged = () => desiredVoice = speechSynthesis.getVoices().find(v => v.name === "Microsoft Zira - English (United States)"); //Find and store the desired voice
        speechSynthesis.onvoiceschanged(); //Handle cases where the event doesn't fire

        shadowRoot.querySelectorAll("#speak, #SpeakingPause").forEach(function(el) {
          el.onclick = function() { //When the speak or the bottom pause BTNs are clicked
            if (speechSynthesis.speaking) {
              speechSynthesis.cancel();
              shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN
              shadowRoot.querySelector("#SpeakingPause").classList.remove('show'); //Hide the pause BTN
            }
            else
            {
              shadowRoot.querySelector("#speak").style.display = 'none'; //Hide the play BTN
              shadowRoot.querySelector("#SpeakingPause").classList.add('show');

              var audio = new SpeechSynthesisUtterance(shadowRoot.querySelector("#finalanswer").innerText.replace(/.*?\(?..-..\)?|[^\p{L}\p{N}\s%.,!?]/gui, '')); //Play the AI response text, removing non-alphanumeric characters and lang locales for better pronunciation
              audio.voice = desiredVoice; //Use the desiredVoice
              speechSynthesis.speak(audio); //Speak the text

              audio.onend = (event) => {
                shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN
                shadowRoot.querySelector("#SpeakingPause").classList.remove('show');
              };
            }
          };
        });

        shadowRoot.querySelector("#NewAnswer").onclick = function() {
          recognition.stop(); //Stop recognizing audio
          speechSynthesis.cancel(); //Stop speaking
          Generate(Prompt, button); //Call the AI API
        };
      } //Finishes the onloadstart event listener
    });//Finishes the GM.xmlHttpRequest function
  } //Finishes the Generate function

  shadowRoot.querySelector("#prompt").addEventListener("keydown", (event) => {
    event.stopPropagation(); //Don't execute event listeners of the main site
    if (event.key === "Enter") {
      Generate(shadowRoot.querySelector("#prompt").value, shadowRoot.querySelector("#prompt").className); //Call the AI API
      shadowRoot.querySelector("#prompt").value = ''; //Erase the prompt text
    }
    if (event.key === "Tab") {
      SwitchMode(event);
    }
    setTimeout(() => { //Wait for the code above to execute
      shadowRoot.querySelector("#prompt").focus(); //Refocus on the input bar
    }, 0);
  });

  shadowRoot.querySelectorAll("#AIBTN").forEach(function(button) {
    button.onmousedown = function(event, i) { //When the Explore or the Translate BTNs are clicked
      if (GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //Set up the API Key if it isn't already set
        GM_setValue("APIKey", prompt('Enter your API key\n*Press OK\n\nYou can get a free API key at https://aistudio.google.com/app/apikey'));
      }
      if (GM_getValue("APIKey") !== null && GM_getValue("APIKey") !== '') {
        Generate(SelectedText, this.className); //Call the AI API
      }
    };
  });

  if (document.body.textContent !== '' || document.body.innerText !== '') //If the body has any text
  {
    document.body.appendChild(HtmlMenu); //Add the script menu div container
  }

  shadowRoot.querySelector('#CopyBTN').onmousedown = function() {
    GM_setClipboard(SelectedText);
  };

  window.addEventListener('scroll', async function() {
    shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the menu
  });
}