ChatGPT撑开页面宽度

将页面宽度展开

目前为 2023-08-17 提交的版本。查看 最新版本

// ==UserScript==
// @name         ChatGPT撑开页面宽度
// @namespace    https://greasyfork.org/
// @version      1.1.7
// @description  将页面宽度展开
// @author       Await
// @match        https://chat.openai.com/
// @match        https://chat.openai.com/c/*
// @match        https://chat.openai.com/?*
// @license      MIT
// ==/UserScript==

(function () {
  "use strict";
  const newMaxWidth = "90rem";
  const targetClassName = ".xl\\:max-w-3xl";
  const targetClass = "flex flex-col text-sm dark:bg-gray-800";
  const desiredMinWidth = 1280;
  const styleId = "await-max-width";
  const btnId = "await-btn";
  // const btnSvgId = "await-svg";
  const btnShowTipId = "await-show-tip";
  const textClass = "await-text";
  const cacheKey = "await-cache";
  const cacheOpenStateKey = "await-cache-open-state";
  const cacheCloseStateKey = "await-cache-close-state";
  const attributeKey = "await-attribute";
  const bodyClass = "antialiased";
  const maxCount = 10;
  const gptTextareaId = "prompt-textarea";

  function setCache(key, value) {
    localStorage.setItem(key, value);
  }
  function getCache(key) {
    return localStorage.getItem(key);
  }

  function getByClass(className) {
    const func = function (name) {
      return document.getElementsByClassName(name);
    };
    return gets(func, className);
  }

  function getById(id) {
    const func = function (id) {
      return document.getElementById(id);
    };
    return gets(func, id);
  }

  function gets(fun, name, count = 0) {
    const btn = fun(name);
    if (!btn) {
      if (count > maxCount) {
        return null; //防止死循环
      }
      setTimeout(function () {
        return getById(id, count);
      }, 1000);
    }
    return btn;
  }

  function styleCreate() {
    const style = document.createElement("style");
    style.innerHTML = `
      .${styleId} {
        max-width: ${newMaxWidth} !important;
      }
      .${btnId}{
        right:2.8rem;
        background-color:transparent !important;
      }
      .${textClass}{

      }
      `;
    document.head.appendChild(style);
  }

  function mutationOB() {
    // 创建MutationObserver实例
    const observer = new MutationObserver((mutationsList, observer) => {
      // 在这里处理每个mutation
      for (const mutation of mutationsList) {
        if (
          mutation.type === "attributes" &&
          mutation.attributeName === "style"
        ) {
          const newStyle = targetElement.getAttribute("style");

          // 判断样式是否为空
          if (!newStyle) {
            // 在样式变为空时执行a方法
            a();
          } else {
            // 在样式被添加值时执行一些操作
            // 比如执行其他方法b
            b();
          }
        }
      }
    });
    // 配置观察器选项
    const config = { attributes: true, attributeFilter: ["style"] };
    // 开始观察目标元素
    observer.observe(targetElement, config);
  }

  function bodyClassFunc() {
    const body = getByClass(bodyClass)[0];
    if (!body) {
      return;
    }
    const observer = new MutationObserver((mutationsList, observer) => {
      for (const mutation of mutationsList) {
        if (
          mutation.type === "attributes" &&
          mutation.attributeName === "style"
        ) {
          const newStyle = body.getAttribute("style");
          if (!newStyle) {
            btnClickAdd(true);
            btnClick(true);
          }
        }
      }
    });
    const config = { attributes: true, attributeFilter: ["style"] };
    observer.observe(body, config);
  }

  function btnClickAdd(tt = false) {
    const promptTextarea = getById(gptTextareaId);
    if (!promptTextarea) {
      if (!tt) {
        return;
      } else {
        setTimeout(function () {
          btnClickAdd(tt);
        }, 1000);
        return;
      }
    }
    promptTextarea.insertAdjacentHTML(
      "afterend",
      `<button id="${btnId}" class="absolute p-1 rounded-md md:bottom-3 md:right-3 dark:hover:bg-gray-900 dark:disabled:hover:bg-transparent right-2 disabled:text-gray-400 enabled:bg-brand-purple text-white bottom-1.5 transition-colors disabled:opacity-40 ${btnId}"></button> `
    );
    if (!promptTextarea.hasAttribute(attributeKey)) {
      const cache2 = getCache(cacheKey);
      if (cache2 === cacheOpenStateKey) {
        openSvg();
      } else {
        closeSvg();
      }
      run();
      promptTextarea.setAttribute(attributeKey, true);
    } else {
      if (tt) {
        setTimeout(function () {
          btnClickAdd(tt);
        }, 1000);
      }
    }
  }

  function removeStyle(el) {
    el.style.transition = "max-width 1s";
    setTimeout(function () {
      el.style.transition = "";
    }, 1000);
    el.classList.remove(styleId);
  }

  function editStyle(el) {
    el.style.transition = "max-width 1s";
    setTimeout(function () {
      el.style.transition = "";
    }, 1000);
    el.classList.add(styleId);
  }

  function openSvg() {
    const btn = getById(btnId);
    //替换btn的svg
    btn.innerHTML = "";
    btn.insertAdjacentHTML(
      "afterbegin",
      `<a id="${btnId}-open" class="flex gap-3 transition-colors duration-200 text-white cursor-pointer text-sm rounded-md border border-white/20 hover:bg-gray-500/10 flex-shrink-0 items-center justify-center"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line></svg><span style="${textClass}">还原</span></a>`
      // `<a class="flex gap-3 transition-colors duration-200 text-white cursor-pointer text-sm rounded-md border border-white/20 hover:bg-gray-500/10 flex-shrink-0 items-center justify-center"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line></svg><span style="position: absolute; border: 0px; width: 1px; height: 1px; padding: 0px; margin: -1px; overflow: hidden; clip: rect(0px, 0px, 0px, 0px); white-space: nowrap; overflow-wrap: normal;">撑开</span></a>`
    );
    const create = getById(`${btnId}-open`);
    setCache(cacheKey, cacheOpenStateKey);
    if (!create.hasAttribute(attributeKey)) {
      create.addEventListener("click", function () {
        closeSvg();
        run();
      });
      create.setAttribute(attributeKey, true);
    }
  }
  function closeSvg() {
    const btn = getById(btnId);
    btn.innerHTML = "";
    btn.insertAdjacentHTML(
      "afterbegin",
      `<a id="${btnId}-open" class="flex gap-3 transition-colors duration-200 text-white cursor-pointer text-sm rounded-md border border-white/20 hover:bg-gray-500/10 flex-shrink-0 items-center justify-center"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="9" y1="3" x2="9" y2="21"></line></svg><span style="${textClass}">撑开</span></a>`
    );
    setCache(cacheKey, cacheCloseStateKey);
    const create = getById(`${btnId}-open`);
    if (!create.hasAttribute(attributeKey)) {
      create.addEventListener("click", function () {
        openSvg();
        run();
      });
      create.setAttribute(attributeKey, true);
    }
  }
  function checkCache() {
    const cache = getCache(cacheKey);
    if (!cache) {
      setCache(cacheKey, true);
    }
  }

  function setStyle(cache, el) {
    if (cache === cacheOpenStateKey) {
      editStyle(el);
    } else {
      removeStyle(el);
    }
  }

  function getNav(count = 0) {
    const nav = document.querySelector("nav");
    if (!nav) {
      if (count > maxCount) {
        return null;
      }
      setTimeout(function () {
        return getNav(count);
      }, 1000);
    }
    return nav;
  }

  function showTip() {
    const toggleButton = getNav();
    if (!toggleButton) {
      return;
    }
    toggleButton.insertAdjacentHTML(
      "beforeend",
      `<div id="${btnShowTipId}" style="position:fixed;top:3rem;left:15rem;z-index:9999;background-color:rgba(0,0,0,0.5);color:#fff;padding:10px;border-radius:5px;">如果页面宽度未展开,请重新点击此树结构导航栏<br>或者直接<span style="color:red">点击我</span><br>提示内容十秒后自动消失</div>`
    );
    const btn = getById(btnShowTipId);
    if (!btn.hasAttribute(attributeKey)) {
      btn.addEventListener("click", function () {
        runAll();
      });
      setTimeout(function () {
        btn.remove();
      }, 10000);
      btn.setAttribute(attributeKey, true);
    }
  }

  function btnClick(tt = false) {
    const toggleButton = getNav();
    if (!toggleButton) {
      if (!tt) return;
      else {
        setTimeout(function () {
          btnClick(tt);
        }, 1000);
        return;
      }
    }
    if (!toggleButton.hasAttribute(attributeKey)) {
      toggleButton.addEventListener("click", function () {
        setTimeout(function () {
          runAll();
          btnClick();
        }, 1000);
      });
      toggleButton.setAttribute(attributeKey, true);
    } else {
      if (tt) {
        setTimeout(function () {
          btnClick(tt);
        }, 1000);
      }
    }
  }

  function checkForm() {
    var elementForm = document.querySelectorAll("form");
    if (!elementForm || elementForm.length === 0) {
      setTimeout(function () {
        checkObserver();
      }, 1000);
      return;
    }
    const cache = getCache(cacheKey);
    elementForm.forEach(function (element) {
      if (element.className.indexOf("xl:max-w-3xl") > -1) {
        setStyle(cache, element);
      }
    });
  }

  function checkObserver() {
    var parentElement = document.getElementsByClassName(targetClass)[0];
    if (!parentElement) {
      setTimeout(function () {
        checkObserver();
      }, 1000);
      return;
    }
    const cache = getCache(cacheKey);
    parentElement.querySelectorAll(targetClassName).forEach(function (flexDiv) {
      setStyle(cache, flexDiv);
    });
    var observer = new MutationObserver(function (mutations) {
      mutations.forEach(function (mutation) {
        if (!document.contains(parentElement)) {
          observer.disconnect();
          console.log("parentElement 被移除");
          return;
        }
        mutation.addedNodes.forEach(function (addedNode) {
          if (addedNode instanceof Document || addedNode instanceof Element) {
            var flexDivList = addedNode.querySelectorAll(targetClassName);
            flexDivList.forEach(function (flexDiv) {
              setStyle(cache, flexDiv);
            });
          }
        });
      });
    });
    var config = { childList: true, subtree: true };
    observer.observe(document.body, config);
    // observer.observe(parentElement, config);
  }

  function runAll() {
    btnClickAdd();
  }
  function run() {
    checkForm();
    checkObserver();
  }
  window.addEventListener("resize", runAll);
  window.onload = function () {
    if (window.innerWidth < desiredMinWidth) {
      return;
    }
    checkCache();
    bodyClassFunc();
    styleCreate();
    showTip();
    btnClick();
    setTimeout(function () {
      runAll();
    }, 2000);
  };
})();