// ==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);
};
})();