ChatGPT reduce react repaint duration

Reduces react repaint duration by limiting initial number of messages

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         ChatGPT reduce react repaint duration
// @namespace    https://chatgpt.com/
// @version      1.0.0
// @description  Reduces react repaint duration by limiting initial number of messages
// @author       SadSalmonTwT
// @license MIT
// @match        https://chatgpt.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=chatgpt.com
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
  'use strict';

  // defines the number of initially loaded messages
  const INIT_NUMBER_OF_MESSAGES = 10;

  // request for chat history regex
  const conversationRegex = /^https:\/\/chatgpt\.com\/backend-api\/conversation\/[a-f0-9\-]{36}$/i;

  const originalFetch = window.fetch;

  // intercept the fetch api
  window.fetch = function (input, init) {
    const url = (typeof input === 'string')
      ? input
      : input.url;

    // process all other requests like normal, but intercept all chat history request responses
    if (!conversationRegex.test(url))
      return originalFetch.apply(this, arguments);
    else
      return originalFetch.apply(this, arguments)
        .then(async (response) => {
          try {
            const data = await response.clone().json();

            const newMapping = [];

            let nextId = data.current_node;
            let numberOfVisibleMessages = 0;

            // traverse tree upwards from the last message leaf towards root
            while (true) {
              const _message = data.mapping[nextId];

              // if root is encountered early, add it and stop loop
              if (_message.id === "client-created-root") {
                newMapping.push(_message);
                break;
              }

              // traverse downwards tree towards child leaves (these messages are hidden)
              const _childIds = [..._message.children];
              while (_childIds.length !== 0) {
                const _childId = _childIds.pop();
                const _child = data.mapping[_childId];

                // if newMapping already contains child, skip it
                if (newMapping.some(_preservedMessage => _preservedMessage.id === _childId))
                  continue;

                // add hidden message
                newMapping.push(_child);

                // add children's children
                _childIds.push(..._child.children);
              }

              // add message itself (if the number was not reached AND its parent exists AND it's not root)
              if (
                numberOfVisibleMessages < INIT_NUMBER_OF_MESSAGES
                && data.mapping[_message.parent]
              )
                newMapping.push(_message);
              else {
                // add "first" message and link it to root
                newMapping.push({
                  ..._message,
                  parent: "client-created-root"
                });

                // add chat root
                newMapping.push({
                  id: "client-created-root",
                  message: null,
                  parent: null,
                  children: [_message.id]
                });

                // stop loop
                break;
              }

              nextId = _message.parent;
              numberOfVisibleMessages++;
            }

            // if new mappings have same length as old ones, return original response
            if (newMapping.length === Object.keys(data.mapping).length) {
              console.log("ChatGPT_reduce_react_repaint_duration: Response was left unchanged");
              return response;
            }

            // update chat history
            data.mapping = Object.fromEntries(
              newMapping.map(_message => ([
                _message.id,
                _message
              ]))
            );

            console.log('ChatGPT_reduce_react_repaint_duration: Response was trimmed', data);

            // return response with modified data
            return new Response(JSON.stringify(data), {
              status: response.status,
              statusText: response.statusText,
              headers: response.headers
            });
          } catch (err) {
            console.error('ChatGPT_reduce_react_repaint_duration: An error has occurred', err);
            return response;
          }
        });
  };
})();