c.ai X Swipes

A toggleable panel with the swipes of the current turn

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

    // ==UserScript==
        // @name         c.ai X Swipes
        // @namespace    c.ai X Swipes
        // @version      2.1
        // @description  A toggleable panel with the swipes of the current turn
        // @author       Vishanka via chatGPT
        // @license      MIT
        // @match        https://*.character.ai/chat*
        // @icon         https://i.imgur.com/iH2r80g.png
        // @grant        none
        // ==/UserScript==



    (function() {
        'use strict';

        var original_prototype_open = XMLHttpRequest.prototype.open;
        const intercepted_data_object_swipes = {};

        XMLHttpRequest.prototype.open = function(method, url, async) {
            if (
                url.startsWith('https://plus.character.ai/chat/history/continue/') ||
                url.startsWith('https://plus.character.ai/chat/character/info') ||
                url.startsWith('https://beta.character.ai/chat/history/continue/') ||
                url.startsWith('https://beta.character.ai/chat/character/info')
            ) {
                this.addEventListener('load', function() {
                    let info1_swipes = JSON.parse(this.responseText);
                    intercepted_data_object_swipes.external_id = info1_swipes.character.external_id;
                    intercepted_data_object_swipes.name = info1_swipes.character.name;
                    console.log("character_id:",intercepted_data_object_swipes.external_id);

                    // Only create the toggle button and the panel once
                    if (!document.getElementById('NeoPanelSwipes')) {
                        createToggleButton_NeoPanelSwipes();
                        createNeoPanelSwipes();
                    }
                });

            } else if (url.startsWith(`https://neo.character.ai/chats/recent/${intercepted_data_object_swipes.external_id}`)) {
                this.addEventListener('load', function() {
                    let info2_swipes = JSON.parse(this.responseText);
                    intercepted_data_object_swipes.chat_id = info2_swipes.chats[0].chat_id;
                    console.log("chat_id:",intercepted_data_object_swipes.chat_id);
                });
            } else if (url.startsWith(`https://neo.character.ai/turns/${intercepted_data_object_swipes.chat_id}`)) {
                this.addEventListener('load', function() {
                    let info3_swipes = JSON.parse(this.responseText);
                    intercepted_data_object_swipes.turn_id = info3_swipes.turns[0].turn_key.turn_id;
                    intercepted_data_object_swipes.total_turns = info3_swipes.turns.length;
                    console.log("turn_id:",intercepted_data_object_swipes.turn_id);
                    console.log("total_turns:", intercepted_data_object_swipes.total_turns);
                    // Extract data from the last turn_id if there are turns
                    if (intercepted_data_object_swipes.total_turns > 0) {
                      const lastTurnIndex = intercepted_data_object_swipes.total_turns - 1;
                      const lastTurnData = info3_swipes.turns[lastTurnIndex];
                      intercepted_data_object_swipes.lastTurnId = lastTurnData.turn_key.turn_id; // Store lastTurnId in intercepted_data_object_swipes
                      console.log("Last turn_id:", intercepted_data_object_swipes.lastTurnId);
                    }
                    // Extract candidates for "turn 0", used for fetching limit
            if (intercepted_data_object_swipes.total_turns > 0) {
                const firstTurnData = info3_swipes.turns[0];
                intercepted_data_object_swipes.candidatesForTurn0 = firstTurnData.candidates;
                intercepted_data_object_swipes.numberOfCandidatesForTurn0 = intercepted_data_object_swipes.candidatesForTurn0.length;
                console.log("Number of candidates for turn 0:", intercepted_data_object_swipes.numberOfCandidatesForTurn0);

                intercepted_data_object_swipes.rawContents = intercepted_data_object_swipes.candidatesForTurn0.map(candidate => candidate.raw_content);
                console.log("Raw contents of candidates for turn 0:", intercepted_data_object_swipes.rawContents);


    // All Styles and Functions of the List Elements

    const swipes = document.createElement('div');
    swipes.style.textAlign = 'left';
    swipes.style.marginTop = '15px';
    swipes.style.overflowWrap = 'break-word';
    swipes.style.whiteSpace = 'pre-wrap';
    swipes.style.maxHeight = 'calc(100% - 12px)'; // Adjust the value as needed
    swipes.style.overflowY = 'auto';
swipes.style.marginLeft = '-10px';
swipes.style.scrollbarWidth = '1px';
//swipes.style.scrollbarColor = 'transparent transparent';

    if (intercepted_data_object_swipes.rawContents) {
        intercepted_data_object_swipes.rawContents.forEach((content, index) => {
            const contentContainer = document.createElement('div'); // Create a container for each content
            contentContainer.style.display = 'flex'; // Use flex layout
            contentContainer.style.alignItems = 'center'; // Center-align vertically
            contentContainer.style.marginBottom = '15px'; // Add some spacing between elements
            contentContainer.style.marginTop = '15px';
            contentContainer.style.direction = 'ltr';
            let isGreen = false; // Flag to track the background color state

            const candidateNumber = index + 1; // Adding 1 to index to make it 1-based
            const numberElement = document.createElement('span'); // Create element for candidate number
            numberElement.textContent = `${candidateNumber}.`;
            numberElement.style.marginRight = '15px'; // Add spacing between number and text
            numberElement.style.direction = 'ltr';
            numberElement.style.marginLeft = '10px';
    //        numberElement.style.alignItems = 'center';
            numberElement.style.marginBottom = '15px'

            const contentElement = document.createElement('div'); // Create element for content
            contentElement.innerHTML = content;
            contentElement.style.direction = 'ltr';
            contentElement.style.color = '#878788';

    // Changes the color of the text
    const formattedContent = content.replace(/(["“”«»].*?["“”«»])/g, '<span style="color: #FFFFFF">$1</span>');
    const finalContent1 = formattedContent.replace(/\*\*(.*?)\*\*/g, '<span style="font-weight: bold;">$1</span>');
    const finalContent = finalContent1.replace(/\*(.*?)\*/g, '<span style="font-style: italic; color: #E0DF7F;">$1</span>');
    // Use regular expressions to find text within backticks and apply formatting
    const formattedBackticks_3 = finalContent.replace(/```([^`]*)```/g, '<div style="display: inline-block; margin: 0px 10px; vertical-align: middle;"><div style="color: white; font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; font-size: 13px; tab-size: 4; hyphens: none; padding: 5px; margin: 0px; overflow: auto; background: rgb(1, 22, 39);"><code style="color: rgb(214, 222, 235); font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, monospace; text-align: left; white-space: pre-wrap; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; font-size: 1em; tab-size: 4; hyphens: none;">$1</code></div></div>');
    const formattedBackticks_tilde = formattedBackticks_3.replace(/~~~([^`]*)~~~/g, '<div style="display: inline-block; margin: 0px 10px; vertical-align: middle;"><div style="color: white; font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; font-size: 13px; tab-size: 4; hyphens: none; padding: 5px; margin: 0px; overflow: auto; background: rgb(1, 22, 39);"><code style="color: rgb(214, 222, 235); font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, monospace; text-align: left; white-space: pre-wrap; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; font-size: 1em; tab-size: 4; hyphens: none;">$1</code></div></div>');

    const formattedBackticks_1 = formattedBackticks_tilde.replace(/`(?!`)(.*?)`/g, '<div style="display: inline-block; margin: 0px 10px; vertical-align: middle;"><div style="color: white; font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, monospace; text-align: left; white-space: pre; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; font-size: 13px; tab-size: 4; hyphens: none; padding: 5px; margin: 0px; overflow: auto; background: rgb(1, 22, 39);"><code style="color: rgb(214, 222, 235); font-family: Consolas, Monaco, &quot;Andale Mono&quot;, &quot;Ubuntu Mono&quot;, monospace; text-align: left; white-space: pre-wrap; word-spacing: normal; word-break: normal; overflow-wrap: normal; line-height: 1.5; font-size: 1em; tab-size: 4; hyphens: none;">$1</code></div></div>');

    // Set the final formatted content as innerHTML
    contentElement.innerHTML = formattedBackticks_1;

            contentContainer.appendChild(numberElement); // Append number element to container
            contentContainer.appendChild(contentElement); // Append content element to container


            contentContainer.addEventListener('click', (event) => {
                if (event.button === 0) {
                    simulateArrowKeyPress(candidateNumber); // Call the function to simulate arrow key press
                }
            });

            contentContainer.addEventListener('dblclick', (event) => {
                if (isGreen) {
                    contentContainer.style.backgroundColor = ''; // Reset to default color
                } else {
                    contentContainer.style.backgroundColor = 'green';
                }
                isGreen = !isGreen; // Toggle the flag
            });

            swipes.appendChild(contentContainer); // Append the container to the swipes

            // Add a horizontal line after each content (except for the last one)
            if (index < intercepted_data_object_swipes.rawContents.length - 1) {
                const divider = document.createElement('hr');
                swipes.appendChild(divider);
            }
        });
    } else {
        const errorMessage = document.createElement('p');
        errorMessage.textContent = "No raw contents available.";
        swipes.appendChild(errorMessage);
    }

    // Function to simulate ArrowRight key press
    function simulateArrowKeyPress(steps) {
        for (let step = 0; step < 50; step++) {
            ArrowLeftKeyDown();
        }
        for (let step = 0; step < steps - 1; step++) {
            ArrowRightKeyDown();
        }
    }

    // Your existing ArrowRightKeyDown function
    function ArrowRightKeyDown() {
        document.body.dispatchEvent(
            new KeyboardEvent('keydown', {
                bubbles: true,
                key: 'ArrowRight',
            })
        );
        console.log("Arrow right pressed");
    }

    // Your existing ArrowLeftKeyDown function
    function ArrowLeftKeyDown() {
        document.body.dispatchEvent(
            new KeyboardEvent('keydown', {
                bubbles: true,
                key: 'ArrowLeft',
            })
        );
        console.log("Arrow left pressed");
    }

    NeoPanelSwipes.appendChild(swipes);



    }



                    XHR_interception_resolve(intercepted_data_object_swipes);
                });
            }
            original_prototype_open.apply(this, [method, url, async]);
        };

        let XHR_interception_resolve;
        const XHR_interception_promise = new Promise(function(resolve, reject) {
            XHR_interception_resolve = resolve;
        });

        XHR_interception_promise.then(function() {
            console.log("Intercepted Data:", intercepted_data_object_swipes);

        });



function createToggleButton_NeoPanelSwipes() {
    const toggleButton_NeoPanelSwipes = document.createElement('button');
    toggleButton_NeoPanelSwipes.innerHTML = '<svg transform="rotate(90)" stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16" height="22" width="22" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M11.854 3.646a.5.5 0 0 1 0 .708L8.207 8l3.647 3.646a.5.5 0 0 1-.708.708l-4-4a.5.5 0 0 1 0-.708l4-4a.5.5 0 0 1 .708 0zM4.5 1a.5.5 0 0 0-.5.5v13a.5.5 0 0 0 1 0v-13a.5.5 0 0 0-.5-.5z"></path></svg>';
    toggleButton_NeoPanelSwipes.style.position = 'fixed';
    toggleButton_NeoPanelSwipes.style.bottom = '0px';
    toggleButton_NeoPanelSwipes.style.right = '3px';
    toggleButton_NeoPanelSwipes.style.backgroundColor = 'none';
    toggleButton_NeoPanelSwipes.style.color = 'white';
    toggleButton_NeoPanelSwipes.style.fontWeight = 'bold';
    toggleButton_NeoPanelSwipes.style.padding = '0px';
    toggleButton_NeoPanelSwipes.style.margin = '0px';
//    toggleButton_NeoPanelSwipes.style.width = '5%';
    toggleButton_NeoPanelSwipes.style.border = 'none';
    toggleButton_NeoPanelSwipes.style.borderRadius = '0px';
    toggleButton_NeoPanelSwipes.style.cursor = 'pointer';
    toggleButton_NeoPanelSwipes.style.userSelect = 'none';
    toggleButton_NeoPanelSwipes.style.zIndex = '101';
    toggleButton_NeoPanelSwipes.addEventListener('click', toggleNeoPanelSwipes);

    document.body.appendChild(toggleButton_NeoPanelSwipes);
}
    function createNeoPanelSwipes() {



    const NeoPanelSwipes = document.createElement('div');
    NeoPanelSwipes.id = 'NeoPanelSwipes';
    NeoPanelSwipes.style.position = 'fixed';
    NeoPanelSwipes.style.bottom = '0px';
    NeoPanelSwipes.style.right = '0%';
    NeoPanelSwipes.style.width = '351px';
    NeoPanelSwipes.style.height = '100%';
    NeoPanelSwipes.style.backgroundColor = '#18181B';
    NeoPanelSwipes.style.borderLeft = '0.3px solid #3F3F46';
    NeoPanelSwipes.style.padding = '10px';
    NeoPanelSwipes.style.zIndex = '100';
    NeoPanelSwipes.style.resize = 'horizontal';
    NeoPanelSwipes.style.direction = 'rtl';
    NeoPanelSwipes.style.overflowX = 'auto';
    NeoPanelSwipes.style.overflowY = 'hidden';
    NeoPanelSwipes.style.display = 'none';
    NeoPanelSwipes.style.paddingBottom = '70px';

const otherElement = document.querySelector('.w-\\[786px\\]');

if (otherElement) {
  // Get the computed style of the other element
  const otherElementStyle = window.getComputedStyle(otherElement);

  // Extract the width property from the computed style
  const otherElementWidth = parseFloat(otherElementStyle.width);

  // Set the width of NeoPanelSwipes to match the other element
  NeoPanelSwipes.style.width = otherElementWidth/1.734 + 'px';
}


        // Add Swipes header to the panel
        const swipes_headline = document.createElement('h5');
        swipes_headline.textContent = 'Swipes';
        swipes_headline.style.marginTop = '5px';
        swipes_headline.style.marginBottom = '15px';
        swipes_headline.style.textAlign = 'center';
        NeoPanelSwipes.appendChild(swipes_headline);

        // Add a horizontal line (divider)
        const divider_swipes1 = document.createElement('hr');
        NeoPanelSwipes.appendChild(divider_swipes1);

        document.body.appendChild(NeoPanelSwipes);
    }

     function toggleNeoPanelSwipes() {
        const NeoPanelSwipes = document.getElementById('NeoPanelSwipes');
        if (NeoPanelSwipes.style.display === 'block') {
            NeoPanelSwipes.style.display = 'none';
        } else {
            NeoPanelSwipes.style.display = 'block';
        }
    }

    })();