AI Everywhere

Highly customizable 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 Converters, all features can be easily modified or disabled.

当前为 2024-08-16 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name AI Everywhere
  3. // @namespace OperaBrowserGestures
  4. // @description Highly customizable 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 Converters, all features can be easily modified or disabled.
  5. // @version 63
  6. // @author hacker09
  7. // @include *
  8. // @exclude https://accounts.google.com/v3/signin/*
  9. // @icon https://i.imgur.com/8iw8GOm.png
  10. // @grant GM_registerMenuCommand
  11. // @grant GM_getResourceText
  12. // @grant GM.xmlHttpRequest
  13. // @grant GM_deleteValue
  14. // @grant GM_openInTab
  15. // @grant window.close
  16. // @run-at document-end
  17. // @grant GM_setValue
  18. // @grant GM_getValue
  19. // @connect google.com
  20. // @connect generativelanguage.googleapis.com
  21. // @resource AICSS https://hacker09.glitch.me/AICSS.css
  22. // @require https://cdnjs.cloudflare.com/ajax/libs/marked/13.0.2/marked.min.js
  23. // ==/UserScript==
  24.  
  25. /* jshint esversion: 11 */
  26.  
  27. const BypassTT = window.trustedTypes?.createPolicy('BypassTT', { createHTML: HTML => HTML }); //Bypass trustedTypes
  28.  
  29. if (GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //Set up the API Key
  30. window.onload = function() {
  31. if (location.href === 'https://aistudio.google.com/app/apikey' && document.querySelector(".apikey-link") !== null) {
  32. setTimeout(function() {
  33. document.querySelectorAll(".apikey-link")[1].click(); //Click on the API Key
  34. setTimeout(function() {
  35. GM_setValue("APIKey", document.querySelector(".apikey-text").innerText); //Store the API Key
  36. (GM_getValue("APIKey") !== undefined && GM_getValue("APIKey") !== null && GM_getValue("APIKey") !== '') ? alert('API Key automatically added!') : alert('Failed to automatically add API Key!');
  37. }, 500);
  38. }, 500);
  39. }
  40. };
  41. }
  42.  
  43. // Mouse Gestures _________________________________________________________________________________________________________________________________________________________
  44. GM_registerMenuCommand("Enable/Disable Mouse Gestures", MouseGestures);
  45. if (GM_getValue("MouseGestures") !== true && GM_getValue("MouseGestures") !== false) {
  46. GM_setValue("MouseGestures", true);
  47. }
  48.  
  49. function MouseGestures() //Enable/disable MouseGestures
  50. {
  51. if (GM_getValue("MouseGestures") === true) {
  52. GM_setValue("MouseGestures", false);
  53. }
  54. else {
  55. GM_setValue("MouseGestures", true);
  56. location.reload();
  57. }
  58. }
  59.  
  60. if (GM_getValue("MouseGestures") === true) //If the MouseGestures is enabled
  61. {
  62. const SENSITIVITY = 3;
  63. const TOLERANCE = 3;
  64.  
  65. const funcs = { //Store the MouseGestures functions
  66.  
  67. 'L': function() { //Detect the Left movement
  68. window.history.back();
  69. },
  70.  
  71. 'R': function() { //Detect the Right movement
  72. window.history.forward();
  73. },
  74.  
  75. 'D': function() { //Detect the Down movement
  76. if (IsShiftNotPressed === true) { //If the shift key isn't being pressed
  77. GM_openInTab(link, {
  78. active: true,
  79. insert: true,
  80. setParent: true
  81. });
  82. }
  83. },
  84.  
  85. 'UD': function() { //Detect the Up+Down movement
  86. location.reload();
  87. },
  88.  
  89. 'DR': function(e) { //Detect the Down+Right movement
  90. top.close();
  91. e.preventDefault();
  92. e.stopPropagation();
  93. },
  94.  
  95. 'DU': function() { //Detect the Down+Up movement
  96. GM_openInTab(link, {
  97. active: false,
  98. insert: true,
  99. setParent: true
  100. });
  101. }
  102.  
  103. };
  104.  
  105. //Math codes to track the mouse movement gestures
  106. const s = 1 << ((7 - SENSITIVITY) << 1);
  107. const t1 = Math.tan(0.15708 * TOLERANCE),t2 = 1 / t1;
  108.  
  109. let x, y, path;
  110.  
  111. const tracer = function(e) { //Start the const tracer
  112. let cx = e.clientX, cy = e.clientY, deltaX = cx - x, deltaY = cy - y, distance = deltaX * deltaX + deltaY * deltaY;
  113. if (distance > s) {
  114. let slope = Math.abs(deltaY / deltaX), direction = '';
  115. if (slope > t1) {
  116. direction = deltaY > 0 ? 'D' : 'U';
  117. } else if (slope <= t2) {
  118. direction = deltaX > 0 ? 'R' : 'L';
  119. }
  120. if (path.charAt(path.length - 1) !== direction) {
  121. path += direction;
  122. }
  123. x = cx;
  124. y = cy;
  125. }
  126. };
  127.  
  128. window.addEventListener('mousedown', function(e) {
  129. if (e.which === 3) {
  130. x = e.clientX;
  131. y = e.clientY;
  132. path = "";
  133. window.addEventListener('mousemove', tracer, false); //Detect the mouse position
  134. }
  135. }, false);
  136.  
  137. var IsShiftNotPressed = true; //Hold the shift key status
  138. window.addEventListener("contextmenu", function(e) { //When the shift key is/isn't pressed
  139. if (e.shiftKey) {
  140. IsShiftNotPressed = false;
  141. open(link, '_blank', 'height=' + screen.height + ',width=' + screen.width);
  142. }
  143. if (LeftClicked === true) { //If the Left Click was released when the Rocker Mouse Gestures were enabled
  144. e.preventDefault();
  145. e.stopPropagation();
  146. }
  147. setTimeout(function() {
  148. IsShiftNotPressed = true;
  149. }, 500);
  150. }, false);
  151.  
  152. window.addEventListener('contextmenu', function(e) { //When the right click BTN is released
  153. window.removeEventListener('mousemove', tracer, false); //Track the mouse movements
  154. if (path !== "") {
  155. e.preventDefault();
  156. if (funcs.hasOwnProperty(path)) {
  157. funcs[path]();
  158. }
  159. }
  160. }, false);
  161.  
  162. var link;
  163. Array.from(document.querySelectorAll('a')).forEach(Element => Element.onmouseover = function() {
  164. link = this.href; //Store the hovered link to a variable
  165. });
  166.  
  167. Array.from(document.querySelectorAll('a')).forEach(Element => Element.onmouseout = function() {
  168. const PreviousLink = link; //Save the hovered link to another variable
  169. setTimeout(function() {
  170. if (PreviousLink === link) //If the hovered link is still the same as the previously hovered Link
  171. {
  172. link = 'about:newtab'; //Make the script open a new browser tab when the mouse leaves any link that was hovered
  173. }
  174. }, 200);
  175. });
  176.  
  177. }
  178.  
  179. //Rocker Mouse Gesture Settings _________________________________________________________________________________________________________________________________________________________
  180. GM_registerMenuCommand("Enable/Disable Rocker Mouse Gestures", RockerMouseGestures);
  181. if (GM_getValue("RockerMouseGestures") !== true && GM_getValue("RockerMouseGestures") !== false) { //Set up the RockerMouseGestures
  182. GM_setValue("RockerMouseGestures", false);
  183. }
  184.  
  185. function RockerMouseGestures() //Enable/disable RockerMouseGestures
  186. {
  187. if (GM_getValue("RockerMouseGestures") === true) {
  188. GM_setValue("RockerMouseGestures", false);
  189. }
  190. else {
  191. GM_setValue("RockerMouseGestures", true);
  192. location.reload();
  193. }
  194. }
  195.  
  196. if (GM_getValue("RockerMouseGestures") === true || GM_getValue("SearchHiLight") === true) //If the RockerMouseGestures or the SearchHiLight is enabled
  197. {
  198. var LeftClicked, RightClicked;
  199. window.addEventListener("mousedown", function(e) { //Track which side of the mouse was the first one to be pressed
  200. switch (e.button) {
  201. case 0:
  202. LeftClicked = true;
  203. break;
  204. case 2:
  205. RightClicked = true;
  206. break;
  207. }
  208. }, false);
  209.  
  210. window.addEventListener("mouseup", function(e) { //Track which side of the mouse was the last one to be released
  211. switch (e.button) {
  212. case 0:
  213. LeftClicked = false;
  214. break;
  215. case 2:
  216. RightClicked = false;
  217. break;
  218. }
  219. if (LeftClicked && RightClicked === false) { //If Left was Clicked and then Right Click was released
  220. history.back(); //Go Back
  221. }
  222. if (RightClicked && LeftClicked === false) { //If Right was Clicked and then Left Click was released
  223. history.forward(); //Go Forward
  224. }
  225. }, false);
  226. }
  227.  
  228. //SearchHighLight + CurrenciesConverter + UnitsConverter _______________________________________________________________________________________________________________________________________
  229. GM_registerMenuCommand("Enable/Disable SearchHiLight", SearchHiLight);
  230. if (GM_getValue("SearchHiLight") !== true && GM_getValue("SearchHiLight") !== false) { //Set up the SearchHiLight
  231. GM_setValue("SearchHiLight", true);
  232. }
  233.  
  234. if (GM_getValue("CurrenciesConverter") !== true && GM_getValue("CurrenciesConverter") !== false) {
  235. GM_setValue("CurrenciesConverter", true);
  236. }
  237.  
  238. if (GM_getValue("UnitsConverter") !== true && GM_getValue("UnitsConverter") !== false) {
  239. GM_setValue("UnitsConverter", true);
  240. }
  241.  
  242. function SearchHiLight() //Enable/disable the SearchHiLight and the Currency/Unit converters
  243. {
  244. if (GM_getValue("SearchHiLight") === true) {
  245. GM_setValue("SearchHiLight", false);
  246. GM_setValue("CurrenciesConverter", false);
  247. GM_deleteValue('YourLocalCurrency');
  248. GM_setValue("UnitsConverter", false);
  249. }
  250. else {
  251. GM_setValue("SearchHiLight", true);
  252.  
  253. if (confirm('If you want to enable the Currency Converter press OK.'))
  254. {
  255. GM_setValue("CurrenciesConverter", true);
  256. }
  257. else
  258. {
  259. GM_setValue("CurrenciesConverter", false);
  260. }
  261.  
  262. if (confirm('If you want to enable the Units Converter press OK.'))
  263. {
  264. GM_setValue("UnitsConverter", true);
  265. }
  266. else
  267. {
  268. GM_setValue("UnitsConverter", false);
  269. }
  270. location.reload();
  271. }
  272. }
  273.  
  274. if (GM_getValue("SearchHiLight") === true) //If the SearchHiLight is enabled
  275. {
  276. var SelectedTextIsLink, FinalCurrency, SelectedText, SelectedTextSearch = '';
  277. const Links = new RegExp(/\.org|\.ly|\.net|\.co|\.tv|\.me|\.biz|\.club|\.site|\.br|\.gov|\.io|\.jp|\.edu|\.au|\.in|\.it|\.ca|\.mx|\.fr|\.tw|\.il|\.uk|\.zoom\.us|\youtu.be/i);
  278.  
  279. window.addEventListener('load', function() { //Start the script after the page loads
  280. document.body.addEventListener('mouseup', function() { //When the user releases the mouse click after selecting something
  281. SelectedText = getSelection().toString(); //Store the selected text
  282. SelectedTextSearch = getSelection().toString().replaceAll('&', '%26'); //Store the selected text to be opened on Google
  283.  
  284. //CurrenciesConverter _______________________________________________________________________________________________________________________________________
  285. if (GM_getValue("CurrenciesConverter") === true) { //If Currencies Converter is enabled
  286. shadowRoot.querySelector("#ShowCurrencyORUnits").innerText = ''; //Remove the previous Currency text
  287. shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '34%'; //Bring the Translate AI box to it's original position
  288. const Currencies = new RegExp(/^[ \t\xA0]*(?=.*?(\d+(?:.\d+)?))(?=(?:\1[ \t\xA0]*)?(Dólares|dolares|dólares|dollars|AUD|BGN|BRL|BCH|BTC|BYN|CAD|CHF|CNY|CZK|DKK|EUR|EGP|ETH|GBP|GEL|HKD|HRK|HUF|IDR|ILS|INR|JPY|LTC|KRW|MXN|MYR|NOK|NZD|PHP|PLN|RON|RM|RUB|SEK|SGD|THB|TRY|USD|UAH|ZAR|KZT|YTL|\$|R\$|HK\$|US\$|\$US|¥|€|Rp|kn|Kč|kr|zł|£|฿|₩))(?:\1[ \t\xA0]*\2|\2[ \t\xA0]*\1)[ \t\xA0]*$/i);
  289.  
  290. if (SelectedText.match(Currencies) !== null) //If the selected text is a currency
  291. {
  292. if (GM_getValue("YourLocalCurrency") === undefined) {
  293. var 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');
  294. GM_setValue("YourLocalCurrency", UserInput);
  295. }
  296.  
  297. (async () => { //Get the final converted currency value
  298. var CurrencySymbol = SelectedText.match(Currencies)[2]; //Store the currency symbol
  299. const CurrencySymbols = new RegExp(/\$|R\$|HK\$|US\$|\$US|¥|€|Rp|kn|Kč|kr|zł|£|฿|₩/i);
  300. if (SelectedText.match(Currencies)[2].match(CurrencySymbols) !== null) //If the selected currency contains a symbol
  301. {
  302. switch (SelectedText.match(CurrencySymbols)[0].toLowerCase()) { //If the selected currency contains a symbol
  303. case '$':
  304. case 'us$':
  305. case '$us':
  306. CurrencySymbol = 'USD';
  307. break;
  308. case 'r$':
  309. CurrencySymbol = 'BRL';
  310. break;
  311. case 'hk$':
  312. CurrencySymbol = 'HKD';
  313. break;
  314. case "¥":
  315. CurrencySymbol = 'JPY';
  316. break;
  317. case '€':
  318. CurrencySymbol = 'EUR';
  319. break;
  320. case 'rp':
  321. CurrencySymbol = 'IDR';
  322. break;
  323. case 'kn':
  324. CurrencySymbol = 'HRK';
  325. break;
  326. case 'kč':
  327. CurrencySymbol = 'CZK';
  328. break;
  329. case 'kr':
  330. CurrencySymbol = 'DKK';
  331. break;
  332. case 'zł':
  333. CurrencySymbol = 'PLN';
  334. break;
  335. case '£':
  336. CurrencySymbol = 'GBP';
  337. break;
  338. case '฿':
  339. CurrencySymbol = 'THB';
  340. break;
  341. case '₩':
  342. CurrencySymbol = 'KRW';
  343. break;
  344. }
  345. }
  346.  
  347. GM.xmlHttpRequest({ //Get the final converted currency value
  348. method: "GET",
  349. url: `https://www.google.com/search?q=${SelectedText.match(Currencies)[1]} ${CurrencySymbol} in ${GM_getValue("YourLocalCurrency")}`,
  350. onload: (response) => {
  351. const newDocument = new DOMParser().parseFromString(response.responseText, 'text/html'); //Parse the fetch response
  352. const FinalCurrency = parseFloat(newDocument.querySelector(".SwHCTb").innerText.split(' ')[0].replaceAll(',', '')); //Store the FinalCurrency and erase all commas
  353.  
  354. shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top
  355. if (SelectedText.match(Currencies)[2].match(CurrencySymbols) !== null) { //If the selected currency contains a symbol
  356. shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(CurrencySymbol + ' 🠂 ' + Intl.NumberFormat(navigator.language, {
  357. style: 'currency',
  358. currency: GM_getValue("YourLocalCurrency")
  359. }).format(FinalCurrency) + '<span class="GreyBar"> │</span>'); //Show the FinalCurrency
  360. } else { //If the selected currency contains no symbol
  361. shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(Intl.NumberFormat(navigator.language, {
  362. style: 'currency',
  363. currency: GM_getValue("YourLocalCurrency")
  364. }).format(FinalCurrency) + '<span class="GreyBar"> │</span>'); //Show the FinalCurrency
  365. }
  366.  
  367. var htmlcode = shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML; //Save the currency html
  368. shadowRoot.querySelector("#ShowCurrencyORUnits").onmousemove = function() { //When the mouse hovers over the currency
  369. shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)('Copy<span class="GreyBar"> │</span>'); //Change the element text to copy
  370. };
  371.  
  372. shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseout = function() { //When the mouse leaves the currency
  373. shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(htmlcode); //Return the previous converted currency html
  374. };
  375.  
  376. shadowRoot.querySelector("#ShowCurrencyORUnits").onclick = function() { //When the user clicks on the currency
  377. shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top
  378. navigator.clipboard.writeText(Intl.NumberFormat(navigator.language, {
  379. style: 'currency',
  380. currency: GM_getValue("YourLocalCurrency")
  381. }).format(FinalCurrency)); //Copy the Final Currency
  382. };
  383. }
  384. });
  385.  
  386. })();
  387. }
  388. }
  389.  
  390. //UnitsConverter _________________________________________________________________________________________________________________________________________________________________________
  391. if (GM_getValue("UnitsConverter") === true) { //If the Units Converter option is enabled
  392. shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '34%'; //Bring the Translate AI box to its original position
  393. shadowRoot.querySelector("#ShowCurrencyORUnits").innerText = ''; //Remove the previous Units text
  394. const Units = new RegExp(/^[ \t\xA0]*(-?\d+(?:[., ]\d+)?)[ \t\xA0]*("|”|inch|inches|in|cms?|centimeters?|meters?|ft|kg|lbs?|pounds?|kilograms?|ounces?|g|ozs?|fl oz|fl oz (us)|fluid ounces?|kphs?|km\/h|kilometers per hours?|mphs?|meters per hours?|°?º?[CF]|km\/hs?|ml|milliliters?|l|liters?|litres?|gal|gallons?|yards?|yd|Millimeter|millimetre|kilometers?|mi|mm|miles?|km|ft|fl|feets?|mts?|grams?|kilowatts?|kws?|brake horsepower|mechanical horsepower|hps?|bhps?|miles per gallons?|mpgs?|liters per 100 kilometers?|l\/100km|liquid quarts?|lqs?|foot-?pounds?|ft-?lbs?|lb fts?|newton-?meters?|nm|\^\d+)[ \t\xA0]*(?:\(\w+\)[ \t\xA0]*)?$/i);
  395.  
  396. if (SelectedText.match(Units) !== null) //If the selected text is an unit
  397. {
  398. const SelectedUnitValue = SelectedText.match(Units)[1].replaceAll(',', '.'); //Get the selected unit value
  399.  
  400. switch (SelectedText.match(Units)[2].toLowerCase()) { //Convert the selected unit
  401. case 'inch':
  402. case 'inches':
  403. case 'in':
  404. case '"':
  405. case '”':
  406. var NewUnit = 'cm';
  407. var ConvertedUnit = parseFloat(SelectedUnitValue * 2.54).toFixed(2);
  408. break;
  409. case 'centimeter':
  410. case 'centimeters':
  411. case 'cm':
  412. case 'cms':
  413. NewUnit = 'in';
  414. ConvertedUnit = parseFloat(SelectedUnitValue / 2.54).toFixed(2);
  415. break;
  416. case 'mt':
  417. case 'mts':
  418. case 'meters':
  419. case 'meter':
  420. NewUnit = 'ft';
  421. ConvertedUnit = parseFloat(SelectedUnitValue * 3.281).toFixed(2);
  422. break;
  423. case 'kg':
  424. case 'kilograms':
  425. case 'kilogram':
  426. NewUnit = 'lb';
  427. ConvertedUnit = parseFloat(SelectedUnitValue * 2.205).toFixed(2);
  428. break;
  429. case 'pound':
  430. case 'pounds':
  431. case 'lb':
  432. case 'lbs':
  433. NewUnit = 'kg';
  434. ConvertedUnit = parseFloat(SelectedUnitValue / 2.205).toFixed(2);
  435. break;
  436. case 'ounce':
  437. case 'ounces':
  438. case 'oz':
  439. case 'ozs':
  440. NewUnit = 'g';
  441. ConvertedUnit = parseFloat(SelectedUnitValue * 28.35).toFixed(2);
  442. break;
  443. case 'g':
  444. case 'gram':
  445. case 'grams':
  446. NewUnit = 'oz';
  447. ConvertedUnit = parseFloat(SelectedUnitValue / 28.35).toFixed(2);
  448. break;
  449. case 'kilometer':
  450. case 'kilometers':
  451. NewUnit = 'mi';
  452. ConvertedUnit = parseFloat(SelectedUnitValue / 1.609).toFixed(2);
  453. break;
  454. case 'kph':
  455. case 'kphs':
  456. case 'km/h':
  457. case 'km/hs':
  458. case 'kilometers per hour':
  459. case 'kilometers per hours':
  460. NewUnit = 'mph';
  461. ConvertedUnit = parseFloat(SelectedUnitValue * 1000).toFixed(2);
  462. break;
  463. case 'mph':
  464. case 'mphs':
  465. case 'meters per hour':
  466. case 'meters per hours':
  467. NewUnit = 'km/h';
  468. ConvertedUnit = parseFloat(SelectedUnitValue / 1.000).toFixed(2);
  469. break;
  470. case 'mi':
  471. case 'mile':
  472. case 'miles':
  473. NewUnit = 'km';
  474. ConvertedUnit = parseFloat(SelectedUnitValue * 1.609).toFixed(2);
  475. break;
  476. case '°c':
  477. NewUnit = '°F';
  478. ConvertedUnit = parseFloat((SelectedUnitValue * 9 / 5) + 32).toFixed(2);
  479. break;
  480. case '°f':
  481. NewUnit = '°C';
  482. ConvertedUnit = parseFloat((SelectedUnitValue - 32) * 5 / 9).toFixed(2);
  483. break;
  484. case 'ºc':
  485. NewUnit = 'ºF';
  486. ConvertedUnit = parseFloat((SelectedUnitValue * 9 / 5) + 32).toFixed(2);
  487. break;
  488. case 'ºf':
  489. NewUnit = 'ºC';
  490. ConvertedUnit = parseFloat((SelectedUnitValue - 32) * 5 / 9).toFixed(2);
  491. break;
  492. case 'ml':
  493. case 'milliliter':
  494. case 'milliliters':
  495. NewUnit = 'fl oz (US)';
  496. ConvertedUnit = parseFloat(SelectedUnitValue / 29.574).toFixed(2);
  497. break;
  498. case 'fl oz (US)':
  499. case 'fl oz':
  500. case 'fl':
  501. case 'fluid ounce':
  502. case 'fluid ounces':
  503. NewUnit = 'ml';
  504. ConvertedUnit = parseFloat(SelectedUnitValue * 29.574).toFixed(2);
  505. break;
  506. case 'l':
  507. case 'litre':
  508. case 'liter':
  509. case 'litres':
  510. case 'liters':
  511. NewUnit = 'gal (US)';
  512. ConvertedUnit = parseFloat(SelectedUnitValue / 3.785).toFixed(2);
  513. break;
  514. case 'gal':
  515. case 'gallon':
  516. case 'gallons':
  517. NewUnit = 'lt';
  518. ConvertedUnit = parseFloat(SelectedUnitValue * 3.785).toFixed(2);
  519. break;
  520. case 'yard':
  521. case 'yards':
  522. case 'yd':
  523. NewUnit = 'm';
  524. ConvertedUnit = parseFloat(SelectedUnitValue / 1.094).toFixed(2);
  525. break;
  526. case 'mm':
  527. case 'millimetre':
  528. case 'Millimeters':
  529. NewUnit = 'in';
  530. ConvertedUnit = parseFloat(SelectedUnitValue / 25.4).toFixed(2);
  531. break;
  532. case 'ft':
  533. case 'feet':
  534. case 'feets':
  535. NewUnit = 'mt';
  536. ConvertedUnit = parseFloat(SelectedUnitValue * 0.3048).toFixed(2);
  537. break;
  538. case 'kw':
  539. case 'kws':
  540. case 'kilowatt':
  541. case 'kilowatts':
  542. NewUnit = 'mhp';
  543. ConvertedUnit = parseFloat(SelectedUnitValue * 1.341).toFixed(2);
  544. break;
  545. case 'mhp':
  546. case 'mhps':
  547. case 'hp':
  548. case 'hps':
  549. case 'brake horsepower':
  550. case 'mechanical horsepower':
  551. NewUnit = 'kw';
  552. ConvertedUnit = parseFloat(SelectedUnitValue / 1.341).toFixed(2);
  553. break;
  554. case 'mpg':
  555. case 'mpgs':
  556. case 'miles per gallon':
  557. case 'miles per gallons':
  558. NewUnit = 'l/100km';
  559. ConvertedUnit = parseFloat(235.215 / SelectedUnitValue).toFixed(2);
  560. break;
  561. case 'l/100km':
  562. case 'liters per 100 kilometer':
  563. case 'liters per 100 kilometers':
  564. NewUnit = 'US mpg';
  565. ConvertedUnit = parseFloat(235.215 / SelectedUnitValue).toFixed(2);
  566. break;
  567. case 'lq':
  568. case 'lqs':
  569. case 'liquid quart':
  570. case 'liquid quarts':
  571. NewUnit = 'l';
  572. ConvertedUnit = parseFloat(SelectedUnitValue / 1.057).toFixed(2);
  573. break;
  574. case 'liter':
  575. case 'liters':
  576. NewUnit = 'lqs';
  577. ConvertedUnit = parseFloat(SelectedUnitValue * 1.057).toFixed(2);
  578. break;
  579. case 'foot-pound':
  580. case 'foot-pounds':
  581. case 'foot pound':
  582. case 'foot pounds':
  583. case 'ft-lbs':
  584. case 'ft-lb':
  585. case 'ft lbs':
  586. case 'ft lb':
  587. case 'lb ft':
  588. case 'lb fts':
  589. NewUnit = 'Nm';
  590. ConvertedUnit = parseFloat(SelectedUnitValue * 1.3558179483).toFixed(2);
  591. break;
  592. case 'newton-meter':
  593. case 'newton-meters':
  594. case 'newton meter':
  595. case 'newton meters':
  596. case 'nm':
  597. NewUnit = 'ft lb';
  598. ConvertedUnit = parseFloat(SelectedUnitValue / 1.3558179483).toFixed(2);
  599. break;
  600. case (SelectedText.match(Units)[2].replaceAll(',', '.').match(/\^/)).input:
  601. NewUnit = 'power';
  602. ConvertedUnit = Math.pow(parseFloat(SelectedUnitValue), parseFloat(SelectedText.split('^')[1]));
  603. break;
  604. }
  605.  
  606. setTimeout(function() {
  607. shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top
  608. shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(ConvertedUnit + ' ' + NewUnit + '<span class="GreyBar"> │</span> '); //Show the converted unit results
  609.  
  610. var htmlcode = shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML; //Save the converted unit value
  611. shadowRoot.querySelector("#ShowCurrencyORUnits").onmousemove = function() { //When the mouse hovers the unit
  612. shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)('Copy<span class="GreyBar"> │</span>'); //Change the element text to copy
  613. };
  614.  
  615. shadowRoot.querySelector("#ShowCurrencyORUnits").onmouseout = function() { //When the mouse leaves the unit
  616. shadowRoot.querySelector("#ShowCurrencyORUnits").innerHTML = (html => BypassTT?.createHTML(html) || html)(htmlcode); //Return the previous html
  617. };
  618.  
  619. shadowRoot.querySelector("#ShowCurrencyORUnits").onclick = function() { //When the unit is clicked
  620. shadowRoot.querySelectorAll('.AI-BG-box button')[1].style.marginTop = '23%'; //Bring the Translate AI box to the top
  621. navigator.clipboard.writeText(ConvertedUnit); //Copy the Final converted unit value
  622. };
  623. }, 0);
  624. }
  625. }
  626. //Menu ___________________________________________________________________________________________________________________________________________________________________________
  627. if (shadowRoot.querySelector("#SearchBTN").innerText === 'Open') //If the Search BTN text is 'Open'
  628. {
  629. shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '19px'; //Increase the menu size
  630. shadowRoot.querySelector("#SearchBTN").innerText = 'Search'; //Display the BTN text as Search again
  631. shadowRoot.querySelector("#OpenAfter").remove(); //Remove the custom Open white hover overlay
  632. SelectedTextIsLink = false; //Make common words searchable again
  633. }
  634.  
  635. if (SelectedText.match(Links) !== null) //If the selected text is a link
  636. {
  637. SelectedTextIsLink = true;
  638. shadowRoot.querySelector("#highlight_menu > ul").style.paddingInlineStart = '27px'; //Increase the menu size
  639. shadowRoot.querySelector("#SearchBTN").innerText = 'Open'; //Change the BTN text to Open
  640. shadowRoot.innerHTML += ` <style id="OpenAfter"> #SearchBTN::after { width: 225% !important; height: 221% !important; transform: translate(-48%, -71%) !important; } </style> `; //Add a custom Open white hover overlay
  641. }
  642.  
  643. const menu = shadowRoot.querySelector("#highlight_menu");
  644. if (document.getSelection().toString().trim() !== '') { //If text has been selected
  645. const p = document.getSelection().getRangeAt(0).getBoundingClientRect(); //Store the selected position
  646.  
  647. menu.classList.add('show'); //Show the menu
  648. menu.offsetHeight; //Trigger reflow by forcing a style calculation
  649. menu.style.left = p.left + (p.width / 2) - (menu.offsetWidth / 2) + 'px';
  650. menu.style.top = p.top - menu.offsetHeight - 10 + 'px';
  651. menu.classList.add('highlight_menu_animate');
  652.  
  653. return; //Keep the menu open
  654. }
  655. menu.classList.remove('show'); //Hide the menu
  656. });
  657. });
  658. //AI Menu ___________________________________________________________________________________________________________________________________________________________________________
  659. var audio, Generating, isRecognizing = false;
  660. const HtmlMenu = document.createElement('div'); //Create a container div
  661. HtmlMenu.setAttribute('style', `width: 0px; height: 0px; display: block;`); //Hide the container div by default
  662. const shadowRoot = HtmlMenu.attachShadow({ mode: 'closed' });
  663. const BGColor = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'rgb(37, 36, 53)' : '#e7edf1'; //Change AI theme according to the browser theme
  664. const IMGsColor = BGColor === '#e7edf1' ? 'filter: invert(1)' : ''; //If on white mode invert black svg colors to white
  665. const TextColor = BGColor === '#e7edf1' ? 'black' : 'white'; //Depending on the browser theme change the AI menu text color
  666. 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
  667. const Lang = UniqueLangs.length > 1 ? `${UniqueLangs[0]} and into ${UniqueLangs[1]}` : UniqueLangs[0]; //Use 1 or 2 languages
  668. const GeminiSVG = '<svg viewBox="0 0 32 32" fill="none"> <path d="M14 28C14 26.0633 13.6267 24.2433 12.88 22.54C12.1567 20.8367 11.165 19.355 9.905 18.095C8.645 16.835 7.16333 15.8433 5.46 15.12C3.75667 14.3733 1.93667 14 0 14C1.93667 14 3.75667 13.6383 5.46 12.915C7.16333 12.1683 8.645 11.165 9.905 9.905C11.165 8.645 12.1567 7.16333 12.88 5.46C13.6267 3.75667 14 1.93667 14 0C14 1.93667 14.3617 3.75667 15.085 5.46C15.8317 7.16333 16.835 8.645 18.095 9.905C19.355 11.165 20.8367 12.1683 22.54 12.915C24.2433 13.6383 26.0633 14 28 14C26.0633 14 24.2433 14.3733 22.54 15.12C20.8367 15.8433 19.355 16.835 18.095 18.095C16.835 19.355 15.8317 20.8367 15.085 22.54C14.3617 24.2433 14 26.0633 14 28Z" fill="url(#paint)"></path></svg>';
  669.  
  670. shadowRoot.innerHTML = (html => BypassTT?.createHTML(html) || html)(`<svg width=" 0" height=" 0">
  671. <defs>
  672. <radialGradient cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(2.77876 11.3795) rotate(18.6832) scale(29.8025 238.737)" id="paint">
  673. <stop offset="0.0671246" stop-color="#9168C0"></stop>
  674. <stop offset="0.342551" stop-color="#5684D1"></stop>
  675. <stop offset="0.672076" stop-color="#1BA1E3"></stop>
  676. </radialGradient>
  677. </defs>
  678. </svg>
  679. <style>
  680. ${GM_getResourceText('AICSS')}
  681.  
  682. .animated-border {
  683. background: border-box border-box ${BGColor};
  684. }
  685.  
  686. #prompt {
  687. color: ${TextColor};
  688. }
  689.  
  690. #AIBox.AnswerBox {
  691. background: ${BGColor};
  692. }
  693. </style>
  694. <div id="highlight_menu">
  695. <div class="AI-BG-box">
  696. <button id="AIBTN">
  697. <div class="MenuGemini">${GeminiSVG}Explore more</div>
  698. </button>
  699. <button id="AIBTN" class="translate">
  700. <div class="MenuGemini">${GeminiSVG}Translate</div>
  701. </button>
  702. </div>
  703. <ul id="MenuList">
  704. <li class="popuptext"></li>
  705. <li id="ShowCurrencyORUnits">${FinalCurrency}</li>
  706. <li class="popuptext" id="SearchBTN">Search</li>
  707. <li class="popuptext" id="CopyBTN">
  708. <span>│</span> Copy
  709. </li>
  710. </ul>
  711. </div>
  712. <div class="animated-border" id="AIBox">
  713. <div id="tabcontext">
  714. <p>Page Context</p>
  715. <p id="TabBox">Tab</p>
  716. </div>
  717. <button id="dictate">
  718. <svg id="dictateSvg" viewBox="0 0 700 700">
  719. <defs>
  720. <path id="commonPath1" d="M439.5,236c0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,70-64,126.9-142.7,126.9s-142.7-56.9-142.7-126.9c0-11.3-9.1-20.4-20.4-20.4s-20.4,9.1-20.4,20.4c0,86.2,71.5,157.4,163.1,166.7v57.5h-23.6c-11.3,0-20.4,9.1-20.4,20.4s9.1,20.4,20.4,20.4h88c11.3,0,20.4-9.1,20.4-20.4s-9.1-20.4-20.4-20.4h-23.6v-57.5C368,393.4,439.5,322.2,439.5,236Z" fill="#fff"></path>
  721. <path id="commonPath2" d="M256,323.5c51,0,92.3-41.3,92.3-92.3v-127.9C348.3,52.3,307,11,256,11s-92.3,41.3-92.3,92.3v127.9c0,51,41.3,92.3,92.3,92.3ZM203.7,103.3C203.7,74.5,227.2,51,256,51s52.3,23.5,52.3,52.3v127.9c0,28.8-23.5,52.3-52.3,52.3s-52.3-23.5-52.3-52.3v-127.9Z" fill="#fff"></path>
  722. <ellipse id="commonEllipse" rx="53" ry="59" transform="translate(255.581 226.12)" fill="#0f0"></ellipse>
  723. </defs>
  724. <g class="state1">
  725. <use href="#commonPath1"></use>
  726. <use href="#commonPath2"></use>
  727. </g>
  728. <g class="state2">
  729. <use href="#commonPath1"></use>
  730. <use href="#commonPath2"></use>
  731. <use href="#commonEllipse"></use>
  732. <rect width="106" height="68.751" transform="translate(202.581 167.12)" fill="#0f0"></rect>
  733. </g>
  734. <g class="state3">
  735. <use href="#commonPath1"></use>
  736. <use href="#commonPath2"></use>
  737. <use href="#commonEllipse"></use>
  738. <ellipse rx="53" ry="59.21" transform="translate(255.581 226.457)" fill="#0f0"></ellipse>
  739. <rect width="106" height="136.9" transform="translate(202.581 89.492)" fill="#0f0"></rect>
  740. <ellipse rx="35" ry="40.072" transform="matrix(1.513 0 0 1 255.557 89.492)" fill="#0f0"></ellipse>
  741. </g>
  742. </svg>
  743. </button>
  744. <button id="TopPause">
  745. <svg width="12" height="12" viewBox="0 0 12 12" fill="none">
  746. <rect x="0.499756" y="0.5" width="11" height="11" rx="1.5" fill="white" stroke="white" />
  747. </svg>
  748. </button>
  749. <div class="BoxGemini" id="gemini">${GeminiSVG}</div>
  750. <div id="context">PAGE CONTEXT</div>
  751. <input class="Prompt" id="prompt" placeholder="Enter your prompt to Gemini">
  752. </div>
  753. <div id="AIBox" class="AnswerBox">
  754. <div id="AIAnswer"></div>
  755. </div>
  756. <div id="CloseOverlay"></div>`); //Set the menu html
  757.  
  758. shadowRoot.querySelector("#AIBTN:first-of-type").classList.add('show-button'); //Animate the Explore BTN
  759. shadowRoot.querySelector("#AIBTN.translate").classList.add('show-button'); //Animate the Translate BTN
  760.  
  761. function RemoveContext() { //Disable the context styles
  762. shadowRoot.querySelector("#gemini").style.display = ''; //Show the Gemini BTN
  763. shadowRoot.querySelector("#prompt").placeholder = 'Enter your prompt to Gemini'; //Return default placeholder
  764. shadowRoot.querySelector("#prompt").style.left = ''; //Return the input bar to its original left position
  765. shadowRoot.querySelector("#prompt").style.width = '64%'; //Return the input bar to its original width position
  766. shadowRoot.querySelector("#context").classList.remove('show'); //Hide the context view
  767. shadowRoot.querySelector("#tabcontext").style.display = 'flex'; //Show the "page context tab"
  768. shadowRoot.querySelector('.animated-border').style.setProperty('--color-OrangeORLilac', '#B969CC'); //Return default lilac border effect color
  769. }
  770.  
  771. function Generate(Prompt, button) { //Call the AI endpoint
  772. const context = !!shadowRoot.querySelector("#context.show") ? `(You're not allowed to say anything like "Based on the provided text")\n"${Prompt} mainly base yourself on the text below\n${document.body.innerText}` : Prompt; //Add the page context if context is enabled
  773. const AIFunction = button.match('translate') ? `(You're not allowed to say anything like "The text is already in ${UniqueLangs[0]}"\nNo translation is needed).\Translate into ${Lang} the following text/word inside quotes "${Prompt}".\nAlso give me a definition and usage examples.` : button.match('Prompt') ? context : `(PS*I'm unable to provide you with more context, so don't ask for it! Also, don't mention that I haven't provided context or anything similar to it!) Help me further explore a term or topic from the text/word: "${Prompt}"`; //AI prompts
  774. const msg = button.match('translate') ? `Translate this text: "${Prompt.length > 215 ? Prompt.trim().slice(0, 215) + '…' : Prompt.trim()}"` : button.match('Prompt') ? Prompt.length > 240 ? Prompt.trim().slice(0, 240) + '…' : Prompt.trim() : `Help me further explore a term or topic from the text: "${Prompt.length > 180 ? Prompt.trim().slice(0, 180) + '…' : Prompt.trim()}"`; //User text
  775.  
  776. function startGeneratingText() {
  777. Generating = setInterval(function() { //Start the interval to change the text
  778. if (shadowRoot.querySelector("#finalanswer").innerText === 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ▋') {
  779. shadowRoot.querySelector("#finalanswer").innerText = 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ';
  780. } else { //Toggle between showing and hiding ▋
  781. shadowRoot.querySelector("#finalanswer").innerText = 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ▋';
  782. }
  783. }, 200);
  784. }
  785.  
  786. const request = GM.xmlHttpRequest({ //Call the AI API
  787. method: "POST",
  788. url: `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash-latest:generateContent?key=${GM_getValue("APIKey")}`,
  789. headers: {
  790. "Content-Type": "application/json"
  791. },
  792. data: JSON.stringify({
  793. contents: [{
  794. parts: [{
  795. text: `${AIFunction}` //Use our AI prompt
  796. }]
  797. }],
  798. safetySettings: [ //Allow all content
  799. {
  800. category: "HARM_CATEGORY_HARASSMENT",
  801. threshold: "BLOCK_NONE"
  802. },
  803. {
  804. category: "HARM_CATEGORY_HATE_SPEECH",
  805. threshold: "BLOCK_NONE"
  806. },
  807. {
  808. category: "HARM_CATEGORY_SEXUALLY_EXPLICIT",
  809. threshold: "BLOCK_NONE"
  810. },
  811. {
  812. category: "HARM_CATEGORY_DANGEROUS_CONTENT",
  813. threshold: "BLOCK_NONE"
  814. }
  815. ],
  816. }),
  817. onerror: function(err) {
  818. clearInterval(Generating); //Stop showing ▋
  819. shadowRoot.querySelector("#finalanswer").innerHTML = `<br>Please copy and paste the error below:<br><a class="feedback" href="https://greasyfork.org/scripts/419825/feedback">Click here to report this bug</a><br><br> Prompt: ${Prompt}<br> Button: ${button}<br> Error: ${err}}<br><br><br>`; //Show an error message
  820. },
  821. onload: function(response) {
  822. clearInterval(Generating); //Stop showing ▋
  823. const AIResponse = JSON.parse(response.responseText).candidates?.[0]?.content?.parts?.[0]?.text;
  824.  
  825. if (AIResponse !== undefined) {
  826. shadowRoot.querySelector("#finalanswer").innerHTML = (html => BypassTT?.createHTML(html) || html)(marked.parse(AIResponse) + '<br>'); //Show the parsed AI response
  827. } else {
  828. shadowRoot.querySelector("#finalanswer").innerHTML = `<br>Please copy and paste the error below:<br><a class="feedback" href="https://greasyfork.org/scripts/419825/feedback">Click here to report this bug</a><br><br> Prompt: ${Prompt}<br> Button: ${button}<br> Error: ${response.responseText}<br><br><br>`; //Show an error message
  829. }
  830.  
  831. audio = new SpeechSynthesisUtterance(AIResponse.replace(/[^a-zA-Z0-9\s%.,!?]/g, '')); //Play the AI response text, removing non-alphanumeric characters for better pronunciation
  832.  
  833. shadowRoot.querySelector("#copyAnswer").onclick = function() {
  834. shadowRoot.querySelector("#copyAnswer").style.display = 'none';
  835. shadowRoot.querySelector("#AnswerCopied").style.display = 'inline-flex';
  836. navigator.clipboard.writeText(AIResponse.replace(/(\*\*|##)/g, '')); //Copy the AI response without duplicated symbols
  837. setTimeout(() => { //Return play BTN svg
  838. shadowRoot.querySelector("#copyAnswer").style.display = 'inline-flex';
  839. shadowRoot.querySelector("#AnswerCopied").style.display = 'none';
  840. }, 1000);
  841. };
  842.  
  843. shadowRoot.querySelector("#dictate").classList.add('show'); //Show the dictate BTN
  844. shadowRoot.querySelector("#TopPause").classList.remove('show'); //Hide the TopPause BTN
  845. shadowRoot.querySelector("#AIMenu").classList.add('show'); //Show the AIMenu BTN
  846. shadowRoot.querySelector("#prompt").focus();
  847. },
  848. onabort: function(response) {
  849. clearInterval(Generating); //Stop showing ▋
  850. shadowRoot.querySelector("#finalanswer").innerText = 'ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤResponse has been interrupted.';
  851. shadowRoot.querySelector("#dictate").classList.add('show'); //Show the dictate BTN
  852. shadowRoot.querySelector("#copyAnswer").style.display = 'none'; //Hide the copy AI answer BTN
  853. shadowRoot.querySelector("#TopPause").classList.remove('show'); //Hide the TopPause BTN
  854. shadowRoot.querySelector("#AIMenu").classList.add('show'); //Show the AIMenu BTN
  855. shadowRoot.querySelector("#speak").style.display = 'none'; //Hide the speak BTN
  856. },
  857. onloadstart: function(response) {
  858. shadowRoot.querySelector("#dictate").classList.remove('show'); //Hide the dictate BTN
  859. shadowRoot.querySelector("#TopPause").classList.add('show'); //Show the TopPause BTN
  860. shadowRoot.querySelector("#AIAnswer").innerHTML = (html => BypassTT?.createHTML(html) || html)(`<div id="avatar">
  861. <svg width="32" height="32" viewBox="0 0 32 32">
  862. <rect width="32" height="32" rx="8" fill="#5021FF"></rect>
  863. <path fill-rule="evenodd" clip-rule="evenodd" d="M12.7375 12.5186C12.7375 10.7594 14.1636 9.33333 15.9228 9.33333C17.6819 9.33333 19.108 10.7594 19.108 12.5186C19.108 14.2778 17.6819 15.7039 15.9228 15.7039C14.1636 15.7039 12.7375 14.2778 12.7375 12.5186ZM15.9228 8C13.4272 8 11.4042 10.023 11.4042 12.5186C11.4042 15.0142 13.4272 17.0372 15.9228 17.0372C18.4183 17.0372 20.4414 15.0142 20.4414 12.5186C20.4414 10.023 18.4183 8 15.9228 8ZM11.5819 17.6255C11.8982 17.437 12.0018 17.0278 11.8133 16.7115C11.6248 16.3952 11.2156 16.2916 10.8993 16.4801C10.6831 16.6089 10.4663 16.8148 10.2746 17.0349C10.0746 17.2644 9.87008 17.546 9.68554 17.8601C9.32327 18.4767 9 19.2839 9 20.1144C9 20.8532 9.12214 21.4899 9.41978 22.0347C9.72071 22.5855 10.1679 22.9818 10.7122 23.2937C11.2082 23.5779 11.7335 23.8486 12.5469 24.0394C13.3432 24.2262 14.3899 24.3308 15.9368 24.3308C19.0007 24.3308 20.5881 23.9091 21.6046 22.9619C22.5374 22.0927 22.9531 21.1528 22.9506 20.1128C22.9489 19.4161 22.6359 18.6481 22.2905 18.0381C21.9435 17.4254 21.4844 16.8333 21.0628 16.5185C20.7678 16.2983 20.3501 16.3589 20.1298 16.6539C19.9095 16.949 19.9701 17.3667 20.2652 17.587C20.4764 17.7446 20.826 18.1578 21.1302 18.695C21.4359 19.2349 21.6164 19.7615 21.6173 20.116C21.6188 20.7365 21.3947 21.3351 20.6956 21.9864C20.0802 22.5599 18.9602 22.9975 15.9368 22.9975C14.4401 22.9975 13.5073 22.8952 12.8514 22.7414C12.2127 22.5915 11.8133 22.3879 11.375 22.1368C10.9852 21.9134 10.7439 21.6773 10.5899 21.3954C10.4326 21.1075 10.3333 20.7113 10.3333 20.1144C10.3333 19.609 10.5393 19.039 10.8351 18.5355C10.9796 18.2896 11.1364 18.0755 11.2798 17.9108C11.4315 17.7368 11.5404 17.6502 11.5819 17.6255Z" fill="white"></path>
  864. </svg>
  865. </div>
  866. <div class="AnswerContainer" style="color: ${TextColor};">
  867. <div id="msg">${msg}</div>
  868. <div id="LineEl"></div>
  869. <div class="BoxGemini" id="ContainerGemini">${GeminiSVG}</div>
  870. <div id="finalanswer"></div>
  871. <div id="AIMenu">
  872. <button id="bottompause" class="MenuBTNs" style="display: none; ${IMGsColor};">
  873. <svg width="16" height="16" viewBox="0 -2 8 13" fill="none">
  874. <path fill-rule="evenodd" clip-rule="evenodd" d="M1.49879 0.5C0.671042 0.5 1.52588e-05 1.17103 1.52588e-05 1.99878V8.00122C1.52588e-05 8.82897 0.671042 9.5 1.49879 9.5C2.32655 9.5 2.99757 8.82897 2.99757 8.00122V1.99878C2.99757 1.17103 2.32655 0.5 1.49879 0.5ZM1.00002 1.99878C1.00002 1.72331 1.22333 1.5 1.49879 1.5C1.77426 1.5 1.99757 1.72331 1.99757 1.99878V8.00122C1.99757 8.27669 1.77426 8.5 1.49879 8.5C1.22333 8.5 1.00002 8.27669 1.00002 8.00122V1.99878ZM6.50575 0.5C5.678 0.5 5.00697 1.17103 5.00697 1.99878V8.00122C5.00697 8.82897 5.678 9.5 6.50575 9.5C7.33351 9.5 8.00453 8.82897 8.00453 8.00122V1.99878C8.00453 1.17103 7.33351 0.5 6.50575 0.5ZM6.00697 1.99878C6.00697 1.72331 6.23028 1.5 6.50575 1.5C6.78122 1.5 7.00453 1.72331 7.00453 1.99878V8.00122C7.00453 8.27669 6.78122 8.5 6.50575 8.5C6.23028 8.5 6.00697 8.27669 6.00697 8.00122V1.99878Z" fill="white"></path>
  875. </svg>
  876. </button>
  877. <button id="speak" class="MenuBTNs" style="display: inline-flex; ${IMGsColor};">
  878. <svg width="16" height="16" viewBox="0 0 16 16" fill="#fff">
  879. <path fill-rule="inherit" clip-rule="evenodd" d="M6.99585 3.24577C6.81771 3.36247 6.58157 3.56921 6.2112 3.89612L4.20926 5.66321L4.18027 5.68895C4.07192 5.78537 3.93427 5.90788 3.75948 5.97398C3.58469 6.04009 3.40043 6.03934 3.25538 6.03875L3.21662 6.03864H2.50001C2.25017 6.03864 2.11309 6.03971 2.018 6.05249L2.01435 6.05299L2.01385 6.05663C2.00107 6.15173 2.00001 6.28881 2.00001 6.53864V9.53864C2.00001 9.78848 2.00107 9.92556 2.01385 10.0207L2.01435 10.0243L2.01799 10.0248C2.11309 10.0376 2.25017 10.0386 2.50001 10.0386H3.2338L3.27297 10.0385C3.41953 10.0379 3.60574 10.0372 3.78206 10.1046C3.95838 10.172 4.09659 10.2968 4.20536 10.395L4.23448 10.4212L6.20852 12.189C6.57949 12.5212 6.81642 12.7317 6.99537 12.8508L7.00796 12.8591L7.01018 12.8442C7.04081 12.6314 7.04208 12.3145 7.04208 11.8166V4.27098C7.04208 3.77697 7.04081 3.46312 7.01041 3.25234L7.00823 3.23776L6.99585 3.24577ZM7.1278 3.17543C7.12773 3.17558 7.1259 3.17617 7.12253 3.17674C7.12619 3.17556 7.12788 3.17528 7.1278 3.17543ZM6.97834 3.11168C6.97653 3.10878 6.97576 3.10702 6.97583 3.10686C6.97589 3.10671 6.9768 3.10815 6.97834 3.11168ZM6.97515 12.9916C6.97508 12.9915 6.97586 12.9897 6.97769 12.9867C6.97613 12.9903 6.97522 12.9918 6.97515 12.9916ZM7.12322 12.9217C7.12662 12.9223 7.12847 12.9229 7.12854 12.9231C7.12862 12.9232 7.12692 12.9229 7.12322 12.9217ZM6.44789 2.40927C6.69128 2.24984 7.05663 2.07222 7.45332 2.25119C7.85002 2.43016 7.95863 2.82161 8.00017 3.10959C8.04215 3.40069 8.04212 3.78842 8.04209 4.23246L8.04208 4.27098V11.8166L8.04209 11.8551C8.04212 12.3031 8.04215 12.6937 7.99998 12.9867C7.95838 13.2757 7.84954 13.67 7.45005 13.8485C7.05057 14.027 6.68423 13.845 6.44119 13.6832C6.19482 13.5192 5.90383 13.2586 5.57013 12.9597L5.57012 12.9597L5.5414 12.934L3.56736 11.1662C3.49189 11.0986 3.45417 11.0652 3.42542 11.0429L3.4238 11.0417L3.42176 11.0415C3.38548 11.0389 3.3351 11.0386 3.2338 11.0386H2.50001L2.47281 11.0386C2.26077 11.0387 2.05471 11.0387 1.88475 11.0159C1.69315 10.9901 1.47451 10.9274 1.2929 10.7458C1.11129 10.5641 1.04853 10.3455 1.02277 10.1539C0.999921 9.98395 0.999962 9.77788 1 9.56585L1.00001 9.53864V6.53864L1 6.51144C0.999962 6.29941 0.999921 6.09334 1.02277 5.92338C1.04853 5.73178 1.11129 5.51315 1.2929 5.33154C1.47451 5.14993 1.69315 5.08717 1.88475 5.06141C2.0547 5.03856 2.26076 5.0386 2.4728 5.03864H2.4728L2.50001 5.03864H3.21662C3.31686 5.03864 3.36669 5.03836 3.40258 5.03584L3.40461 5.03569L3.40622 5.03446C3.4348 5.0126 3.47234 4.97984 3.5475 4.9135L5.54944 3.14641L5.57832 3.12091L5.57834 3.1209L5.57835 3.12089C5.91122 2.82703 6.20187 2.57043 6.44789 2.40927ZM10.1345 5.33693C10.2257 5.07628 10.5109 4.93892 10.7716 5.03012C11.5468 5.30139 12.1156 5.71974 12.4845 6.27343C12.851 6.82368 12.9896 7.46152 12.9888 8.12059C12.9881 8.77784 12.8479 9.36588 12.4685 9.8652C12.095 10.3568 11.5331 10.7045 10.8038 10.9736C10.5448 11.0692 10.2573 10.9367 10.1617 10.6777C10.0661 10.4186 10.1986 10.1311 10.4577 10.0355C11.0913 9.80166 11.4579 9.54244 11.6723 9.2602C11.8809 8.98566 11.9883 8.63292 11.9888 8.11953C11.9894 7.59876 11.8804 7.1703 11.6522 6.82786C11.4264 6.48888 11.0533 6.18815 10.4413 5.97401C10.1807 5.88281 10.0433 5.59758 10.1345 5.33693ZM10.5874 3.04341C10.3154 2.99552 10.0561 3.17716 10.0082 3.44912C9.96033 3.72108 10.142 3.98036 10.4139 4.02825C11.5592 4.22994 12.4289 4.71111 13.0117 5.38193C13.5932 6.05126 13.9207 6.94547 13.9201 8.03323C13.9195 9.12625 13.5988 9.99297 13.0242 10.6448C12.4457 11.3008 11.573 11.7808 10.3984 12.026C10.1281 12.0825 9.95475 12.3474 10.0112 12.6177C10.0676 12.888 10.3325 13.0614 10.6028 13.0049C11.9332 12.7271 13.0195 12.1622 13.7743 11.3061C14.5328 10.4457 14.9194 9.33294 14.9201 8.03375C14.9208 6.7372 14.5263 5.60054 13.7666 4.72609C13.0082 3.85314 11.9174 3.27763 10.5874 3.04341Z"></path>
  880. </svg>
  881. </button>
  882. <button id="AnswerCopied" class="MenuBTNs" style="display: none; ${IMGsColor};">
  883. <svg width="16" height="16" viewBox="0 0 10 8" fill="none">
  884. <path d="M1.02063 3.68066L3.67635 6.65194L8.97935 1.34802" stroke="white" stroke-linecap="round" />
  885. </svg>
  886. </button>
  887. <button id="copyAnswer" class="MenuBTNs" style="display: inline-flex; ${IMGsColor};">
  888. <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
  889. <path fill-rule="evenodd" clip-rule="evenodd" d="M4.22727 4.5H3.5C2.39543 4.5 1.5 5.39543 1.5 6.5V12.5C1.5 13.6046 2.39543 14.5 3.5 14.5H9.5C10.6046 14.5 11.5 13.6046 11.5 12.5V11.7727H10.5V12.5C10.5 13.0523 10.0523 13.5 9.5 13.5H3.5C2.94772 13.5 2.5 13.0523 2.5 12.5V6.5C2.5 5.94772 2.94772 5.5 3.5 5.5H4.22727V4.5Z" fill="white"></path>
  890. <rect x="5" y="2" width="9" height="9" rx="1.5" stroke="white"></rect>
  891. </svg>
  892. </button>
  893. <button id="NewAnswer" class="MenuBTNs" style="display: inline-flex; ${IMGsColor};">
  894. <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
  895. <path d="M3.64362 8.00003C3.64362 5.70974 5.50027 3.85309 7.79056 3.85309C9.06546 3.85309 10.2057 4.42786 10.9671 5.33402C11.0399 5.42069 11.0743 5.56527 10.9942 5.64533L10.5291 6.11045C10.2141 6.42543 10.4372 6.964 10.8826 6.964H12.75C13.0261 6.964 13.25 6.74015 13.25 6.464V4.59664C13.25 4.15119 12.7114 3.92811 12.3964 4.24309L11.8532 4.78632C11.7983 4.84128 11.7013 4.81866 11.6513 4.75915C10.7273 3.65956 9.34048 2.95947 7.79056 2.95947C5.00674 2.95947 2.75 5.21621 2.75 8.00003C2.75 10.7839 5.00674 13.0406 7.79056 13.0406C9.95003 13.0406 11.7913 11.6828 12.5092 9.77593C12.5962 9.54499 12.4795 9.28729 12.2485 9.20034C12.0176 9.11338 11.7599 9.2301 11.6729 9.46104C11.0818 11.0312 9.5659 12.147 7.79056 12.147C5.50027 12.147 3.64362 10.2903 3.64362 8.00003Z" fill="white"></path>
  896. </svg>
  897. </button>
  898. </div>`); //Create the AI menu HTML
  899.  
  900. var transcript = ""; //Add words
  901. startGeneratingText();
  902. shadowRoot.querySelector("#CloseOverlay").classList.add('show'); //Show a black overlay
  903. var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition;
  904. var recognition = new SpeechRecognition();
  905. recognition.interimResults = true; //Show partial results
  906. recognition.continuous = true; //Keep listening until stopped
  907. shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the mini menu on the page
  908. shadowRoot.querySelectorAll("#AIBox, .animated-border, #AIBox.AnswerBox").forEach(el => el.classList.add('show')); //Show the AI input and box
  909. getSelection().removeAllRanges(); //UnSelect the selected text so that if the user clicks on the past selected text the menu won't show up again
  910.  
  911. shadowRoot.querySelector("#CloseOverlay").onclick = function() {
  912. shadowRoot.querySelectorAll("#AIBox, .animated-border, #AIBox.AnswerBox").forEach(el => el.classList.remove('show')); //Hide the AI input and box
  913. this.classList.remove('show');
  914. recognition.stop(); //Stop recognizing audio
  915. speechSynthesis.cancel(); //Stop speaking
  916. request.abort(); //Abort any ongoing request
  917. if (shadowRoot.querySelector("#gemini").style.display === 'none') { //If the Gemini BTN is hidden
  918. RemoveContext(); //Return original prompt input styles
  919. }
  920. };
  921.  
  922. shadowRoot.querySelector("#TopPause").onclick = function() {
  923. shadowRoot.querySelector("#dictate").classList.add('show'); //Show the dictate BTN
  924. shadowRoot.querySelector("#TopPause").classList.remove('show'); //Hide the TopPause BTN
  925. request.abort(); //Abort the request
  926. };
  927.  
  928. recognition.onend = function() {
  929. shadowRoot.querySelectorAll('.state1, .state2, .state3').forEach((state, index) => { //ForEach SVG animation state
  930. index.toString().match(/1|2/) ? state.style.display = 'none' : ''; //Show only the 1 state
  931.  
  932. state.classList.remove('animate'+index); //Stop the voice recording animation
  933. });
  934. isRecognizing = false;
  935. transcript !== '' ? Generate(transcript, shadowRoot.querySelector("#prompt").className) : shadowRoot.querySelector("#finalanswer").innerHTML = `<br>No audio detected. Please try again or check your mic settings.ㅤㅤㅤㅤㅤㅤㅤㅤㅤ<br><br>`; //Call the AI API if audio has been detected or show an error message
  936. }; //Finish the recognition end event listener
  937.  
  938. recognition.onresult = function(event) { //Handle voice recognition results
  939. transcript = ""; //Clear the transcript at the start of the event
  940. for (var i = 0; i < event.results.length; i++) { //For all transcript results
  941. transcript += event.results[i][0].transcript + ' '; //Concatenate all intermediate transcripts
  942. }
  943. shadowRoot.querySelector("#msg").innerText = transcript.length > 240 ? transcript.slice(0, 240) + '…' : transcript; //Display recognized words
  944. };
  945.  
  946. shadowRoot.querySelector("#dictate").onclick = function() {
  947. if (isRecognizing) {
  948. recognition.stop();
  949. } else {
  950. isRecognizing = true;
  951. recognition.start();
  952. shadowRoot.querySelectorAll('.state1, .state2, .state3').forEach((state, index) => { //ForEach SVG animation state
  953. state.style.display = 'unset'; //Show all states
  954. state.classList.add('animate'+index); //Start the voice recording animation
  955. });
  956. }
  957. };
  958.  
  959. var desiredVoice = null;
  960. speechSynthesis.onvoiceschanged = () => desiredVoice = speechSynthesis.getVoices().find(v => v.name === "Microsoft Zira - English (United States)"); //Get and store the desired voice
  961. speechSynthesis.onvoiceschanged(); //Handle cases where the event doesn't fire
  962.  
  963. function speakText(text) {
  964. audio.voice = desiredVoice; //Use the desiredVoice
  965. speechSynthesis.speak(audio); //Speak the text
  966. }
  967.  
  968. shadowRoot.querySelectorAll("#speak, #bottompause").forEach(function(el) {
  969. el.onclick = function() { //When speak or the bottom pause BTNs are clicked
  970. if (speechSynthesis.speaking) {
  971. speechSynthesis.cancel();
  972. shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN
  973. shadowRoot.querySelector("#bottompause").classList.remove('show'); //Hide the pause BTN
  974. }
  975. else
  976. {
  977. shadowRoot.querySelector("#speak").style.display = 'none'; //Hide the play BTN
  978. shadowRoot.querySelector("#bottompause").classList.add('show'); //Show the pause BTN
  979.  
  980. speakText(audio); //Speak the AI reponse text
  981.  
  982. audio.onend = (event) => {
  983. shadowRoot.querySelector("#speak").style.display = 'inline-flex'; //Show the play BTN
  984. shadowRoot.querySelector("#bottompause").classList.remove('show'); //Hide the pause BTN
  985. };
  986. }
  987. };
  988. });
  989.  
  990. shadowRoot.querySelector("#NewAnswer").onclick = function() {
  991. shadowRoot.querySelector("#dictate").classList.remove('show'); //Hide the dictate BTN
  992. shadowRoot.querySelector("#TopPause").classList.add('show'); //Show the top pause BTN
  993. Generate(Prompt, button); //Call the AI API
  994. };
  995. } //Finish the onloadstart event listener
  996. });//Finish the GM.xmlHttpRequest function
  997. } //Finish the Generate function
  998.  
  999. shadowRoot.querySelector("#prompt").addEventListener("keydown", (event) => {
  1000. if (event.key === "Enter") {
  1001. Generate(shadowRoot.querySelector("#prompt").value, shadowRoot.querySelector("#prompt").className); //Call the AI API
  1002. shadowRoot.querySelector("#prompt").value = ''; //Erase the prompt text
  1003. }
  1004. if (event.key === "Tab") {
  1005. if (shadowRoot.querySelector("#gemini").style.display === 'none') { //If the Gemini BTN is hidden
  1006. RemoveContext(); //Return original prompt input styles
  1007. }
  1008. else
  1009. {
  1010. shadowRoot.querySelector("#gemini").style.display = 'none'; //Hide the Gemini BTN
  1011. shadowRoot.querySelector("#prompt").placeholder = `Gemini is using ${location.host.replace('www.','')} for context...`; //Change placeholder
  1012. shadowRoot.querySelector("#prompt").style.left = '12%'; //Push the input bar to the left
  1013. shadowRoot.querySelector("#prompt").style.width = '75%'; //Increase the input bar width
  1014. shadowRoot.querySelector("#context").classList.add('show'); //Show the context view
  1015. shadowRoot.querySelector("#tabcontext").style.display = 'none'; //Hide the "page context tab"
  1016. shadowRoot.querySelector('.animated-border').style.setProperty('--color-OrangeORLilac', '#FF8051'); //Change the border effect color to orange
  1017. }
  1018. }
  1019. setTimeout(() => { //Wait for the code above to execute
  1020. shadowRoot.querySelector("#prompt").focus(); //Refocus on the input bar
  1021. }, 0);
  1022. });
  1023.  
  1024. if (document.body.textContent !== '' || document.body.innerText !== '') //If the body has any text
  1025. {
  1026. document.body.appendChild(HtmlMenu); //Add the script menu div container
  1027. }
  1028.  
  1029. shadowRoot.querySelector("#SearchBTN").onmousedown = function() {
  1030. var LinkfyOrSearch = 'https://www.google.com/search?q=';
  1031. if (SelectedTextIsLink === true)
  1032. {
  1033. LinkfyOrSearch = 'https://'; //Make the non-HTTP and non-HTTPS links able to be opened
  1034. }
  1035. if (SelectedText.match(/http:|https:/) !== null) //If the selected text is a link that already has HTTP or HTTPS
  1036. {
  1037. LinkfyOrSearch = ''; //Remove the https:// that was previously added to this variable
  1038. }
  1039.  
  1040. GM_openInTab(LinkfyOrSearch + SelectedTextSearch, { //Open google and search for the selected text
  1041. active: true,
  1042. setParent: true,
  1043. loadInBackground: true
  1044. });
  1045. getSelection().removeAllRanges(); //UnSelect the selected text after the search BTN is clicked so that if the user clicks on the past selected text the menu won't show up again.
  1046. shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the menu
  1047. };
  1048.  
  1049. shadowRoot.querySelector("#CopyBTN").onmousedown = function() {
  1050. navigator.clipboard.writeText(SelectedText); //Copy the selected text
  1051. };
  1052.  
  1053. shadowRoot.querySelectorAll("#AIBTN").forEach(function(button) {
  1054. button.onmousedown = function(event,i) { //When the Explore and Translate BTNs are Clicked
  1055.  
  1056. if (GM_getValue("APIKey") === undefined || GM_getValue("APIKey") === null || GM_getValue("APIKey") === '') { //Set up the API Key isn't already set
  1057. GM_setValue("APIKey", prompt('Enter your API key\n*Press OK'));
  1058. }
  1059. if (GM_getValue("APIKey") !== null && GM_getValue("APIKey") !== '') {
  1060. Generate(SelectedText, this.className); //Call the AI API
  1061. }
  1062. };
  1063. });
  1064. //Allow the script in iframes__________________________________________________________________________________________________________________________________________________________________
  1065. setTimeout(function() {
  1066. const AllIframes = document.querySelectorAll("iframe");
  1067. for (var i = AllIframes.length; i--;) {
  1068. if (AllIframes[i].allow.match('clipboard-write;') === null && AllIframes[i].src.match(Links) !== null && AllIframes[i].src.match(/recaptcha|rt.*.*.edu|challenges.cloudflare|youtube|dailymotion|vimeo|streamtape|mcloud|vidstream|mp4upload|googlevideo|kaltura|crunchyroll|animesup|google.com\/recaptcha\/|blank.html|\.mp4/) === null) //If the iframe doesn't have the clipboard-write attribute yet, it has a link and it isn't a video
  1069. {
  1070. AllIframes[i].allow = AllIframes[i].allow + 'clipboard-write;'; //Add the permission to copy the iframe text
  1071. AllIframes[i].src = AllIframes[i].src; //Reload the iframe to apply the new permissions
  1072. }
  1073. }
  1074. }, 4000);
  1075.  
  1076. window.addEventListener('scroll', async function() {
  1077. shadowRoot.querySelector("#highlight_menu").classList.remove('show'); //Hide the menu
  1078. if (LeftClicked === false && SelectedText !== '') { //If the Left Click isn't being held, and if something is currently selected
  1079. getSelection().removeAllRanges(); //UnSelect the selected text when scrolling the page down so that if the user clicks on the past selected text the menu won't show up again
  1080. }
  1081. });
  1082. }