X(Twitter) - Indexed DB Version Add notes to the user

Add notes (aliases/tags) for users to help identify and search, optimized for large datasets

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name                X(Twitter) - Indexed DB Version Add notes to the user
// @name:zh-CN          X(Twitter) - Indexed DB 增强版为用户添加备注(别名/标签)
// @name:zh-TW          X(Twitter) - Indexed DB 增强版為使用者新增備註(別名/標籤)
// @namespace           https://greasyfork.org/zh-CN/users/193133-pana
// @homepage            https://greasyfork.org/zh-CN/users/193133-pana
// @icon                data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+
// @version             6.1.15
// @description         Add notes (aliases/tags) for users to help identify and search, optimized for large datasets
// @description:zh-CN   为用户添加备注(别名/标签)功能,针对大数据量优化,帮助识别和搜索
// @description:zh-TW   為使用者新增備註(別名/標籤)功能,針對大數據量最佳化,幫助識別和搜尋
// @author              pana
// @license             GNU General Public License v3.0
// @compatible          chrome
// @compatible          firefox
// @match               *://x.com/*
// @match               *://*twitter.com/*
// @require             https://gcore.jsdelivr.net/gh/LightAPIs/greasy-fork-library@47d998f5f1e438fe137647b8735b1e17a77e4b69/Note_Obj.js
// @connect             *
// @noframes
// @grant               GM_info
// @grant               GM_getValue
// @grant               GM_setValue
// @grant               GM_deleteValue
// @grant               GM_listValues
// @grant               GM_openInTab
// @grant               GM_addStyle
// @grant               GM_xmlhttpRequest
// @grant               GM_registerMenuCommand
// @grant               GM_unregisterMenuCommand
// @grant               GM_addValueChangeListener
// @grant               GM_removeValueChangeListener
// ==/UserScript==

