RamiEjailat / Remove Unread Count from Page Title

// ==UserScript==
// @name         Remove Unread Count from Page Title
// @description  Removes unread notification counts from page titles
// @author       Rami S Ejailat
// @copyright    RamiEjailat (https://openuserjs.org/users/RamiEjailat)
// @updateURL    https://openuserjs.org/meta/RamiEjailat/Remove_Unread_Count_from_Page_Title.meta.js
// @downloadURL  https://openuserjs.org/install/RamiEjailat/Remove_Unread_Count_from_Page_Title.user.js
// @license      MIT
// @version      20260215
// @namespace    http://tampermonkey.net/
// @match        *://*/*
// @grant        none
// @icon         https://cdn-icons-png.flaticon.com/256/13927/13927871.png
// @run-at       document-start
// ==/UserScript==

(() => {
  'use strict';

  // Regular expression that matches a leading "(digits)" possibly surrounded by whitespace.
  const unreadRegex = /^\s*\(\d+\)\s*/;

  // Clean the document.title by removing the unread count.
  function cleanTitle() {
    const original = document.title;
    // Exit if there is no '(' or ')'
    if (!original.includes('(') || !original.includes(')')) return false;

    const cleaned = original.replace(unreadRegex, '').trim();
    if (cleaned !== original) {
      document.title = cleaned;
      return true;
    }
    return false;
  }

  // Set up a MutationObserver that watches the <title> element
  const observer = new MutationObserver((mutations) => {
    for (const m of mutations) {
      if (
        // The <title> element itself changed its text
        (m.type === 'childList' && m.target.nodeName === 'TITLE') ||
        // An attribute on the <title> node changed (unlikely)
        (m.type === 'attributes' && m.target.nodeName === 'TITLE')
      ) {
        cleanTitle();
        break; // No need to check further mutations in this batch.
      }
    }
  });

  // Observe the current <title> element and watch for any future insertion of a <title>
  function startObserving() {
    const titleEl = document.querySelector('title');
    if (titleEl) {
      observer.observe(titleEl, {
        childList: true, // textContent changes
        characterData: true,
        subtree: true,
        attributes: true, // in case the site swaps the whole node
        attributeFilter: ['text'] // just to be explicit
      });
    }

    // In case the <title> element is added later (some SPA frameworks)
    observer.observe(document.documentElement, {
      childList: true,
      subtree: true
      // We only care about nodes being added/removed; filter inside callback.
    });
  }

  // Run once immediately and start the observer
  cleanTitle();
  startObserving();

  // Clean the title when the page becomes visible again (the title may get updated while the tab was not showing)
  document.addEventListener('visibilitychange', () => {
    if (!document.hidden) cleanTitle();
  });
})();