Chatgpt 角色扮演助手/Chatgpt Role Play Helper

在屏幕中央弹出一个可拖动位置的悬浮窗,悬浮窗内有三个文本框并且可以编辑,以及一个按钮,点击按钮后将这三个文本框的内容合并,并将合并后的文本输入到页面中符合特定CSS类别的文本框中,最后触发符合特定CSS类别的提交按钮以提交表单。

目前為 2023-03-30 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Chatgpt 角色扮演助手/Chatgpt Role Play Helper
// @namespace    http://tampermonkey.net/
// @version      2.6
// @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 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);

    // 创建悬浮窗和文本框
    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%)';
    floatingDiv.style.zIndex = '9999';
    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 = '&#10060;';
    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 = '&#10060;';
    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;
        }
    });
    // 点击加载存档按钮弹出加载存档弹窗
    archiveButtonLeft.addEventListener('click', function() {
        // 获取加载存档按钮的位置和尺寸
        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';
    });

    // 点击加载存档弹窗的关闭按钮或其他地方关闭弹窗
    closeArchiveButton.addEventListener('click', function() {
        archivePopup.style.display = 'none';
        archivePopupVisible = false;
    });

    archivePopup.addEventListener('click', function(event) {
        if (event.target === archivePopup) {
            archivePopup.style.display = 'none';
            archivePopupVisible = false;
        }
    });

    // 点击保存按钮弹出保存弹窗
    let savePopupVisible = false;
    archiveButtonRight.addEventListener('click', function() {
        // 获取保存按钮的位置和尺寸
        const savebuttonRect = archiveButtonRight.getBoundingClientRect();
        const savebuttonWidth = savebuttonRect.width;
        const savebuttonHeight = savebuttonRect.height;

        // 计算弹窗的位置
        const savepopupLeft = savebuttonRect.left + savebuttonWidth + savePopup.offsetWidth;
        const savepopupTop = savebuttonRect.top + savebuttonHeight + savePopup.offsetHeight;

        // 计算弹窗的最大左边距和上边距,防止弹窗超出浏览器窗口
        const maxLeft = window.innerWidth - savePopup.offsetWidth;
        const maxTop = window.innerHeight - savePopup.offsetHeight;

        // 设置弹窗的位置
        savePopup.style.left = Math.min(savepopupLeft, maxLeft) - savePopup.offsetWidth + 'px';
        savePopup.style.top = Math.min(savepopupTop, maxTop) - savePopup.offsetHeight + 'px';

        // 显示弹窗
        savePopup.style.display = 'block';
    });


    // 点击保存弹窗的关闭按钮或其他地方关闭弹窗
    closeSaveButton.addEventListener('click', function() {
        savePopup.style.display = 'none';
        savePopupVisible = false;
    });

    savePopup.addEventListener('click', function(event) {
        if (event.target === savePopup) {
            savePopup.style.display = 'none';
            savePopupVisible = false;
        }
    });


    // 创建发送按钮
    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;
        event.preventDefault(); // 阻止浏览器默认下拉刷新行为
    } 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) {
        if (event.cancelable) {
            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();
    }
}

    });

    // 点击悬浮按钮展开悬浮窗
    floatingButton.addEventListener('click', function() {
        if (floatingDiv.style.display === 'none') {
            floatingDiv.style.display = 'block';
            floatingButton.innerText = '收起/Fold';
        } else {
            floatingDiv.style.display = 'none';
            floatingButton.innerText = '展开/Unfold';

        }
    });})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址