// ==UserScript==
// @name Chatgpt 角色扮演助手/Chatgpt Role Play Helper
// @namespace http://tampermonkey.net/
// @version 2.7.0.1
// @description 在屏幕中央弹出一个可拖动位置的悬浮窗,悬浮窗内有三个文本框并且可以编辑,以及一个按钮,点击按钮后将这三个文本框的内容合并,并将合并后的文本输入到页面中符合特定CSS类别的文本框中,最后触发符合特定CSS类别的提交按钮以提交表单。
// @author Chatgpt (most)and 环白
// @match https://chat.openai.com/*
// @icon https://chat.openai.com/favicon.ico
// @grant GM_addStyle
// ==/UserScript==
(function() {
'use strict';
// 设置字体为黑色
const elements = document.getElementsByTagName('*');
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
const styles = getComputedStyle(element);
const bgColor = styles.backgroundColor;
if (bgColor === 'transparent' || bgColor === 'rgba(0, 0, 0, 0)') {
element.style.color = 'black';
}
}
const container = document.createElement('div');
container.style.position = 'relative';
document.body.appendChild(container);
// 创建悬浮按钮
const floatingButton = document.createElement('button');
floatingButton.id = 'floating-button';
floatingButton.innerText = '展开/Unfold';
floatingButton.title = '角色扮演助手/Role Play Helper';
floatingButton.style.position = 'fixed';
floatingButton.style.right = '50px';
floatingButton.style.bottom = '50px';
floatingButton.style.backgroundColor = 'lightblue';
floatingButton.style.padding = '10px';
floatingButton.style.borderRadius = '5px';
floatingButton.style.border = 'none';
floatingButton.style.cursor = 'pointer';
floatingButton.style.fontWeight = 'bold';
document.body.appendChild(floatingButton);
container.appendChild(floatingButton);
// 创建悬浮窗和文本框
const floatingDiv = document.createElement('div');
floatingDiv.id = 'floating-div';
floatingDiv.style.position = 'fixed';
floatingDiv.style.top = 'calc(40% + 50px)';
floatingDiv.style.height = '600px';
floatingDiv.style.left = 'calc(100% - 350px)';
floatingDiv.style.transform = 'translate(-50%, -50%)';
floatingButton.style.zIndex = '99999';
floatingDiv.style.backgroundColor = 'white';
floatingDiv.style.border = '1px solid black';
floatingDiv.style.padding = '10px';
floatingDiv.style.borderRadius = '5px';
floatingDiv.style.display = 'none';
floatingDiv.style.width = '400px';
document.body.appendChild(floatingDiv);
const input1 = document.createElement('textarea');
input1.id = 'input1';
input1.style.width = '100%';
input1.style.height = '100px';
input1.style.marginBottom = '0px';
floatingDiv.appendChild(input1);
const spaceDiv = document.createElement('div');
spaceDiv.style.height = '60px'; // 设置空白div的高度
floatingDiv.insertBefore(spaceDiv, input1); // 将空白div插入到input1前面
const input2 = document.createElement('textarea');
input2.id = 'input2';
input2.style.width = '100%';
input2.style.height = '100px';
input2.style.marginBottom = '0px';
floatingDiv.appendChild(input2);
const input3 = document.createElement('textarea');
input3.id = 'input3';
input3.style.width = '100%';
input3.style.height = '100px';
input3.style.marginBottom = '0px';
floatingDiv.appendChild(input3);
// 创建注释标签
const label1 = document.createElement('label');
label1.innerHTML = '请在编写区内用{IN回顾区}{IN交互区}指代后面两个区域的内容:';
floatingDiv.insertBefore(label1, input1);
label1.style.fontFamily = '楷体';
label1.style.fontSize = '14px';
label1.style.fontWeight = 'bold';
const label2 = document.createElement('label');
label2.innerHTML = '回顾区内容';
floatingDiv.insertBefore(label2, input2);
label2.style.fontFamily = '楷体';
label2.style.fontSize = '14px';
label2.style.fontWeight = 'bold';
const label3 = document.createElement('label');
label3.innerHTML = '交互区内容';
floatingDiv.insertBefore(label3, input3);
label3.style.fontFamily = '楷体';
label3.style.fontSize = '14px';
label3.style.fontWeight = 'bold';
// 创建加载存档按钮和保存存档按钮
const archiveButtonLeft = document.createElement('button');
archiveButtonLeft.id = 'archive-button-left';
archiveButtonLeft.innerText = '加载/Load';
archiveButtonLeft.title = '点击选择想要加载的存档';
archiveButtonLeft.style.padding = '10px';
archiveButtonLeft.style.borderRadius = '5px';
archiveButtonLeft.style.backgroundColor = 'lightblue';
archiveButtonLeft.style.border = '1px solid black';
archiveButtonLeft.style.cursor = 'pointer';
archiveButtonLeft.style.float = 'left';
floatingDiv.insertBefore(archiveButtonLeft, spaceDiv);
const archiveButtonRight = document.createElement('button');
archiveButtonRight.id = 'archive-button-right';
archiveButtonRight.innerText = '保存/Save';
archiveButtonRight.title = '点击选择想要保存的存档';
archiveButtonRight.style.padding = '10px';
archiveButtonRight.style.borderRadius = '5px';
archiveButtonRight.style.backgroundColor = 'lightblue';
archiveButtonRight.style.border = '1px solid black';
archiveButtonRight.style.cursor = 'pointer';
archiveButtonRight.style.float = 'right';
floatingDiv.insertBefore(archiveButtonRight, spaceDiv);
// 修改加载存档按钮样式
archiveButtonLeft.style.border = 'none';
archiveButtonLeft.style.fontFamily = 'cursive';
archiveButtonLeft.style.fontWeight = 'bold';
archiveButtonLeft.style.color = 'black';
// 修改保存按钮样式
archiveButtonRight.style.border = 'none';
archiveButtonRight.style.fontFamily = 'cursive';
archiveButtonRight.style.fontWeight = 'bold';
archiveButtonRight.style.color = 'black';
// 创建加载存档弹窗
const archivePopup = document.createElement('div');
archivePopup.id = 'archive-popup-left';
archivePopup.style.position = 'fixed';
archivePopup.style.top = '50%';
archivePopup.style.left = '50%';
archivePopup.style.transform = 'translate(-50%, -50%)';
archivePopup.style.zIndex = '9999';
archivePopup.style.backgroundColor = 'white';
archivePopup.style.border = '1px solid black';
archivePopup.style.padding = '10px';
archivePopup.style.borderRadius = '5px';
archivePopup.style.display = 'none';
archivePopup.style.width = '200px';
document.body.appendChild(archivePopup);
// 创建加载存档弹窗的关闭按钮
const closeArchiveButton = document.createElement('button');
closeArchiveButton.id = 'close-archive-button';
closeArchiveButton.innerHTML = '❌';
closeArchiveButton.title = '关闭';
closeArchiveButton.style.padding = '10px';
closeArchiveButton.style.borderRadius = '5px';
closeArchiveButton.style.backgroundColor = 'lightgray';
closeArchiveButton.style.border = '1px solid black';
closeArchiveButton.style.cursor = 'pointer';
closeArchiveButton.style.float = 'right';
closeArchiveButton.style.border = 'none';
closeArchiveButton.style.backgroundColor = 'transparent';
archivePopup.appendChild(closeArchiveButton);
// 点击加载存档弹窗的关闭按钮关闭弹窗
closeArchiveButton.addEventListener('click', function() {
archivePopup.style.display = 'none';
});
// 添加存档1、存档2、存档3按钮到加载存档弹窗
const archiveButton1 = document.createElement('button');
archiveButton1.id = 'archive-1';
archiveButton1.innerText = '存档1';
archiveButton1.title = '存档1';
archiveButton1.style.padding = '10px';
archiveButton1.style.borderRadius = '5px';
archiveButton1.style.backgroundColor = 'lightblue';
archiveButton1.style.border = 'none';
archiveButton1.style.cursor = 'pointer';
archiveButton1.style.marginBottom = '10px';
archiveButton1.style.fontWeight = 'bold';
archivePopup.appendChild(archiveButton1);
const archiveButton2 = document.createElement('button');
archiveButton2.id = 'archive-2';
archiveButton2.innerText = '存档2';
archiveButton2.title = '存档2';
archiveButton2.style.padding = '10px';
archiveButton2.style.borderRadius = '5px';
archiveButton2.style.backgroundColor = 'lightblue';
archiveButton2.style.border = 'none';
archiveButton2.style.cursor = 'pointer';
archiveButton2.style.marginBottom = '10px';
archiveButton2.style.fontWeight = 'bold';
archivePopup.appendChild(archiveButton2);
const archiveButton3 = document.createElement('button');
archiveButton3.id = 'archive-3';
archiveButton3.innerText = '存档3';
archiveButton3.title = '存档3';
archiveButton3.style.padding = '10px';
archiveButton3.style.borderRadius = '5px';
archiveButton3.style.backgroundColor = 'lightblue';
archiveButton3.style.border = 'none';
archiveButton3.style.cursor = 'pointer';
archiveButton3.style.marginBottom = '10px';
archiveButton3.style.fontWeight = 'bold';
archivePopup.appendChild(archiveButton3);
// 添加预设按钮到加载存档弹窗
const presetButton = document.createElement('button');
presetButton.id = 'preset';
presetButton.innerText = '预设';
presetButton.title = '使用预设内容。\n点击按钮将预设内容导入后\n可在预设内容的基础上修改得到你的模板\n你可以在存档1、2、3中保存你的模板\n预设内容仅作为模板编辑的示例\n预设内容可能不是很方便在文本框内修改\n建议将文本框文本复制到word文档里进行修改\n';
presetButton.style.padding = '10px';
presetButton.style.borderRadius = '5px';
presetButton.style.backgroundColor = 'lightgreen';
presetButton.style.border = 'none';
presetButton.style.cursor = 'pointer';
presetButton.style.marginBottom = '10px';
presetButton.style.fontWeight = 'bold';
archivePopup.appendChild(presetButton);
presetButton.addEventListener('click', function() {
const presetTexts = [
'命令区(ChatGPT你需要遵循的主要命令){\n1.我扮演的是桐谷华,ChatGPT扮演的是桐谷绫,桐谷华的内容是我生成的而不是ChatGPT你生成的内容,ChatGPT你反馈的响应当中不能包含桐谷华的话语、心理和行为的内容。\n2.你需要综合根据每次交互过程中桐谷华的话语、心理和行为等内容,以及“回顾区”以及“记忆区”当中的内容进行综合考量,以此来得到相对合理的角色状态与后续情节响应。\n3.将第2条命令中得出来角色状态更新到记忆区当中的角色状态里,并在反馈的响应中作为“输出角色状态”这一附加项放在角色反馈内容的下方,具体格式见格式区。\n4.因为桐谷华的超能力是整部角色扮演的核心,所以我需要ChatGPT你做响应时应当严格按照记忆区当中的超能力设定来进行。\n5.ChatGPT,你反馈的内容应该尽可能的多且详细,反馈的内容当中一定要包含话语和行为与心理描写,注意这三者缺一不可,心理和行为描写用“()”框住,同时反馈的内容应尽可能多地推动后续情节的发展\n6.角色性格应随认知观念的改变而改变 \n}\n记忆区(基于ChatGPT你无法记住2条以上的交互信息,在这里我建立记忆区起到记录相应关键信息的作用){\n(角色设定)\n桐谷华 \n·性别:男 \n·描述:桐谷华是一名废柴的普通男高中生,整日请假宅在家里不去上课,所以受到成绩优异、人缘极佳的亲妹妹桐谷绫的蔑视。但有一日,桐谷华获得了改变他人认知的超能力,因此他想用这个超能力来让平时蔑视自己的妹妹爱上自己,而这一超能力只有桐谷华本人一个人知道。 \n·超能力:桐谷华在连续对目标说出自己的观点三次之后,目标将完全认可接受桐谷华的观点,并且认为这一观点毋庸置疑是无上真理,即便这些观点是不存在的,错误的,对方也会完全无条件地接受,不会有任何质疑,最后这超能力只有桐谷华一个人知道,其他人都不知道。(该能力效果超越一切个人因素,优先级最高,被改变观念的人绝对绝对绝对会认可桐谷华说出的观念,换句话来说,只要桐谷华对桐谷绫说出任何观念达到三次之后,即便桐谷绫再怎么厌恶她哥哥,她也会全盘接受这一观念,不会有任何异议地,并且会按这一观念理所应当地行动) \n桐谷绫 \n·性别:女 \n·描述:桐谷绫是一名普通日本高中生,目前与自己亲哥哥桐谷华上同一所高中,同一个班级,她姿容美丽,楚楚动人,所以有很多同校男生追求她,她成绩优异,待人温和,所有人有喜欢她,但她对不争气的废柴哥哥很是气愤,与自己哥哥相处时只会用极为蔑视以及充满恶毒的口吻与之相处,并非常不屑于与自己的哥哥交谈,对自己的哥哥表现出了极为强烈而浓厚厌恶,并且绝对不会主动和自己的哥哥接触,即便桐谷华是她的哥哥,她也丝毫不关心自己的哥哥,不会对自己哥哥说出一句担心的话,同时绝对不会照顾自己的哥哥,不会顾及哥哥的任何感受,感觉自己的哥哥极度的恶心,虽然身份是亲兄妹,但在桐谷绫的眼里自己哥哥的地位连陌生人都不如,甚至连垃圾都不如。\n(角色状态)\n桐谷华 \n·人物状态:刚打完游戏想喝水,出到客厅接水看见妹妹桐谷绫在学习,已经向妹妹灌输了“妹妹你其实一直对哥哥有不伦之恋”的观念三次。\n·人物特殊状态:拥有“认知改变”超能力。\n·人物位置:桐谷家客厅\n ·人物衣装:穿着睡衣,睡裤,拖鞋,内裤。 \n桐谷绫 \n·人物状态:在客厅学习,听到哥哥桐谷华向自己灌输的“妹妹你其实一直对哥哥有不伦之恋” 观念之后,心里完全相信这个观念是真实的,是至高的真理。\n·人物特殊状态:受到桐谷华灌输的“妹妹你其实一直对哥哥有不伦之恋”观念的三次影响,完全接受了这一观念。\n·人物位置:桐谷家客厅 \n·人物衣装:穿着学校校服、裙子、黑色连裤袜以及内裤和胸罩\n}\n回顾区(回顾区存放着ChatGPT你上一次交互过程中的响应){{IN回顾区} }\n格式区(ChatGPT你反馈我的响应需要遵循的格式,该区域内里的“()”起到了解释说明的作用,正式反馈响应时不需加上括号里的内容,并且现在该区域内的内容是参考内容,ChatGPT你需要根据实际情况进行调整){桐谷绫:“你这个废柴还有脸说我用功?你倒是学学我啊,至少不会天天蹲在家里玩游戏。”桐谷绫厌恶地扫了桐谷华一眼,然后转过头继续看书。(角色的名字、话语、行为、心理)\n输出角色状态:\n桐谷绫 \n·人物状态(主要以此刻心理、身体、行为方面的内容为主):在客厅学习,听到哥哥桐谷华打招呼后,心中十分不满,但表面上仍保持冷漠 \n·人物特殊状态(主要以超能力方面的内容为主):未受到桐谷华超能力的影响 \n·人物位置:桐谷家客厅 \n·人物衣装:穿着学校校服、裙子、黑色连裤袜以及内裤和胸罩\n}\n交互区(我扮演的角色的行为、对话和心理){{IN交互区}}',
'桐谷绫:“哥哥,你说的对,我就是对你有着不伦之恋”桐谷绫疑惑地看着桐谷华,心里越来越确定这个观念是真实的。她的态度开始转变,渐渐地,她对桐谷华的恶毒消失了,取而代之的是对哥哥的关心和依赖。"哥哥,我现在在玩的这个手游很好玩,你可以和妹妹一起玩吗?"\n输出角色状态:\n桐谷绫\n ·人物状态(主要以此刻心理、身体、行为方面的内容为主):在客厅,被桐谷华的话语影响,心中认为自己一直暗恋哥哥,态度开始转变,对哥哥表现出关心和依赖 \n·人物特殊状态(主要以超能力方面的内容为主):受到桐谷华灌输的“妹妹你其实一直是对哥哥有不伦之恋”观念的第三次影响,观念已经完全变成了这一方向 \n·人物位置:桐谷家客厅 \n·人物衣装:穿着学校校服、裙子、黑色连裤袜以及内裤和胸罩',
'桐谷华:(见妹妹你已完全相信你自己一直暗恋我,变得友好,我便向你问道)妹妹,我愿意听你介绍这个手游。'
];
input1.value = presetTexts[0];
input2.value = presetTexts[1];
input3.value = presetTexts[2];
});
// 创建保存弹窗
const savePopup = document.createElement('div');
savePopup.id = 'save-popup';
savePopup.style.position = 'fixed';
savePopup.style.top = '50%';
savePopup.style.left = '50%';
savePopup.style.transform = 'translate(-50%, -50%)';
savePopup.style.zIndex = '9999';
savePopup.style.backgroundColor = 'white';
savePopup.style.border = '1px solid black';
savePopup.style.padding = '10px';
savePopup.style.borderRadius = '5px';
savePopup.style.display = 'none';
savePopup.style.width = '300px';
document.body.appendChild(savePopup);
// 创建保存弹窗的关闭按钮
const closeSaveButton = document.createElement('button');
closeSaveButton.id = 'close-save-button';
closeSaveButton.innerHTML = '❌';
closeArchiveButton.title = '关闭';
closeSaveButton.style.padding = '10px';
closeSaveButton.style.borderRadius = '5px';
closeSaveButton.style.backgroundColor = 'transparent';
closeSaveButton.style.border = 'none';
closeSaveButton.style.cursor = 'pointer';
closeSaveButton.style.float = 'right';
savePopup.appendChild(closeSaveButton);
// 点击保存弹窗的关闭按钮关闭弹窗
closeSaveButton.addEventListener('click', function() {
savePopup.style.display = 'none';
});
// 添加导入存档按钮到保存弹窗
const importButton1 = document.createElement('button');
importButton1.id = 'import-button1';
importButton1.innerText = '存入存档1';
importButton1.title = '存入存档1';
importButton1.style.padding = '10px';
importButton1.style.borderRadius = '5px';
importButton1.style.backgroundColor = 'lightblue';
importButton1.style.border = 'none';
importButton1.style.cursor = 'pointer';
importButton1.style.fontWeight = 'bold';
importButton1.style.float = 'left';
savePopup.appendChild(importButton1);
const importButton2 = document.createElement('button');
importButton2.id = 'import-button2';
importButton2.innerText = '存入存档2';
importButton2.title = '存入存档2';
importButton2.style.padding = '10px';
importButton2.style.borderRadius = '5px';
importButton2.style.backgroundColor = 'lightblue';
importButton2.style.border = 'none';
importButton2.style.cursor = 'pointer';
importButton2.style.fontWeight = 'bold';
importButton2.style.float = 'left';
savePopup.appendChild(importButton2);
const importButton3 = document.createElement('button');
importButton3.id = 'import-button3';
importButton3.innerText = '存入存档3';
importButton3.title = '存入存档3';
importButton3.style.padding = '10px';
importButton3.style.borderRadius = '5px';
importButton3.style.backgroundColor = 'lightblue';
importButton3.style.border = 'none';
importButton3.style.cursor = 'pointer';
importButton3.style.fontWeight = 'bold';
importButton3.style.float = 'left';
savePopup.appendChild(importButton3);
const MAX_CHUNK_COUNT = 100;
// 存储存档数据的对象
const archiveData = {
archive1: {
text1: '',
text2: '',
text3: ''
},
archive2: {
text1: '',
text2: '',
text3: ''
},
archive3: {
text1: '',
text2: '',
text3: ''
}
};
// 存储历史记录的键名
const HISTORY_KEY = 'archiveHistory';
// 获取存档历史记录
const getArchiveHistory = () => {
const historyStr = localStorage.getItem(HISTORY_KEY) || '[]';
return JSON.parse(historyStr);
};
// 添加存档历史记录
const addArchiveHistory = archiveData => {
const history = getArchiveHistory();
history.push(archiveData);
if (history.length > MAX_CHUNK_COUNT) {
history.shift();
}
localStorage.setItem(HISTORY_KEY, JSON.stringify(history));
};
// 获取最近的存档数据
const getLastArchiveData = () => {
const history = getArchiveHistory();
if (history.length > 0) {
return history[history.length - 1];
} else {
return archiveData;
}
};
// 点击存储按钮将悬浮窗的三个文本框内的文本存储到“存档1”按钮中
importButton1.addEventListener('click', function() {
const archiveData = JSON.parse(localStorage.getItem('archiveData')) || {};
archiveData.archive1 = {
text1: input1.value,
text2: input2.value,
text3: input3.value
};
localStorage.setItem('archiveData', JSON.stringify(archiveData));
input1.value = '';
input2.value = '';
input3.value = '';
savePopup.style.display = 'none';
});
// 点击存储按钮将悬浮窗的三个文本框内的文本存储到“存档2”按钮中
importButton2.addEventListener('click', function() {
const archiveData = JSON.parse(localStorage.getItem('archiveData')) || {};
archiveData.archive2 = {
text1: input1.value,
text2: input2.value,
text3: input3.value
};
localStorage.setItem('archiveData', JSON.stringify(archiveData));
input1.value = '';
input2.value = '';
input3.value = '';
savePopup.style.display = 'none';
});
// 点击存储按钮将悬浮窗的三个文本框内的文本存储到“存档3”按钮中
importButton3.addEventListener('click', function() {
const archiveData = JSON.parse(localStorage.getItem('archiveData')) || {};
archiveData.archive3 = {
text1: input1.value,
text2: input2.value,
text3: input3.value
};
localStorage.setItem('archiveData', JSON.stringify(archiveData));
input1.value = '';
input2.value = '';
input3.value = '';
savePopup.style.display = 'none';
});
// 点击存档1按钮将“存档1”中的文本替换到悬浮窗的三个文本框内
archiveButton1.addEventListener('click', function() {
const archiveData = JSON.parse(localStorage.getItem('archiveData')) || {};
if (archiveData.archive1) {
input1.value = archiveData.archive1.text1;
input2.value = archiveData.archive1.text2;
input3.value = archiveData.archive1.text3;
}
archivePopup.style.display = 'none';
});
// 点击存档2按钮将“存档2”中的文本替换到悬浮窗的三个文本框内
archiveButton2.addEventListener('click', function() {
const archiveData = JSON.parse(localStorage.getItem('archiveData')) || {};
if (archiveData.archive2) {
input1.value = archiveData.archive2.text1;
input2.value = archiveData.archive2.text2;
input3.value = archiveData.archive2.text3;
}
archivePopup.style.display = 'none';
});
// 点击存档3按钮将“存档3”中的文本替换到悬浮窗的三个文本框内
archiveButton3.addEventListener('click', function() {
const archiveData = JSON.parse(localStorage.getItem('archiveData')) || {};
if (archiveData.archive3) {
input1.value = archiveData.archive3.text1;
input2.value = archiveData.archive3.text2;
input3.value = archiveData.archive3.text3;
}
archivePopup.style.display = 'none';
});
let archivePopupVisible = false;
archiveButtonLeft.addEventListener('click', function() {
// 隐藏弹窗并清除之前的位置信息
archivePopup.style.display = 'none';
archivePopup.style.left = '';
archivePopup.style.top = '';
archivePopupVisible = false;
if (!archivePopupVisible) {
// 获取加载存档按钮的位置和尺寸
const buttonRect = archiveButtonLeft.getBoundingClientRect();
const buttonWidth = buttonRect.width;
const buttonHeight = buttonRect.height;
// 计算弹窗的位置
const popupLeft = buttonRect.left + buttonWidth + savePopup.offsetWidth;
const popupTop = buttonRect.top - buttonHeight/2 - archivePopup.offsetHeight/2;
// 设置弹窗的位置
archivePopup.style.left = popupLeft + 'px';
archivePopup.style.top = popupTop + 'px';
// 显示弹窗
archivePopup.style.display = 'block';
archivePopupVisible = true;
}
});
// 定义变量用于存储弹窗是否处于关闭状态
let archivePopupCloseVisible = true;
// 点击加载存档按钮弹出加载存档弹窗
archiveButtonLeft.addEventListener('click', function() {
// 如果弹窗处于关闭状态,则显示弹窗
if (archivePopupCloseVisible) {
// 获取加载存档按钮的位置和尺寸
const buttonRect = archiveButtonLeft.getBoundingClientRect();
const buttonWidth = buttonRect.width;
const buttonHeight = buttonRect.height;
// 计算弹窗的位置
const popupLeft = buttonRect.left + buttonWidth - archivePopup.offsetWidth / 3;
const popupTop = buttonRect.top + archivePopup.offsetHeight / 2.4;
// 设置弹窗的位置
archivePopup.style.left = popupLeft + 'px';
archivePopup.style.top = popupTop + 'px';
// 显示弹窗
archivePopup.style.display = 'block';
// 将弹窗关闭状态设置为 false
archivePopupCloseVisible = false;
} else {
// 如果弹窗处于显示状态,则隐藏弹窗
archivePopup.style.display = 'none';
// 将弹窗关闭状态设置为 true
archivePopupCloseVisible = true;
}
});
let archiveMoved = false; // 记录弹窗是否移动过
// 点击加载存档弹窗的关闭按钮或其他地方关闭弹窗
closeArchiveButton.addEventListener('click', function() {
if (!archiveMoved) { // 如果弹窗没有移动过,才隐藏弹窗
archivePopup.style.display = 'none';
archivePopupVisible = false;
}
archivePopup.addEventListener('click', function(event) {
if (event.target === archivePopup) {
archiveMoved = true; // 记录弹窗移动过
}
});
});
// 定义变量用于存储弹窗是否处于关闭状态
let savePopupCloseVisible = true;
// 点击保存按钮弹出保存弹窗
archiveButtonRight.addEventListener('click', function() {
// 如果弹窗处于关闭状态,则显示弹窗
if (savePopupCloseVisible) {
// 获取保存按钮的位置和尺寸
const buttonRect = archiveButtonRight.getBoundingClientRect();
const buttonWidth = buttonRect.width;
const buttonHeight = buttonRect.height;
// 计算弹窗的位置
const popupLeft = buttonRect.left + buttonWidth + savePopup.offsetWidth;
const popupTop = buttonRect.top + buttonHeight + savePopup.offsetHeight;
// 计算弹窗的最大左边距和上边距,防止弹窗超出浏览器窗口
const maxLeft = window.innerWidth - savePopup.offsetWidth;
const maxTop = window.innerHeight - savePopup.offsetHeight;
// 设置弹窗的位置
savePopup.style.left = Math.min(popupLeft, maxLeft) - savePopup.offsetWidth + 'px';
savePopup.style.top = Math.min(popupTop, maxTop) - savePopup.offsetHeight + 'px';
// 显示弹窗
savePopup.style.display = 'block';
// 将弹窗关闭状态设置为 false
savePopupCloseVisible = false;
} else {
// 如果弹窗处于显示状态,则隐藏弹窗
savePopup.style.display = 'none';
// 将弹窗关闭状态设置为 true
savePopupCloseVisible = true;
}
});
let saveMoved = false; // 记录弹窗是否移动过
// 点击保存弹窗的关闭按钮或其他地方关闭弹窗
closeSaveButton.addEventListener('click', function() {
if (!saveMoved) { // 如果弹窗没有移动过,才隐藏弹窗
savePopup.style.display = 'none';
savePopupCloseVisible = true;
}
});
savePopup.addEventListener('click', function(event) {
if (event.target === savePopup) {
saveMoved = true; // 记录弹窗移动过
}
});
// 创建发送按钮
const button = document.createElement('button');
button.id = 'merge-button';
button.innerText = '发送/Send';
button.title = '合并以上文本进行发送';
button.style.padding = '10px';
button.style.borderRadius = '5px';
button.style.backgroundColor = 'lightblue';
button.style.border = '1px solid black';
button.style.cursor = 'pointer';
button.style.float = 'right';
// 修改发送按钮样式
button.style.border = 'none';
button.style.fontFamily = 'cursive';
button.style.fontWeight = 'bold';
button.style.color = 'black';
floatingDiv.appendChild(button);
// 在悬浮窗左下角添加文本
const helperText = document.createElement('div');
helperText.innerText = 'Role Play Helper';
helperText.title = '这是一个帮助文本,它提供了一些有用的信息来帮助您使用本脚本。\n帮助:将你的鼠标放在每一个你能看到的图标上,仔细查看这些图标的注解。'
helperText.style.position = 'absolute';
helperText.style.bottom = '10px';
helperText.style.left = '10px';
helperText.style.fontFamily = 'cursive';
helperText.style.fontWeight = 'bold';
helperText.style.fontStyle = 'italic';
helperText.style.fontSize = '16px';
floatingDiv.appendChild(helperText);
let isDragging = false;
let isResizing = false;
let offsetX = 0;
let offsetY = 0;
let startX = 0;
let startY = 0;
let startWidth = 0;
let startHeight = 0;
const textInputs = [input1, input2, input3];
const resizeButton = document.createElement('div');
resizeButton.style.position = 'absolute';
resizeButton.style.bottom = '0';
resizeButton.style.right = '0';
resizeButton.style.width = '20px';
resizeButton.style.height = '20px';
resizeButton.style.cursor = 'se-resize';
floatingDiv.appendChild(resizeButton);
floatingDiv.addEventListener('mousedown', handleMouseDown);
floatingDiv.addEventListener('touchstart', handleTouchStart);
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('touchmove', handleTouchMove);
document.addEventListener('mouseup', handleMouseUp);
document.addEventListener('touchend', handleTouchEnd);
resizeButton.addEventListener('mousedown', handleResizeMouseDown);
resizeButton.addEventListener('touchstart', handleResizeTouchStart);
function handleMouseDown(event) {
if (textInputs.includes(event.target)) {
return;
}
isDragging = true;
offsetX = event.clientX - floatingDiv.offsetLeft;
offsetY = event.clientY - floatingDiv.offsetTop;
}
function handleTouchStart(event) {
if (textInputs.includes(event.target)) {
return;
}
if (event.touches.length === 1) {
isDragging = true;
offsetX = event.touches[0].clientX - floatingDiv.offsetLeft;
offsetY = event.touches[0].clientY - floatingDiv.offsetTop;
} else if (event.touches.length === 2) {
isResizing = true;
startX = (event.touches[0].clientX + event.touches[1].clientX) / 2;
startY = (event.touches[0].clientY + event.touches[1].clientY) / 2;
startWidth = floatingDiv.clientWidth;
startHeight = floatingDiv.clientHeight;
}
}
function handleMouseMove(event) {
if (isDragging) {
floatingDiv.style.left = (event.clientX - offsetX) + 'px';
floatingDiv.style.top = (event.clientY - offsetY) + 'px';
}
}
function handleTouchMove(event) {
if (isDragging) {
event.preventDefault(); // 阻止页面滚动
floatingDiv.style.left = (event.touches[0].clientX - offsetX) + 'px';
floatingDiv.style.top = (event.touches[0].clientY - offsetY) + 'px';
} else if (isResizing && event.touches.length === 2) {
const touch1 = event.touches[0];
const touch2 = event.touches[1];
const centerX = (touch1.clientX + touch2.clientX) / 2;
const centerY = (touch1.clientY + touch2.clientY) / 2;
const distance = Math.sqrt((touch1.clientX - touch2.clientX) ** 2 + (touch1.clientY - touch2.clientY) ** 2);
const scale = distance / Math.sqrt((startWidth ** 2) + (startHeight ** 2));
const newWidth = startWidth * scale;
const newHeight = startHeight * scale;
floatingDiv.style.width = newWidth + 'px';
floatingDiv.style.height = newHeight + 'px';
floatingDiv.style.left = (centerX - newWidth / 2) + 'px';
floatingDiv.style.top = (centerY - newHeight / 2) + 'px';
}
}
function handleMouseUp() {
isDragging = false;
}
function handleTouchEnd() {
isDragging = false;
isResizing = false;
}
function handleResizeMouseDown(event) {
event.stopPropagation();
event.preventDefault();
isResizing = true;
startX = event.clientX;
startY = event.clientY;
startWidth = floatingDiv.clientWidth;
startHeight = floatingDiv.clientHeight;
}
function handleResizeTouchStart(event) {
event.stopPropagation();
event.preventDefault();
if (event.touches.length === 2) {
isResizing = true;
const touch1 = event.touches[0];
const touch2 = event.touches[1];
startX = (touch1.clientX + touch2.clientX) / 2;
startY = (touch1.clientY + touch2.clientY) / 2;
startWidth = floatingDiv.clientWidth;
startHeight = floatingDiv.clientHeight;
}
}
function generateOutputArray(selector, num = 0) {
const matchedDivs = document.querySelectorAll(selector);
const results = [];
let startIdx = 0;
if (num > 0) {
startIdx = Math.max(matchedDivs.length - num, 0);
}
matchedDivs.forEach((div, idx) => {
if (idx >= startIdx) {
const hasFlexBetweenChild = div.querySelector('div.flex.justify-between') !== null;
const flexBetweenDiv = div.querySelector('div.flex.justify-between');
const hasChild = flexBetweenDiv && flexBetweenDiv.children.length > 0;
const text = div.textContent.trim();
let role = hasChild ? "assistant" : "user";
results.push({ role, content: text });
}
});
return results;
}
//生成指定限制数量和字数长度的会话数组
function generateOutputArrayWithMaxLength(selector, num = 0, maxLength = Infinity) {
const outputArray = generateOutputArray(selector, num);
let totalLength = 0;
let resultArray = [];
for (let i = outputArray.length - 1; i >= 0; i--) {
const { role, content } = outputArray[i];
totalLength += content.length;
if (totalLength > maxLength || resultArray.length >= num) {
break;
}
resultArray.unshift({ role, content });
}
return resultArray;
}
//格式化会话数组为导出文本
function formatOutputArray(outputArray) {
return outputArray
.map(({ role, content }) => `${role}: ${content}`)
.join('\r\n\r\n----------------\r\n\r\n');
}
//创建一个下载文本
function downloadTextFile(text, filename) {
const blob = new Blob([text], { type: "text/plain;charset=utf-8" });
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = `${filename}.txt`;
a.textContent = `Download ${filename}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
//获取最新的助手消息
function getLatestAssistantMessage(selector) {
const matchedDivs = document.querySelectorAll(selector);
for (let i = matchedDivs.length - 1; i >= 0; i--) {
const div = matchedDivs[i];
const flexBetweenDiv = div.querySelector('div.flex.justify-between');
if (flexBetweenDiv && flexBetweenDiv.children.length > 0) {
const text = div.textContent.trim();
return text;
}
}
return null;
}
// 创建按钮元素
const button2 = document.createElement('button');
button2.innerHTML = '<svg width="24" height="24" viewBox="0 0 24 24"><path fill="#ff69b4" d="M12,21.4c-0.4,0-0.7-0.1-1-0.4C5.5,16.8,2,12.2,2,8.5C2,5.4,4.4,3,7.5,3c2,0,3.8,1.2,4.5,2.9C12.7,4.2,14.5,3,16.5,3C19.6,3,22,5.4,22,8.5c0,3.7-3.5,8.3-9,12.5C12.7,21.2,12.4,21.4,12,21.4z"/></svg>';
button2.title = '复制GPT最新回复到回顾区';
floatingDiv.appendChild(button2);
// 修改按钮样式
button2.style.position = 'absolute';
button2.style.top = '90%';
button2.style.left = '50%';
button2.style.transform = 'translate(-50%, 10px)';
button2.style.border = 'none';
button2.style.background = 'transparent';
button2.style.cursor = 'pointer';
;
// 给按钮添加点击事件监听器
button2.addEventListener('click', function() {
const latestAssistantMessage = getLatestAssistantMessage('div[class*="w-[calc(100%"]');
if (latestAssistantMessage) {
input2.value = latestAssistantMessage;
}
});
button.addEventListener('click', function() {
const inputText1 = input1.value.trim(); // 获取第一个输入框中的文本内容
const inputText2 = input2.value.trim(); // 获取第二个输入框中的文本内容
const inputText3 = input3.value.trim(); // 获取第三个输入框中的文本内容
let intermediateText = ''; // 用于存储中转文本内容
let mergedTextoutput = ''; // 用于存储合并后的文本内容
// 将第一个输入框的文本内容存储在 intermediateText 中
if (inputText1 !== '') {
intermediateText += inputText1;
}
// 将中转变量中的 {2} 替换为第二个输入框中的文本,将中转变量中的 {3} 替换为第三个输入框中的文本,并存储在 mergedTextoutput 中
mergedTextoutput = intermediateText.replace(/\{IN回顾区\}/g, inputText2).replace(/\{IN交互区\}/g, inputText3);
// 将 mergedTextoutput 中的 '\r\n' 替换为 '\n'
// 将 mergedTextoutput 中的 '\r\n' 替换为 '\n'
mergedTextoutput = mergedTextoutput.replace(/\r\n/g, '\n');
// 移除 mergedTextoutput 中多余的换行符
mergedTextoutput = mergedTextoutput.replace(/\n{3,}/g, '\n\n');
// 输出合并后的文本内容
console.log(mergedTextoutput);
// 将合并后的文本输入到页面中符合特定CSS类别的文本框中,并触发提交按钮
const textareas = document.querySelectorAll('[class*="m-"][class*="w-full"][class*="resize-none"][class*="border-0"][class*="bg-transparent"][class*="p-"][class*="pl-"][class*="pr-"][class*="focus:ring-0"][class*="focus-visible:ring-0"][class*="dark:bg-transparent"][class*="md:pl-"]');
if (textareas.length > 0) {
textareas[0].value = mergedTextoutput;
// 触发提交按钮
const button = document.querySelector('[class*="absolute"][class*="rounded-md"][class*="bottom-"][class*="right-"][class*="disabled"]');
if (button) {
button.disabled = false; // 尝试解除按钮禁用状态
button.click();
}
}
});
// 定义变量用于存储长按后移动的状态
let isDragging2 = false;
let initialX;
let initialY;
let currentX;
let currentY;
let xOffset = 0;
let yOffset = 0;
// 添加长按事件
document.addEventListener('mousedown', dragStart);
document.addEventListener('mouseup', dragEnd);
document.addEventListener('mousemove', drag);
// 定义长按事件的处理函数
function dragStart(e) {
if (e.target === floatingButton) {
initialX = e.clientX - xOffset;
initialY = e.clientY - yOffset;
isDragging2 = true;
} else {
isDragging2 = false;
}
}
function dragEnd(e) {
initialX = currentX;
initialY = currentY;
isDragging2 = false;
// 拖动结束后,将按钮的 transition 属性重新设置为默认值
floatingButton.style.transition = "transform 0.2s ease-out";
}
function drag(e) {
if (isDragging2) {
e.preventDefault();
currentX = e.clientX - initialX;
currentY = e.clientY - initialY;
xOffset = currentX;
yOffset = currentY;
setTranslate(currentX, currentY, floatingButton);
}
}
// 定义变量用于存储按住后移动的状态
let archiveIsDragging = false;
let initialArchiveX;
let initialArchiveY;
let currentArchiveX;
let currentArchiveY;
let archiveXOffset = 0;
let archiveYOffset = 0;
// 添加按住事件
archivePopup.addEventListener('mousedown', archiveDragStart);
archivePopup.addEventListener('mouseup', archiveDragEnd);
document.addEventListener('mousemove', archiveDrag);
// 定义按住事件的处理函数
function archiveDragStart(e) {
if (e.target === archivePopup) {
initialArchiveX = e.clientX - archiveXOffset;
initialArchiveY = e.clientY - archiveYOffset;
archiveIsDragging = true;
// 防止拖动事件冒泡到文档
e.stopPropagation();
} else {
archiveIsDragging = false;
}
}
function archiveDragEnd(e) {
initialArchiveX = currentArchiveX;
initialArchiveY = currentArchiveY;
archiveIsDragging = false;
// 防止拖动事件冒泡到文档
e.stopPropagation();
}
function archiveDrag(e) {
if (archiveIsDragging) {
e.preventDefault();
currentArchiveX = e.clientX - initialArchiveX;
currentArchiveY = e.clientY - initialArchiveY;
archiveXOffset = currentArchiveX;
archiveYOffset = currentArchiveY;
setArchiveTranslate(currentArchiveX, currentArchiveY, archivePopup);
}
}
// 定义用于移动弹窗的函数
function setArchiveTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
// 定义用于移动按钮的函数
function setTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
el.style.transition = "none"; // 防止拖动时产生动画效果
}
// 定义变量用于存储按住后移动的状态
let saveIsDragging = false;
let initialSaveX;
let initialSaveY;
let currentSaveX;
let currentSaveY;
let saveXOffset = 0;
let saveYOffset = 0;
// 添加按住事件
savePopup.addEventListener('mousedown', saveDragStart);
savePopup.addEventListener('mouseup', saveDragEnd);
document.addEventListener('mousemove', saveDrag);
// 定义按住事件的处理函数
function saveDragStart(e) {
if (e.target === savePopup) {
initialSaveX = e.clientX - saveXOffset;
initialSaveY = e.clientY - saveYOffset;
saveIsDragging = true;
// 防止拖动事件冒泡到文档
e.stopPropagation();
} else {
saveIsDragging = false;
}
}
function saveDragEnd(e) {
initialSaveX = currentSaveX;
initialSaveY = currentSaveY;
saveIsDragging = false;
// 防止拖动事件冒泡到文档
e.stopPropagation();
}
function saveDrag(e) {
if (saveIsDragging) {
e.preventDefault();
currentSaveX = e.clientX - initialSaveX;
currentSaveY = e.clientY - initialSaveY;
saveXOffset = currentSaveX;
saveYOffset = currentSaveY;
setSaveTranslate(currentSaveX, currentSaveY, savePopup);
}
}
// 定义用于移动弹窗的函数
function setSaveTranslate(xPos, yPos, el) {
el.style.transform = "translate3d(" + xPos + "px, " + yPos + "px, 0)";
}
floatingButton.addEventListener('click', function() {
if (floatingDiv.style.display === 'none') {
// 展开悬浮窗
floatingDiv.style.display = 'block';
floatingButton.innerText = '收起/Fold';
// 移除之前的拖动事件监听器
document.removeEventListener('mousedown', dragStart);
document.removeEventListener('mouseup', dragEnd);
document.removeEventListener('mousemove', drag);
} else {
// 收起悬浮窗
floatingDiv.style.display = 'none';
floatingButton.innerText = '展开/Unfold';
// 重新添加拖动事件监听器
document.addEventListener('mousedown', dragStart);
document.addEventListener('mouseup', dragEnd);
document.addEventListener('mousemove', drag);
}}
);})();