Middle Text Ellipsis for ChatGPT

Adds a button to trim the middle part of text inside `.text-message` elements on chatgpt.com.

  1. // ==UserScript==
  2. // @name Middle Text Ellipsis for ChatGPT
  3. // @namespace http://yournamespace.example.com
  4. // @version 1.1
  5. // @description Adds a button to trim the middle part of text inside `.text-message` elements on chatgpt.com.
  6. // @match *://*.chatgpt.com/*
  7. // @license MIT
  8. // @locale en
  9. // ==/UserScript==
  10.  
  11. (function() {
  12. 'use strict';
  13.  
  14. // Function to trim the middle of a string, keeping the first and last parts with an ellipsis in the middle
  15. const trimMiddle = (text, startChars, endChars) => {
  16. if (text.length <= startChars + endChars) return text;
  17. return `${text.slice(0, startChars)}...${text.slice(-endChars)}`;
  18. };
  19.  
  20. // Function to apply the trimming logic to all eligible `.text-message` elements
  21. const applyTrim = () => {
  22. const textMessages = document.querySelectorAll(".text-message");
  23. // console.log("Applying trim to text messages:", textMessages);
  24. Array.from(textMessages).map((messageElement, index, allMessages) => {
  25. // Only apply the function to the last fifth of the elements
  26. if (index > allMessages.length * 0.8) return;
  27.  
  28. // Traverse through specific child elements of the current '.text-message' element
  29. Array.from(messageElement.children[0].children[0]?.children).map((childElement, childIndex) => {
  30. childElement.innerHTML = trimMiddle(childElement.innerHTML, 16, 16); // Adjusted to 16 chars
  31. });
  32. });
  33. // alert("Trim applied to the last fifth of text-message elements!"); // Notify when trim is applied
  34. };
  35.  
  36. // Function to create and inject the button
  37. const createButton = () => {
  38. const button = document.createElement('button');
  39. button.id = "radix-r3";
  40. button.className = 'flex h-6 w-6 items-center justify-center rounded-full border border-token-border-light text-xs text-token-text-secondary';
  41. button.setAttribute('aria-haspopup', 'menu');
  42. button.setAttribute('aria-expanded', 'false');
  43. button.setAttribute('data-state', 'closed');
  44. button.innerHTML = '✂️'; // Icon for trimming messages
  45.  
  46. // When the button is clicked, apply the trim function
  47. button.addEventListener('click', applyTrim);
  48.  
  49. // Log a message when the button is created
  50. // console.log("Trim button created!");
  51.  
  52. // Find the composer-parent element and append the button to its sibling children
  53. const composerParent = document.querySelector('.composer-parent');
  54. if (composerParent && composerParent.parentElement) {
  55. const siblings = composerParent.parentElement.children;
  56. if (siblings.length > 1) {
  57. siblings[1].appendChild(button); // Appending to the second child (sibling of composer-parent)
  58. // console.log("Trim button appended to the second sibling of composer-parent.");
  59. } else {
  60. console.error("Sibling element not found for appending the button");
  61. }
  62. } else {
  63. console.error("Composer parent element not found or it has no parent for appending the button");
  64. }
  65. };
  66.  
  67. // Observe DOM changes to ensure elements are fully loaded
  68. const observer = new MutationObserver(() => {
  69. const composerParent = document.querySelector('.composer-parent');
  70. if (composerParent) {
  71. createButton(); // Create and append the button when composer-parent is found
  72. observer.disconnect(); // Stop observing after the button is appended
  73. }
  74. });
  75.  
  76. // Start observing the document body for child element changes (DOM loading)
  77. observer.observe(document.body, { childList: true, subtree: true });
  78.  
  79. })();