// ==UserScript== // @name Chatgpt 角色扮演助手/Chatgpt Role Play Helper // @namespace http://tampermonkey.net/ // @version 2.7.0.7 // @description 在屏幕中央弹出一个可拖动位置的悬浮窗,悬浮窗内有三个文本框并且可以编辑,以及一个按钮,点击按钮后将这三个文本框的内容合并,并将合并后的文本输入到页面中符合特定CSS类别的文本框中,最后触发符合特定CSS类别的提交按钮以提交表单。 // @author Chatgpt (most)and 环白 // @match https://chat.openai.com/* // @icon https://chat.openai.com/favicon.ico // @grant GM_addStyle // @downloadURL none // ==/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); function mobileDrag(event) { // 阻止浏览器默认行为 event.preventDefault(); // 移动悬浮按钮 floatingButton.style.left = event.touches[0].clientX - offsetX + 'px'; floatingButton.style.top = event.touches[0].clientY - offsetY + 'px'; } // 创建悬浮按钮 const floatingButton = document.createElement('button'); floatingButton.id = 'floating-button'; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // 如果是手机设备,修改按钮样式和文字 floatingButton.innerText = 'ON'; floatingButton.style.backgroundColor = 'rgba(255, 165, 0, 0.8)'; floatingButton.style.borderRadius = '30px'; floatingButton.style.width = '50px'; floatingButton.style.height = '50px'; floatingButton.style.fontSize = '16px'; floatingButton.style.lineHeight = '50px'; floatingButton.style.textAlign = 'center'; floatingButton.style.color = 'white'; floatingButton.style.boxShadow = '2px 2px 3px gray'; floatingButton.style.position = 'fixed'; floatingButton.style.right = '20px'; floatingButton.style.top = '45%'; // 添加触摸事件监听器 floatingButton.addEventListener('touchstart', function(event) { // 计算偏移量 offsetX = event.touches[0].clientX - floatingButton.offsetLeft; offsetY = event.touches[0].clientY - floatingButton.offsetTop; // 添加拖动事件监听器 floatingButton.addEventListener('touchmove', mobileDrag); }); floatingButton.addEventListener('touchend', function() { // 移除拖动事件监听器 floatingButton.removeEventListener('touchmove', mobileDrag); });} else { // 如果是电脑设备,保留原来的样式和文字 floatingButton.innerText = '展开/Unfold'; floatingButton.style.backgroundColor = 'lightblue'; floatingButton.style.padding = '10px'; floatingButton.style.borderRadius = '5px'; floatingButton.style.border = 'none'; floatingButton.style.cursor = 'pointer'; floatingButton.style.fontWeight = 'bold'; floatingButton.style.position = 'fixed'; floatingButton.style.right = '50px'; floatingButton.style.bottom = '50px'; } floatingButton.title = '角色扮演助手/Role Play Helper'; 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 = '编写区:{review}=回顾区内容;{dialog}=交互区内容'; 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'; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // 如果是手机设备,修改悬浮窗样式 floatingDiv.style.top = 'calc(20% + 50px)'; const screenHeight = window.innerHeight; floatingDiv.style.height = `${screenHeight*0.6}px`; floatingDiv.style.left = 'calc(100% - 250px)'; floatingDiv.style.width = `${screenHeight*0.4}px`;; input1.style.width = '100%'; input1.style.height = '15%'; input1.style.fontSize = 'calc(16px + 0.5vw)'; // 设置 input2 样式 input2.style.width = '100%'; input2.style.height = 'calc(15%)'; input2.style.marginTop = '0px'; input2.style.fontSize = 'calc(16px + 0.5vw)'; // 设置 input3 样式 input3.style.width = '100%'; input3.style.height = 'calc(15%)'; input3.style.marginTop = '0px'; input3.style.fontSize = 'calc(16px + 0.5vw)'; const sizeRatio = 0.02; // 根据实际情况调整比例 input1.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; input2.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; input3.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; label1.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; label2.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; label3.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; } if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // 如果是手机设备,修改空白div高度 const spaceRatio = 0.10; spaceDiv.style.height = `${spaceRatio * parseInt(floatingDiv.style.height)}px`; } // 创建加载存档按钮和保存存档按钮 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 = 'none'; archiveButtonLeft.style.fontFamily = 'cursive'; archiveButtonLeft.style.fontWeight = 'bold'; archiveButtonLeft.style.color = '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 = 'none'; archiveButtonRight.style.fontFamily = 'cursive'; archiveButtonRight.style.fontWeight = 'bold'; archiveButtonRight.style.color = 'black'; archiveButtonRight.style.cursor = 'pointer'; archiveButtonRight.style.float = 'right'; floatingDiv.insertBefore(archiveButtonRight, spaceDiv); // 在手机上将存档按钮的大小随着悬浮窗大小等比例变化 if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { const sizeRatio = 0.02; archiveButtonLeft.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; archiveButtonLeft.style.padding = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; archiveButtonRight.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; archiveButtonRight.style.padding = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; } // 创建加载存档弹窗 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); if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // 如果是手机设备,修改按钮和帮助文本字体大小 const sizeRatio = 0.02; // 根据实际情况调整比例 button.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; helperText.style.fontSize = `${sizeRatio * parseInt(floatingDiv.style.height)}px`; } 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'; // 重新计算悬浮窗高度 const windowHeight = window.innerHeight; const floatingDivHeight = parseInt(floatingDiv.style.height); const newHeight = Math.min(floatingDivHeight, windowHeight - event.clientY + offsetY); floatingDiv.style.height = newHeight + '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)); } else if (event.touches.length === 2) { // 两指放大 const touch1 = event.touches[0]; const touch2 = event.touches[1]; const distance = Math.sqrt((touch1.clientX - touch2.clientX) ** 2 + (touch1.clientY - touch2.clientY) ** 2); const centerX = (touch1.clientX + touch2.clientX) / 2; const centerY = (touch1.clientY + touch2.clientY) / 2; const scale = distance / Math.sqrt((floatingDiv.clientWidth ** 2) + (floatingDiv.clientHeight ** 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 = ''; button2.title = '复制GPT最新回复到回顾区'; floatingDiv.appendChild(button2); // 修改按钮样式 button2.style.position = 'absolute'; button2.style.left = '50%'; button2.style.transform = 'translate(-50%, 10px)'; button2.style.border = 'none'; button2.style.background = 'transparent'; button2.style.cursor = 'pointer'; button2.style.bottom = `${0.1 * parseInt(floatingDiv.style.height)}px`; ; // 给按钮添加点击事件监听器 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回顾区\}|{review}|{\回顾区\}|{\回顾\}|{\回\}|{R}|{r}|{H}|{h}/g, inputText2).replace(/{\IN交互区\}|{dialog}|{\交互区\}|{\交互\}|{\交\}|{D}|{d}|{J}|{j}/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'; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // 如果是手机设备,修改按钮文字 floatingButton.innerText = 'OFF'; } else { // 如果是电脑设备,保留原来的文字 floatingButton.innerText = '收起/Fold'; } // 移除之前的拖动事件监听器 document.removeEventListener('mousedown', dragStart); document.removeEventListener('mouseup', dragEnd); document.removeEventListener('mousemove', drag); } else { // 收起悬浮窗 floatingDiv.style.display = 'none'; if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { // 如果是手机设备,修改按钮文字 floatingButton.innerText = 'ON'; } else { // 如果是电脑设备,保留原来的文字 floatingButton.innerText = '展开/Unfold'; } // 重新添加拖动事件监听器 document.addEventListener('mousedown', dragStart); document.addEventListener('mouseup', dragEnd); document.addEventListener('mousemove', drag); }} );})();