// ==UserScript== // @name 小红书优化 // @namespace https://github.com/WhiteSevs/TamperMonkeyScript // @version 2025.7.12 // @author WhiteSevs // @description 屏蔽登录弹窗、屏蔽广告、优化评论浏览、优化图片浏览、允许复制、禁止唤醒App、禁止唤醒弹窗、修复正确跳转等 // @license GPL-3.0-only // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAx9JREFUWEfNl09MU0EQxn/beFTDDRI41FAMcNGbBw62oPEGid6UULxg1EhEEzExgdBEEzRqlKDxZCHgDZJ6U8TWAyaQGIsHMQLSA0S8VYQT2NXp9tnX0vKnpi2TNH1vd3bmm5lv9+0o0kQ73SXsc7QCx1EcjU9rnOl6O3pXRNAqCjqCIsB6LKQioYh9rbK/6MMnWojFHgElO3KwWyUBBD1q9q3fWvoPgHY1dIHu2a3N3PRVt5ob98naOABdVd+K5nluxnJc5dBe9TU4qHS128lvRzDnOufoH4iyETukihJ9EnSH0i5PAFRj7oH8z0r9UmlXw0fQZrsVWhQRKcFCEepvQo0DcNXrQgeechDtbQAVpbCyBiurqUmqqYSD+2FyOnPyZE50ln7A4vKWCc5egvIyCA3DzV4YeZ00UlEGQ/eN88670HsjOTczZ8bbvXCiDqbC8HkeBkahuhLE5sBICqDdAzh9yjh1n4OlZZgdTxqcDEPfIAw9SI1aMjg1DVrDpe5tAIRewOJ36LyXzIAgv+IFz1ljXN5FJAOjrwwIcd583YwfO2L0JHvW2qqGjKXYnAExJkYfDyYBaGWibmyDGhe0t/z9bikDSMQO4NZlEO5YJTggfHCBf8SUIo0TqQCEPB8C0Ddg6m5xQIj4xAcXu+DLPASHjY5/1BDUDkAyWF6amXjCkcYLW5Sg1gWBZ3C7H6Y+mWdJ48y35LiQ0HvGGLHzIFsJLAJLSSQzssYmmzMg0TVfM9vMqqMYkcwIejEiv59rhliy3URP2H6n3/zXJsbsO+ipz+huCUCQSb2E3eJQRNL+ZsIQS/a1ALQIKDtCxu0i4EUs8GPvk7YEXFPbNrvAmj5ZJ3dB49wSYbTlUIgqANJFzoFfq4aE8izBiC0h49iEmctagszUyevoHvgYFf1zXEwA6PBeuJLVXwUe5pVp2Yyr2HmVaMUW8tYNZXWuI6xrT6IxcbeiHYVtTCT62ZDf1pp5ekB1FaYU2qfmgvGLQWpzKi0adOfxlhxF0ZGxObUiT7RqbjRNoJ0oVZIzINMNy5Eehtg7NvCrSChqz/IfgUZkW/BhLsQAAAAASUVORK5CYII= // @supportURL https://github.com/WhiteSevs/TamperMonkeyScript/issues // @match *://www.xiaohongshu.com/* // @require https://fastly.jsdelivr.net/gh/WhiteSevs/TamperMonkeyScript@86be74b83fca4fa47521cded28377b35e1d7d2ac/lib/CoverUMD/index.js // @require https://fastly.jsdelivr.net/npm/@whitesev/utils@2.7.0/dist/index.umd.js // @require https://fastly.jsdelivr.net/npm/@whitesev/domutils@1.5.11/dist/index.umd.js // @require https://fastly.jsdelivr.net/npm/@whitesev/pops@2.1.13/dist/index.umd.js // @require https://fastly.jsdelivr.net/npm/qmsg@1.3.8/dist/index.umd.js // @require https://fastly.jsdelivr.net/npm/viewerjs@1.11.7/dist/viewer.min.js // @resource ViewerCSS https://fastly.jsdelivr.net/npm/viewerjs@1.11.7/dist/viewer.min.css // @connect edith.xiaohongshu.com // @grant GM_deleteValue // @grant GM_getResourceText // @grant GM_getValue // @grant GM_info // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_unregisterMenuCommand // @grant GM_xmlhttpRequest // @grant unsafeWindow // @run-at document-start // ==/UserScript== (function (Qmsg, DOMUtils, Utils, pops, Viewer) { 'use strict'; var _GM_deleteValue = /* @__PURE__ */ (() => typeof GM_deleteValue != "undefined" ? GM_deleteValue : void 0)(); var _GM_getResourceText = /* @__PURE__ */ (() => typeof GM_getResourceText != "undefined" ? GM_getResourceText : void 0)(); var _GM_getValue = /* @__PURE__ */ (() => typeof GM_getValue != "undefined" ? GM_getValue : void 0)(); var _GM_info = /* @__PURE__ */ (() => typeof GM_info != "undefined" ? GM_info : void 0)(); var _GM_registerMenuCommand = /* @__PURE__ */ (() => typeof GM_registerMenuCommand != "undefined" ? GM_registerMenuCommand : void 0)(); var _GM_setValue = /* @__PURE__ */ (() => typeof GM_setValue != "undefined" ? GM_setValue : void 0)(); var _GM_unregisterMenuCommand = /* @__PURE__ */ (() => typeof GM_unregisterMenuCommand != "undefined" ? GM_unregisterMenuCommand : void 0)(); var _GM_xmlhttpRequest = /* @__PURE__ */ (() => typeof GM_xmlhttpRequest != "undefined" ? GM_xmlhttpRequest : void 0)(); var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)(); var _monkeyWindow = /* @__PURE__ */ (() => window)(); const blockCSS$2 = "/* 用户主页 */\r\n/* 底部的-App内打开 */\r\n.launch-app-container.bottom-bar,\r\n/* 顶部的-打开看看 */\r\n.main-container > .scroll-view-container > .launch-app-container:first-child,\r\n/* 底部的-打开小红书看更多精彩内容 */\r\n.bottom-launch-app-tip.show-bottom-bar,\r\n/* 首页-顶部横幅 */\r\n#app .launch-app-container,\r\n/* 笔记-顶部横幅 */\r\n.note-view-container .nav-bar-box-expand ,\r\n.note-view-container .nav-bar-box-expand+.placeholder-expand,\r\n/* 404页面 顶部的打开看看 */\r\n.not-found-container .nav-bar-box-expand:has(.share-info-box):has(.launch-btn),\r\n/* 404页面 底部的-App内打开 */\r\n.not-found-container #fmp {\r\n display: none !important;\r\n}\r\n"; const ScriptRouter = { /** * 判断是否是笔记页面 */ isArticle() { return globalThis.location.pathname.startsWith("/discovery/item/") || globalThis.location.pathname.startsWith("/explore/"); }, /** * 判断是否是用户主页页面 */ isUserHome() { return globalThis.location.pathname.startsWith("/user/profile/"); }, /** * 判断是否是主页 */ isHome() { return globalThis.location.href === "https://www.xiaohongshu.com/" || globalThis.location.href === "https://www.xiaohongshu.com"; }, /** * 判断是否是搜索页面 */ isSearch() { return globalThis.location.pathname.startsWith("/search_result/"); } }; const KEY = "GM_Panel"; const ATTRIBUTE_INIT = "data-init"; const ATTRIBUTE_KEY = "data-key"; const ATTRIBUTE_DEFAULT_VALUE = "data-default-value"; const ATTRIBUTE_INIT_MORE_VALUE = "data-init-more-value"; const PROPS_STORAGE_API = "data-storage-api"; const PanelUISize = { /** * 一般设置界面的尺寸 */ setting: { get width() { if (window.innerWidth < 550) { return "88vw"; } else if (window.innerWidth < 700) { return "550px"; } else { return "700px"; } }, get height() { if (window.innerHeight < 450) { return "70vh"; } else if (window.innerHeight < 550) { return "450px"; } else { return "550px"; } } } }; class StorageUtils { /** 存储的键名 */ storageKey; listenerData; /** * 存储的键名,可以是多层的,如:a.b.c * * 那就是 * { * "a": { * "b": { * "c": { * ...你的数据 * } * } * } * } * @param key */ constructor(key) { if (typeof key === "string") { let trimKey = key.trim(); if (trimKey == "") { throw new Error("key参数不能为空字符串"); } this.storageKey = trimKey; } else { throw new Error("key参数类型错误,必须是字符串"); } this.listenerData = new Utils.Dictionary(); } /** * 获取本地值 */ getLocalValue() { let localValue = _GM_getValue(this.storageKey); if (localValue == null) { localValue = {}; this.setLocalValue(localValue); } return localValue; } /** * 设置本地值 * @param value */ setLocalValue(value) { _GM_setValue(this.storageKey, value); } /** * 设置值 * @param key 键 * @param value 值 */ set(key, value) { let oldValue = this.get(key); let localValue = this.getLocalValue(); Reflect.set(localValue, key, value); this.setLocalValue(localValue); this.triggerValueChangeListener(key, oldValue, value); } /** * 获取值 * @param key 键 * @param defaultValue 默认值 */ get(key, defaultValue) { let localValue = this.getLocalValue(); return Reflect.get(localValue, key) ?? defaultValue; } /** * 获取所有值 */ getAll() { let localValue = this.getLocalValue(); return localValue; } /** * 删除值 * @param key 键 */ delete(key) { let oldValue = this.get(key); let localValue = this.getLocalValue(); Reflect.deleteProperty(localValue, key); this.setLocalValue(localValue); this.triggerValueChangeListener(key, oldValue, void 0); } /** * 判断是否存在该值 */ has(key) { let localValue = this.getLocalValue(); return Reflect.has(localValue, key); } /** * 获取所有键 */ keys() { let localValue = this.getLocalValue(); return Reflect.ownKeys(localValue); } /** * 获取所有值 */ values() { let localValue = this.getLocalValue(); return Reflect.ownKeys(localValue).map( (key) => Reflect.get(localValue, key) ); } /** * 清空所有值 */ clear() { _GM_deleteValue(this.storageKey); } /** * 监听值改变 * + .set * + .delete * @param key 监听的键 * @param callback 值改变的回调函数 */ addValueChangeListener(key, callback) { let listenerId = Math.random(); let listenerData = this.listenerData.get(key) || []; listenerData.push({ id: listenerId, key, callback }); this.listenerData.set(key, listenerData); return listenerId; } /** * 移除监听 * @param listenerId 监听的id或键名 */ removeValueChangeListener(listenerId) { let flag = false; for (const [key, listenerData] of this.listenerData.entries()) { for (let index = 0; index < listenerData.length; index++) { const value = listenerData[index]; if (typeof listenerId === "string" && value.key === listenerId || typeof listenerId === "number" && value.id === listenerId) { listenerData.splice(index, 1); index--; flag = true; } } this.listenerData.set(key, listenerData); } return flag; } /** * 主动触发监听器 * @param key 键 * @param oldValue (可选)旧值 * @param newValue (可选)新值 */ triggerValueChangeListener(key, oldValue, newValue) { if (!this.listenerData.has(key)) { return; } let listenerData = this.listenerData.get(key); for (let index = 0; index < listenerData.length; index++) { const data = listenerData[index]; if (typeof data.callback === "function") { let value = this.get(key); let __newValue; let __oldValue; if (typeof oldValue !== "undefined" && arguments.length >= 2) { __oldValue = oldValue; } else { __oldValue = value; } if (typeof newValue !== "undefined" && arguments.length > 2) { __newValue = newValue; } else { __newValue = value; } data.callback(key, __oldValue, __newValue); } } } } const PopsPanelStorageApi = new StorageUtils(KEY); const PanelContent = { $data: { /** * @private */ __contentConfig: null, get contentConfig() { if (this.__contentConfig == null) { this.__contentConfig = new utils.Dictionary(); } return this.__contentConfig; } }, /** * 设置所有配置项,用于初始化默认的值 * * 如果是第一组添加的话,那么它默认就是设置菜单打开的配置 * @param configList 配置项 */ addContentConfig(configList) { if (!Array.isArray(configList)) { configList = [configList]; } let index = this.$data.contentConfig.keys().length; this.$data.contentConfig.set(index, configList); }, /** * 获取所有的配置内容,用于初始化默认的值 */ getAllContentConfig() { return this.$data.contentConfig.values().flat(); }, /** * 获取配置内容 * @param index 配置索引 */ getConfig(index = 0) { return this.$data.contentConfig.get(index) ?? []; }, /** * 获取默认左侧底部的配置项 */ getDefaultBottomContentConfig() { return [ { id: "script-version", title: `版本:${_GM_info?.script?.version || "未知"}`, isBottom: true, forms: [], clickFirstCallback(event, rightHeaderElement, rightContainerElement) { let supportURL = _GM_info?.script?.supportURL || _GM_info?.script?.namespace; if (typeof supportURL === "string" && utils.isNotNull(supportURL)) { window.open(supportURL, "_blank"); } return false; } } ]; } }; const PanelMenu = { $data: { __menuOption: [ { key: "show_pops_panel_setting", text: "⚙ 设置", autoReload: false, isStoreValue: false, showText(text) { return text; }, callback: () => { Panel.showPanel(PanelContent.getConfig(0)); } } ], get menuOption() { return this.__menuOption; } }, init() { this.initExtensionsMenu(); }, /** * 初始化菜单项 */ initExtensionsMenu() { if (!Panel.isTopWindow()) { return; } GM_Menu.add(this.$data.menuOption); }, /** * 添加菜单项 * @param option 菜单配置 */ addMenuOption(option) { if (!Array.isArray(option)) { option = [option]; } this.$data.menuOption.push(...option); }, /** * 更新菜单项 * @param option 菜单配置 */ updateMenuOption(option) { if (!Array.isArray(option)) { option = [option]; } option.forEach((optionItem) => { let findIndex = this.$data.menuOption.findIndex((it) => { return it.key === optionItem.key; }); if (findIndex !== -1) { this.$data.menuOption[findIndex] = optionItem; } }); }, /** * 获取菜单项 * @param [index=0] 索引 */ getMenuOption(index = 0) { return this.$data.menuOption[index]; }, /** * 删除菜单项 * @param [index=0] 索引 */ deleteMenuOption(index = 0) { this.$data.menuOption.splice(index, 1); } }; const Panel = { /** 数据 */ $data: { /** * @private */ __configDefaultValueData: null, /** * @private */ __onceExecMenuData: null, /** * @private */ __onceExecData: null, /** * @private */ __panelConfig: {}, $panel: null, /** * 菜单项的默认值 */ get configDefaultValueData() { if (this.__configDefaultValueData == null) { this.__configDefaultValueData = new utils.Dictionary(); } return this.__configDefaultValueData; }, /** * 成功只执行了一次的项 */ get onceExecMenuData() { if (this.__onceExecMenuData == null) { this.__onceExecMenuData = new utils.Dictionary(); } return this.__onceExecMenuData; }, /** * 成功只执行了一次的项 */ get onceExecData() { if (this.__onceExecData == null) { this.__onceExecData = new utils.Dictionary(); } return this.__onceExecData; }, /** 脚本名,一般用在设置的标题上 */ get scriptName() { return SCRIPT_NAME; }, /** * pops.panel的默认配置 */ get panelConfig() { return this.__panelConfig; }, set panelConfig(value) { this.__panelConfig = value; }, /** 菜单项的总值在本地数据配置的键名 */ key: KEY, /** 菜单项在attributes上配置的菜单键 */ attributeKeyName: ATTRIBUTE_KEY, /** 菜单项在attributes上配置的菜单默认值 */ attributeDefaultValueName: ATTRIBUTE_DEFAULT_VALUE }, init() { this.initContentDefaultValue(); PanelMenu.init(); }, /** 判断是否是顶层窗口 */ isTopWindow() { return _unsafeWindow.top === _unsafeWindow.self; }, /** 初始化菜单项的默认值保存到本地数据中 */ initContentDefaultValue() { const initDefaultValue = (config) => { if (!config.attributes) { return; } if (config.type === "button" || config.type === "forms" || config.type === "deepMenu") { return; } let needInitConfig = {}; let key = config.attributes[ATTRIBUTE_KEY]; if (key != null) { needInitConfig[key] = config.attributes[ATTRIBUTE_DEFAULT_VALUE]; } let __attr_init__ = config.attributes[ATTRIBUTE_INIT]; if (typeof __attr_init__ === "function") { let __attr_result__ = __attr_init__(); if (typeof __attr_result__ === "boolean" && !__attr_result__) { return; } } let initMoreValue = config.attributes[ATTRIBUTE_INIT_MORE_VALUE]; if (initMoreValue && typeof initMoreValue === "object") { Object.assign(needInitConfig, initMoreValue); } let needInitConfigList = Object.keys(needInitConfig); if (!needInitConfigList.length) { log.warn(["请先配置键", config]); return; } needInitConfigList.forEach((__key) => { let __defaultValue = needInitConfig[__key]; this.setDefaultValue(__key, __defaultValue); }); }; const loopInitDefaultValue = (configList) => { for (let index = 0; index < configList.length; index++) { let configItem = configList[index]; initDefaultValue(configItem); let childForms = configItem.forms; if (childForms && Array.isArray(childForms)) { loopInitDefaultValue(childForms); } } }; const contentConfigList = [...PanelContent.getAllContentConfig()]; for (let index = 0; index < contentConfigList.length; index++) { let leftContentConfigItem = contentConfigList[index]; if (!leftContentConfigItem.forms) { continue; } const rightContentConfigList = leftContentConfigItem.forms; if (rightContentConfigList && Array.isArray(rightContentConfigList)) { loopInitDefaultValue(rightContentConfigList); } } }, /** * 设置初始化使用的默认值 */ setDefaultValue(key, defaultValue) { if (this.$data.configDefaultValueData.has(key)) { log.warn("请检查该key(已存在): " + key); } this.$data.configDefaultValueData.set(key, defaultValue); }, /** * 设置值 * @param key 键 * @param value 值 */ setValue(key, value) { PopsPanelStorageApi.set(key, value); }, /** * 获取值 * @param key 键 * @param defaultValue 默认值 */ getValue(key, defaultValue) { let localValue = PopsPanelStorageApi.get(key); if (localValue == null) { if (this.$data.configDefaultValueData.has(key)) { return this.$data.configDefaultValueData.get(key); } return defaultValue; } return localValue; }, /** * 删除值 * @param key 键 */ deleteValue(key) { PopsPanelStorageApi.delete(key); }, /** * 判断该键是否存在 * @param key 键 */ hasKey(key) { return PopsPanelStorageApi.has(key); }, /** * 监听调用setValue、deleteValue * @param key 需要监听的键 * @param callback */ addValueChangeListener(key, callback) { let listenerId = PopsPanelStorageApi.addValueChangeListener( key, (__key, __newValue, __oldValue) => { callback(key, __oldValue, __newValue); } ); return listenerId; }, /** * 移除监听 * @param listenerId 监听的id */ removeValueChangeListener(listenerId) { PopsPanelStorageApi.removeValueChangeListener(listenerId); }, /** * 主动触发菜单值改变的回调 * @param key 菜单键 * @param newValue 想要触发的新值,默认使用当前值 * @param oldValue 想要触发的旧值,默认使用当前值 */ triggerMenuValueChange(key, newValue, oldValue) { PopsPanelStorageApi.triggerValueChangeListener(key, oldValue, newValue); }, /** * 移除已执行的仅执行一次的菜单 * @param key 键 */ deleteExecMenuOnce(key) { this.$data.onceExecMenuData.delete(key); let flag = PopsPanelStorageApi.removeValueChangeListener(key); return flag; }, /** * 移除已执行的仅执行一次的菜单 * @param key 键 */ deleteOnceExec(key) { this.$data.onceExecData.delete(key); }, /** * 执行菜单 * * @param queryKey 键|键数组 * @param callback 执行的回调函数 * @param checkExec 判断是否执行回调 * * (默认)如果想要每个菜单是`与`关系,即每个菜单都判断为开启,那么就判断它们的值&就行 * * 如果想要任意菜单存在true再执行,那么判断它们的值|就行 * * + 返回值都为`true`,执行回调,如果回调返回了 ` ) }); Comments.commentContainer = commentContainer; Comments.init(); let totalElement = domUtils.createElement("div", { className: "little-red-book-comments-total", innerHTML: `共 ${Comments.commentData["commentCount"] ?? Comments.noteData["comments"]} 条评论` }); commentContainer.appendChild(totalElement); if (Comments.commentData && Comments.commentData["comments"]) { log.info("从固定的评论中加载"); Comments.commentData["comments"].forEach((commentItem) => { let commentItemElement = Comments.getCommentElement(commentItem); commentContainer.appendChild(commentItemElement); }); } domUtils.append(noteViewContainer, commentContainer); }); }, /** * 优化图片浏览 */ optimizeImageBrowsing() { log.info("优化图片浏览"); CommonUtil.setGMResourceCSS(GM_RESOURCE_MAPPING.Viewer); function viewIMG(imgSrcList = [], index = 0) { let viewerULNodeHTML = ""; imgSrcList.forEach((item) => { viewerULNodeHTML += `
  • `; }); let viewerULNode = domUtils.createElement("ul", { innerHTML: viewerULNodeHTML }); let viewer = new __viewer(viewerULNode, { inline: false, url: "data-src", zIndex: utils.getMaxZIndex() + 100, hidden: () => { viewer.destroy(); } }); index = index < 0 ? 0 : index; viewer.view(index); viewer.zoomTo(1); viewer.show(); } domUtils.on(document, "click", ".note-image-box", function(event) { let clickElement = event.target; let imgElement = clickElement.querySelector("img"); let imgList = []; let imgBoxList = []; if (clickElement.closest(".onix-carousel-item")) { imgBoxList = Array.from( clickElement.closest(".onix-carousel-item").parentElement.querySelectorAll("img") ); } else { imgBoxList = [imgElement]; } let index = imgBoxList.findIndex((value) => { return value == imgElement; }); imgBoxList.forEach((element) => { let imgSrc = element.getAttribute("src") || element.getAttribute("data-src") || element.getAttribute("alt"); if (imgSrc) { imgList.push(imgSrc); } }); log.success(["点击浏览图片👉", imgList[index]]); viewIMG(imgList, index); }); } }; const M_XHSHome = { init() { domUtils.ready(() => { Panel.execMenuOnce("little-red-book-repariClick", () => { M_XHSHome.repariClick(); }); }); }, /** * 修复正确的点击跳转-用户主页 * 点啥都不好使,都会跳转至下载页面 */ repariClick() { log.info("修复正确的点击跳转"); domUtils.on( document, "click", void 0, function(event) { let clickElement = event.target; log.info(["点击的按钮元素", clickElement]); if (clickElement?.className?.includes("follow-btn")) { log.success("点击-关注按钮"); } else if (clickElement?.closest("button.reds-button.message-btn")) { log.success("点击-私信按钮"); } else if (clickElement?.closest("div.reds-tab-item")) { log.success("点击-笔记/收藏按钮"); } else if (clickElement?.closest("section.reds-note-card")) { log.success("点击-笔记卡片"); let sectionElement = clickElement?.closest( "section.reds-note-card" ); let note_id = sectionElement.getAttribute("id") || utils.toJSON(sectionElement.getAttribute("impression"))?.["noteTarget"]?.["value"]?.["noteId"]; if (note_id) { window.open( `https://www.xiaohongshu.com/discovery/item/${clickElement?.closest("section.reds-note-card")?.getAttribute("id")}`, "_blank" ); } else { Qmsg.error("获取笔记note_id失败"); } } utils.preventEvent(event); return false; }, { capture: true } ); } }; const M_XHS = { init() { Panel.execMenuOnce("little-red-book-shieldAd", () => { log.info("注入默认屏蔽CSS"); return addStyle(blockCSS$2); }); Panel.execMenuOnce("little-red-book-allowCopy", () => { return M_XHS.allowCopy(); }); if (ScriptRouter.isArticle()) { M_XHSArticle.init(); } else if (ScriptRouter.isUserHome()) { M_XHSHome.init(); } }, /** * 允许复制 */ allowCopy() { log.info("允许复制文字"); return addStyle( /*css*/ ` *{ -webkit-user-select: unset !important; user-select: unset !important; } ` ); } }; const blockCSS = ""; const XHSBlock = { init() { Panel.execMenuOnce("pc-xhs-shieldAd", () => { return addStyle(blockCSS); }); Panel.execMenuOnce("pc-xhs-shield-select-text-search-position", () => { return this.blockSelectTextVisibleSearchPosition(); }); Panel.execMenuOnce("pc-xhs-shield-topToolbar", () => { return this.blockTopToolbar(); }); domUtils.ready(() => { Panel.execMenuOnce("pc-xhs-shield-login-dialog", () => { this.blockLoginContainer(); }); }); }, /** * 屏蔽登录弹窗显示 */ blockLoginContainer() { log.info("添加屏蔽登录弹窗CSS,监听登录弹窗出现"); CommonUtil.addBlockCSS(".login-container"); utils.mutationObserver(document.body, { config: { subtree: true, childList: true }, callback: () => { let $close = document.querySelector( ".login-container .icon-btn-wrapper" ); if ($close) { $close.click(); log.success("登录弹窗出现,关闭"); } } }); }, /** * 屏蔽选择文字弹出的搜索提示 */ blockSelectTextVisibleSearchPosition() { log.info("屏蔽选择文字弹出的搜索提示"); return CommonUtil.addBlockCSS(".search-position"); }, /** * 【屏蔽】顶部工具栏 */ blockTopToolbar() { log.info("【屏蔽】顶部工具栏"); return [ CommonUtil.addBlockCSS("#headerContainer", ".header-container"), addStyle( /*css*/ ` /* 主内容去除padding */ #mfContainer{ padding-top: 0 !important; } .outer-link-container{ margin-top: 0 !important; height: 100vh !important; padding: 30px 0; } #noteContainer{ height: 100%; } ` ) ]; } }; const XHSUrlApi = { /** * 获取搜索链接 * @param searchText * @returns */ getSearchUrl(searchText) { return `https://www.xiaohongshu.com/search_result?keyword=${searchText}&source=web_explore_feed`; } }; const VueUtils = { /** * 获取vue2实例 * @param $el */ getVue($el) { if ($el == null) { return; } return $el["__vue__"] || $el["__Ivue__"] || $el["__IVue__"]; }, /** * 获取vue3实例 * @param $el */ getVue3($el) { if ($el == null) { return; } return $el["__vueParentComponent"]; }, /** * 等待vue属性并进行设置 * @param $el 目标对象 * @param checkOption 需要设置的配置 */ waitVuePropToSet($el, checkOption) { if (!Array.isArray(checkOption)) { checkOption = [checkOption]; } function getTarget() { let __target__ = null; if (typeof $el === "string") { __target__ = domUtils.selector($el); } else if (typeof $el === "function") { __target__ = $el(); } else if ($el instanceof HTMLElement) { __target__ = $el; } return __target__; } checkOption.forEach((needSetOption) => { if (typeof needSetOption.msg === "string") { log.info(needSetOption.msg); } function checkTarget() { let $targetEl = getTarget(); if ($targetEl == null) { return { status: false, isTimeout: true, inst: null, $el: $targetEl }; } let vueInst = VueUtils.getVue($targetEl); if (vueInst == null) { return { status: false, isTimeout: false, inst: null, $el: $targetEl }; } let checkResult = needSetOption.check(vueInst, $targetEl); checkResult = Boolean(checkResult); return { status: checkResult, isTimeout: false, inst: vueInst, $el: $targetEl }; } utils.waitVueByInterval( () => { return getTarget(); }, () => checkTarget().status, 250, 1e4 ).then((result) => { let checkTargetResult = checkTarget(); if (checkTargetResult.status) { let vueInst = checkTargetResult.inst; needSetOption.set(vueInst, checkTargetResult.$el); } else { if (typeof needSetOption.failWait === "function") { needSetOption.failWait(checkTargetResult.isTimeout); } } }); }); }, /** * 观察vue属性的变化 * @param $el 目标对象 * @param key 需要观察的属性 * @param callback 监听回调 * @param watchConfig 监听配置 * @param failWait 当检测失败/超时触发该回调 */ watchVuePropChange($el, key, callback, watchConfig, failWait) { let config = utils.assign( { immediate: true, deep: false }, watchConfig || {} ); return new Promise((resolve) => { VueUtils.waitVuePropToSet($el, { check(vueInstance) { return typeof vueInstance?.$watch === "function"; }, set(vueInstance) { let removeWatch = null; if (typeof key === "function") { removeWatch = vueInstance.$watch( () => { return key(vueInstance); }, (newValue, oldValue) => { callback(vueInstance, newValue, oldValue); }, config ); } else { removeWatch = vueInstance.$watch( key, (newValue, oldValue) => { callback(vueInstance, newValue, oldValue); }, config ); } resolve(removeWatch); }, failWait }); }); }, /** * 前往网址 * @param $el 包含vue属性的元素 * @param path 需要跳转的路径 * @param [useRouter=false] 是否强制使用Vue的Router来进行跳转,默认false */ goToUrl($el, path, useRouter = false) { if ($el == null) { Qmsg.error("跳转Url: $vueNode为空"); log.error("跳转Url: $vueNode为空:" + path); return; } let vueInstance = VueUtils.getVue($el); if (vueInstance == null) { Qmsg.error("获取vue属性失败", { consoleLogContent: true }); return; } let $router = vueInstance.$router; let isBlank = true; log.info("即将跳转URL:" + path); if (useRouter) { isBlank = false; } if (isBlank) { window.open(path, "_blank"); } else { if (path.startsWith("http") || path.startsWith("//")) { if (path.startsWith("//")) { path = window.location.protocol + path; } let urlObj = new URL(path); if (urlObj.origin === window.location.origin) { path = urlObj.pathname + urlObj.search + urlObj.hash; } else { log.info("不同域名,直接本页打开,不用Router:" + path); window.location.href = path; return; } } log.info("$router push跳转Url:" + path); $router.push(path); } }, /** * 手势返回 * @param option 配置 */ hookGestureReturnByVueRouter(option) { function popstateEvent() { log.success("触发popstate事件"); resumeBack(true); } function banBack() { log.success("监听地址改变"); option.vueInst.$router.history.push(option.hash); domUtils.on(_unsafeWindow, "popstate", popstateEvent); } async function resumeBack(isFromPopState = false) { domUtils.off(_unsafeWindow, "popstate", popstateEvent); let callbackResult = option.callback(isFromPopState); if (callbackResult) { return; } while (1) { if (option.vueInst.$router.history.current.hash === option.hash) { log.info("后退!"); option.vueInst.$router.back(); await utils.sleep(250); } else { return; } } } banBack(); return { resumeBack }; } }; const XHS_Article = { init() { if (Panel.getValue("pc-xhs-search-open-blank-btn") || Panel.getValue("pc-xhs-search-open-blank-keyboard-enter")) { this.optimizationSearch(); } Panel.execMenuOnce("pc-xhs-article-fullWidth", () => { return this.fullWidth(); }); }, /** * 优化搜索 */ optimizationSearch() { function blankSearchText(searchText, isBlank = true) { { let $searchText = document.querySelector("#search-input"); if ($searchText) { let searchText2 = $searchText.value; let searchUrl = XHSUrlApi.getSearchUrl(searchText2); log.info("搜索内容: " + searchText2); window.open(searchUrl, isBlank ? "_blank" : "_self"); } else { Qmsg.error("未找到搜索的输入框"); } } } utils.waitNode("#search-input").then(($searchInput) => { $searchInput.placeholder = "搜索小红书"; Panel.execMenu("pc-xhs-search-open-blank-keyboard-enter", () => { domUtils.listenKeyboard( $searchInput, "keydown", (keyName, keyValue, otherCodeList, event) => { if (keyName === "Enter" && !otherCodeList.length) { log.info("按下回车键"); utils.preventEvent(event); $searchInput.blur(); blankSearchText(); } } ); }); }); utils.waitNode("#search-input + .input-button .search-icon").then(($searchIconBtn) => { Panel.execMenu("pc-xhs-search-open-blank-btn", () => { domUtils.on( $searchIconBtn, "click", (event) => { utils.preventEvent(event); log.info("点击搜索按钮"); blankSearchText(); }, { capture: true } ); }); }); }, /** * 笔记宽屏 */ fullWidth() { log.info("笔记宽屏"); let noteContainerWidth = Panel.getValue( "pc-xhs-article-fullWidth-widthSize", 90 ); return addStyle( /*css*/ ` .main-container .main-content{ padding-left: 0 !important; } .outer-link-container{ width: 100% !important; } /* 隐藏左侧工具栏 */ .main-container .side-bar{ display: none !important; } #noteContainer{ width: ${noteContainerWidth}vw; } ` ); }, /** * 转换笔记发布时间 */ transformPublishTime() { log.info(`转换笔记发布时间`); let lockFn = new utils.LockFunction(() => { $$(".note-content:not([data-edit-date])").forEach( ($noteContent) => { let vueInstance = VueUtils.getVue($noteContent); if (!vueInstance) { return; } let note = vueInstance?._?.props?.note; if (note == null) { return; } let publishTime = note.time; let lastUpdateTime = note.lastUpdateTime; let ipLocation = note.ipLocation; if (typeof publishTime === "number") { let detailTimeLocationInfo = []; detailTimeLocationInfo.push( `发布:${utils.formatTime(publishTime)}` ); if (typeof lastUpdateTime === "number") { detailTimeLocationInfo.push( `修改:${utils.formatTime(lastUpdateTime)}` ); } if (typeof ipLocation === "string" && utils.isNotNull(ipLocation)) { detailTimeLocationInfo.push(ipLocation); } let $date = $noteContent.querySelector(".date"); domUtils.html($date, detailTimeLocationInfo.join("
    ")); $noteContent.setAttribute("data-edit-date", ""); } } ); }); utils.mutationObserver(document, { config: { subtree: true, childList: true }, callback: () => { lockFn.run(); } }); } }; const XHS = { init() { Panel.execMenuOnce("pc-xhs-hook-vue", () => { XHS_Hook.hookVue(); }); Panel.execMenuOnce("pc-xhs-allowCopy", () => { XHS.allowPCCopy(); }); Panel.execMenuOnce("pc-xhs-open-blank-article", () => { XHS.openBlankArticle(); }); XHSBlock.init(); Panel.execMenuOnce("pc-xhs-article-showPubsliushTime", () => { XHS_Article.transformPublishTime(); }); if (ScriptRouter.isArticle()) { log.info("Router: 笔记页面"); XHS_Article.init(); } }, /** * 允许复制 */ allowPCCopy() { log.success("允许复制文字"); domUtils.on( _unsafeWindow, "copy", void 0, function(event) { utils.preventEvent(event); let selectText = _unsafeWindow.getSelection(); if (selectText) { utils.setClip(selectText.toString()); } else { log.error("未选中任何内容"); } return false; }, { capture: true } ); }, /** * 新标签页打开文章 */ openBlankArticle() { log.success("新标签页打开文章"); domUtils.on( document, "click", ".feeds-container .note-item", function(event) { utils.preventEvent(event); let $click = event.target; let $url = $click.querySelector("a.cover[href]"); let url = $url?.href; if (url) { log.info("跳转文章: " + url); let urlInstance = new URL(url); urlInstance.pathname = urlInstance.pathname.replace( /^\/user\/profile\/[a-z0-9A-Z]+\//i, "/discovery/item/" ); url = urlInstance.toString(); window.open(url, "_blank"); } else { Qmsg.error("未找到文章链接"); } }, { capture: true } ); } }; const PanelComponents = { $data: { __storeApiFn: null, get storeApiValue() { if (!this.__storeApiFn) { this.__storeApiFn = new Utils.Dictionary(); } return this.__storeApiFn; } }, /** * 获取自定义的存储接口 * @param type 组件类型 */ getStorageApi(type) { if (!this.hasStorageApi(type)) { return; } return this.$data.storeApiValue.get(type); }, /** * 判断是否存在自定义的存储接口 * @param type 组件类型 */ hasStorageApi(type) { return this.$data.storeApiValue.has(type); }, /** * 设置自定义的存储接口 * @param type 组件类型 * @param storageApiValue 存储接口 */ setStorageApi(type, storageApiValue) { this.$data.storeApiValue.set(type, storageApiValue); }, /** * 初始化组件的存储接口属性 * * @param type 组件类型 * @param config 组件配置,必须包含prop属性 * @param storageApiValue 存储接口 */ initComponentsStorageApi(type, config, storageApiValue) { let propsStorageApi; if (this.hasStorageApi(type)) { propsStorageApi = this.getStorageApi(type); } else { propsStorageApi = storageApiValue; } this.setComponentsStorageApiProperty(config, propsStorageApi); }, /** * 设置组件的存储接口属性 * @param config 组件配置,必须包含prop属性 * @param storageApiValue 存储接口 */ setComponentsStorageApiProperty(config, storageApiValue) { Reflect.set(config.props, PROPS_STORAGE_API, storageApiValue); } }; const UISelect = function(text, key, defaultValue, data, changeCallback, description) { let selectData = []; if (typeof data === "function") { selectData = data(); } else { selectData = data; } let result = { text, type: "select", description, attributes: {}, props: {}, getValue() { let storageApiValue = this.props[PROPS_STORAGE_API]; return storageApiValue.get(key, defaultValue); }, callback(event, isSelectedValue, isSelectedText) { let value = isSelectedValue; log.info(`选择:${isSelectedText}`); if (typeof changeCallback === "function") { let result2 = changeCallback(event, value, isSelectedText); if (result2) { return; } } let storageApiValue = this.props[PROPS_STORAGE_API]; storageApiValue.set(key, value); }, data: selectData }; Reflect.set(result.attributes, ATTRIBUTE_KEY, key); Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue); PanelComponents.initComponentsStorageApi( "select", result, { get(key2, defaultValue2) { return Panel.getValue(key2, defaultValue2); }, set(key2, value) { Panel.setValue(key2, value); } } ); return result; }; const UISwitch = function(text, key, defaultValue, clickCallback, description, afterAddToUListCallBack) { let result = { text, type: "switch", description, attributes: {}, props: {}, getValue() { let storageApiValue = this.props[PROPS_STORAGE_API]; return Boolean(storageApiValue.get(key, defaultValue)); }, callback(event, __value) { let value = Boolean(__value); log.success(`${value ? "开启" : "关闭"} ${text}`); let storageApiValue = this.props[PROPS_STORAGE_API]; storageApiValue.set(key, value); }, afterAddToUListCallBack }; Reflect.set(result.attributes, ATTRIBUTE_KEY, key); Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue); PanelComponents.initComponentsStorageApi( "switch", result, { get(key2, defaultValue2) { return Panel.getValue(key2, defaultValue2); }, set(key2, value) { Panel.setValue(key2, value); } } ); return result; }; const SettingUI_Common = { id: "xhs-panel-config-common", title: "通用", forms: [ { text: "", type: "forms", forms: [ { text: "功能", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "允许复制", "pc-xhs-allowCopy", true, void 0, "可以选择文字并复制" ), UISwitch( "新标签页打开文章", "pc-xhs-open-blank-article", false, void 0, "点击文章不会在本页展开,会打开新标签页" ) ] } ] }, { text: "搜索", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "新标签页打开-搜索按钮", "pc-xhs-search-open-blank-btn", false, void 0, "点击右边的搜索按钮直接新标签页打开搜索内容" ), UISwitch( "新标签页打开-回车键", "pc-xhs-search-open-blank-keyboard-enter", false, void 0, "按下回车键直接新标签页打开搜索内容" ) ] } ] }, { text: "屏蔽", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "【屏蔽】广告", "pc-xhs-shieldAd", true, void 0, "屏蔽元素" ), UISwitch( "【屏蔽】登录弹窗", "pc-xhs-shield-login-dialog", true, void 0, "屏蔽会自动弹出的登录弹窗" ), UISwitch( "【屏蔽】选择文字弹出的搜索提示", "pc-xhs-shield-select-text-search-position", false, void 0, "屏蔽元素" ), UISwitch( "【屏蔽】顶部工具栏", "pc-xhs-shield-topToolbar", false, void 0, "屏蔽元素" ) ] } ] }, { text: "劫持/拦截", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "劫持Vue", "pc-xhs-hook-vue", true, void 0, "恢复__vue__属性" ) ] } ] }, { text: "Toast配置", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISelect( "Toast位置", "qmsg-config-position", "bottom", [ { value: "topleft", text: "左上角" }, { value: "top", text: "顶部" }, { value: "topright", text: "右上角" }, { value: "left", text: "左边" }, { value: "center", text: "中间" }, { value: "right", text: "右边" }, { value: "bottomleft", text: "左下角" }, { value: "bottom", text: "底部" }, { value: "bottomright", text: "右下角" } ], (event, isSelectValue, isSelectText) => { log.info("设置当前Qmsg弹出位置" + isSelectText); }, "Toast显示在页面九宫格的位置" ), UISelect( "最多显示的数量", "qmsg-config-maxnums", 3, [ { value: 1, text: "1" }, { value: 2, text: "2" }, { value: 3, text: "3" }, { value: 4, text: "4" }, { value: 5, text: "5" } ], void 0, "限制Toast显示的数量" ), UISwitch( "逆序弹出", "qmsg-config-showreverse", false, void 0, "修改Toast弹出的顺序" ) ] } ] } ] } ] }; const UISlider = function(text, key, defaultValue, min, max, changeCallback, getToolTipContent, description, step) { let result = { text, type: "slider", description, attributes: {}, props: {}, getValue() { let storageApiValue = this.props[PROPS_STORAGE_API]; return storageApiValue.get(key, defaultValue); }, getToolTipContent(value) { if (typeof getToolTipContent === "function") { return getToolTipContent(value); } else { return `${value}`; } }, callback(event, value) { if (typeof changeCallback === "function") { let result2 = changeCallback(event, value); if (result2) { return; } } let storageApiValue = this.props[PROPS_STORAGE_API]; storageApiValue.set(key, value); }, min, max, step }; Reflect.set(result.attributes, ATTRIBUTE_KEY, key); Reflect.set(result.attributes, ATTRIBUTE_DEFAULT_VALUE, defaultValue); PanelComponents.initComponentsStorageApi( "slider", result, { get(key2, defaultValue2) { return Panel.getValue(key2, defaultValue2); }, set(key2, value) { Panel.setValue(key2, value); } } ); return result; }; const SettingUI_Article = { id: "xhs-panel-config-article", title: "笔记", forms: [ { type: "forms", text: "功能", forms: [ UISwitch( "显示发布、修改的绝对时间", "pc-xhs-article-showPubsliushTime", false, void 0, "注:需要开启通用-劫持/拦截-劫持Vue" ) ] }, { text: "笔记宽屏", type: "forms", forms: [ UISwitch( "启用", "pc-xhs-article-fullWidth", false, void 0, `让笔记占据宽屏,当页面可视宽度>=960px时才会触发该功能,当前页面可视宽度: ${window.innerWidth}px` ), UISlider( "占据范围", "pc-xhs-article-fullWidth-widthSize", 90, 30, 100, (event, value) => { let $noteContainer = document.querySelector("#noteContainer"); if (!$noteContainer) { log.error("未找到笔记容器"); return; } $noteContainer.style.width = `${value}vw`; }, (value) => { return `${value}%,默认:90%`; }, "调整笔记页面占据的页面范围" ) ] } ] }; const MSettingUI_Common = { id: "little-red-book-panel-config-common", title: "通用", forms: [ { text: "", type: "forms", forms: [ { text: "Toast配置", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISelect( "Toast位置", "qmsg-config-position", "bottom", [ { value: "topleft", text: "左上角" }, { value: "top", text: "顶部" }, { value: "topright", text: "右上角" }, { value: "left", text: "左边" }, { value: "center", text: "中间" }, { value: "right", text: "右边" }, { value: "bottomleft", text: "左下角" }, { value: "bottom", text: "底部" }, { value: "bottomright", text: "右下角" } ], (event, isSelectValue, isSelectText) => { log.info("设置当前Qmsg弹出位置" + isSelectText); }, "Toast显示在页面九宫格的位置" ), UISelect( "最多显示的数量", "qmsg-config-maxnums", 3, [ { value: 1, text: "1" }, { value: 2, text: "2" }, { value: 3, text: "3" }, { value: 4, text: "4" }, { value: 5, text: "5" } ], void 0, "限制Toast显示的数量" ), UISwitch( "逆序弹出", "qmsg-config-showreverse", false, void 0, "修改Toast弹出的顺序" ) ] } ] } ] }, { text: "", type: "forms", forms: [ { text: "屏蔽", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "【屏蔽】广告", "little-red-book-shieldAd", true, void 0, "如:App内打开" ), UISwitch( "【屏蔽】底部搜索发现", "little-red-book-shieldBottomSearchFind", true, void 0, "建议开启" ), UISwitch( "【屏蔽】底部工具栏", "little-red-book-shieldBottomToorBar", true, void 0, "建议开启" ) ] } ] } // { // text: "劫持/拦截", // type: "deepMenu", // forms: [ // { // text: "", // type: "forms", // forms: [ // UISwitch( // "劫持Vue", // "little-red-book-hijack-vue", // true, // void 0, // "恢复__vue__属性" // ), // ], // }, // ], // }, ] } ] }; const MSettingUI_Home = { id: "little-red-book-panel-config-home", title: "主页", forms: [ { text: "", type: "forms", forms: [ { text: "劫持/拦截", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "劫持点击事件", "little-red-book-repariClick", true, void 0, "可阻止点击跳转至下载页面" ) ] } ] } ] } ] }; const MSettingUI_Notes = { id: "little-red-book-panel-config-note", title: "笔记", forms: [ { text: "", type: "forms", forms: [ { text: "视频笔记", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "优化视频描述", "little-red-book-optimizeVideoNoteDesc", true, void 0, "让视频描述可以滚动显示更多" ), UISwitch( "【屏蔽】作者热门笔记", "little-red-book-shieldAuthorHotNote", true, void 0, "建议开启" ), UISwitch( "【屏蔽】热门推荐", "little-red-book-shieldHotRecommendNote", true, void 0, "建议开启" ) ] } ] } ] }, { text: "", type: "forms", forms: [ { text: "功能", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "优化评论浏览", "little-red-book-optimizeCommentBrowsing", true, void 0, "目前仅可加载部分评论" ), UISwitch( "优化图片浏览", "little-red-book-optimizeImageBrowsing", true, void 0, "更方便的浏览图片" ), UISwitch( "允许复制", "little-red-book-allowCopy", true, void 0, "可以复制笔记的内容" ) ] } ] }, { text: "劫持/拦截", type: "deepMenu", forms: [ { text: "", type: "forms", forms: [ UISwitch( "劫持webpack-弹窗", "little-red-book-hijack-webpack-mask", true, void 0, "如:打开App弹窗、登录弹窗" ), UISwitch( "劫持webpack-唤醒App", "little-red-book-hijack-webpack-scheme", true, void 0, "禁止跳转商店小红书详情页/小红书" ) ] } ] } ] } ] }; addStyle( /*css*/ ` .qmsg svg.animate-turn { fill: none; } ` ); PanelContent.addContentConfig([SettingUI_Common, SettingUI_Article]); PanelContent.addContentConfig([ MSettingUI_Common, MSettingUI_Home, MSettingUI_Notes ]); const defaultMenuOption = PanelMenu.getMenuOption(); defaultMenuOption.text = "⚙ PC-设置"; PanelMenu.updateMenuOption(defaultMenuOption); PanelMenu.addMenuOption({ key: "show_mobile_setting", text: "⚙ 移动端-设置", autoReload: false, isStoreValue: false, showText(text) { return text; }, callback: () => { Panel.showPanel(PanelContent.getConfig(1), `${_SCRIPT_NAME_}-移动端设置`); } }); Panel.init(); let isMobile = utils.isPhone(); let CHANGE_ENV_SET_KEY = "change_env_set"; let chooseMode = _GM_getValue(CHANGE_ENV_SET_KEY); GM_Menu.add({ key: CHANGE_ENV_SET_KEY, text: `⚙ 自动: ${isMobile ? "移动端" : "PC端"}`, autoReload: false, isStoreValue: false, showText(text) { if (chooseMode == null) { return text; } return text + ` 手动: ${chooseMode == 1 ? "移动端" : chooseMode == 2 ? "PC端" : "未知"}`; }, callback: () => { let allowValue = [0, 1, 2]; let chooseText = window.prompt( "请输入当前脚本环境判定\n\n自动判断: 0\n移动端: 1\nPC端: 2", "0" ); if (!chooseText) { return; } let chooseMode2 = parseInt(chooseText); if (isNaN(chooseMode2)) { Qmsg.error("输入的不是规范的数字"); return; } if (!allowValue.includes(chooseMode2)) { Qmsg.error("输入的值必须是0或1或2"); return; } if (chooseMode2 == 0) { _GM_deleteValue(CHANGE_ENV_SET_KEY); } else { _GM_setValue(CHANGE_ENV_SET_KEY, chooseMode2); } } }); if (chooseMode != null) { log.info(`手动判定为${chooseMode === 1 ? "移动端" : "PC端"}`); if (chooseMode == 1) { M_XHS.init(); } else if (chooseMode == 2) { XHS.init(); } else { Qmsg.error("意外,手动判定的值不在范围内"); _GM_deleteValue(CHANGE_ENV_SET_KEY); } } else { if (isMobile) { log.info("自动判定为移动端"); M_XHS.init(); } else { log.info("自动判定为PC端"); XHS.init(); } } })(Qmsg, DOMUtils, Utils, pops, Viewer);