// ==UserScript==
// @name 网页定制(ChatGPT版)
// @namespace http://tampermonkey.net/
// @version v1.3.1
// @author yiqiuzheng
// @description ChatGPT助手,支持搜索增强、选中文本拓展、总结文章以及定制网页!网页版地址:https://chatgpt-echo.zeabur.app/ ,可关注微信公众号秋博士,获取ChatGPT访问密码
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAH1UExURUxpcXeqn3WqnHWonHSpnHWonHWpnG22knWpnHWpnHSmm3apm3SpnHWonHWpnHSonHWpnHWpm3apnXWpnHWpm3WpnP///8fc19fm43mrn67Nxf7///r8+6HFvNPk4JS8ssXb1XirnsDY0sPa1Pj7+qbHv5i/tXeqnvz9/X6uoo65roq2q+Tu7P3+/qrKwqDEu9bm4vP39qfIwPv9/NXl4ezz8Xqsn+nx73msn5/Dusnd2N7q59zp5pC6r4CwpKLFvIOxpszf2oSypsTa1fn7+/P49t/r6JrAt8LZ1L/X0d3q53aqnczf287h3Ie0qc7g3Pr8/LTQybDOxpvBuObv7c/h3PX5+Ory8ODr6OPt65G7sLnTzYWzp/n7+oi1qv7+/tTk4J7Cucve2Z3Cub7X0H+vo8LZ053CuKnJwff6+tnn4/3+/fD29XytoYWzqJe+tJa+tHapnHeqnaHEu8vf2oGxpazLw3utoMre2ZW9s7XRyu/19H2uou/186XHv6jJwNDi3sjd2OLt6u308ufw7tfm4rjTzK3MxOjw7tvp5dHi3sjd15m/tvL39q/Nxvb5+OPu64y3rIOyptnn5LbSy+Ds6eHs6tbl4cHZ0/v8/H6vo4GwpZ7Dus/h3fb6+ZK7sfT49/f6+aLFvavLw6zLxM3g28bc1pQLf2QAAAAVdFJOUwAtv5bz1PQH/dUuj5WQ/CyYwJHykqKEGP8AAAAJcEhZcwAAAHYAAAB2AU57JggAAAIcSURBVDjLhdNle9swEABgFdK0Kw7uHMfp6iTeAksaThpoUmZuV1x5zMxbx8wM7Xj7nZNjx/L2rNl9kXR6H51snwmhsWFTWQn8FSWGygKihLGmFP4ZpUXG7P5GWDcKZVEDeaKC1mfnHxUvoSV19YQOVFWTLdpiUfJ2POx/jOEzAy4tWU7KctPG95FpOjT0IA2PT80aSHEOpKQ5mSUxIA7bD2OzI5vdTNTt1QXBDvAxMT/7qkE+h8PdyoYC+DX0YgYyX4W+FwBunqYOhpp0YAl/1eN22Or5DPD8Jd6sBTiOZgYa8SfUysAMH+wWW/AK3ndbUWRADKUVMGIex1YrRGcs3uvYxcCzKVCAJTb66FZsFGDXTgHPMjD2WgWcFeCkHd/uoOshj0MD16QoLOI2+Q406ifpPXh4gisaOIXD4JiZXUoqwARx/Ab80zB7TJMzmK17nr4BK2eCOnocJGMMBBH9tO6FqYhveUJSwZsxBrpRDDltl6G3G7/8+K6AtLOZARu65hYwcLfL8s4l30EGCTzGwH6MA3Tew9u0Tp1HBmYOT+u+xZ62nl4AB91uGRQ+ZWAZ53HQqgMwgn3n6BC90+bl0nLJB51qH+QaphUD3EWuHVNuuhiQwlrPaS3n6zhEW+2G3I3TkSE3A5XalG860o/j/sSkcGAf62tS8MdvFfe3Oyf2tugyhBRB3qC/XuF/ADFWVOUHhFSXG4rXA78BYbiLJDUXqsMAAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII=
// @match http*://*/*
// @run-at document-end
// @require https://cdn.staticfile.org/vue/2.7.0/vue.min.js
// @require https://unpkg.com/element-ui/lib/index.js
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/showdown.min.js
// @grant GM_log
// @grant GM_notification
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_setClipboard
// @grant GM_xmlhttpRequest
// @grant GM_addElement
// @grant GM_openInTab
// @grant GM_getResourceText
// @grant GM_registerMenuCommand
// @grant GM_info
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const $ = (selector) => {
return document.querySelector(selector);
};
const genLink = (url) => {
const linkEl = document.createElement('link');
linkEl.rel = 'stylesheet';
linkEl.href = url;
$('head').insertBefore(linkEl, $('head').firstChild);
}
genLink('https://cdnjs.cloudflare.com/ajax/libs/element-ui/2.15.13/theme-chalk/index.min.css')
genLink('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.1.0/github-markdown.css">')
// 聊天按钮样式
GM_addStyle(`
#chatBtn.continue-chat {
height: 32px;
border-radius: 16px;
font-weight: 400;
}
#chatBtn.primary-outline-button.btn {
background: #ffffff;
border: 1px solid rgba(56,114,224,.48);
border-radius: 6px;
align-items: center;
height: 36px;
display: flex;
justify-content: center;
font-weight: 700;
font-size: 14px;
color: #3872e0;
line-height: 24px;
}
#chatBtn.primary-outline-button.btn:hover {
background: #eff3ff;
}
.monica-btn {
box-sizing: border-box;
padding: 0 15px;
cursor: pointer;
user-select: none;
-webkit-user-drag: none;
white-space: nowrap;
}
.continue-chat .chatgpt-text {
margin-left: 8px;
}
`);
GM_addStyle(`
.btn-area .btn-box {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: 13px;
box-sizing: border-box;
border: 1px solid #611fec;
box-shadow: 0 2px #611fec;
border-radius: 6px;
background: rgba(255,255,255,.45);
transition: background-color .2s ease,top .1s ease,box-shadow .1s ease;
color: #212b36;
cursor: pointer;
user-select: none;
-webkit-user-drag: none;
position: relative;
top: -1px;
}
.btn-area .btn-box .title {
flex: 1;
height: 100%;
line-height: 24px;
font-weight: 700;
box-sizing: border-box;
padding: 0 4px 0 8px;
min-width: 0;
max-width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
border-radius: 4px 0 0 4px;
transition: all .2s ease-in-out;
}
.btn-box .title:hover {
background: rgba(235,202,254,.24);
text-decoration: none!important;
}
`);
// 公用样式类
GM_addStyle(`
.flex {
display: flex;
}
.flex-1 {
flex: 1;
}
.flex-0.2 {
flex: 0.2
}
.flex-8 {
flex: 8
}
.items-center {
align-items: center;
}
.justify-center {
justify-content: center;
}
.chatgpt-mb-1 {
margin-bottom: 10px;
}
.cursor-pointer {
cursor: pointer;
}
`);
let vm = null;
let dialogVm = null;
// 本地调试ChatGPT的iframe
//const domain = 'http://localhost:3000/#/';
// 线上ChatGPT的iframe地址
const domain = 'https://chatgpt-echo.zeabur.app/';
const isDomain = (platform) => {
return window.location.host.includes(platform);
};
// 设置油猴API,方便油猴脚本助手直接运行代码
const setGMAPI = () => {
unsafeWindow.GM_log = GM_log;
unsafeWindow.GM_notification = GM_notification;
unsafeWindow.unsafeWindow = unsafeWindow;
unsafeWindow.GM_addStyle = GM_addStyle;
unsafeWindow.GM_setValue = GM_setValue;
unsafeWindow.GM_getValue = GM_getValue;
unsafeWindow.GM_setClipboard = GM_setClipboard;
unsafeWindow.GM_xmlhttpRequest = GM_xmlhttpRequest;
// unsafeWindow.GM_addElement = GM_addElement;
unsafeWindow.GM_openInTab = GM_openInTab;
unsafeWindow.GM_getResourceText = GM_getResourceText;
unsafeWindow.GM_registerMenuCommand = GM_registerMenuCommand;
unsafeWindow.GM_info = GM_info;
};
setGMAPI();
const getSearchContent = () => {
if (isDomain('baidu')) {
return $('#kw').value;
} else if (isDomain('google')) {
return $('#APjFqb').innerHTML;
} else {
return '';
}
};
// 默认选项
var defaultOptions = {
apiKey: '',
model: 'microsoft/DialoGPT-large',
customization: [],
};
// 获取配置选项,并使用默认选项填充未定义的选项
var getConfig = function () {
var config = JSON.parse(localStorage.getItem('pageCustomizationConfig'));
if (config) {
config = Object.assign({}, defaultOptions, config);
} else {
config = defaultOptions;
}
return config;
};
// 保存新的配置选项
var saveConfig = function (config) {
localStorage.setItem('pageCustomizationConfig', JSON.stringify(config));
};
// 初始化配置
var initConfig = function () {
var config = getConfig();
var apiKeyInput = document.querySelector('#api-key');
if (apiKeyInput) {
// “API密钥”输入框
apiKeyInput.value = config.apiKey;
apiKeyInput.addEventListener('change', function () {
config.apiKey = apiKeyInput.value;
saveConfig(config);
});
}
var modelSelect = document.querySelector('#model-select');
if (modelSelect) {
// “交互模型”下拉框
modelSelect.value = config.model;
modelSelect.addEventListener('change', function () {
config.model = modelSelect.value;
saveConfig(config);
});
}
var customizationInput = document.querySelector(
'#customization-user-input'
);
if (customizationInput) {
// “对网页的定制要求”输入框
customizationInput.value = config.customization.join('\n');
customizationInput.addEventListener('change', function () {
config.customization = customizationInput.value
.split('\n')
.map(function (item) {
return item.trim();
});
saveConfig(config);
});
}
var saveButton = document.querySelector('#save-button');
if (saveButton) {
// “保存”按钮
saveButton.addEventListener('click', function () {
const userInput = document.querySelector('#customization-user-input');
const chatHistory = document.querySelector(
'#customization-chat-history'
);
var message = userInput.value.trim();
if (message === '') {
alert('请先输入定制要求!');
return;
}
// 清空用户输入框
userInput.value = '';
// 在历史记录区域中显示用户发送的消息
addUserMessage(message);
var config = getConfig();
getCode(config)
.then(function (code) {
const res = addCustomization(code);
addAiMessage(code);
})
.catch(function (error) {
console.log(error);
});
// 提示保存成功
var successMessage = document.createElement('div');
successMessage.innerHTML = '保存成功';
successMessage.style.position = 'fixed';
successMessage.style.top = '50%';
successMessage.style.left = '50%';
successMessage.style.transform = 'translate(-50%, -50%)';
successMessage.style.backgroundColor = '#fff';
successMessage.style.border = '2px solid #ddd';
successMessage.style.padding = '10px';
document.body.appendChild(successMessage);
// 3 秒后移除提示信息
setTimeout(function () {
document.body.removeChild(successMessage);
}, 3000);
});
}
};
// 获取chatgpt生成的代码
var getCode = function (config) {
// 调用chatgpt接口,获取生成的代码
var url = '';
var headers = {
'Content-Type': 'application/json',
};
var promise = fetch(url, {
method: 'POST',
headers: headers,
body: JSON.stringify({
messages: Array.isArray(config.customization.join(''))
? config.customization.join('')
: [
{
role: 'user',
content: config.customization.join('')
? config.customization.join('') +
'生成对应js代码,直接返回代码即可'
: config.customization.join(''),
},
],
}),
})
.then(function (response) {
if (!response.ok) {
throw new Error('chatgpt接口调用出错');
}
return response.text();
})
.then(function (data) {
var code = data;
// 替换特殊字符,以便在脚本里使用
const regex = /(?<=\`).*?(?=\`)/g;
const matches = code.match(regex);
return matches ? matches[0] : code;
});
return promise;
};
// 创建用户界面
var createUI = function () {
// 创建悬浮按钮
var button = document.createElement('div');
button.id = 'customization-button';
button.style.position = 'fixed';
button.style.bottom = '20px';
button.style.right = '20px';
button.style.width = '50px';
button.style.height = '50px';
button.style.borderRadius = '50%';
button.style.backgroundColor = '#409EFF';
button.style.color = '#fff';
button.style.fontSize = '24px';
button.style.textAlign = 'center';
button.style.lineHeight = '50px';
button.style.cursor = 'pointer';
button.textContent = '⚙';
// 创建侧边栏抽屉
var drawer = document.createElement('div');
drawer.id = 'customization-drawer';
drawer.style.position = 'fixed';
drawer.style.top = '0';
drawer.style.right = '-500px';
drawer.style.width = '500px';
drawer.style.height = '100%';
drawer.style.backgroundColor = '#f0f0f0';
drawer.style.transition = 'right 0.3s ease';
drawer.style.overflowY = 'auto';
drawer.style.zIndex = '2000';
// 创建关闭按钮
var closeButton = document.createElement('div');
closeButton.id = 'customization-close-button';
closeButton.style.position = 'absolute';
closeButton.style.top = '10px';
closeButton.style.right = '10px';
closeButton.style.width = '20px';
closeButton.style.height = '20px';
closeButton.style.borderRadius = '50%';
closeButton.style.backgroundColor = '#aaa';
closeButton.style.color = '#fff';
closeButton.style.fontSize = '12px';
closeButton.style.textAlign = 'center';
closeButton.style.lineHeight = '20px';
closeButton.style.cursor = 'pointer';
closeButton.style.zIndex = '10000';
closeButton.textContent = '×';
closeButton.addEventListener('click', function () {
drawer.style.right = '-500px';
});
// 创建聊天室
var chatRoom = document.createElement('div');
chatRoom.id = 'customization-chat-room';
chatRoom.style.height = '100%';
chatRoom.innerHTML = `
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="ChatGPT" name="chatgpt">
<div id="chatgpt-box" class="tab-item">
<iframe id="chatgpt-iframe" src="${domain}"></iframe>
</div>
</el-tab-pane>
<el-tab-pane label="更新日志" name="log">
<div class="tab-item">
<el-result subTitle="关注微信公众号:秋博士,获取访问密码">
<template slot="icon">
<div class="flex">
<el-image src="https://i.postimg.cc/dtWjSGDN/me.jpg"></el-image>
</div>
</template>
<template slot="extra">
<el-button @click="window.open('https://greasyfork.org/zh-CN/scripts/467425-%E7%BD%91%E9%A1%B5%E5%AE%9A%E5%88%B6-chatgpt%E7%89%88/feedback', '_blank')" type="primary" size="medium">觉得好用?请给个五星好评⭐️~</el-button>
</template>
</el-result>
<el-collapse v-model="activeCollapse">
<el-collapse-item :key="version.id" v-for="(version, index) in logs" :title="version.title" :name="version.id">
<div :key="detail" v-for="detail in version.content">{{detail}}</div>
</el-collapse-item>
</el-collapse>
</div>
</el-tab-pane>
<el-tab-pane name="plugins">
<el-badge label="插件管理" slot="label" :is-dot="isGetNew" class="tab-item-badge">
插件管理
</el-badge>
<div v-if="localCode.length > 0" class="tab-item plugins-box">
<el-alert
class="chatgpt-mb-1"
title="勾选即下次进入网页自动生效,单击按钮即单次执行生效"
type="success"
show-icon>
</el-alert>
<el-collapse>
<el-collapse-item :key="item.key" v-for="item in localCode">
<template slot="title">
<el-checkbox @change="(isChecked) => handleCheckboxChange(isChecked, item)" v-model="item.isChecked">
<el-button size="mini" @click.stop="evalCode(item.content)">{{item.name}}</el-button>
</el-checkbox>
</template>
<el-input
class="chatgpt-mb-1"
type="textarea"
autosize
v-model="item.content"
></el-input>
<div class="flex justify-center chatgpt-mb-1">
<el-button class="flex-1" size="mini" @click.stop="updateStorageItem(item)">更新</el-button>
<el-button class="flex-1" size="mini" @click.stop="deleteStorageItem(item.key)">删除</el-button>
</div>
</el-collapse-item>
</el-collapse>
</div>
<div v-else class="tab-item plugins-box">
暂无插件,请在聊天中生成油猴脚本点击运行代码即可生成。
</div>
</el-tab-pane>
<el-tab-pane label="跳转网页版" name="web">
<div class="tab-item">
跳转网页版
</div>
</el-tab-pane>
</el-tabs>
`;
// 将聊天室添加到侧边栏抽屉中
drawer.appendChild(closeButton);
drawer.appendChild(chatRoom);
// 将悬浮按钮和侧边栏抽屉添加到页面上
document.body.appendChild(button);
document.body.appendChild(drawer);
// 点击悬浮按钮显示侧边栏抽屉
button.addEventListener('click', function () {
if (drawer.style.right === '-500px') {
drawer.style.right = '0';
} else {
drawer.style.right = '-500px';
}
});
// element
GM_addStyle(`
#customization-chat-room .el-tabs {
height: inherit;
background: #e7f8ff;
}
#customization-chat-room .el-tabs__active-bar {
width: 58px!important;
}
#customization-chat-room .el-tabs__content {
height: inherit;
box-sizing: border-box;
overflow: auto;
}
#customization-chat-room .el-tab-pane {
height: inherit;
box-sizing: border-box;
width: 100%;
}
#customization-chat-room .el-tabs__header {
margin: 0 0 10px;
padding: 0 10px;
}
#chatgpt-iframe {
width: 100%;
height: inherit;
}
#customization-chat-room .el-collapse-item__header {
padding: 0 10px;
}
#customization-chat-room .el-collapse-item__wrap {
padding: 0 10px;
}
#customization-chat-room .tab-item-badge .el-badge__content.is-fixed {
top: 5px;
}
`);
// 美化页面
GM_addStyle(`
#customization-chat-room .tab-item {
padding: 10px;
height: 100%;
}
#customization-chat-room .match-item {
width: 100%;
}
#customization-chat-room {
border-left: 1px solid #e5e8eb;
}
iframe {
border: medium none;
}
#customization-button:hover {
background-color: #66b1ff;
}
#customization-close-button:hover {
background-color: #ff7875;
}
#customization-drawer::-webkit-scrollbar {
width: 8px;
height: 8px;
}
#customization-drawer::-webkit-scrollbar-thumb {
background: #aaa;
border-radius: 6px;
}
#customization-drawer::-webkit-scrollbar-track {
background: #f0f0f0;
border-radius: 6px;
}
#customization-user-input:focus {
outline: none;
border-color: #409eff;
}
#save-button:hover {
background-color: #66b1ff;
}
#customization-chat-history {
display: flex;
flex-direction: column;
}
.customization-message-wrapper {
margin: 10px;
display: flex;
justify-content: flex-end;
}
.customization-message-wrapper:hover {
background-color: #eee;
cursor: pointer;
}
.customization-message-bubble {
max-width: 80%;
padding: 10px;
border-radius: 10px;
margin-top: 5px;
margin-bottom: 5px;
font-size: 16px;
}
.customization-user-message {
align-self: flex-end;
}
.customization-ai-message {
align-self: flex-start;
}
.customization-user-message, .customization-ai-message {
background-color: #409eff;
color: white;
padding: 8px;
border-radius: 10px;
}
.customization-save-icon-wrapper {
position: absolute;
bottom: -10px;
}
`);
};
const evalCode = (content) => {
const myFunc = new Function(content);
myFunc();
};
const showToast = (tips) => {
// 提示保存成功
var successMessage = document.createElement('div');
successMessage.innerHTML = tips;
successMessage.style.position = 'fixed';
successMessage.style.top = '50%';
successMessage.style.left = '50%';
successMessage.style.transform = 'translate(-50%, -50%)';
successMessage.style.backgroundColor = '#fff';
successMessage.style.border = '1px solid #ddd';
successMessage.style.borderRadius = '10px';
successMessage.style.padding = '10px';
successMessage.style.zIndex = '10000';
document.body.appendChild(successMessage);
// 3 秒后移除提示信息
setTimeout(function () {
document.body.removeChild(successMessage);
}, 3000);
};
// 在历史记录区域中显示用户发送的消息
var addUserMessage = function (message) {
const chatHistory = document.querySelector('#customization-chat-history');
var messageWrapper = document.createElement('div');
messageWrapper.className = 'customization-message-wrapper';
messageWrapper.id = 'customization-message-wrapper';
var messageBubble = document.createElement('div');
messageBubble.className =
'customization-message-bubble customization-user-message';
messageBubble.textContent = message;
messageWrapper.appendChild(messageBubble);
chatHistory.appendChild(messageWrapper);
};
// 在历史记录区域中显示 AI 回复的消息
var addAiMessage = function (message) {
const chatHistory = document.querySelector('#customization-chat-history');
var messageWrapper = document.createElement('div');
messageWrapper.className = 'customization-message-wrapper-left';
var messageBubble = document.createElement('div');
messageBubble.className =
'customization-message-bubble customization-ai-message';
messageBubble.textContent = message;
messageWrapper.appendChild(messageBubble);
chatHistory.appendChild(messageWrapper);
};
// 添加定制功能
var addCustomization = function (code) {
try {
eval(code);
} catch (err) {
console.log(err);
}
};
function copyToClipboard(text) {
try {
navigator.clipboard.writeText(text);
showToast('复制成功');
} catch (error) {
const textArea = document.createElement('textarea');
textArea.value = text;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
showToast('复制成功');
} catch (error) {
showToast('复制失败,请手动复制');
}
document.body.removeChild(textArea);
}
}
const handleTextAutoHeight = () => {
var textarea = document.querySelector('textarea');
textarea.addEventListener('input', (e) => {
textarea.style.height = '100px';
textarea.style.height = e.target.scrollHeight + 'px';
});
};
const creatChatBtn = (parentNode) => {
const btnBox = document.createElement('div');
btnBox.id = 'chatBtn';
btnBox.className = 'monica-btn btn continue-chat primary-outline-button';
btnBox.innerHTML = `
<svg aria-hidden="true" focusable="false" role="img" class="octicon octicon-comment" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"><path d="M1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 13.25 12H9.06l-2.573 2.573A1.458 1.458 0 0 1 4 13.543V12H2.75A1.75 1.75 0 0 1 1 10.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h4.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
<span class="text">在聊天中继续</span>
`;
parentNode && parentNode.appendChild(btnBox);
$('#chatBtn').addEventListener('click', () => {
$('#customization-drawer').style.right = 0;
});
};
const creatBtn = ({ parentNode, btnText, clickFn }) => {
const btnBox = document.createElement('div');
btnBox.className = 'btn-area';
btnBox.innerHTML = `
<div class="btn-box">
<div class="title">${btnText}</div>
</div>
`;
parentNode && parentNode.appendChild(btnBox);
btnBox.addEventListener('click', () => {
clickFn && clickFn();
});
};
// 谷歌搜索ChatGPT应答
const generateSearchEnhance = () => {
const messageWrapper = document.createElement('div');
messageWrapper.className = 'chatgpt-search-enhance';
messageWrapper.id = 'chatgpt-search-enhance';
messageWrapper.innerHTML = `
<div class="header-7QHGYk">
<div class="lt-znd2I9">
<a class="title" href="${domain}" target="_blank" rel="noreferrer">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="30" height="30" fill="none" class="user-avatar"><defs><path id="bot_svg__a" d="M0 0h30v30H0z"></path><path id="bot_svg__c" d="M0 0h20.455v20.455H0z"></path></defs><g><rect fill="#E7F8FF" width="30" height="30" rx="10"></rect><mask id="bot_svg__b" fill="#fff"><use xlink:href="#bot_svg__a"></use></mask><g mask="url(#bot_svg__b)"><g transform="translate(4.773 4.773)"><mask id="bot_svg__d" fill="#fff"><use xlink:href="#bot_svg__c"></use></mask><g mask="url(#bot_svg__d)"><path fill-rule="evenodd" d="M19.11 8.37c.17-.52.26-1.06.26-1.61 0-.9-.24-1.79-.71-2.57a5.24 5.24 0 0 0-4.53-2.59c-.37 0-.73.04-1.09.11A5.201 5.201 0 0 0 9.17 0h-.04C6.86 0 4.86 1.44 4.16 3.57A5.11 5.11 0 0 0 .71 6.04C.24 6.83 0 7.72 0 8.63c0 1.27.48 2.51 1.35 3.45-.18.52-.27 1.07-.27 1.61 0 .91.25 1.8.71 2.58 1.13 1.94 3.41 2.94 5.63 2.47a5.18 5.18 0 0 0 3.86 1.71h.05c2.26 0 4.27-1.44 4.97-3.57a5.132 5.132 0 0 0 3.45-2.47c.46-.78.7-1.67.7-2.58 0-1.28-.48-2.51-1.34-3.46ZM8.947 18.158c-.04.03-.08.05-.12.07.7.58 1.57.89 2.48.89h.01c2.14 0 3.88-1.72 3.88-3.83v-4.76c0-.02-.02-.04-.04-.05l-1.74-.99v5.75c0 .23-.13.45-.34.57l-4.13 2.35Zm-.67-1.153 4.17-2.38c.02-.01.03-.03.03-.05v-1.99l-5.04 2.87c-.21.12-.47.12-.68 0l-4.13-2.35c-.04-.02-.09-.06-.12-.07-.04.21-.06.43-.06.65 0 .67.18 1.33.52 1.92v-.01c.7 1.19 1.98 1.92 3.37 1.92.68 0 1.35-.18 1.94-.51ZM3.903 5.168v-.14c-.85.31-1.57.9-2.02 1.68a3.78 3.78 0 0 0-.52 1.91c0 1.37.74 2.64 1.94 3.33l4.17 2.37c.02.01.04.01.06 0l1.75-1-5.04-2.87a.64.64 0 0 1-.34-.57v-4.71Zm13.253 3.337-4.18-2.38c-.02 0-.04 0-.06.01l-1.74.99 5.04 2.87c.21.12.34.34.34.58v4.85c1.52-.56 2.54-1.99 2.54-3.6 0-1.37-.74-2.63-1.94-3.32ZM8.014 5.83c-.02.01-.03.03-.03.05v1.99L13.024 5a.692.692 0 0 1 .68 0l4.13 2.35c.04.02.08.05.12.07.03-.21.05-.43.05-.65 0-2.11-1.74-3.83-3.88-3.83-.68 0-1.35.18-1.94.51l-4.17 2.38Zm1.133-4.492c-2.15 0-3.89 1.72-3.89 3.83v4.76c0 .02.02.03.03.04l1.75 1v-5.75c0-.23.13-.45.34-.57l4.13-2.35c.04-.03.09-.06.12-.07-.7-.58-1.58-.89-2.48-.89ZM7.983 11.51l2.24 1.27 2.25-1.27V8.95l-2.25-1.28-2.24 1.28v2.56Z" style="fill: rgb(31, 148, 140);"></path></g></g></g></g></svg>
<span>ChatGPT</span>
</a>
</div>
<div id="header-right" class="header-right">
<div id="toolbar" class="toolbar">
<el-tooltip class="item" effect="dark" content="Top Left 提示文字" placement="top-start">
<span title="复制" class="toolbar-item-EhZYV5">
<svg id="copy" width="16" height="16" fill="none" viewBox="0 0 16 16" style="min-width: 16px; min-height: 16px;"><g><path data-follow-stroke="currentColor" d="M4.334 4.145v-1.54c0-.517.42-.937.937-.937h8.125c.518 0 .938.42.938.937v8.125c0 .518-.42.938-.938.938H11.84" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"></path><path data-follow-stroke="currentColor" d="M10.729 4.332H2.604a.937.937 0 0 0-.938.938v8.125c0 .517.42.937.938.937h8.125c.517 0 .937-.42.937-.938V5.27a.937.937 0 0 0-.938-.938Z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"></path></g>
</svg>
</span>
</el-tooltip>
</div>
</div>
</div>
<div id="answer" class="answer">
</div>
<div id="footer" class="footer">
</div>
`;
GM_addStyle(`
.chatgpt-search-enhance {
border-radius: 8px;
border: 1px solid #dadce0;
padding: 0 16px;
overflow: auto;
}
.chatgpt-search-enhance .header-7QHGYk {
height: 56px;
display: flex;
box-sizing: border-box;
}
.header-7QHGYk .lt-znd2I9 .title span {
font-size: 15px;
color: #000;
font-weight: 700;
}
.chatgpt-search-enhance .header-7QHGYk .lt-znd2I9 {
display: inline-flex;
align-items: center;
gap: 6px;
}
.chatgpt-search-enhance .header-7QHGYk .lt-znd2I9 .title {
display: inline-flex;
align-items: center;
cursor: pointer;
gap: 6px;
}
.chatgpt-search-enhance .header-7QHGYk .header-right {
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
gap: 8px;
}
.chatgpt-search-enhance .header-7QHGYk .header-right .btn-area {
height: 24px;
}
.chatgpt-search-enhance .toolbar {
display: none;
align-items: center;
gap: 8px;
color: #595959;
}
.toolbar-item-EhZYV5 svg:hover {
cursor: pointer;
}
.chatgpt-search-enhance .title:hover {
cursor: pointer;
user-select: none;
-webkit-user-drag: none;
text-decoration: none;
color: #3872e0!important;
background-color: transparent;
}
.chatgpt-search-enhance .footer {
box-sizing: border-box;
display: none;
align-items: center;
margin: 16px 0;
}
`);
const searchBox = document.getElementById(
isDomain('google') ? 'rhs' : 'content_right'
);
searchBox?.appendChild(messageWrapper);
creatChatBtn($('#footer'));
creatBtn({
parentNode: $('#header-right'),
btnText: '询问ChatGPT',
clickFn: () => {
postMsg({
type: 'search',
content: getSearchContent(),
});
$('#answer').innerHTML = '加载中...';
$('#copy').addEventListener('click', () => {
const text = $('#answer').innerText;
copyToClipboard(text);
});
$('#footer').style.display = 'flex';
},
});
};
//md转换
function mdConverter(rawData) {
var converter = new showdown.Converter(); //增加拓展table
converter.setOption('tables', true); //启用表格选项。从showdown 1.2.0版开始,表支持已作为可选功能移入核心拓展,showdown.table.min.js扩展已被弃用
var view = converter.makeHtml(rawData);
return view;
}
// 流式输出递归更新节点
function updateNode(element, target) {
//如果是标签节点
if (element.nodeType == 1 && element.outerHTML != target.outerHTML) {
target.parentNode.replaceChild(element.cloneNode(true), target)
//如果是文本节点
} else if (element.nodeType == 3 && element.nodeValue != target.nodeValue) {
target.parentNode.replaceChild(element.cloneNode(true), target)
}
}
function diffAndUpdate(target, newContent) {
var array = newContent.childNodes
var org = target.childNodes
for (let index = 0; index < org.length; index++) {
if (!array[index]) {
org[index].parentNode.removeChild(org[index])
}
if (array[index] && array[index].cloneNode().outerHTML != org[index].cloneNode().outerHTML) {
org[index].parentNode.replaceChild(array[index].cloneNode(), org[index])
}
}
for (let index = 0; index < array.length; index++) {
const element = array[index];
if (org[index]) {
//如果有子节点,递归
if (element.childNodes && element.childNodes.length > 0) {
//首先要把当前节点更改成空节点
// if (org[index].nodeType == 1 && org[index].outerHTML != element.outerHTML) {
// org[index].parentNode.replaceChild(element.cloneNode(), org[index])
// }
diffAndUpdate(org[index], element)
} else
updateNode(element, org[index])
} else {
//新增
if (target.nodeType == 1)
target.appendChild(element.cloneNode(true))
}
}
}
function demandInnerHTML(target, html) {
var copy = document.createElement('div')
copy.insertAdjacentHTML('beforeend', html)
diffAndUpdate(target, copy)
}
// 谷歌搜索ChatGPT应答
const handleGoogleSearch = () => {
window.addEventListener('load', () => {
// generateSearchEnhance();
});
};
const postMsg = ({ type, ...data }) => {
const chatgptIframe = $('#chatgpt-iframe');
chatgptIframe.contentWindow.postMessage(
{ data, type, origin: 'parent' },
domain
);
};
const extractArticle = () => {
function isText(node) {
return node.nodeType === Node.TEXT_NODE;
}
function getTextContent(node) {
return node.textContent.trim();
}
function isArticleNode(node) {
if (node.nodeName === 'P' || /^H[1-6]$/.test(node.nodeName)) {
return getTextContent(node) !== '';
}
// if (node.nodeName === 'UL' || node.nodeName === 'OL') {
// return node.querySelector('li') !== null;
// }
if (node.nodeName === 'DIV') {
return node.classList.contains('page-main_content') || node.classList.contains('content-main');
}
if (node.nodeName === 'SECTION' || node.nodeName === 'ARTICLE') {
return node.querySelector('p,h1,h2,h3,h4,h5,h6,ul,ol,div') !== null;
}
return false;
}
function getArticleNodes() {
const articleNodes = [];
function traverse(node) {
if (isArticleNode(node)) {
articleNodes.push(node);
}
node.childNodes.forEach(traverse);
}
traverse(document.body);
return articleNodes;
}
function getArticleText() {
const articleNodes = getArticleNodes();
const articleText = articleNodes.map(node => getTextContent(node)).join('\n');
return articleText.replace(/\s+/g, ' ').trim();
}
const result = getArticleText();
const splitStr = (str) => {
const maxLength = 4400;
const strArr = [];
let temp = '';
for (let i = 0; i < str.length; i++) {
temp += str[i];
if ((i + 1) % maxLength === 0 || i === str.length - 1) {
strArr.push(temp);
temp = '';
}
}
return strArr;
};
const formatResult = splitStr(result);
return formatResult;
};
// 计算输入框大小
// input:(必填)输入框
// rowh:(必填)当没有内容时默认高度,如果不传当没有内容时会没高度
// colw:(可选)当没有内容时默认宽度,如果不传宽度会很窄
function inputSizeChange(textarea) {
let text = textarea.value;
let lines = text.split('\n');
let lineHeight = 32;
let height = lines.length * lineHeight;
textarea.style.height = height + 'px';
}
// 获取油猴脚本信息
const getCodeInfo = (content) => {
const match = content.match(/@name\s*([^\n;]+)/);
const name = match ? match[1].trim() : '未命名脚本';
return { name };
};
// 匹配油猴脚本中生效和不生效的网址
function getUrls(script) {
const matchRegex = /(@match|@include)\s*([^]*?)\n/g;
const blockRegex = /(\S+)\s*>=\s*0\s*\)/g;
let matches, blocks;
const urls = {
matches: [],
blocks: [],
};
while ((matches = matchRegex.exec(script)) !== null) {
if (matches[1] === '@match') {
urls.matches.push(matches[2]);
} else if (matches[1] === '@include') {
urls.matches.push(matches[2]);
}
}
while ((blocks = blockRegex.exec(script)) !== null) {
urls.blocks.push(blocks[1]);
}
return urls;
}
// 处理监听ChatGPT-web的消息
const handlePostMessage = () => {
// 父级,在frame处抛出接收事件
window.addEventListener(
'message',
(event) => {
if (event.data.origin && event.data.origin === 'chatgpt-web') {
// console.log('chatgpt-web', event.data, GM_addStyle);
const { content, key } = event.data.data;
if (event.data.type === 'code') {
evalCode(content);
const chatgptKey = `chatgpt-${key}`;
const item = localStorage.getItem(chatgptKey);
const { name } = getCodeInfo(content);
const storageItem = {
content,
key: chatgptKey,
isChecked: true,
name,
...getUrls(content),
};
localStorage.setItem(chatgptKey, JSON.stringify(storageItem));
vm.updateStorageItem(storageItem);
vm.isGetNew = true;
}
if (event.data.type === 'read') {
const article = extractArticle();
postMsg({ type: 'read', content: article });
}
if (event.data.type === 'ready' && (isDomain('google') || isDomain('baidu'))) {
const rId = isDomain('google') ? 'rhs' : 'content_right'
const searchBox = document.getElementById(rId);
const contentBox = document.getElementById(
isDomain('google') ? 'rcnt' : 'container'
);
if(!searchBox) {
const rIdBox = document.createElement('rId');
rIdBox.id = rId;
contentBox?.appendChild(rIdBox);
}
!$('#chatgpt-search-enhance') && generateSearchEnhance();
}
if (event.data.type === 'search') {
// $('#answer').innerHTML = mdConverter(event.data.data.content);
demandInnerHTML($('#answer'), mdConverter(event.data.data.content))
$('#toolbar').style.display = 'inline-flex';
}
if (event.data.type === 'selectText') {
// $('#selectResult').value = event.data.data.content;
// $('#selectResult').addec;
// inputSizeChange($('#selectResult'));
dialogVm.result = event.data.data.content;
}
}
},
false
);
};
// 选中文本的弹出框
function createDialogBox() {
const dialogBox = document.createElement('div');
// dialogBox.id="floatDialog"
dialogBox.innerHTML = `
<div id="floatDialog" v-show="isShowDialog">
<div v-show="isShowBrief" id="briefContent" class="brief-content">
<div class="brief-title lt-znd2I9">
<a class="title" target="_blank" rel="noreferrer">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="30" height="30" fill="none" class="user-avatar"><defs><path id="bot_svg__a" d="M0 0h30v30H0z"></path><path id="bot_svg__c" d="M0 0h20.455v20.455H0z"></path></defs><g><rect fill="#E7F8FF" width="30" height="30" rx="10"></rect><mask id="bot_svg__b" fill="#fff"><use xlink:href="#bot_svg__a"></use></mask><g mask="url(#bot_svg__b)"><g transform="translate(4.773 4.773)"><mask id="bot_svg__d" fill="#fff"><use xlink:href="#bot_svg__c"></use></mask><g mask="url(#bot_svg__d)"><path fill-rule="evenodd" d="M19.11 8.37c.17-.52.26-1.06.26-1.61 0-.9-.24-1.79-.71-2.57a5.24 5.24 0 0 0-4.53-2.59c-.37 0-.73.04-1.09.11A5.201 5.201 0 0 0 9.17 0h-.04C6.86 0 4.86 1.44 4.16 3.57A5.11 5.11 0 0 0 .71 6.04C.24 6.83 0 7.72 0 8.63c0 1.27.48 2.51 1.35 3.45-.18.52-.27 1.07-.27 1.61 0 .91.25 1.8.71 2.58 1.13 1.94 3.41 2.94 5.63 2.47a5.18 5.18 0 0 0 3.86 1.71h.05c2.26 0 4.27-1.44 4.97-3.57a5.132 5.132 0 0 0 3.45-2.47c.46-.78.7-1.67.7-2.58 0-1.28-.48-2.51-1.34-3.46ZM8.947 18.158c-.04.03-.08.05-.12.07.7.58 1.57.89 2.48.89h.01c2.14 0 3.88-1.72 3.88-3.83v-4.76c0-.02-.02-.04-.04-.05l-1.74-.99v5.75c0 .23-.13.45-.34.57l-4.13 2.35Zm-.67-1.153 4.17-2.38c.02-.01.03-.03.03-.05v-1.99l-5.04 2.87c-.21.12-.47.12-.68 0l-4.13-2.35c-.04-.02-.09-.06-.12-.07-.04.21-.06.43-.06.65 0 .67.18 1.33.52 1.92v-.01c.7 1.19 1.98 1.92 3.37 1.92.68 0 1.35-.18 1.94-.51ZM3.903 5.168v-.14c-.85.31-1.57.9-2.02 1.68a3.78 3.78 0 0 0-.52 1.91c0 1.37.74 2.64 1.94 3.33l4.17 2.37c.02.01.04.01.06 0l1.75-1-5.04-2.87a.64.64 0 0 1-.34-.57v-4.71Zm13.253 3.337-4.18-2.38c-.02 0-.04 0-.06.01l-1.74.99 5.04 2.87c.21.12.34.34.34.58v4.85c1.52-.56 2.54-1.99 2.54-3.6 0-1.37-.74-2.63-1.94-3.32ZM8.014 5.83c-.02.01-.03.03-.03.05v1.99L13.024 5a.692.692 0 0 1 .68 0l4.13 2.35c.04.02.08.05.12.07.03-.21.05-.43.05-.65 0-2.11-1.74-3.83-3.88-3.83-.68 0-1.35.18-1.94.51l-4.17 2.38Zm1.133-4.492c-2.15 0-3.89 1.72-3.89 3.83v4.76c0 .02.02.03.03.04l1.75 1v-5.75c0-.23.13-.45.34-.57l4.13-2.35c.04-.03.09-.06.12-.07-.7-.58-1.58-.89-2.48-.89ZM7.983 11.51l2.24 1.27 2.25-1.27V8.95l-2.25-1.28-2.24 1.28v2.56Z" style="fill: rgb(31, 148, 140);"></path></g></g></g></g></svg>
</a>
</div>
<div class="action-bar-lite-window-vuGdUx action-bar-SA76od" style="opacity: 1;">
<div class="box-NzPGeV">
<span @click="handleClick('translate')" id="translate" data-id="751059" class="tag-button tag-button-active-y5uMAc" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M16.5 3v9l-3-2.25-3 2.25V3M4.5 20.25V21H18" data-follow-stroke="#000"></path><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M4.5 20.25A2.25 2.25 0 0 1 6.75 18H19.5V3H6.75A2.25 2.25 0 0 0 4.5 5.25v15Z" data-follow-stroke="#000"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="翻译">
<span class="text-0Pw8ng">翻译</span>
</span>
</span>
<span @click="handleClick('summary')" id="quickSummary" data-id="751059" class="tag-button tag-button-active-y5uMAc" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M16.5 3v9l-3-2.25-3 2.25V3M4.5 20.25V21H18" data-follow-stroke="#000"></path><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M4.5 20.25A2.25 2.25 0 0 1 6.75 18H19.5V3H6.75A2.25 2.25 0 0 0 4.5 5.25v15Z" data-follow-stroke="#000"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="总结">
<span class="text-0Pw8ng">总结</span>
</span>
</span>
</div>
</div>
<el-dropdown id="selectTextDropDown" @command="handleClickDropDown">
<span class="el-dropdown-link">
<i class="el-icon-arrow-down el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="translate">翻译</el-dropdown-item>
<el-dropdown-item command="summary">总结</el-dropdown-item>
<el-dropdown-item command="explain">解释</el-dropdown-item>
<el-dropdown-item command="explainCode">解释代码</el-dropdown-item>
<el-dropdown-item command="grammar">语法</el-dropdown-item>
<el-dropdown-item command="rewrite">重写</el-dropdown-item>
<el-dropdown-item command="qa">问答</el-dropdown-item>
<el-dropdown-item command="expansion">扩写</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</div>
<div v-show="isShowComplete" id="complateContent">
<div id="dialogBoxSelectBox">
<h5 id="dialogBoxSelectTitle">选中文本<h5/>
<el-input
id="selectVal"
class="text-area"
type="textarea"
:autosize="{ minRows: 2, maxRows: 5 }"
placeholder="请输入内容"
v-model="selectVal">
</el-input>
<div>
<div id="selectActions">
<div class="action-bar-lite-window-vuGdUx action-bar-SA76od" style="opacity: 1;">
<div class="box-NzPGeV">
<div @click="handleClick('summary')" id="summary" data-id="751061" class="tag-button" style="opacity: 1; pointer-events: auto;">
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M10.5 10.5h6M10.5 13.5h6M19.5 3.75h-15a.75.75 0 0 0-.75.75v15c0 .414.336.75.75.75h15a.75.75 0 0 0 .75-.75v-15a.75.75 0 0 0-.75-.75ZM7.5 3.75v16.5" data-follow-stroke="#000"></path></g></svg>
<div class="text-wrapper-92ojRl" data-text="总结"><div class="text-0Pw8ng">摘要</div></div></div>
<span @click="handleClick('grammar')" id="grammar" data-id="1924664" class="tag-button" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 32 32" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="m27 26-5-10-5 10M18.428 23.143h7.143M5 18h9M5 23h9M5.006 8h21M5.006 13h21" data-follow-stroke="#000"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="语法"><span class="text-0Pw8ng">语法</span></span></span>
<span @click="handleClick('explainCode')" id="explainCode" data-id="1924663" class="tag-button" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path fill="currentColor" d="M7.8 19.2H7A1.2 1.2 0 0 1 5.8 18v-4.1a1.8 1.8 0 0 0-1.236-1.71L3.987 12l.577-.19A1.8 1.8 0 0 0 5.8 10.1V6A1.2 1.2 0 0 1 7 4.8h.8V3.2H7A2.8 2.8 0 0 0 4.2 6v3.7a1.7 1.7 0 0 1-1.7 1.7h-.3v1.2h.3a1.7 1.7 0 0 1 1.7 1.7V18A2.8 2.8 0 0 0 7 20.8h.8v-1.6ZM16.2 19.2h.8a1.2 1.2 0 0 0 1.2-1.2v-4.1a1.8 1.8 0 0 1 1.236-1.71l.577-.19-.577-.19A1.8 1.8 0 0 1 18.2 10.1V6A1.2 1.2 0 0 0 17 4.8h-.8V3.2h.8A2.8 2.8 0 0 1 19.8 6v3.7a1.701 1.701 0 0 0 1.7 1.7h.3v1.2h-.3a1.7 1.7 0 0 0-1.7 1.7V18a2.8 2.8 0 0 1-2.8 2.8h-.8v-1.6Z" clip-rule="evenodd" fill-rule="evenodd" data-follow-fill="#09121F"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="解释代码"><span class="text-0Pw8ng">解释代码</span></span></span>
<span @click="handleClick('rewrite')" id="rewrite" data-id="1924662" class="tag-button" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M12.75 6 18 11.25M20.25 20.25H9l-5.202-5.202" data-follow-stroke="#000"></path><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M9 20.25H4.5a.75.75 0 0 1-.75-.75v-4.19a.75.75 0 0 1 .22-.53L15.22 3.53a.75.75 0 0 1 1.06 0l4.19 4.19a.75.75 0 0 1 0 1.06L9 20.25Z" data-follow-stroke="#000"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="重写"><span class="text-0Pw8ng">重写</span></span></span>
<span @click="handleClick('explain')" id="explain" data-id="751059" class="tag-button tag-button-active-y5uMAc" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M16.5 3v9l-3-2.25-3 2.25V3M4.5 20.25V21H18" data-follow-stroke="#000"></path><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M4.5 20.25A2.25 2.25 0 0 1 6.75 18H19.5V3H6.75A2.25 2.25 0 0 0 4.5 5.25v15Z" data-follow-stroke="#000"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="解释"><span class="text-0Pw8ng">解释</span></span></span>
<span @click="handleClick('translate')" id="translate" data-id="751060" class="tag-button" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M21.75 20.25 16.5 9.75l-5.25 10.5M12.75 17.25h7.5M8.25 3v2.25M2.25 5.25h12M11.25 5.25a9 9 0 0 1-9 9" data-follow-stroke="#000"></path><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M5.763 8.25a9.004 9.004 0 0 0 8.486 5.997" data-follow-stroke="#000"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="翻译"><span class="text-0Pw8ng">翻译</span></span></span>
<span @click="handleClick('qa')" id="qa" data-id="1924661" class="tag-button" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 24 24" style="min-width: 16px; min-height: 16px;"><g><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M20.95 11.625a8.625 8.625 0 0 1-8.625 8.625H3.7v-8.625a8.625 8.625 0 0 1 17.25 0Z" data-follow-stroke="#000"></path><path stroke-linejoin="round" stroke-linecap="round" stroke-width="1.5" stroke="currentColor" d="M12.325 13.62v-1.725a2.587 2.587 0 1 0-2.588-2.588" data-follow-stroke="#000"></path><path fill="currentColor" d="M12.325 17.501a1.078 1.078 0 1 0 0-2.156 1.078 1.078 0 0 0 0 2.156Z" clip-rule="evenodd" fill-rule="evenodd" data-follow-fill="#000"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="问答"><span class="text-0Pw8ng">问答</span></span></span>
<span @click="handleClick('expansion')" id="expansion" data-id="1924660" class="tag-button" style="opacity: 1; pointer-events: auto;"><svg width="16" height="16" fill="none" viewBox="0 0 16 16" style="min-width: 16px; min-height: 16px;"><g><path data-follow-stroke="#637381" d="M10 3h3v3m-3.5.5L13 3" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path><path data-follow-stroke="#37404A" d="M6 13H3v-3m3.5-.5L3 13" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>
<span class="text-wrapper-92ojRl" data-text="扩写"><span class="text-0Pw8ng">扩写</span></span></span></div>
</div>
</div>
<div>
<h5 id="dialogBoxResultTitle">结果<h5/>
<el-input
class="text-area"
id="selectResult"
type="textarea"
:autosize="{ minRows: 2, maxRows: 20 }"
placeholder="请输入内容"
v-model="result">
</el-input>
<div>
<div class="monica-btn btn continue-chat primary-outline-button" id="chatBtn">
<svg aria-hidden="true" focusable="false" role="img" class="octicon octicon-comment" viewBox="0 0 16 16" width="16" height="16" fill="currentColor" style="display: inline-block; user-select: none; vertical-align: text-bottom; overflow: visible;"><path d="M1 2.75C1 1.784 1.784 1 2.75 1h10.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 13.25 12H9.06l-2.573 2.573A1.458 1.458 0 0 1 4 13.543V12H2.75A1.75 1.75 0 0 1 1 10.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h2a.75.75 0 0 1 .75.75v2.19l2.72-2.72a.749.749 0 0 1 .53-.22h4.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
<span class="chatgpt-text">在聊天中继续</span>
</div>
</div>
</div>
`
GM_addStyle(`
#floatDialog {
position: absolute;
background-color: white;
padding: 10px;
z-index: 999;
display: block;
border-radius: 8px;
border: 1px solid #dadce0;
left: -1000px;
}
#briefContent {
align-items: center;
display: flex;
}
#briefContent .brief-title {
margin-right: 10px;
}
#complateContent {
display: flex;
}
#complateContent .brief-title {
margin-right: 10px;
}
#complateContent .text-area {
width: 99%;
margin-bottom: 10px;
}
#selectActions {
margin-bottom: 10px;
}
.action-bar-SA76od.action-bar-lite-window-vuGdUx .box-NzPGeV {
justify-content: left;
}
.action-bar-SA76od .box-NzPGeV {
min-width: 0;
display: inline-flex;
flex-flow: wrap;
height: 24px;
gap: 10px;
align-content: flex-start;
justify-content: right;
}
.action-bar-SA76od .box-NzPGeV {
min-width: 0;
display: inline-flex;
flex-flow: wrap;
height: 24px;
gap: 10px;
align-content: flex-start;
justify-content: right;
}
#floatDialog .tag-button {
position: relative;
cursor: pointer;
user-select: none;
-webkit-user-drag: none;
height: 24px;
min-height: 24px;
padding: 0 8px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(56,114,224,.48);
border-radius: 4px;
font-size: 13px;
color: #3872e0;
gap: 4px;
max-width: 150px;
background: #fff;
}
#floatDialog .tag-button .text-wrapper-92ojRl {
flex: 1;
min-width: 0px;
display: flex;
flex-direction: column;
}
#floatDialog .tag-button .text-wrapper-92ojRl {
flex: 1;
min-width: 0px;
display: flex;
flex-direction: column;
}
#floatDialog .tag-button:hover {
background: rgba(235,202,254,.24);
text-decoration: none!important;
}
`);
// dialogBox.appendChild(complateContent);
document.body.appendChild(dialogBox);
// creatChatBtn(complateContent);
return dialogBox;
}
// 主函数
var main = function () {
// 创建用户界面
createUI();
// 初始化配置
initConfig();
// 处理监听ChatGPT-web的消息
handlePostMessage();
// 插件管理
vm = new Vue({
el: '#customization-chat-room',
data: {
activeName: 'chatgpt',
localCode: [],
isGetNew: false,
activeCollapse: [3],
logs: [
{
title: '版本 1.3.1',
id: 3,
content: [
'1. 支持流式输出,响应更快啦!',
'2. 支持选中文本快捷操作',
'3. 增加请求频率控制',
'4. 优化兼容',
]
},
{
title: '版本 1.1.7',
id: 2,
content: [
'1. 支持 Midjourney 出图,聊天框输入/mj即可体验,(如/mj iron man)'
]
},
{
title: '版本 1.1.6',
id: 1,
content: [
'1. 支持百度、谷歌搜索增强',
'2. 支持选中文本总结、翻译、扩写、代码解释、解释、语法、问答',
'3. 支持ChatGPT聊天的完整交互,可自定义prompt、定义角色等等',
'4. 支持一句话生成油猴脚本,插件管理',
]
}
]
},
mounted() {
this.setGMAPI();
window.addEventListener('storage', this.handleStorageChange);
this.handleStorageChange();
$('.el-tabs__content').style.height = window.innerHeight - 50 + 'px';
$('.el-tabs__content').style.overflowY = 'hidden';
},
beforeUnmount() {
window.removeEventListener('storage', this.handleStorageChange);
},
methods: {
handleClick(tab, event) {
if (tab.name === 'web') {
window.open(domain, '_blank');
} else if (tab.name === 'plugins') {
this.isGetNew = false;
$('.plugins-box') &&
($('.plugins-box').style.height = window.innerHeight - 80 + 'px');
$('.el-tabs__content').style.overflowY = 'auto';
} else {
$('.el-tabs__content').style.overflowY = 'hidden';
}
},
setGMAPI() {
try {
unsafeWindow.GM_log = GM_log;
unsafeWindow.GM_notification = GM_notification;
unsafeWindow.unsafeWindow = unsafeWindow;
unsafeWindow.GM_addStyle = GM_addStyle;
unsafeWindow.GM_setValue = GM_setValue;
unsafeWindow.GM_getValue = GM_getValue;
unsafeWindow.GM_setClipboard = GM_setClipboard;
unsafeWindow.GM_xmlhttpRequest = GM_xmlhttpRequest;
// unsafeWindow.GM_addElement = GM_addElement;
unsafeWindow.GM_openInTab = GM_openInTab;
unsafeWindow.GM_getResourceText = GM_getResourceText;
unsafeWindow.GM_registerMenuCommand = GM_registerMenuCommand;
unsafeWindow.GM_info = GM_info;
} catch (error) {
console.error('setGMAPI', error);
}
},
evalCode(content) {
const myFunc = () => content;
myFunc();
},
handleCheckboxChange(isChecked, item) {
item.isChecked = isChecked;
this.updateStorageItem(item);
},
deleteStorageItem(key) {
this.localCode = this.localCode.filter((item) => item.key !== key);
localStorage.removeItem(key);
},
updateStorageItem(item) {
localStorage.setItem(
item.key,
JSON.stringify({
...item,
...getUrls(item.content),
...getCodeInfo(item.content),
})
);
this.handleStorageChange();
},
isScriptMatched(matches = []) {
const currentURL = window.location.href;
// 将通配符转换成正则表达式语法
const patterns = matches.map((pattern) => {
if (pattern.includes("*")) {
// 将 * 转换成 .*
pattern = pattern.replace(/\*/g, ".*");
}
if (pattern.includes("?")) {
// 将 ? 转换成 .
pattern = pattern.replace(/\?/g, ".");
}
return `^${pattern}$`;
});
// 检查油猴脚本匹配模式是否包含当前网页的网址
return patterns.some((pattern) => {
const regex = new RegExp(pattern);
return regex.test(currentURL);
});
},
handleStorageChange() {
this.localCode = [];
for (let key in localStorage) {
if (key.split('-')[0] === 'chatgpt') {
const storageItem = JSON.parse(localStorage.getItem(key));
this.localCode.push(storageItem);
if (
storageItem.isChecked &&
this.isScriptMatched(storageItem.matches) &&
!this.isScriptMatched(storageItem.blocks)
) {
evalCode(storageItem.content);
}
}
}
},
},
});
// 选中文本的弹出框
createDialogBox();
// handleTextAutoHeight();
dialogVm = new Vue({
el: '#floatDialog',
data: {
dialogBox: $('#floatDialog'),
isShowDialog: false,
isShowBrief: true,
isShowComplete: false,
result: '',
mapDict: {
summary: '给一个二年级的学生总结一下',
explain: '解释{your content here},并说明其中使用的任何技术术语',
grammar: '校对并纠正这段文字',
explainCode: '解释以下代码',
rewrite: '重新表述这段文字',
translate: '给出以下文字的中文以及英文翻译',
qa: '回答这个问题',
expansion: '详细说明这段文字'
},
selectVal: ''
},
mounted() {
// 处理选中文本
this.handleSelect();
$('#briefContent').addEventListener('click', () => {
// $('#briefContent').style.display = 'none';
// complateContent.style.display = 'block';
this.isShowBrief = false;
this.isShowComplete = true;
});
this.registerClick();
},
beforeUnmount() {},
methods: {
handleClickDropDown(type) {
this.handleClick(type)
this.isShowComplete = true;
this.isShowBrief = false;
},
handleClick(type) {
this.result = '加载中...';
postMsg({
type: 'selectText',
content: `${this.mapDict[type]}: ${this.selectVal}`,
});
},
addClickAndPostMsg({ targetId, type, content, resultId, clickFn }) {
$(`#${targetId}`).addEventListener('click', () => {
postMsg({ type, content });
$(`#${resultId}`).value = '加载中...';
clickFn && clickFn();
});
},
registerClick() {
$('#chatBtn').addEventListener('click', () => {
$('#customization-drawer').style.right = 0;
});
},
isInDialogBox() {
const element = $('#floatDialog'); // 获取需要检查的元素
const rect = element?.getBoundingClientRect(); // 获取元素位置和尺寸信息
if(!rect) return false;
const x = event.clientX; // 获取鼠标点击的X坐标
const y = event.clientY; // 获取鼠标点击的Y坐标
// let selectedElement = document.getSelection()?.focusNode?.parentNode;
// console.log('选中的元素是:', document.getSelection(), selectedElement, {
// rect,
// x,
// y,
// });
// 判断鼠标点击的坐标是否在元素位置之内
// if (
// rect &&
// x >= rect.left &&
// x <= rect.right &&
// y >= rect.top &&
// y <= rect.bottom
// ) {
// // console.log('点击的位置处于该元素之中!');
// } else {
// // console.log('点击的位置不在该元素之中。');
// }
return (
x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
);
},
setDialogBoxPos() {
let sel = window.getSelection();
let range = sel.getRangeAt(0);
let rect = range?.getBoundingClientRect();
$('#floatDialog').style.top = rect.bottom + window.pageYOffset + 'px';
$('#floatDialog').style.left =
(rect.left + rect.right) / 2 + window.pageXOffset + 'px';
},
// 选中文本逻辑处理
handleSelect() {
const that = this;
let selectionText = document.addEventListener('mouseup', (e) => {
selectionText = window.getSelection().toString();
if (selectionText && !that.isInDialogBox()) {
// 如果选中的是文本,而不是DOM元素
that.selectVal = selectionText;
that.isShowDialog = true;
that.isShowBrief = true;
that.isShowComplete = false;
that.setDialogBoxPos();
} else {
let selectedElement = document.getSelection()?.focusNode?.parentNode;
const classList = selectedElement?.classList?.value
if(classList && !classList.includes('chat') && !classList.includes('tab-item-badge')) {
// postMsg({type: 'clickElement', content: classList})
}
if(!that.isInDialogBox() && classList !== 'el-dropdown-menu__item') {
that.isShowDialog = false;
}
}
});
},
}
});
const iframe = $('#chatgpt-iframe');
iframe.onerror = function () {
// iframe 加载失败
const chatRoom = $('#customization-chat-room');
chatRoom.innerHTML = `
<p>抱歉,由于某些原因,无法加载此内容。</p>
<p>请跳转网页版体验:${domain}。</p>
`;
};
// 网页内容读取
if (isDomain('google') || isDomain('baidu')) {
handleGoogleSearch();
}
};
// 执行主函数
const isQqMails = () => isDomain('mail.qq.com');
try {
if (window.self !== window.top && isQqMails()) {
main();
} else if(window.self === window.top && !isQqMails()) {
main();
}
} catch (error) {
console.error(error);
}
})();