Discord Message Colorizer Enhanced

Colors <em> text yellow and text within parentheses blue in Discord messages, handling split spans

  1. // ==UserScript==
  2. // @name Discord Message Colorizer Enhanced
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2
  5. // @description Colors <em> text yellow and text within parentheses blue in Discord messages, handling split spans
  6. // @author Vishanka
  7. // @match https://discord.com/channels/*
  8. // @grant none
  9. // ==/UserScript==
  10.  
  11.  
  12.  
  13. (function() {
  14. // 1. Inject CSS classes for styling
  15. function injectStyles() {
  16. const style = document.createElement('style');
  17. style.textContent = `
  18. /* Baseline color for all message content */
  19. div[class*="messageContent_"] {
  20. color: #A2A2AC;
  21. }
  22.  
  23. /* Highlight colors for specific patterns */
  24. .highlight-yellow {
  25. color: #E0DF7F !important;
  26. }
  27. .highlight-blue {
  28. color: #737373 !important;
  29. }
  30. .highlight-white {
  31. color: #FFFFFF !important;
  32. }
  33. `;
  34. document.head.appendChild(style);
  35. }
  36.  
  37. // 2. Function to style message content
  38. function styleMessageContent() {
  39. // Select all message items based on the class prefix
  40. const messageItems = document.querySelectorAll('li[class^="messageListItem_"]');
  41.  
  42. messageItems.forEach(messageItem => {
  43. const contentDiv = messageItem.querySelector('div[class*="messageContent_"]');
  44. if (!contentDiv) return; // Skip if no content div found
  45.  
  46. // a. Color all <em> elements yellow
  47. const emElements = contentDiv.querySelectorAll('em');
  48. emElements.forEach(em => {
  49. if (!em.classList.contains('highlight-yellow')) { // Prevent reapplying
  50. em.classList.add('highlight-yellow');
  51. }
  52. });
  53.  
  54. // b. Color text within parentheses blue
  55. colorTextWithinDelimiters(contentDiv, '(', ')', 'highlight-blue', false);
  56.  
  57. // c. Color text within quotation marks white (#FFFFFF)
  58. // Supports straight quotes and smart quotes
  59. colorTextWithinQuotes(contentDiv, ['"', '“', '‘'], ['"', '”', '’'], 'highlight-white');
  60. });
  61. }
  62.  
  63. /**
  64. * Helper function to color text within matched quotation marks.
  65. * @param {HTMLElement} container - The container element to search within.
  66. * @param {string[]} openDelimiters - The opening quotation marks.
  67. * @param {string[]} closeDelimiters - The closing quotation marks.
  68. * @param {string} highlightClass - The CSS class to apply for highlighting.
  69. */
  70. function colorTextWithinQuotes(container, openDelimiters, closeDelimiters, highlightClass) {
  71. const spans = Array.from(container.querySelectorAll('span'));
  72. let buffer = []; // Collect spans inside the current quote
  73.  
  74. spans.forEach(span => {
  75. const text = span.textContent;
  76.  
  77. // Process span content character by character
  78. for (let i = 0; i < text.length; i++) {
  79. const char = text[i];
  80.  
  81. if (openDelimiters.includes(char) && buffer.length === 0) {
  82. // Start a new quote
  83. buffer.push(span);
  84. } else if (closeDelimiters.includes(char) && buffer.length > 0) {
  85. // End the current quote
  86. buffer.forEach(s => s.classList.add(highlightClass));
  87. buffer = []; // Clear buffer
  88. } else if (buffer.length > 0) {
  89. // Inside a quote
  90. buffer.push(span);
  91. }
  92. }
  93. });
  94. }
  95.  
  96. /**
  97. * Helper function to color text within specified delimiters.
  98. * For non-quotation mark delimiters like parentheses.
  99. * @param {HTMLElement} container - The container element to search within.
  100. * @param {string|string[]} openDelimiter - The opening delimiter character(s).
  101. * @param {string|string[]} closeDelimiter - The closing delimiter character(s).
  102. * @param {string} highlightClass - The CSS class to apply for highlighting.
  103. * @param {boolean} isToggle - Whether to toggle highlighting (true for quotes).
  104. */
  105. function colorTextWithinDelimiters(container, openDelimiter, closeDelimiter, highlightClass, isToggle) {
  106. const spans = container.querySelectorAll('span');
  107. let isWithin = false;
  108.  
  109. // Normalize delimiters to arrays
  110. const openDelims = Array.isArray(openDelimiter) ? openDelimiter : [openDelimiter];
  111. const closeDelims = Array.isArray(closeDelimiter) ? closeDelimiter : [closeDelimiter];
  112.  
  113. spans.forEach(span => {
  114. const text = span.textContent;
  115.  
  116. if (isToggle) {
  117. let hasOpening = false;
  118. let hasClosing = false;
  119.  
  120. // Check for any closing delimiters first
  121. closeDelims.forEach(close => {
  122. if (text.includes(close)) {
  123. hasClosing = true;
  124. }
  125. });
  126.  
  127. if (hasClosing && isWithin) {
  128. // Apply highlight before closing
  129. span.classList.add(highlightClass);
  130. isWithin = false;
  131. }
  132.  
  133. // Apply highlight if currently within delimiters
  134. if (isWithin) {
  135. span.classList.add(highlightClass);
  136. }
  137.  
  138. // Check for any opening delimiters
  139. openDelims.forEach(open => {
  140. if (text.includes(open)) {
  141. hasOpening = true;
  142. isWithin = true;
  143. }
  144. });
  145.  
  146. if (hasOpening) {
  147. // Apply highlight for the span containing the opening delimiter
  148. span.classList.add(highlightClass);
  149. }
  150. } else {
  151. // Non-toggle: e.g., parentheses
  152. if (text.includes(openDelimiter)) {
  153. isWithin = true;
  154. }
  155.  
  156. if (isWithin) {
  157. span.classList.add(highlightClass);
  158. }
  159.  
  160. if (text.includes(closeDelimiter)) {
  161. isWithin = false;
  162. }
  163. }
  164. });
  165. }
  166.  
  167. // 3. Initialize the styling process
  168. function initializeStyling() {
  169. injectStyles();
  170. styleMessageContent();
  171.  
  172. // Observe for new messages being added to the DOM
  173. const observer = new MutationObserver(mutations => {
  174. mutations.forEach(() => {
  175. styleMessageContent();
  176. });
  177. });
  178.  
  179. // Start observing the entire document body for changes
  180. observer.observe(document.body, { childList: true, subtree: true });
  181. }
  182.  
  183. // 4. Run the initialization
  184. initializeStyling();
  185. })();
  186.