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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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();
})();