(function () {
  "use strict";
  const UPDATED = "2024-05-15";
  const TWITTER_ICON = {
    NOTE_GRAY:
      "url(data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2IoMTAxLCAxMTksIDEzNCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYigxMDEsIDExOSwgMTM0KSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+)",
    NOTE_BLUE:
      "url(data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+)",
  };
  const selector = {
    root: "#react-root",
    homepage: {
      id: 'div[data-testid="User-Name"] a[role="link"] > div[dir] > span',
      article: "article",
      toolBar: '[tabindex="0"]:scope [role="group"][id]',
      showName:
        'div[data-testid="User-Name"] a[role="link"] > div > div[dir] > span',
      reprintA: "a[role][dir][id]",
      reprintName: '[data-testid="socialContext"] [dir]',
      at: '[data-testid="tweetText"]  a[dir][role="link"]',
      blockquote: 'div[aria-labelledby][id] div[id] div[role="link"]',
      blockquoteId: 'div[data-testid="User-Name"] div[tabindex] div[dir]',
      blockquoteShowName: 'div[data-testid="User-Name"] div[dir]',
    },
    userpage: {
      main: ".css-175oi2r.r-ttdzmv.r-1ifxtd0",
      id: '[data-testid="UserName"] div[tabindex] div[dir] > span',
      showName: '[data-testid="UserName"] div[dir] > span',
      follow: ".css-175oi2r.r-obd0qt.r-18u37iz.r-1w6e6rj.r-1h0z5md.r-dnmrzs",
    },
    comment: {
      toolBar: '[tabindex="-1"]:scope [role="group"][id]',
    },
    hover: {
      panel: 'div[data-testid="HoverCard"] > div > div',
      userAvatar: '[data-testid^="UserAvatar-Container-"]',
      id: 'a[role="link"]',
      showName: 'a[role="link"] > div > [dir] > span',
    },
    modal: {
      cell: '[aria-labelledby="modal-header"] [data-testid="UserCell"]',
      id: 'a[role="link"]',
      showName: 'a[role="link"] > div > [dir] > span',
    },
    follow: {
      cell: '[data-testid="cellInnerDiv"] [data-testid="UserCell"]',
      id: 'a[role="link"]',
      showName: 'a[role="link"] > div > [dir] > span',
    },
    rightRecommended: {
      cell: '[role="complementary"] [data-testid="UserCell"]',
      id: 'a[role="link"]',
      showName: 'a[role="link"] > div > [dir]',
    },
  };
  const nameSet = {
    blueTag: "note-obj-twitter-blue-tag",
    noteBtn: "note-obj-twitter-note-btn",
    panelBtn: "note-obj-twitter-panel-btn",
    beforeFollowNoteBtn: "note-obj-twitter-before-follow-note-btn",
    baseToolBarBtn: "note-obj-twitter-base-tool-bar-btn",
    commentToolBarBtn: "note-obj-twitter-comment-tool-bar-btn",
  };
  const style = `
      .${nameSet.blueTag} {
        background-color: #3c81df;
        color: #fff;
        display: inline-flex;
        align-items: center;
        padding: 2px 10px;
        line-height: 100%;
        border-radius: 50px;
      }
      .${nameSet.noteBtn} {
        background-image: ${TWITTER_ICON.NOTE_GRAY};
        background-repeat: no-repeat;
        background-position: center;
        background-color: rgba(0, 0, 0, 0);
        border-bottom-left-radius: 9999px;
        border-bottom-right-radius: 9999px;
        border-top-left-radius: 9999px;
        border-top-right-radius: 9999px;
        transition-property: background-color, box-shadow;
        transition-duration: 0.2s;
      }
      .${nameSet.noteBtn}:hover {
        background-image: ${TWITTER_ICON.NOTE_BLUE};
        background-color: rgba(29, 161, 242, .1);
      }
      .${nameSet.panelBtn} {
        height: 32px;
        width: 32px;
        margin: 5px 0px 0px 0px;
        background-size: 28px auto;
        cursor: pointer !important;
        border-radius: 0px;
      }
      .${nameSet.panelBtn}:hover::after {
        content: "";
        display: flex;
        position: relative;
        background-color: rgba(29, 161, 242, .1);
        width: 48px;
        height: 48px;
        top: -8px;
        left: -8px;
        border-radius: 99px;
      }
      .${nameSet.beforeFollowNoteBtn} {
        height: 36px;
        width: 36px;
        background-image: ${TWITTER_ICON.NOTE_BLUE};
        background-repeat: no-repeat;
        background-size: 19px auto;
        background-position: center;
        margin-bottom: 12px;
        margin-right: 12px;
        cursor: pointer;
        border: 1px solid rgba(29, 161, 242, 1);
        border-bottom-left-radius: 9999px;
        border-bottom-right-radius: 9999px;
        border-top-left-radius: 9999px;
        border-top-right-radius: 9999px;
        background-color: rgba(0, 0, 0, 0);
        transition-property: background-color, box-shadow;
        transition-duration: 0.2s;
      }
      .${nameSet.beforeFollowNoteBtn}:hover {
        background-color: rgba(29, 161, 242, .1);
      }
      .${nameSet.baseToolBarBtn} {
        height: 18px;
        width: 18px;
        margin: 0px -40px 0px 0px;
        background-size: 20px auto;
        border-radius: 0px;
        margin: 0 12px;
      }
      .${nameSet.baseToolBarBtn}:hover::after {
        content: "";
        position: absolute;
        background-color: rgba(29, 161, 242, .1);
        width: 34px;
        height: 34px;
        top: -8px;
        right: 5px;
        border-radius: 99px;
      }
      .${nameSet.commentToolBarBtn} {
        height: 24px;
        width: 24px;
        margin: 10px 0px 0px 0px;
        background-size: 24px auto;
        border-radius: 0px;
        cursor: pointer;
        margin-left: 12px;
      }
      .${nameSet.commentToolBarBtn}:hover::after {
        content: "";
        position: absolute;
        background-color: rgba(29, 161, 242, .1);
        width: 38px;
        height: 38px;
        top: 3px;
        right: -2px;
        border-radius: 99px;
      }
      ${selector.homepage.showName}, ${selector.modal.showName} {
        white-space: normal;
      }
      .note-obj-add-frame-dialog button {
        text-align: center;
      }
      .note-obj-management-frame-save-content,
      .note-obj-management-frame-cancel-content,
      .note-obj-group-frame-save-content,
      .note-obj-group-frame-cancel-content {
        font-size: 12px;
      }`;
  const noteObj = new Note_Obj({
    id: "myTwitterNote",
    script: {
      author: {
        name: "pana",
        homepage: "https://greasyfork.org/zh-CN/users/193133-pana",
      },
      url: "https://greasyfork.org/scripts/404587",
      updated: UPDATED,
    },
    style,
    changeEvent: changeEvent,
    settings: {
      showToolbarButton: {
        type: "checkbox",
        lang: {
          en: 'Display the "Note" button in the toolbar below each tweet (if there is no such button in the user\'s hover information panel, this option can be turned on)',
          zhHans:
            '在每条推特下方的工具栏里显示"备注"按钮 (如果在用户的悬停信息面板里没有此按钮时,可以打开此选项)',
          zhHant:
            '在每條推特下方的工具欄裡顯示"備註"按鈕 (如果在使用者的懸停資訊面板裡沒有此按鈕時,可以開啟此選項)',
        },
        default: false,
        event: insertToolbarButtonEvent,
      },
      disableInTweets: {
        type: "checkbox",
        lang: {
          en: "Disable replacing @user with @note in tweets",
          zhHans: "禁用将推文中的 @user 替换为 @note",
          zhHant: "禁用將推文中的 @user 替換為 @note",
        },
        default: false,
        event: disableInTweetsEvent,
      },
    },
  });
  function atFilter(text) {
    return text.replace(/^@/, "");
  }
  function hrefComparator(href) {
    return /^\/[^/]+$/i.test(href);
  }
  function toolBarNoteButton(ele, state) {
    const eleId = noteObj.fn.getText(
      ele,
      selector.homepage.id,
      "error",
      atFilter
    );
    if (eleId) {
      const eleName = noteObj.fn.getText(
        ele,
        selector.homepage.showName,
        "info"
      );
      const homepageToolBar = noteObj.fn.query(
        ele,
        selector.homepage.toolBar,
        "info"
      );
      const commentToolBar = noteObj.fn.query(
        ele,
        selector.comment.toolBar,
        "info"
      );
      if (homepageToolBar) {
        const homepageToolBarBtn = noteObj.fn.query(
          homepageToolBar,
          "." + Note_Obj.btnClassName,
          "none"
        );
        if (state) {
          !homepageToolBarBtn &&
            homepageToolBar.appendChild(
              noteObj.createNoteBtn(eleId, eleName, [
                nameSet.noteBtn,
                nameSet.baseToolBarBtn,
              ])
            );
        } else {
          homepageToolBarBtn && homepageToolBarBtn.remove();
        }
      }
      if (commentToolBar) {
        const commentToolBarBtn = noteObj.fn.query(
          commentToolBar,
          "." + Note_Obj.btnClassName,
          "none"
        );
        if (state) {
          !commentToolBarBtn &&
            commentToolBar.appendChild(
              noteObj.createNoteBtn(eleId, eleName, [
                nameSet.noteBtn,
                nameSet.commentToolBarBtn,
              ])
            );
        } else {
          commentToolBarBtn && commentToolBarBtn.remove();
        }
      }
    }
  }
  function homepageNote(ele, changeId) {
    const eleId = noteObj.fn.getText(
      ele,
      selector.homepage.id,
      "error",
      atFilter
    );
    if (eleId) {
      if (changeId) {
        changeId === eleId &&
          noteObj.handler(eleId, ele, selector.homepage.showName, {
            add: "span",
            className: [nameSet.blueTag],
          });
      } else {
        const eleName = noteObj.fn.getText(
          ele,
          selector.homepage.showName,
          "info"
        );
        noteObj.handler(
          eleId,
          ele,
          selector.homepage.showName,
          {
            add: "span",
            className: [nameSet.blueTag],
          },
          eleName
        );
      }
    }
  }
  function reprintANote(ele, changeId) {
    const reprintA = noteObj.fn.queryAnchor(
      ele,
      selector.homepage.reprintA,
      "info"
    );
    if (reprintA) {
      const eleId = noteObj.fn.getIdFromUrl(reprintA.href);
      if (!changeId || changeId === eleId) {
        noteObj.handler(eleId, reprintA, selector.homepage.reprintName, {
          add: "span",
          className: [nameSet.blueTag],
          offsetWidth: 30,
        });
      }
    }
  }
  function blockquoteNote(ele, changeId) {
    const blockquote = noteObj.fn.query(
      ele,
      selector.homepage.blockquote,
      "info"
    );
    if (blockquote) {
      const blockquoteUser = noteObj.fn.query(
        blockquote,
        selector.homepage.blockquoteShowName
      );
      if (blockquoteUser) {
        const eleId = noteObj.fn.getText(
          blockquote,
          selector.homepage.blockquoteId,
          "error",
          atFilter
        );
        if (!changeId || changeId === eleId) {
          noteObj.handler(eleId, blockquoteUser, undefined, {
            add: "span",
            className: [nameSet.blueTag],
          });
        }
      }
    }
  }
  function homepageAtNote(ele, state, changeId) {
    for (const atUser of noteObj.fn.queryAllAnchor(
      ele,
      selector.homepage.at,
      "info"
    )) {
      if (hrefComparator(atUser.getAttribute("href") || "")) {
        const atUserId = noteObj.fn.getIdFromUrl(atUser.href);
        if (!changeId || changeId === atUserId) {
          noteObj.handler(atUserId, atUser, undefined, {
            prefix: "@",
            restore: state,
          });
        }
      }
    }
  }
  function userpageNote(ele, changeId) {
    const eleId = noteObj.fn.getText(
      ele,
      selector.userpage.id,
      "error",
      atFilter
    );
    if (changeId) {
      changeId === eleId &&
        noteObj.handler(eleId, ele, selector.userpage.showName, {
          add: "span",
          className: [nameSet.blueTag],
        });
    } else {
      const eleName = noteObj.fn.getText(
        ele,
        selector.userpage.showName,
        "info"
      );
      noteObj.handler(
        eleId,
        ele,
        selector.userpage.showName,
        {
          add: "span",
          className: [nameSet.blueTag],
        },
        eleName
      );
    }
  }
  function followNote(ele, changeId) {
    spanItemNote(ele, selector.follow.id, selector.follow.showName, changeId);
  }
  function rightRecommendedNote(ele, changeId) {
    spanItemNote(
      ele,
      selector.rightRecommended.id,
      selector.rightRecommended.showName,
      changeId
    );
  }
  function modalNote(ele, changeId) {
    spanItemNote(ele, selector.modal.id, selector.modal.showName, changeId);
  }
  function spanItemNote(ele, idSelector, nameSelector, changeId) {
    const eleId = noteObj.fn.getUrlId(ele, idSelector);
    if (!changeId || changeId === eleId) {
      noteObj.handler(eleId, ele, nameSelector, {
        add: "span",
        className: [nameSet.blueTag],
      });
    }
  }
  function disableInTweetsEvent(status) {
    noteObj.fn.queryAll(selector.homepage.article, "none").forEach((ele) => {
      homepageAtNote(ele, status);
    });
  }
  function insertToolbarButtonEvent(status) {
    noteObj.fn.queryAll(selector.homepage.article, "none").forEach((ele) => {
      toolBarNoteButton(ele, status);
    });
  }
  function changeEvent(changeId) {
    const articles = noteObj.fn.queryAll(selector.homepage.article, "none");

    batchProcess(articles, (ele) => {
      try {
        homepageNote(ele, changeId);
        reprintANote(ele, changeId);
        blockquoteNote(ele, changeId);
        homepageAtNote(
          ele,
          noteObj.getOtherConfig().disableInTweets === true,
          changeId
        );
      } catch (e) {
        console.error("Process element error:", e);
      }
    });

    noteObj.fn.queryAll(selector.userpage.main).forEach((ele) => {
      userpageNote(ele, changeId);
    });
    noteObj.fn.queryAll(selector.follow.cell, "info").forEach((ele) => {
      followNote(ele, changeId);
    });
    noteObj.fn.queryAll(selector.rightRecommended.cell).forEach((ele) => {
      rightRecommendedNote(ele, changeId);
    });
    noteObj.fn.queryAll(selector.modal.cell, "info").forEach((ele) => {
      modalNote(ele, changeId);
    });
  }
  function init() {
    try {
      const arriveOption = {
        fireOnAttributesModification: true,
        existing: true,
      };

      const rootDom = noteObj.fn.query(selector.root);
      if (!rootDom) {
        console.warn("Root element not found");
        return;
      }

      const throttledCallback = throttle((ele) => {
        try {
          toolBarNoteButton(
            ele,
            noteObj.getOtherConfig().showToolbarButton === true
          );
          homepageNote(ele);
          reprintANote(ele);
          blockquoteNote(ele);

          const disableInTweets =
            noteObj.getOtherConfig().disableInTweets === true;
          if (!disableInTweets) {
            homepageAtNote(ele, disableInTweets);
          }
        } catch (e) {
          console.error("Article processing error:", e);
        }
      }, 100);

      noteObj.arrive(
        rootDom,
        selector.homepage.article,
        arriveOption,
        throttledCallback
      );

      noteObj.arrive(rootDom, selector.userpage.main, arriveOption, (ele) => {
        const eleId = noteObj.fn.getText(
          ele,
          selector.userpage.id,
          "error",
          atFilter
        );
        if (eleId) {
          const eleName = noteObj.fn.getText(
            ele,
            selector.userpage.showName,
            "info"
          );
          let followNoteBtn;
          const userpageFollow = noteObj.fn.query(
            ele,
            selector.userpage.follow
          );
          if (userpageFollow) {
            followNoteBtn = noteObj.createNoteBtn(eleId, eleName, [
              nameSet.beforeFollowNoteBtn,
              "css-901oao",
            ]);
            userpageFollow.insertAdjacentElement("afterbegin", followNoteBtn);
          }
          const userIdChange = new MutationObserver(() => {
            const newUserId = noteObj.fn.getText(
              ele,
              selector.userpage.id,
              "error",
              atFilter
            );
            if (newUserId) {
              noteObj.handler("", ele, selector.userpage.showName, {
                add: "span",
                className: [nameSet.blueTag],
              });
              const newUserName = noteObj.fn.getText(
                ele,
                selector.userpage.showName,
                "info"
              );
              if (followNoteBtn) {
                followNoteBtn.remove();
                followNoteBtn = noteObj.createNoteBtn(newUserId, newUserName, [
                  nameSet.beforeFollowNoteBtn,
                  "css-901oao",
                ]);
                userpageFollow &&
                  userpageFollow.insertAdjacentElement(
                    "afterbegin",
                    followNoteBtn
                  );
              }
              noteObj.handler(
                newUserId,
                ele,
                selector.userpage.showName,
                {
                  add: "span",
                  className: [nameSet.blueTag],
                },
                newUserName
              );
            }
          });
          const obId = noteObj.fn.query(ele, selector.userpage.id);
          obId &&
            userIdChange.observe(obId, {
              subtree: true,
              characterData: true,
            });
        }
        userpageNote(ele);
      });
      noteObj.arrive(rootDom, selector.follow.cell, arriveOption, (ele) => {
        followNote(ele);
      });
      noteObj.arrive(
        rootDom,
        selector.rightRecommended.cell,
        arriveOption,
        (ele) => {
          rightRecommendedNote(ele);
        }
      );
      noteObj.arrive(rootDom, selector.modal.cell, arriveOption, (ele) => {
        modalNote(ele);
      });
      noteObj.arrive(rootDom, selector.hover.panel, arriveOption, (ele) => {
        const eleId = noteObj.fn.getUrlId(ele, selector.hover.id);
        if (eleId) {
          const userShowNameText = noteObj.fn.getText(
            ele,
            selector.hover.showName,
            "info"
          );
          const userAvatar = noteObj.fn.query(ele, selector.hover.userAvatar);
          userAvatar &&
            userAvatar.after(
              noteObj.createNoteBtn(eleId, userShowNameText, [
                nameSet.noteBtn,
                nameSet.panelBtn,
              ])
            );
          noteObj.handler(
            eleId,
            ele,
            selector.hover.showName,
            {
              add: "span",
              className: [nameSet.blueTag],
            },
            userShowNameText
          );
        }
      });
    } catch (error) {
      console.error("Initialization failed:", error);
    }
  }
  init();
})();