Voice Control for ChatGPT

Expands ChatGPT with voice control and read aloud. fork from https://chrome.google.com/webstore/detail/eollffkcakegifhacjnlnegohfdlidhn

Versión del día 22/05/2023. Echa un vistazo a la versión más reciente.

// ==UserScript==
// @name         Voice Control for ChatGPT
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Expands ChatGPT with voice control and read aloud. fork from https://chrome.google.com/webstore/detail/eollffkcakegifhacjnlnegohfdlidhn
// @author       You
// @match        https://chat.openai.com/*
// @match        https://chat-shared1.zhile.io/c/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=openai.com
// @license      MIT
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  // Your code here...
  const e = document.createElement("style");
  var n;
  e.innerHTML = '\n#sai-root {\n  display: flex;\n  justify-content: space-between;\n  align-items: center;\n  margin-top: 10px;\n}\n\n#sai-input-wrapper {\n  position: relative;\n  cursor: pointer;\n  background-color: #e02d2d;\n  animation-name: red-pulsating-color;\n  animation-duration: 2s;\n  animation-iteration-count: infinite;\n  max-width: 75%;\n}\n\n#sai-input-wrapper:hover {\n  opacity: 0.7;\n}\n\n#sai-input-wrapper div.w-full {\n  padding-right: 35px;\n}\n\n#sai-input-wrapper div {\n  display: block;\n  min-height: 24px;\n  color: #fff;\n}\n\n#sai-input-wrapper.is-idle {\n  background-color: #9a8e81;\n  animation: none;\n}\n\n/*.light #sai-input-wrapper.is-idle {\n  background-color: #7f7a89;\n}*/\n\n#sai-input-wrapper.is-idle #sai-speech-button {\n  right: 50%;\n  margin-right: -13px;\n  width: 24px;\n  height: 24px;\n  top: 12px;\n}\n\n#sai-input-wrapper.is-idle #sai-speech-button svg {\n  width: 24px;\n  height: 24px;\n}\n\n#sai-speech-button {\n  position: absolute;\n  top: 10px;\n  right: 12px;\n  width: 18px;\n  transition: 0.5s;\n  right: 10px;\n  user-select: none;\n}\n\n#sai-speech-button svg {\n  width: 18px;\n  height: 18px;\n}\n\n#sai-input-wrapper.is-idle #sai-cancel-msg {\n  visibility: hidden;\n  opacity: 0;\n}\n\n#sai-button-wrapper {\n  display: flex;\n  justify-content: space-between;\n  flex: 1;\n  padding: 10px 15px;\n  background: #eeeeee;\n  margin-left: 15px;\n  border-radius: 5px;\n  z-index: 10;\n}\n\n.dark #sai-button-wrapper {\n  background: #eeeeee4a;\n}\n\n#sai-cancel-msg {\n  font-size: 8px;\n  color: #fff;\n  position: absolute;\n  bottom: -7px;\n  right: 12px;\n  transition: 0.2s;\n  user-select: none;\n  visibility: visible;\n  opacity: 1;\n}\n\n#sai-speech-button path {\n  fill: #fff;\n}\n\n#sai-lang-selector-wrapper {\n  display: flex;\n  align-items: center;\n}\n\n#sai-no-voices {\n  font-size: 12px;\n  cursor: pointer;\n  min-width: 75px;\n  text-decoration: underline;\n  color: #1abc9c;\n}\n\n#sai-no-voices:hover {\n  opacity: 0.5;\n}\n\n#sai-lang-selector {\n  font-size: 12px;\n  height: 25px;\n  padding: 0 10px;\n  user-select: none;\n  height: 30px;\n}\n\n#sai-lang-selector.sai-hide {\n  display: none;\n}\n\n.dark #sai-lang-selector {\n  color: #000 !important;\n}\n\n#sai-settings-button {\n  background-color: #1a82bc;\n  padding: 3px 4px;\n  border-radius: 5px;\n}\n\n#sai-settings-button svg {\n  width: 24px;\n  height: 22px;\n  margin-top: 1px;\n}\n\n#sai-skip-read-aloud.sai-active:hover,\n#sai-disable-read-aloud:hover,\n#sai-settings-button:hover {\n  opacity: 0.8;\n  cursor: pointer;\n}\n\n\n#sai-disable-read-aloud {\n  background-color: #1abc9c;\n  padding: 3px 4px;\n  border-radius: 5px;\n  margin-left: 10px;\n  margin-right: 10px;\n  position: relative;\n}\n\n#sai-disable-read-aloud.disabled {\n  background-color: #cb4b4b;\n}\n\n#sai-disable-read-aloud.disabled:before {\n  content: "";\n  width: 2px;\n  height: 25px;\n  background-color: #fff;\n  position: absolute;\n  transform: rotate(45deg);\n  left: 13px;\n}\n\n#sai-disable-read-aloud svg {\n  fill: rgba(0,0,0,0.0);\n  width: 24px;\n}\n\n#sai-skip-read-aloud {\n  background-color: #969696;\n  padding: 3px 4px;\n  border-radius: 5px;\n  margin-left: 10px;\n  position: relative;\n}\n\n#sai-skip-read-aloud.sai-active {\n  animation-name: yellow-pulsating-color;\n  animation-duration: 2s;\n  animation-iteration-count: infinite;\n  background-color: #daa266;\n}\n\n#sai-skip-read-aloud svg {\n  fill: #fff;\n  height: 16px;\n  width: 24px;\n  margin-top: 6px;\n}\n\n@media only screen and (max-width:450px) {\n  #sai-skip-read-aloud {\n    display: none;\n  }\n\n  .sai-compact #sai-skip-read-aloud {\n    display: block;\n  }\n}\n\n@keyframes red-pulsating-color {\n  0% {\n    background-color: #e02d2d;\n  }\n  50% {\n    background-color: #ef8585;\n  }\n  100 {\n    background-color: #e02d2d;\n  }\n}\n\n@keyframes yellow-pulsating-color {\n  0% {\n    background-color: #daa266;\n  }\n  50% {\n    background-color: #c78d4f;\n  }\n  100 {\n    background-color: #daa266;\n  }\n}\n\ndiv.px-3.pt-2.pb-3.text-center.text-xs {\n  padding: 6px;\n  font-size: 0.6rem;\n}\n\n#sai-error-message {\n  position: fixed;\n  top: 0;\n  right: 0;\n  width: 200px;\n  min-height: 100px;\n  background-color: #cb4b4b;\n  padding: 15px;\n  box-shadow: rgb(0 0 0 / 21%) 0px 0px 10px 2px;\n  color: #fff;\n  font-weight: bold;\n  font-size: 12px;\n}\n\n\n/* ==== SETTINGS ====== */\n\n#sai-settings-view {\n  position: fixed;\n  right: 0;\n  top: 0;\n  width: 100%;\n  background-color: rgb(30 30 30 / 90%);\n  height: 100vh;\n  padding: 25px;\n  z-index: 100000;\n}\n\n#sai-settings-view.sai-hide {\n  display: none;\n}\n\n#sai-settings-view-inner {\n  max-width: 700px;\n  margin: 0 auto;\n  display: flex;\n  justify-content: space-between;\n}\n\n.sai-settings-col {\n  width: 45%;\n}\n\n#sai-settings-header {\n  display: flex;\n  justify-content: space-between;\n  align-items: flex-start;\n  max-width: 700px;\n  margin: 0 auto;\n  border-bottom: 1px solid #777;\n  margin-bottom: 25px;\n  padding-bottom: 10px;\n}\n\n#sai-settings-view a {\n  color: #1abc9c;\n  text-decoration: none;\n  font-weight: bold;\n}\n\n.sai-button {\n  all: unset;\n  background-color: #1abc9c;\n  color: #fff;\n  padding: 10px 15px;\n  font-weight: bold;\n  border-radius: 5px;\n  font-size: 14px;\n  color: #fff !important;\n  cursor: pointer;\n  line-height: 1.6;\n}\n\n.sai-button:hover {\n  opacity: 0.8;\n}\n\n\n#sai-settings-view h3,\n#sai-settings-view h4,\n#sai-settings-view p  {\n  color: #fff;\n  margin-bottom: 25px;\n}\n\n#sai-settings-view li {\n  color: #fff;\n}\n\n#sai-settings-view h3 {\n  font-size: 20px;\n}\n\n#sai-settings-view h4 {\n  font-size: 17px;\n  font-weight:bold;\n  margin-bottom: 15px;\n}\n\n\n.sai-settings-section {\n  margin-top: 35px;\n  padding-top: 25px;\n  border-top: 1px solid #777;\n}\n\n#sai-settings-view li strong {\n  color: #ffca92;\n}\n\n#sai-settings-view ul {\n  padding-left: 0;\n  margin: 0;\n  list-style: none;\n}\n\n#sai-settings-view li {\n  margin-top: 10px;\n}\n\n#sai-settings-read-aloud-header {\n\n}\n\n#sai-settings-voice-link {\n  display: inline-block;\n  margin-top: 7px;\n  font-size: 12px;\n}\n\n.sai-slidecontainer {\n  width: 100%;\n}\n\n.sai-slider {\n  -webkit-appearance: none;\n  width: 100%;\n  height: 15px;\n  border-radius: 5px;\n  background: #d3d3d3;\n  outline: none;\n  opacity: 0.7;\n  -webkit-transition: 0.2s;\n  transition: opacity 0.2s;\n}\n\n.sai-slider:hover {\n  opacity: 1;\n}\n\n.sai-slider::-webkit-slider-thumb {\n  -webkit-appearance: none;\n  appearance: none;\n  width: 25px;\n  height: 25px;\n  border-radius: 50%;\n  background: #1abc9c;\n  cursor: pointer;\n}\n\n.sai-link-talkio {\n  color: #ac99ff !important;\n}\n\n@media only screen and (max-height: 720px) {\n  #sai-settings-header {\n    margin-bottom: 15px;\n    padding-bottom: 0;\n  }\n\n  #sai-settings-view {\n    font-size: 12px;\n    overflow-y: auto;\n  }\n\n  #sai-settings-view h4 {\n    font-size: 16px;\n  }\n\n  .sai-settings-section {\n    margin-top: 20px;\n    padding-top: 10px;\n  }\n}\n\n/* ======== REPEAT BUTTON ======= */\n.sai-repeat-button {\n  border-radius: 5px;\n  width: 22px;\n  height: 22px;\n  cursor: pointer;\n  position: relative;\n}\n\n.sai-repeat-button.sai-disabled {\n  display: none;\n}\n\n.sai-repeat-button svg {\n  width: 18px;\n  height: 18px;\n  margin-top: 2px;\n  margin-left: 2px;\n}\n\n.sai-repeat-button path {\n  fill: #acacbe !important;\n}\n\n.sai-repeat-button:hover {\n  background: #ececf1;\n}\n\n.sai-repeat-button:hover path {\n  fill: #40414f !important;\n}\n\n.dark .sai-repeat-button:hover {\n  background: #40414f;\n}\n\n.dark .sai-repeat-button:hover path {\n  fill: #fff !important;\n}\n\n\n/* ======== HIDE SAI ======= */\n.sai-hidden #sai-input-wrapper,\n.sai-hidden #sai-lang-selector-wrapper,\n.sai-hidden #sai-skip-read-aloud,\n.sai-hidden #sai-disable-read-aloud {\n  display: none;\n}\n\n.sai-hidden #sai-button-wrapper {\n  background: transparent;\n  padding: 0;\n}\n\n.sai-hidden #sai-settings-button {\n  border-radius: 5px;\n  position: fixed;\n  top: 7px;\n  right: 45px;\n  z-index: 10000;\n}\n\n@media only screen and (min-width: 768px) {\n  .sai-hidden #sai-settings-button {\n    top: 20px;\n    right: 20px\n  }\n}\n\n@media only screen and (max-width: 768px) {\n  form > div.relative.flex.h-full {\n    flex-direction: column;\n  }\n\n  #sai-input-wrapper {\n    height: 50px;\n  }\n}\n\n/* ======== SAI COMPACT ======= */\n.sai-compact #sai-root {\n  height: 0;\n  margin: 0;\n  position: relative;\n}\n\n.sai-compact #sai-input-wrapper{\n  position: absolute;\n  width: 30px;\n  height: 30px;\n  right: 10px;\n  top: 8px;\n  border: none;\n  z-index: 10;\n}\n\n.sai-compact #sai-input-wrapper.is-idle {\n  background: none;\n  border: none;\n  box-shadow: none;\n  opacity: 0.5;\n}\n\n.sai-compact .sai-input {\n  display: none !important;\n}\n\n.sai-compact #sai-speech-button {\n  width: 20px !important;\n  height: 20px !important;\n  top: 4px !important;\n  right: 0 !important;\n  margin-right: 4px !important;\n}\n\n.sai-compact #sai-speech-button svg {\n  width: 20px !important;\n  height: 20px !important;\n}\n\n.sai-compact #sai-input-wrapper.is-idle #sai-speech-button svg path {\n  fill: #999;\n}\n\n.sai-compact #sai-cancel-msg {\n  display: none;\n}\n\n.sai-compact #sai-button-wrapper {\n  position: absolute;\n  bottom: 15px;\n  right: 0;\n  padding: 5px 7px;\n}\n\n.sai-compact #sai-lang-selector {\n  font-size: 10px !important;\n  height: 25px;\n}\n\n.sai-compact #sai-settings-button svg,\n.sai-compact #sai-disable-read-aloud svg{\n  width: 20px !important;\n  height: 20px !important;\n  margin-top: 0px !important;\n}\n\n.sai-compact #sai-skip-read-aloud svg {\n  width: 20px !important;\n  height: 13px !important;\n  margin-top: 5px !important;\n}\n\n.sai-compact #sai-disable-read-aloud.disabled:before {\n  left: 11px;\n  bottom: 1px;\n}\n\n.sai-compact textarea {\n  padding-right: 4rem !important;\n}\n\n.sai-compact textarea + button {\n  margin-right: 35px;\n}\n\n@media only screen and (max-width: 900px) {\n  .sai-compact .flex.ml-1.gap-0.justify-center{\n    position: static;\n    justify-content: flex-start !important;\n  }\n}\n\n@media only screen and (max-width: 768px) {\n  .sai-compact .w-full.h-32.flex-shrink-0 {\n    margin-top: 25px;\n  }\n\n  .sai-compact .flex.ml-1.gap-0.justify-center{\n    position: absolute;\n    bottom: 62px;\n    height: 30px;\n  }\n}\n\n@media only screen and (min-width: 768px) {\n  .sai-compact #sai-input-wrapper {\n    top: 12px;\n  }\n\n  .sai-compact #sai-button-wrapper {\n    bottom: 10px;\n  }\n\n  .sai-compact .flex.ml-1.gap-0.justify-center{\n    position: absolute;\n    top: -46px;\n    max-height: 36px;\n  }\n}\n\n',
    document.body.appendChild(e),
    function (e) {
      e.info = "info",
        e.warning = "warning",
        e.error = "error",
        e.verbose = "verbose",
        e.success = "success"
    }(n || (n = {}));
  class t {
    constructor(e = !0) {
      this.logToConsole = e,
        window.addEventListener("sai-print-logs", (() => {
          console.log("All logs:"),
            console.log(t.allLogs)
        }))
    }
    static info(e, t) {
      this.instance.write(e, n.info, t)
    }
    static success(e, t) {
      this.instance.write(e, n.success, t)
    }
    static warn(e, t) {
      this.instance.write(e, n.warning, t)
    }
    static error(e, t) {
      this.instance.write(e, n.error, t)
    }
    static verbose(e, i) {
      this.instance.logToConsole && t.allLogs.push([Date.now(), n.verbose, e, i])
    }
    static setup() {
      if (!t.instance) {
        const e = "true" === window.localStorage.getItem("sai-log");
        this.instance = new t(e)
      }
      return t.instance
    }
    write(e, n, i) {
      if (this.logToConsole) {
        const s = `color: ${this.getConsoleColor(n)}`;
        i ? console.log(`%c[${n}] ${e}`, s, i) : console.log(`%c[${n}] ${e}`, s),
          t.allLogs.push([Date.now(), n, e, i])
      }
    }
    getConsoleColor(e) {
      return e === n.info ? "#2e99d9" : e === n.warning ? "#ffbb00" : e === n.success ? "#1abc9c" : "#b91e1e"
    }
  }
  t.allLogs = [];
  class i {
    constructor(e) {
      this.element = e,
        this.isVisible = !1
    }
    write(e, n = 3e3) {
      this.isVisible && clearTimeout(this.timer),
        this.element.innerHTML = e,
        this.setVisible(!0),
        this.timer = setTimeout((() => {
          this.setVisible(!1),
            this.element.innerHTML = ""
        }), n)
    }
    setVisible(e) {
      this.element.style.display = e ? "block" : "none",
        this.isVisible = e
    }
  }

  function s(e, n) {
    return e === n || ("zh-CN" === e && "cmn-Hans-CN" === n || ("zh-TW" === e && "cmn-Hant-TW" === n || "zh-HK" === e && "yue-Hant-HK" === n))
  }
  const a = [
    ["English (US)", "en-US"],
    ["English (UK)", "en-GB"],
    ["English (AU)", "en-AU"],
    ["English (CA)", "en-CA"],
    ["English (IN)", "en-IN"],
    ["English (NZ)", "en-NZ"],
    ["普通话 (中国大陆)", "cmn-Hans-CN"],
    ["中文 (台灣)", "cmn-Hant-TW"],
    ["粵語 (香港)", "yue-Hant-HK"],
    ["Afrikaans", "af-ZA"],
    ["Bahasa Indonesia", "id-ID"],
    ["Bahasa Melayu", "ms-MY"],
    ["Català", "ca-ES"],
    ["Čeština", "cs-CZ"],
    ["Dansk", "da-DK"],
    ["Deutsch", "de-DE"],
    ["Español (ES)", "es-ES"],
    ["Español (MX)", "es-MX"],
    ["Español (AR)", "es-AR"],
    ["Español (CO)", "es-CO"],
    ["Español (PE)", "es-PE"],
    ["Español (VE)", "es-VE"],
    ["Euskara", "eu-ES"],
    ["Français", "fr-FR"],
    ["Galego", "gl-ES"],
    ["Hrvatski", "hr_HR"],
    ["IsiZulu", "zu-ZA"],
    ["Íslenska", "is-IS"],
    ["Italiano", "it-IT"],
    ["Magyar", "hu-HU"],
    ["Nederlands", "nl-NL"],
    ["Norsk bokmål", "nb-NO"],
    ["Polski", "pl-PL"],
    ["Português (PT)", "pt-PT"],
    ["Português (BR)", "pt-BR"],
    ["Română", "ro-RO"],
    ["Slovenčina", "sk-SK"],
    ["Suomi", "fi-FI"],
    ["Svenska", "sv-SE"],
    ["Türkçe", "tr-TR"],
    ["български", "bg-BG"],
    ["日本語", "ja-JP"],
    ["한국어", "ko-KR"],
    ["Pусский", "ru-RU"],
    ["Српски", "sr-RS"]
  ];
  let o = [];
  async function r() {
    if (o.length > 0)
      return o;
    const e = await new Promise((e => {
      window.speechSynthesis.onvoiceschanged = () => {
        const n = window.speechSynthesis.getVoices();
        e(n)
      }
    }));
    return a.forEach((n => {
        e.some((e => s(e.lang, n[1]))) ? o.push(n) : t.warn(`${n[0]} not supported. Removed from selector.`)
      })),
      o
  }
  class l {
    constructor(e, n) {
      this.selectionCb = e,
        this.selected = n,
        this.storageKey = "sai-language",
        this.setDefaultFromStorage(),
        this.element = document.createElement("div"),
        this.selector = document.createElement("select"),
        this.element.id = "sai-lang-selector-wrapper",
        this.selector.id = "sai-lang-selector",
        r().then((e => {
          if (0 === e.length) {
            this.selector.classList.add("sai-hide");
            const e = document.createElement("div");
            return e.id = "sai-no-voices",
              e.innerHTML = "<a href='https://voicecontrol.chat/install-voices' target='_blank'>Install voices</a>",
              void this.element.appendChild(e)
          }
          e.forEach((([e, n]) => {
              const t = document.createElement("option");
              t.innerText = e,
                t.value = n,
                n === this.selected && (t.selected = !0),
                this.selector.appendChild(t)
            })),
            this.element.appendChild(this.selector),
            this.selector.onchange = e => {
              const n = e.target;
              this.selectLanguage(n.value)
            }
        }))
    }
    selectLanguage(e) {
      window.localStorage.setItem(this.storageKey, e),
        this.selectionCb(e)
    }
    setDefaultFromStorage() {
      let e = window.localStorage.getItem(this.storageKey);
      e && (this.selected = e,
        this.selectLanguage(e))
    }
  }
  class c {
    constructor(e, n, i) {
      this.lang = e,
        this.waitForContent = i,
        this.lastText = "",
        this.lastRead = Date.now(),
        this.lastUtter = Date.now(),
        this.lastUtterCharCount = 0,
        this.lastTimeout = 0,
        this.lastTimeSinceLastUtter = 0,
        this.synth = window.speechSynthesis,
        this.queue = [],
        this.enabled = !0,
        this.storageKey = "sai-read-aloud",
        this.queueIdle = !0,
        this.disableButton = document.createElement("div"),
        this.disableButton.innerHTML = '<?xml version="1.0" encoding="iso-8859-1"?>\n<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\n<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\n\t viewBox="0 0 496.159 496.159" style="enable-background:new 0 0 496.159 496.159;" xml:space="preserve">\n<path class="sai-svg-color-path" d="M496.159,248.085c0-137.023-111.07-248.082-248.076-248.082C111.071,0.003,0,111.063,0,248.085\n\tc0,137.001,111.07,248.07,248.083,248.07C385.089,496.155,496.159,385.086,496.159,248.085z"/>\n<g>\n\t<path style="fill:#FFFFFF;" d="M247.711,125.252c-3.41-1.851-7.559-1.688-10.813,0.426l-95.137,61.789h-35.164\n\t\tc-5.845,0-10.583,4.738-10.583,10.584v92.727c0,5.845,4.738,10.583,10.583,10.583h35.164l95.137,61.79\n\t\tc1.748,1.135,3.753,1.707,5.765,1.707c1.733,0,3.471-0.425,5.049-1.281c3.41-1.852,5.534-5.421,5.534-9.301V134.553\n\t\tC253.244,130.672,251.121,127.103,247.711,125.252z"/>\n\t<path style="fill:#FFFFFF;" d="M282.701,319.271c0.894,0,1.801-0.162,2.685-0.504c24.239-9.412,40.524-38.49,40.524-72.359\n\t\tc0-29.957-13.2-57.049-33.63-69.018c-3.534-2.072-8.08-0.885-10.153,2.65c-2.073,3.536-0.885,8.082,2.651,10.153\n\t\tc15.971,9.358,26.291,31.424,26.291,56.214c0,27.359-12.77,51.424-31.055,58.525c-3.82,1.481-5.714,5.781-4.231,9.602\n\t\tC276.924,317.474,279.729,319.271,282.701,319.271z"/>\n\t<path style="fill:#FFFFFF;" d="M302.073,350.217c0.895,0,1.802-0.162,2.684-0.504c34.046-13.219,57.822-55.979,57.822-103.988\n\t\tc0-43.187-18.884-82.156-48.11-99.279c-3.534-2.072-8.082-0.885-10.152,2.652c-2.073,3.535-0.885,8.081,2.651,10.152\n\t\tc24.768,14.512,40.771,48.455,40.771,86.475c0,42.027-19.883,79.1-48.353,90.154c-3.82,1.481-5.715,5.781-4.231,9.602\n\t\tC296.295,348.418,299.1,350.217,302.073,350.217z"/>\n\t<path style="fill:#FFFFFF;" d="M322.025,379.715c-3.005,0-5.841-1.818-6.994-4.788c-1.499-3.861,0.416-8.206,4.277-9.706\n\t\tc38.764-15.051,65.837-64.404,65.837-120.019c0-50.136-21.609-95.192-55.052-114.786c-3.574-2.094-4.773-6.688-2.68-10.262\n\t\tc2.094-3.574,6.688-4.774,10.263-2.68c37.948,22.232,62.469,72.369,62.469,127.728c0,61.66-31.009,116.764-75.409,134.002\n\t\tC323.846,379.551,322.928,379.715,322.025,379.715z"/>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n<g>\n</g>\n</svg>\n',
        this.disableButton.id = "sai-disable-read-aloud",
        this.disableButton.title = "Toggle read aloud",
        this.skipButton = document.createElement("div"),
        this.skipButton.innerHTML = '<?xml version="1.0" encoding="UTF-8"?>\n<svg width="700pt" height="700pt" version="1.1" viewBox="0 0 700 700" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n <defs>\n  <symbol id="s" overflow="visible">\n   <path d="m18.766-1.125c-0.96875 0.5-1.9805 0.875-3.0312 1.125-1.043 0.25781-2.1367 0.39062-3.2812 0.39062-3.3984 0-6.0898-0.94531-8.0781-2.8438-1.9922-1.9062-2.9844-4.4844-2.9844-7.7344 0-3.2578 0.99219-5.8359 2.9844-7.7344 1.9883-1.9062 4.6797-2.8594 8.0781-2.8594 1.1445 0 2.2383 0.13281 3.2812 0.39062 1.0508 0.25 2.0625 0.625 3.0312 1.125v4.2188c-0.98047-0.65625-1.9453-1.1406-2.8906-1.4531-0.94922-0.3125-1.9492-0.46875-3-0.46875-1.875 0-3.3516 0.60547-4.4219 1.8125-1.0742 1.1992-1.6094 2.8555-1.6094 4.9688 0 2.1055 0.53516 3.7617 1.6094 4.9688 1.0703 1.1992 2.5469 1.7969 4.4219 1.7969 1.0508 0 2.0508-0.14844 3-0.45312 0.94531-0.3125 1.9102-0.80078 2.8906-1.4688z"/>\n  </symbol>\n  <symbol id="b" overflow="visible">\n   <path d="m13.734-11.141c-0.4375-0.19531-0.87109-0.34375-1.2969-0.4375-0.41797-0.10156-0.83984-0.15625-1.2656-0.15625-1.2617 0-2.2305 0.40625-2.9062 1.2188-0.67969 0.80469-1.0156 1.9531-1.0156 3.4531v7.0625h-4.8906v-15.312h4.8906v2.5156c0.625-1 1.3438-1.7266 2.1562-2.1875 0.82031-0.46875 1.8008-0.70312 2.9375-0.70312 0.16406 0 0.34375 0.011719 0.53125 0.03125 0.19531 0.011719 0.47656 0.039062 0.84375 0.078125z"/>\n  </symbol>\n  <symbol id="a" overflow="visible">\n   <path d="m17.641-7.7031v1.4062h-11.453c0.125 1.1484 0.53906 2.0078 1.25 2.5781 0.70703 0.57422 1.7031 0.85938 2.9844 0.85938 1.0312 0 2.082-0.14844 3.1562-0.45312 1.082-0.3125 2.1914-0.77344 3.3281-1.3906v3.7656c-1.1562 0.4375-2.3125 0.76562-3.4688 0.98438-1.1562 0.22656-2.3125 0.34375-3.4688 0.34375-2.7734 0-4.9297-0.70312-6.4688-2.1094-1.5312-1.4062-2.2969-3.3789-2.2969-5.9219 0-2.5 0.75391-4.4609 2.2656-5.8906 1.5078-1.4375 3.582-2.1562 6.2188-2.1562 2.4062 0 4.332 0.73047 5.7812 2.1875 1.4453 1.4492 2.1719 3.3828 2.1719 5.7969zm-5.0312-1.625c0-0.92578-0.27344-1.6719-0.8125-2.2344-0.54297-0.57031-1.25-0.85938-2.125-0.85938-0.94922 0-1.7188 0.26562-2.3125 0.79688s-0.96484 1.2969-1.1094 2.2969z"/>\n  </symbol>\n  <symbol id="d" overflow="visible">\n   <path d="m9.2188-6.8906c-1.0234 0-1.793 0.17188-2.3125 0.51562-0.51172 0.34375-0.76562 0.85547-0.76562 1.5312 0 0.625 0.20703 1.1172 0.625 1.4688 0.41406 0.34375 0.98828 0.51562 1.7188 0.51562 0.92578 0 1.7031-0.32812 2.3281-0.98438 0.63281-0.66406 0.95312-1.4922 0.95312-2.4844v-0.5625zm7.4688-1.8438v8.7344h-4.9219v-2.2656c-0.65625 0.92969-1.3984 1.6055-2.2188 2.0312-0.82422 0.41406-1.8242 0.625-3 0.625-1.5859 0-2.8711-0.45703-3.8594-1.375-0.99219-0.92578-1.4844-2.1289-1.4844-3.6094 0-1.7891 0.61328-3.1016 1.8438-3.9375 1.2383-0.84375 3.1797-1.2656 5.8281-1.2656h2.8906v-0.39062c0-0.76953-0.30859-1.332-0.92188-1.6875-0.61719-0.36328-1.5703-0.54688-2.8594-0.54688-1.0547 0-2.0312 0.10547-2.9375 0.3125-0.89844 0.21094-1.7305 0.52344-2.5 0.9375v-3.7344c1.0391-0.25 2.0859-0.44141 3.1406-0.57812 1.0625-0.13281 2.125-0.20312 3.1875-0.20312 2.7578 0 4.75 0.54688 5.9688 1.6406 1.2266 1.0859 1.8438 2.8555 1.8438 5.3125z"/>\n  </symbol>\n  <symbol id="c" overflow="visible">\n   <path d="m7.7031-19.656v4.3438h5.0469v3.5h-5.0469v6.5c0 0.71094 0.14062 1.1875 0.42188 1.4375s0.83594 0.375 1.6719 0.375h2.5156v3.5h-4.1875c-1.9375 0-3.3125-0.39844-4.125-1.2031-0.80469-0.8125-1.2031-2.1797-1.2031-4.1094v-6.5h-2.4219v-3.5h2.4219v-4.3438z"/>\n  </symbol>\n  <symbol id="j" overflow="visible">\n   <path d="m12.766-13.078v-8.2031h4.9219v21.281h-4.9219v-2.2188c-0.66797 0.90625-1.4062 1.5703-2.2188 1.9844s-1.7578 0.625-2.8281 0.625c-1.8867 0-3.4336-0.75-4.6406-2.25-1.2109-1.5-1.8125-3.4258-1.8125-5.7812 0-2.3633 0.60156-4.2969 1.8125-5.7969 1.207-1.5 2.7539-2.25 4.6406-2.25 1.0625 0 2 0.21484 2.8125 0.64062 0.82031 0.42969 1.5664 1.0859 2.2344 1.9688zm-3.2188 9.9219c1.0391 0 1.8359-0.37891 2.3906-1.1406 0.55078-0.76953 0.82812-1.8828 0.82812-3.3438 0-1.457-0.27734-2.5664-0.82812-3.3281-0.55469-0.76953-1.3516-1.1562-2.3906-1.1562-1.043 0-1.8398 0.38672-2.3906 1.1562-0.55469 0.76172-0.82812 1.8711-0.82812 3.3281 0 1.4609 0.27344 2.5742 0.82812 3.3438 0.55078 0.76172 1.3477 1.1406 2.3906 1.1406z"/>\n  </symbol>\n  <symbol id="i" overflow="visible">\n   <path d="m10.5-3.1562c1.0508 0 1.8516-0.37891 2.4062-1.1406 0.55078-0.76953 0.82812-1.8828 0.82812-3.3438 0-1.457-0.27734-2.5664-0.82812-3.3281-0.55469-0.76953-1.3555-1.1562-2.4062-1.1562-1.0547 0-1.8594 0.38672-2.4219 1.1562-0.55469 0.77344-0.82812 1.8828-0.82812 3.3281 0 1.4492 0.27344 2.5586 0.82812 3.3281 0.5625 0.77344 1.3672 1.1562 2.4219 1.1562zm-3.25-9.9219c0.67578-0.88281 1.4219-1.5391 2.2344-1.9688 0.82031-0.42578 1.7656-0.64062 2.8281-0.64062 1.8945 0 3.4453 0.75 4.6562 2.25 1.207 1.5 1.8125 3.4336 1.8125 5.7969 0 2.3555-0.60547 4.2812-1.8125 5.7812-1.2109 1.5-2.7617 2.25-4.6562 2.25-1.0625 0-2.0078-0.21094-2.8281-0.625-0.8125-0.42578-1.5586-1.0859-2.2344-1.9844v2.2188h-4.8906v-21.281h4.8906z"/>\n  </symbol>\n  <symbol id="h" overflow="visible">\n   <path d="m0.34375-15.312h4.8906l4.125 10.391 3.5-10.391h4.8906l-6.4375 16.766c-0.64844 1.6953-1.4023 2.8828-2.2656 3.5625-0.86719 0.6875-2 1.0312-3.4062 1.0312h-2.8438v-3.2188h1.5312c0.83203 0 1.4375-0.13672 1.8125-0.40625 0.38281-0.26172 0.67969-0.73047 0.89062-1.4062l0.14062-0.42188z"/>\n  </symbol>\n  <symbol id="g" overflow="visible">\n   <path d="m2.3594-21.281h4.8906v21.281h-4.8906z"/>\n  </symbol>\n  <symbol id="f" overflow="visible">\n   <path d="m16.547-12.766c0.61328-0.94531 1.3477-1.6719 2.2031-2.1719 0.85156-0.5 1.7891-0.75 2.8125-0.75 1.7578 0 3.0977 0.54688 4.0156 1.6406 0.92578 1.0859 1.3906 2.6562 1.3906 4.7188v9.3281h-4.9219v-7.9844-0.35938c0.007813-0.13281 0.015625-0.32031 0.015625-0.5625 0-1.082-0.16406-1.8633-0.48438-2.3438-0.3125-0.48828-0.82422-0.73438-1.5312-0.73438-0.92969 0-1.6484 0.38672-2.1562 1.1562-0.51172 0.76172-0.77344 1.8672-0.78125 3.3125v7.5156h-4.9219v-7.9844c0-1.6953-0.14844-2.7852-0.4375-3.2656-0.29297-0.48828-0.8125-0.73438-1.5625-0.73438-0.9375 0-1.6641 0.38672-2.1719 1.1562-0.51172 0.76172-0.76562 1.8594-0.76562 3.2969v7.5312h-4.9219v-15.312h4.9219v2.2344c0.60156-0.86328 1.2891-1.5156 2.0625-1.9531 0.78125-0.4375 1.6406-0.65625 2.5781-0.65625 1.0625 0 2 0.25781 2.8125 0.76562 0.8125 0.51172 1.4258 1.2305 1.8438 2.1562z"/>\n  </symbol>\n  <symbol id="r" overflow="visible">\n   <path d="m12.422-21.281v3.2188h-2.7031c-0.6875 0-1.1719 0.125-1.4531 0.375-0.27344 0.25-0.40625 0.6875-0.40625 1.3125v1.0625h4.1875v3.5h-4.1875v11.812h-4.8906v-11.812h-2.4375v-3.5h2.4375v-1.0625c0-1.6641 0.46094-2.8984 1.3906-3.7031 0.92578-0.80078 2.3672-1.2031 4.3281-1.2031z"/>\n  </symbol>\n  <symbol id="e" overflow="visible">\n   <path d="m9.6406-12.188c-1.0859 0-1.9141 0.39062-2.4844 1.1719-0.57422 0.78125-0.85938 1.9062-0.85938 3.375s0.28516 2.5938 0.85938 3.375c0.57031 0.77344 1.3984 1.1562 2.4844 1.1562 1.0625 0 1.875-0.38281 2.4375-1.1562 0.57031-0.78125 0.85938-1.9062 0.85938-3.375s-0.28906-2.5938-0.85938-3.375c-0.5625-0.78125-1.375-1.1719-2.4375-1.1719zm0-3.5c2.6328 0 4.6914 0.71484 6.1719 2.1406 1.4766 1.418 2.2188 3.3867 2.2188 5.9062 0 2.5117-0.74219 4.4805-2.2188 5.9062-1.4805 1.418-3.5391 2.125-6.1719 2.125-2.6484 0-4.7148-0.70703-6.2031-2.125-1.4922-1.4258-2.2344-3.3945-2.2344-5.9062 0-2.5195 0.74219-4.4883 2.2344-5.9062 1.4883-1.4258 3.5547-2.1406 6.2031-2.1406z"/>\n  </symbol>\n  <symbol id="q" overflow="visible">\n   <path d="m17.75-9.3281v9.3281h-4.9219v-7.1094c0-1.3438-0.03125-2.2656-0.09375-2.7656s-0.16797-0.86719-0.3125-1.1094c-0.1875-0.3125-0.44922-0.55469-0.78125-0.73438-0.32422-0.17578-0.69531-0.26562-1.1094-0.26562-1.0234 0-1.8242 0.39844-2.4062 1.1875-0.58594 0.78125-0.875 1.8711-0.875 3.2656v7.5312h-4.8906v-21.281h4.8906v8.2031c0.73828-0.88281 1.5195-1.5391 2.3438-1.9688 0.83203-0.42578 1.75-0.64062 2.75-0.64062 1.7695 0 3.1133 0.54688 4.0312 1.6406 0.91406 1.0859 1.375 2.6562 1.375 4.7188z"/>\n  </symbol>\n  <symbol id="p" overflow="visible">\n   <path d="m2.5781-20.406h5.875l7.4219 14v-14h4.9844v20.406h-5.875l-7.4219-14v14h-4.9844z"/>\n  </symbol>\n  <symbol id="o" overflow="visible">\n   <path d="m2.1875-5.9688v-9.3438h4.9219v1.5312c0 0.83594-0.007813 1.875-0.015625 3.125-0.011719 1.25-0.015625 2.0859-0.015625 2.5 0 1.2422 0.03125 2.1328 0.09375 2.6719 0.070313 0.54297 0.17969 0.93359 0.32812 1.1719 0.20703 0.32422 0.47266 0.57422 0.79688 0.75 0.32031 0.16797 0.69141 0.25 1.1094 0.25 1.0195 0 1.8203-0.39062 2.4062-1.1719 0.58203-0.78125 0.875-1.8672 0.875-3.2656v-7.5625h4.8906v15.312h-4.8906v-2.2188c-0.74219 0.89844-1.5234 1.5586-2.3438 1.9844-0.82422 0.41406-1.7344 0.625-2.7344 0.625-1.7617 0-3.1055-0.53906-4.0312-1.625-0.92969-1.082-1.3906-2.6602-1.3906-4.7344z"/>\n  </symbol>\n  <symbol id="n" overflow="visible">\n   <path d="m17.75-9.3281v9.3281h-4.9219v-7.1406c0-1.3203-0.03125-2.2344-0.09375-2.7344s-0.16797-0.86719-0.3125-1.1094c-0.1875-0.3125-0.44922-0.55469-0.78125-0.73438-0.32422-0.17578-0.69531-0.26562-1.1094-0.26562-1.0234 0-1.8242 0.39844-2.4062 1.1875-0.58594 0.78125-0.875 1.8711-0.875 3.2656v7.5312h-4.8906v-15.312h4.8906v2.2344c0.73828-0.88281 1.5195-1.5391 2.3438-1.9688 0.83203-0.42578 1.75-0.64062 2.75-0.64062 1.7695 0 3.1133 0.54688 4.0312 1.6406 0.91406 1.0859 1.375 2.6562 1.375 4.7188z"/>\n  </symbol>\n  <symbol id="m" overflow="visible">\n   <path d="m2.5781-20.406h8.7344c2.5938 0 4.582 0.57812 5.9688 1.7344 1.3945 1.1484 2.0938 2.7891 2.0938 4.9219 0 2.1367-0.69922 3.7812-2.0938 4.9375-1.3867 1.1562-3.375 1.7344-5.9688 1.7344h-3.4844v7.0781h-5.25zm5.25 3.8125v5.7031h2.9219c1.0195 0 1.8047-0.25 2.3594-0.75 0.5625-0.5 0.84375-1.2031 0.84375-2.1094 0-0.91406-0.28125-1.6172-0.84375-2.1094-0.55469-0.48828-1.3398-0.73438-2.3594-0.73438z"/>\n  </symbol>\n  <symbol id="l" overflow="visible">\n   <path d="m2.3594-15.312h4.8906v15.031c0 2.0508-0.49609 3.6172-1.4844 4.7031-0.98047 1.082-2.4062 1.625-4.2812 1.625h-2.4219v-3.2188h0.85938c0.92578 0 1.5625-0.21094 1.9062-0.625 0.35156-0.41797 0.53125-1.2461 0.53125-2.4844zm0-5.9688h4.8906v4h-4.8906z"/>\n  </symbol>\n  <symbol id="k" overflow="visible">\n   <path d="m14.719-14.828v3.9844c-0.65625-0.45703-1.3242-0.79688-2-1.0156-0.66797-0.21875-1.3594-0.32812-2.0781-0.32812-1.3672 0-2.4336 0.40234-3.2031 1.2031-0.76172 0.79297-1.1406 1.9062-1.1406 3.3438 0 1.4297 0.37891 2.543 1.1406 3.3438 0.76953 0.79297 1.8359 1.1875 3.2031 1.1875 0.75781 0 1.4844-0.10938 2.1719-0.32812 0.6875-0.22656 1.3203-0.56641 1.9062-1.0156v4c-0.76172 0.28125-1.5391 0.48828-2.3281 0.625-0.78125 0.14453-1.5742 0.21875-2.375 0.21875-2.7617 0-4.9219-0.70703-6.4844-2.125-1.5547-1.4141-2.3281-3.3828-2.3281-5.9062 0-2.5312 0.77344-4.5039 2.3281-5.9219 1.5625-1.4141 3.7227-2.125 6.4844-2.125 0.80078 0 1.5938 0.074219 2.375 0.21875 0.78125 0.13672 1.5547 0.35156 2.3281 0.64062z"/>\n  </symbol>\n </defs>\n <g>\n  <path d="m134.69 38.246 293.43 235.17c4.2656 3.2578 3.6094 9.9531 0 13.102l-293.43 235.11c-6.4414 4.7617-13.645 0.21875-13.645-6.5508v-74.953c0-5.0703 2.1523-6.9141 5.6211-9.8086l187.64-150.36-189.11-151.59c-3.1445-2.5273-4.1523-4.9883-4.1523-8.6016v-74.98c0-7.6094 7.7539-10.453 13.645-6.5508z" fill-rule="evenodd"/>\n  <path d="m570.56 36.402c4.6367 0 8.3945 3.7578 8.3945 8.3945v470.29c0 4.6367-3.7578 8.3945-8.3945 8.3945h-65.09 0.003906c-4.6367 0-8.3945-3.7578-8.3984-8.3945v-470.29c0.003906-4.6367 3.7617-8.3945 8.3984-8.3945h65.09z" fill-rule="evenodd"/>\n </g>\n</svg>\n',
        this.skipButton.id = "sai-skip-read-aloud",
        this.skipButton.title = "Skip read aloud",
        window.speechSynthesis.cancel(),
        this.disableButton.addEventListener("click", (() => {
          this.enabled ? this.disableReadAloud() : this.enableReadAloud()
        })),
        this.skipButton.onclick = () => {
          this.skipReading()
        },
        this.setReadAloudFromStorage(),
        t.info(`reInit ${n}, lastTextLength: ${this.lastText.length}`),
        n && this.reset()
    }
    async runQueue() {
      if (t.info(`Queue is idle: ${this.queueIdle}`),
        this.queue.length > 0 && this.queueIdle) {
        this.skipButton.classList.add("sai-active"),
          this.queueIdle = !1;
        const e = this.queue.shift();
        await this.readAloud(e),
          this.queueIdle = !0,
          this.skipButton.classList.remove("sai-active"),
          this.queue.length > 0 && this.runQueue()
      }
    }
    observerCallback(e) {
      const n = this.getText();
      if (0 === n.length)
        t.info("No text, reset"),
        this.reset();
      else if (this.waitForContent)
        return t.info("Wait for content"),
          this.lastText = n,
          void(this.waitForContent = !1);
      const i = n.replace(this.lastText.trim(), "").trim(),
        s = i[i.length - 1],
        a = this.lastRead + 1e4 < Date.now();
      if (i.length > 0 && ("." === s || "?" === s || "!" === s || ":" === s || "。" === s || a)) {
        a && (t.warn(`Long time since last read. Queue length: ${this.queue.length}`),
            this.queueIdle = !0),
          t.info(`Push to queue: ${i}`);
        i.split(".").filter((e => e.length > 0)).forEach((e => {
            this.queue.push(e)
          })),
          this.runQueue(),
          this.lastRead = Date.now(),
          this.lastText = n
      }
    }
    setLang(e) {
      this.lang = e
    }
    skipReading() {
      this.synth.cancel(),
        this.queue = [],
        this.queueIdle = !0;
      const e = document.querySelectorAll(".text-base");
      for (var n = 0; n < e.length; n++)
        e[n]?.classList.add("sai-skip")
    }
    repeat(e) {
      this.synth.cancel(),
        this.queue = [],
        this.queueIdle = !0;
      const n = this.getText(e);
      t.info(`Repeat: ${n}`),
        this.queue.push(n),
        this.runQueue()
    }
    readAloud(e) {
      return new Promise(((n, i) => {
        if (!this.enabled)
          return t.info("Read aloud disabled"),
            void n(void 0);
        if (!e)
          return t.info("No text to read"),
            void n(void 0);
        if (!document.getElementById("sai-root"))
          return void n(void 0);
        let a = e.replace(/([0-9]+)\.(?=[0-9]+(?!\.))/g, "$1,");
        this.synth = window.speechSynthesis;
        const o = new SpeechSynthesisUtterance(a),
          r = this.synth.getVoices().reverse().filter((e => s(e.lang, this.lang))),
          l = window.localStorage.getItem("sai-voice-preference" + this.lang),
          c = r.find((e => e.voiceURI === l)) ?? r[0];
        if (!c)
          throw new Error(`unknown voice: ${c} lang: ${this.lang}`);
        o.volume = 1,
          o.voice = c;
        const d = window.localStorage.getItem("sai-voice-speed-v2");
        d || t.error("No speed stored in storage");
        const p = function (e) {
          switch (e) {
            case "1":
              return .1;
            case "2":
              return .2;
            case "3":
              return .3;
            case "4":
              return .4;
            case "5":
              return .5;
            case "6":
              return .6;
            case "7":
              return .7;
            case "8":
              return .8;
            case "9":
              return .9;
            case "10":
            default:
              return 1;
            case "11":
              return 1.1;
            case "12":
              return 1.13;
            case "13":
              return 1.15;
            case "14":
              return 1.17;
            case "15":
              return 1.2;
            case "16":
              return 1.25;
            case "17":
              return 1.3;
            case "18":
              return 1.35;
            case "19":
              return 1.4;
            case "20":
              return 1.45
          }
        }(d);
        let h;
        o.rate = p,
          t.success(`Voice name: ${c.name}, lang: ${c.lang}, rate: ${p}: ${a}`);
        const u = () => {
          const [e, n] = function (e, n, i, s, a, o) {
            const r = Date.now() - i,
              l = function (e, n, t) {
                let i = 100;
                return "zh-CN" !== e && "zh-TW" !== e && "zh-HK" !== e || (i = 240),
                  "zh-TW" === e && (i = 300),
                  "ja-JP" === e && (i = 260),
                  "ko-KR" === e && (i = 240),
                  7e3 + n * i * (1 / t)
              }(e, a, n);
            if (t.warn(`[resumeInfinity] Time since last utter: ${r.toFixed(1)}. Timeout: ${l.toFixed(1)}. Last char count: ${a}`),
              window.navigator.userAgent.search("Mac") > -1 && 0 === r && o > 0) {
              const e = s - o,
                n = e / s * 100;
              t.warn(`Last timeout safety gap: ${e.toFixed(1)}ms. ${n.toFixed(1)}%`),
                n < 25 && t.error(`________Safety gap ${n.toFixed(1)}% too low!________`)
            }
            return r > l ? (t.error(`No utter timeout ${l.toFixed(1)} - cancel.`),
              window.speechSynthesis.cancel(),
              setTimeout((() => {
                window.speechSynthesis.resume()
              }), 50),
              [0, 0]) : [l, r]
          }(c.lang, p, this.lastUtter, this.lastTimeout, this.lastUtterCharCount, this.lastTimeSinceLastUtter);
          this.lastTimeout = e,
            this.lastTimeSinceLastUtter = n,
            window.speechSynthesis.pause(),
            window.speechSynthesis.resume(),
            h = setTimeout(u, 7e3)
        };
        o.addEventListener("error", (e => {
            t.error(`Read aloud error ${e.error}`, e),
              n(void 0),
              clearTimeout(h)
          })),
          o.addEventListener("start", (() => {
            t.info(`Speech has started. Volume: ${o.volume}`),
              this.lastUtter = Date.now(),
              u()
          })),
          o.addEventListener("end", (function (e) {
            t.info("Speech has ended"),
              n(void 0),
              clearTimeout(h)
          })),
          o.addEventListener("pause", (function (e) {
            t.verbose("Speech has paused", e)
          })),
          o.addEventListener("resume", (function (e) {
            t.verbose("Speech has resumed", e)
          })),
          o.addEventListener("boundary", (function (e) {
            t.verbose(`Speech reached boundary. CharIndex: ${e.charIndex}`, e)
          })),
          o.addEventListener("mark", (function (e) {
            t.info("Speech reached mark", e)
          })),
          this.synth.speak(o),
          this.lastUtterCharCount = a.length
      }))
    }
    enableReadAloud() {
      this.enabled = !0,
        this.disableButton.classList.remove("disabled"),
        this.updateStorage(),
        document.querySelectorAll(".sai-repeat-button").forEach((e => {
          e.classList.remove("sai-disabled")
        }))
    }
    disableReadAloud() {
      this.queue = [],
        this.queueIdle = !0,
        this.synth.cancel(),
        this.disableButton.classList.add("disabled"),
        this.enabled = !1,
        this.updateStorage(),
        document.querySelectorAll(".sai-repeat-button").forEach((e => {
          e.classList.add("sai-disabled")
        }))
    }
    updateStorage() {
      window.localStorage.setItem(this.storageKey, this.enabled.toString())
    }
    setReadAloudFromStorage() {
      const e = window.localStorage.getItem(this.storageKey);
      e && (this.enabled = "true" === e,
        this.enabled ? this.enableReadAloud() : this.disableReadAloud())
    }
    getText(e) {
      const n = document.querySelectorAll(".text-base:not(.sai-skip) .markdown"),
        t = (e || n[n.length - 1])?.children ?? [];
      let i = "";
      for (const e of t)
        "PRE" !== e.nodeName && (i += e.textContent);
      return i = i.replace(/`/g, "").replace(/\*/g, "").replace(/\"/g, "").replace(/\\n/g, "").replace(/\\t/g, "").replace(/\\b/g, "").replace(/(/g, " (").replace(/)/g, ") ").replace(/?/g, "? ").replace(/:/g, ": ").replace(/!/g, "! ").replace(/。/g, ". "),
        i
    }
    reset() {
      t.warn("RESET read aloud queue"),
        this.queue = [],
        this.lastRead = Date.now()
    }
  }
  class d {
    constructor(e, n, i) {
      this.lang = e,
        this.errorMessage = n,
        this.transcript = "",
        this.recognition = new webkitSpeechRecognition,
        this.isRecording = !1,
        this.recognition.continuous = !0,
        this.recognition.interimResults = !0,
        this.recognition.onstart = () => {},
        this.recognition.onresult = e => {
          let n = "";
          for (let t = e.resultIndex; t < e.results.length; ++t)
            e.results[t].isFinal ? this.isRecording && (this.transcript += e.results[t][0].transcript,
              i(this.transcript)) : n += e.results[t][0].transcript;
          this.isRecording && i(this.transcript + n)
        },
        this.recognition.onerror = e => {
          let n = e.error;
          "not-allowed" === e.error && (n = "The webpage is not allowed to access your microphone"),
            "no-speech" === e.error && (n = "No sound from the microphone");
          let i = `\n        <span>\n          Error from Voice Control:\n          <br />\n          ${n}\n          <br /><br />\n          <em style="font-size: 10px; font-weight: normal;">\n            See voicecontrol.chat/support for help\n          </em>\n        </span>\n      `;
          this.errorMessage.write(i, 8e3),
            t.error(`recognition.onerror ${e.error}`)
        },
        this.recognition.onend = () => {
          t.info("Ended"),
            this.endCallback?.()
        }
    }
    start(e) {
      t.info("Start"),
        this.endCallback = e,
        this.recognition.lang = this.lang,
        this.recognition.start(),
        this.isRecording = !0
    }
    stop() {
      t.info(`Stop: ${this.transcript}`),
        this.isRecording = !1,
        this.recognition.stop(),
        this.endCallback = void 0
    }
    reset() {
      this.isRecording = !1,
        this.transcript = ""
    }
    setLang(e) {
      this.lang = e
    }
  }
  class p {
    constructor(e) {
      this.readAloud = e,
        this.showCompactUi = "true" === window.localStorage.getItem("sai-compact-ui"),
        this.appIsHidden = "true" === window.localStorage.getItem('"sai-hidden"'),
        this.settingsView = document.createElement("div"),
        this.settingsView.innerHTML = `\n    <div id="sai-settings-header">\n      <h3>Voice Control for ChatGPT</h3>\n      <button class="sai-button" id="sai-close-settings">Close</button>\n    </div>\n    <div id="sai-settings-view-inner">\n      <div class="sai-settings-col">\n        <section>\n          <h4 id="sai-settings-read-aloud-header">Read aloud speed: <span id="sai-read-aloud-speed"></span></h4>\n          <div class="sai-slidecontainer">\n            <input\n              type="range"\n              min="1"\n              max="20"\n              value="10"\n              step="1"\n              class="sai-slider"\n              id="sai-popup-range-slider"\n            />\n          </div>\n        </section>\n\n        <section class="sai-settings-section">\n          <h4>Voice preference</h4>\n          <div id="sai-voice-settings"></div>\n          <a href="https://voicecontrol.chat/install-voices" id="sai-settings-voice-link" target="_blank">\n            Install more voices\n          </a>\n        </section>\n\n        <section class="sai-settings-section">\n          <h4>Display settings</h4>\n          <p></p>\n          <button id="sai-ui-toggle" class="sai-button">\n            ${this.showCompactUi ? "Use classic interface" : "Use compact interface"}\n          </button>\n          <p></p>\n          <button id="sai-display-toggle" class="sai-button">\n            ${this.appIsHidden ? "Show " : "Hide "}\n            Voice Control\n          </button>\n        </section>\n\n\n        <section class="sai-settings-section">\n          <h4>Need help or have a suggestion?</h4>\n          <p>\n            If you have trouble loading voices or need help troubleshooting please\n            <a href="https://voicecontrol.chat/support" target="_blank">\n              see the FAQ.\n            </a>\n          </p>\n\n          <p>\n            If you have suggestions on how to improve the extension please share your ideas\n            <a href="https://forms.gle/BA3AU9LdApsZDBW28" target="_blank">\n              here.\n            </a>\n          </p>\n        </section>\n      </div>\n      <div class="sai-settings-col">\n        <h4>Keyboard shortcuts</h4>\n\n        <ul>\n        <li>\n          Press-and-hold <strong>SPACE</strong> (outside text input) to\n          record, and release to submit\n        </li>\n        <li>\n          Press <strong>ESC</strong> or <strong>Q</strong> to cancel a\n          transcription\n        </li>\n        <li>\n          Press <strong>E</strong> to stop and copy the transcription to the\n          ChatGPT input field without submitting\n        </li>\n      </ul>\n\n      <section class="sai-settings-section">\n        <p><em>Upgrade your language learning experience with <a class="sai-link-talkio" href="https://talkio.ai" target="_blank">Talkio AI</a>,\n        the premium version of this extension designed specifically for language learners.</em></p>\n      </section>\n\n      <section class="sai-settings-section">\n        <p>\n          The extension is created by <a href="https://twitter.com/theisof" target="_blank">Theis Frøhlich</a>\n          <br />\n          Please <a href="https://chrome.google.com/webstore/detail/voice-control-for-chatgpt/eollffkcakegifhacjnlnegohfdlidhn" target="_blank">leave a review</a>\n          if you like this extension.\n        </p>\n      </section>\n      </div>\n    </div>`,
        this.settingsView.id = "sai-settings-view",
        this.settingsView.classList.add("sai-hide")
    }
    setupListeners() {
      const e = document.getElementById("sai-popup-range-slider"),
        n = document.getElementById("sai-read-aloud-speed"),
        i = document.getElementById("sai-close-settings"),
        s = document.getElementById("sai-display-toggle"),
        a = document.getElementById("sai-ui-toggle");
      if (!(e && n && i && s && a))
        return void t.warn("settings element missing");
      i.onclick = () => {
          this.settingsView.classList.add("sai-hide")
        },
        s.onclick = () => {
          document.body.classList.toggle("sai-hidden");
          const e = window.localStorage.getItem('"sai-hidden"');
          e && "true" === e ? (window.localStorage.setItem('"sai-hidden"', "false"),
            this.appIsHidden = !1,
            s.innerText = "Hide Voice Control") : (window.localStorage.setItem('"sai-hidden"', "true"),
            this.appIsHidden = !0,
            this.readAloud.disableReadAloud(),
            s.innerText = "Show Voice Control")
        },
        a.onclick = () => {
          document.body.classList.toggle("sai-compact");
          const e = window.localStorage.getItem("sai-compact-ui");
          e && "true" === e ? (window.localStorage.setItem("sai-compact-ui", "false"),
            this.showCompactUi = !1,
            a.innerText = "Compact interface") : (window.localStorage.setItem("sai-compact-ui", "true"),
            this.showCompactUi = !0,
            a.innerText = "Classic interface");
          document.getElementById("sai-root")?.remove()
        };
      const o = t => {
        n.innerHTML = this.labelFromSpeedValue(t);
        e.value = t,
          window.localStorage.setItem("sai-voice-speed-v2", t)
      };
      e.oninput = e => {
        const n = e.target;
        o(n.value)
      };
      const r = window.localStorage.getItem("sai-voice-speed-v2");
      r && o(r)
    }
    createVoiceSelector() {
      const e = window.localStorage.getItem("sai-language") ?? "en-US",
        n = window.speechSynthesis.getVoices().filter((n => s(n.lang, e))).reverse(),
        t = window.localStorage.getItem("sai-voice-preference" + e),
        i = document.createElement("select");
      i.id = "sai-voice-selector",
        i.style.color = "black",
        i.style.width = "100%",
        n.forEach((e => {
          const n = document.createElement("option");
          n.innerText = e.name,
            n.value = e.voiceURI,
            t === n.value && (n.selected = !0),
            i.appendChild(n)
        })),
        i.onchange = n => {
          const t = n.target;
          window.localStorage.setItem("sai-voice-preference" + e, t.value)
        };
      const a = document.getElementById("sai-voice-settings");
      a && (a.innerHTML = "",
        a.appendChild(i))
    }
    labelFromSpeedValue(e) {
      return e
    }
  }
  class h {
    constructor(e) {
      const n = "true" === window.localStorage.getItem("sai-read-aloud");
      this.element = document.createElement("div"),
        this.element.innerHTML = '<?xml version="1.0" encoding="utf-8"?>\n<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">\n<path fill-rule="evenodd" clip-rule="evenodd" d="M12.1657 2.14424C12.8728 2.50021 13 3.27314 13 3.7446V20.2561C13 20.7286 12.8717 21.4998 12.1656 21.8554C11.416 22.2331 10.7175 21.8081 10.3623 21.4891L4.95001 16.6248H3.00001C1.89544 16.6248 1.00001 15.7293 1.00001 14.6248L1 9.43717C1 8.3326 1.89543 7.43717 3 7.43717H4.94661L10.3623 2.51158C10.7163 2.19354 11.4151 1.76635 12.1657 2.14424ZM11 4.63507L6.00618 9.17696C5.82209 9.34439 5.58219 9.43717 5.33334 9.43717H3L3.00001 14.6248H5.33334C5.58015 14.6248 5.81823 14.716 6.00179 14.881L11 19.3731V4.63507Z" fill="#000000"/>\n<path d="M16.0368 4.73124C16.1852 4.19927 16.7368 3.88837 17.2688 4.03681C20.6116 4.9696 23 8.22106 23 12C23 15.779 20.6116 19.0304 17.2688 19.9632C16.7368 20.1117 16.1852 19.8007 16.0368 19.2688C15.8884 18.7368 16.1993 18.1852 16.7312 18.0368C19.1391 17.3649 21 14.9567 21 12C21 9.04332 19.1391 6.63512 16.7312 5.96321C16.1993 5.81477 15.8884 5.2632 16.0368 4.73124Z" fill="#000000"/>\n<path d="M16.2865 8.04192C15.7573 7.88372 15.2001 8.18443 15.0419 8.71357C14.8837 9.24271 15.1844 9.79992 15.7136 9.95812C16.3702 10.1544 17 10.9209 17 12C17 13.0791 16.3702 13.8456 15.7136 14.0419C15.1844 14.2001 14.8837 14.7573 15.0419 15.2865C15.2001 15.8156 15.7573 16.1163 16.2865 15.9581C17.9301 15.4667 19 13.8076 19 12C19 10.1924 17.9301 8.53333 16.2865 8.04192Z" fill="#000000"/>\n</svg>',
        this.element.classList.add("sai-repeat-button"),
        n || this.element.classList.add("sai-disabled"),
        this.element.onclick = () => {
          const n = this.element.closest(".text-base")?.querySelector(".markdown");
          n ? e.repeat(n) : t.warn("Could not find text element to repeat")
        }
    }
  }
  class u {
    constructor(e) {
      this.readAloud = e
    }
    injectRepeatButtons() {
      document.querySelectorAll(".text-base .text-gray-400.flex.self-end.justify-center.mt-2.gap-2.visible .flex.gap-1").forEach((e => {
        if (e.querySelectorAll(".sai-repeat-button").length > 0)
          return;
        const n = new h(this.readAloud);
        e.appendChild(n.element)
      }))
    }
  }
  const g = a[0][1];
  class m {
    constructor(e = !1, n = !1) {
      this.isRecording = !1,
        this.language = g,
        this.spaceIsDown = !1,
        this.isCompact = "true" === window.localStorage.getItem("sai-compact-ui"),
        t.info("Init app");
      const s = document.querySelector("textarea"),
        a = s?.parentElement,
        o = a?.querySelector("button");
      if (!s || !a || !o)
        throw new Error("Missing elements");
      this.chatGptInput = s,
        this.chatGptInputParent = a,
        this.chatGptSubmitButton = o,
        this.saiRoot = document.createElement("div"),
        this.saiRoot.id = "sai-root",
        this.saiInput = document.createElement("div"),
        this.saiInput.className = s.classList.value + " sai-input",
        this.saiInputWrapper = document.createElement("div"),
        this.saiInputWrapper.id = "sai-input-wrapper",
        this.saiInputWrapper.className = this.chatGptInputParent.classList.value + " is-idle",
        this.saiInputWrapper.appendChild(this.saiInput),
        this.saiRoot.appendChild(this.saiInputWrapper),
        this.isCompact ? this.chatGptInputParent.before(this.saiRoot) : this.chatGptInputParent.after(this.saiRoot),
        this.saiCancelMsg = document.createElement("div"),
        this.saiCancelMsg.id = "sai-cancel-msg",
        this.saiCancelMsg.innerHTML = "press esc to cancel",
        this.saiInputWrapper.appendChild(this.saiCancelMsg),
        this.saiRecordButton = document.createElement("div"),
        this.saiRecordButton.id = "sai-speech-button",
        this.saiRecordButton.innerHTML = '<?xml version="1.0" ?><svg baseProfile="tiny" height="24px" id="Layer_1" version="1.2" viewBox="0 0 24 24" width="24px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g><path d="M12,16c2.206,0,4-1.795,4-4V6c0-2.206-1.794-4-4-4S8,3.794,8,6v6C8,14.205,9.794,16,12,16z"/><path d="M19,12v-2c0-0.552-0.447-1-1-1s-1,0.448-1,1v2c0,2.757-2.243,5-5,5s-5-2.243-5-5v-2c0-0.552-0.447-1-1-1s-1,0.448-1,1v2   c0,3.52,2.613,6.432,6,6.92V20H8c-0.553,0-1,0.447-1,1s0.447,1,1,1h8c0.553,0,1-0.447,1-1s-0.447-1-1-1h-3v-1.08   C16.387,18.432,19,15.52,19,12z"/></g></svg>',
        this.saiInputWrapper.appendChild(this.saiRecordButton),
        this.saiButtonWrapper = document.createElement("div"),
        this.saiButtonWrapper.id = "sai-button-wrapper",
        this.saiSettingsButton = document.createElement("div"),
        this.saiSettingsButton.id = "sai-settings-button",
        this.saiSettingsButton.innerHTML = '<?xml version="1.0" encoding="UTF-8"?>\n<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->\n<svg width="800px" height="800px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n\n    <title>/svg/ic-settings</title>\n    <desc>Created with Sketch.</desc>\n    <defs>\n\n</defs>\n    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">\n        <g id="ic-settings" fill="#ffffff">\n            <path d="M1,5 C1,4.44771525 1.44266033,4 1.99895656,4 L3.00104344,4 C3.55275191,4 4,4.44386482 4,5 C4,5.55228475 3.55733967,6 3.00104344,6 L1.99895656,6 C1.44724809,6 1,5.55613518 1,5 Z M12,5 C12,4.44771525 12.444837,4 12.9955775,4 L22.0044225,4 C22.5542648,4 23,4.44386482 23,5 C23,5.55228475 22.555163,6 22.0044225,6 L12.9955775,6 C12.4457352,6 12,5.55613518 12,5 Z M8,6 C7.44771525,6 7,5.55228475 7,5 C7,4.44771525 7.44771525,4 8,4 C8.55228475,4 9,4.44771525 9,5 C9,5.55228475 8.55228475,6 8,6 Z M8,8 C6.34314575,8 5,6.65685425 5,5 C5,3.34314575 6.34314575,2 8,2 C9.65685425,2 11,3.34314575 11,5 C11,6.65685425 9.65685425,8 8,8 Z M1,19 C1,18.4477153 1.44266033,18 1.99895656,18 L3.00104344,18 C3.55275191,18 4,18.4438648 4,19 C4,19.5522847 3.55733967,20 3.00104344,20 L1.99895656,20 C1.44724809,20 1,19.5561352 1,19 Z M12,19 C12,18.4477153 12.444837,18 12.9955775,18 L22.0044225,18 C22.5542648,18 23,18.4438648 23,19 C23,19.5522847 22.555163,20 22.0044225,20 L12.9955775,20 C12.4457352,20 12,19.5561352 12,19 Z M8,20 C7.44771525,20 7,19.5522847 7,19 C7,18.4477153 7.44771525,18 8,18 C8.55228475,18 9,18.4477153 9,19 C9,19.5522847 8.55228475,20 8,20 Z M8,22 C6.34314575,22 5,20.6568542 5,19 C5,17.3431458 6.34314575,16 8,16 C9.65685425,16 11,17.3431458 11,19 C11,20.6568542 9.65685425,22 8,22 Z M1,12 C1,11.4477153 1.4556644,11 1.99539757,11 L10.0046024,11 C10.5543453,11 11,11.4438648 11,12 C11,12.5522847 10.5443356,13 10.0046024,13 L1.99539757,13 C1.44565467,13 1,12.5561352 1,12 Z M19,12 C19,11.4477153 19.4433532,11 20.0093689,11 L21.9906311,11 C22.5480902,11 23,11.4438648 23,12 C23,12.5522847 22.5566468,13 21.9906311,13 L20.0093689,13 C19.4519098,13 19,12.5561352 19,12 Z M15,13 C14.4477153,13 14,12.5522847 14,12 C14,11.4477153 14.4477153,11 15,11 C15.5522847,11 16,11.4477153 16,12 C16,12.5522847 15.5522847,13 15,13 Z M15,15 C13.3431458,15 12,13.6568542 12,12 C12,10.3431458 13.3431458,9 15,9 C16.6568542,9 18,10.3431458 18,12 C18,13.6568542 16.6568542,15 15,15 Z" id="Combined-Shape">\n\n</path>\n        </g>\n    </g>\n</svg>',
        this.saiSettingsButton.onclick = () => {
          document.getElementById("sai-settings-view")?.classList.remove("sai-hide"),
            this.settings.createVoiceSelector()
        },
        this.saiErrorMessage = document.createElement("div"),
        this.saiErrorMessage.id = "sai-error-message",
        this.saiErrorMessage.innerHTML = "error",
        this.saiErrorMessage.style.display = "none",
        this.saiRoot.append(this.saiErrorMessage),
        this.errorMessage = new i(this.saiErrorMessage),
        this.speech = new d(this.language, this.errorMessage, this.speechCallback.bind(this)),
        this.readAloud = new c(this.language, e, n),
        this.settings = new p(this.readAloud),
        this.speechHandlers();
      const r = new l(this.setLanguage.bind(this), g);
      this.saiButtonWrapper.appendChild(r.element),
        this.saiRoot.appendChild(this.saiButtonWrapper),
        this.saiButtonWrapper.appendChild(this.readAloud.skipButton),
        this.saiButtonWrapper.appendChild(this.readAloud.disableButton),
        this.saiButtonWrapper.appendChild(this.saiSettingsButton),
        this.saiRoot.appendChild(this.settings.settingsView),
        this.settings.setupListeners(),
        this.repeatHandler = new u(this.readAloud);
      document.querySelectorAll("#sai-root").length > 1 && this.errorMessage.write("<span>\n        Looks like Voice Control for ChatGPT is installed twice.\n        Please go to chrome://extensions and disable one of the installations\n        </span>\n        ", 7e3)
    }
    keyDownHandler(e) {
      const n = e.target;
      if ("textarea" === n?.localName || "Space" !== e.code || this.spaceIsDown || (this.holdSpaceTimer = setTimeout((() => {
            t.info("Space start"),
              this.startRecording(),
              this.speech.start((() => {
                this.stopRecording()
              }))
          }), 250),
          this.spaceIsDown = !0),
        "textarea" !== n?.localName && "Space" === e.code && this.isRecording && this.isCompact && this.appToIdle(),
        "textarea" === n?.localName || "Escape" !== e.code && "KeyQ" !== e.code || !this.isRecording || (t.info(`Pressed ${e.code}`),
          this.appToIdle()),
        ("Escape" === e.code || "KeyQ" === e.code) && !this.isRecording) {
        t.info(`Pressed ${e.code}. Close settings`);
        document.getElementById("sai-settings-view")?.classList.add("sai-hide")
      }
      "KeyE" === e.code && this.isRecording && (t.info("Pressed KeyE"),
          this.chatGptInput.value = this.saiInput.innerText,
          this.appToIdle()),
        "textarea" !== n?.localName && "Enter" === e.code && this.isRecording && (t.info("Enter stop"),
          this.submitToChatGPT(this.chatGptInput.value),
          this.appToIdle())
    }
    keyUpHandler(e) {
      this.spaceIsDown && "Space" === e.code && (this.isCompact || (t.info("Space stop"),
        this.stopRecording()))
    }
    onSubmit() {
      t.info("on Submit"),
        this.appToIdle()
    }
    adjustCompactIconPos() {
      if (!this.isCompact)
        return;
      const e = this.chatGptInput.offsetHeight,
        n = document.querySelector("textarea + button.absolute.p-1.rounded-md");
      n && (n.style.marginRight = e > 30 ? "0" : "35px")
    }
    speechHandlers() {
      this.spaceIsDown = !1,
        this.saiInputWrapper.onclick = () => {
          this.isRecording ? (this.speech.stop(),
            this.stopRecording()) : (this.startRecording(),
            this.speech.start((() => {
              this.stopRecording()
            })))
        }
    }
    startRecording() {
      this.isRecording = !0,
        this.saiInputWrapper.classList.remove("is-idle")
    }
    stopRecording() {
      if (this.isCompact)
        this.chatGptInput.dispatchEvent(new Event("input", {
          bubbles: !0
        }));
      else {
        const e = this.saiInput.innerText;
        this.submitToChatGPT(e)
      }
      this.appToIdle()
    }
    submitToChatGPT(e) {
      e.length > 0 && (this.chatGptInput.value = e,
        this.chatGptInput.dispatchEvent(new Event("input", {
          bubbles: !0
        })),
        this.chatGptSubmitButton.click())
    }
    appToIdle() {
      this.speech.stop(),
        this.speech.reset(),
        this.isRecording = !1,
        this.saiInput.innerText = "",
        this.saiInputWrapper.classList.add("is-idle"),
        this.spaceIsDown = !1,
        clearTimeout(this.holdSpaceTimer)
    }
    speechCallback(e) {
      this.isCompact ? e.length > 0 && (this.chatGptInput.value = e,
        this.chatGptInput.dispatchEvent(new Event("input", {
          bubbles: !0
        }))) : this.saiInput.innerText = e
    }
    setLanguage(e) {
      this.language = e,
        this.readAloud.setLang(e),
        this.speech.setLang(e)
    }
  }
  t.setup();
  window.localStorage.getItem("sai-voice-speed-v2") || window.localStorage.setItem("sai-voice-speed-v2", "10");
  "true" === window.localStorage.getItem('"sai-hidden"') && document.body.classList.add("sai-hidden");

  function v() {
    const e = () => /^\/c\/(.*)$/.test(window.location.pathname);
    let n = new m(!1, e()),
      i = !1;
    const s = e => {
        n.keyDownHandler(e)
      },
      a = e => {
        n.keyUpHandler(e)
      },
      o = e => {
        "Enter" === e.code && n.onSubmit()
      },
      r = () => {
        n.onSubmit()
      };
    document.addEventListener("keydown", s),
      document.addEventListener("keyup", a);
    const l = "form button.absolute.p-1.rounded-md.text-gray-500";

    function c(c) {
      const d = document.getElementById("sai-root"),
        p = document.querySelector("textarea");
      n?.adjustCompactIconPos(),
        n?.repeatHandler?.injectRepeatButtons(),
        i && p && (t.info("Re-init app"),
          d && d.remove(),
          n = new m(!0, e()),
          i = !1,
          document.addEventListener("keydown", s),
          document.addEventListener("keyup", a),
          document.querySelector("textarea")?.addEventListener("keyup", o),
          document.querySelector(l)?.addEventListener("click", r)),
        d && p || (t.warn("App removed"),
          d && d.remove(),
          n.readAloud.reset(),
          document.removeEventListener("keydown", s),
          document.removeEventListener("keyup", a),
          document.querySelector("textarea")?.removeEventListener("keyup", o),
          document.querySelector(l)?.removeEventListener("click", r),
          i = !0),
        d && n.readAloud.observerCallback(c)
    }
    document.querySelector("textarea")?.addEventListener("keyup", o),
      document.querySelector(l)?.addEventListener("click", r);
    new MutationObserver(c).observe(document.body, {
        childList: !0,
        subtree: !0,
        characterData: !0
      }),
      setInterval((() => {
        c([])
      }), 3500)
  }
  "true" === window.localStorage.getItem("sai-compact-ui") && document.body.classList.add("sai-compact"),
    // chrome.runtime.onMessage.addListener(((e,n,t)=>("sai-on-chatgpt-message" === e.key && t({
    //     value: "yes-we-are-here"
    // }),
    // !0)));
    document.querySelector("textarea") ? v() : setTimeout((() => {
      v()
    }), 2e3);
  //  window.top.document.getElementById("on-app")?.classList.remove("invisible");

})();