Greasy Fork

来自缓存

Greasy Fork is available in English.

知乎++

清理标题括号数字,替换消息/私信按钮,收藏夹可配置,解除复制限制,隐藏侧边栏,并增强过滤干扰内容、调整布局,重新对阅读页面卡片化设计,美化界面极大提升阅读体验,新增护眼色开关(全局持久化)

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         知乎++
// @description  清理标题括号数字,替换消息/私信按钮,收藏夹可配置,解除复制限制,隐藏侧边栏,并增强过滤干扰内容、调整布局,重新对阅读页面卡片化设计,美化界面极大提升阅读体验,新增护眼色开关(全局持久化)
// @namespace    http://tampermonkey.net/
// @icon         https://www.zhihu.com/favicon.ico
// @license      MIT
// @version      4.0
// @author       ddrwin
// @match        *://*/*
// @match        https://*.zhihu.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @note         2026.03.22 V1.0  全局清理网页标题开头的未读数字提示(如 (3条消息))
// @note         2026.03.23 V1.2  将首页消息/私信按钮替换为收藏夹、最近浏览
// @note         2026.03.24 V1.3  隐藏右侧边栏及创作中心,清理消息/私信按钮上的通知数字
// @note         2026.03.25 V2.1  创作中心改为可配置的收藏设置,允许清空地址,解除复制版权限制,仅保留纯净文本
// @note         2026.03.25 V2.2  增加文章页居中并自适应宽度,自动点击“查看全部回答”,隐藏首页“热榜”和“视频”选项卡,只保留“推荐”
// @note         2026.03.25 V3.0  首页、搜索结果页、问题页答案卡片美化,热榜条目卡片美化;过滤推荐页中的视频、文章、链接卡片、教育卡片、专栏、电商等干扰内容,过滤答案页中的视频答案,调整推荐页、问题页、搜索页主列宽度为900px,在问题页标题下方显示修改时间, 隐藏搜索框占位符,加大问题标题字体
// @note         2026.03.26 V3.1  统一卡片样式为 Baidu++ 风格(浅绿色背景、蓝色边框、阴影)
// @note         2026.03.27 V3.5  动态更新页面类型类,改用 CSS 变量方案实现护眼色,彻底消除 SPA 路由切换后类残留导致的样式错乱失效的问题
// @note         2026.03.28 V3.9  彻底解决 SPA 路由切换后类残留导致的样式错乱;新增圈子页面(/ring)完整样式支持;优化专栏主页及个人主页卡片布局。
// @note         2026.03.28 V4.0  设置面板增加动画开关,新增 ANIM_KEY 存储键,默认值 true(默认开启动画),与护眼色独立存储、独立控制。
// ==/UserScript==

(function() {
    'use strict';

    // ============================================================
    // 1. 颜色配置:两套色板,面板切换时只改 CSS 变量,样式规则不变
    // ============================================================

    /**
     * 在这里集中管理两套颜色。
     * - softEye:护眼绿色系
     * - white:默认白色系
     *
     * 新增/修改颜色时只需改这里,CSS 规则无需动。
     */
    const COLOR_PALETTES = {
        softEye: {
            cardBg:          '#F5FCFA',
            cardBgHover:     '#E9F7F3',
            cardBorder:      '#3D7CD4',
            cardBorderHover: '#2c6fc7',
            cardShadow:      '0 8px 20px rgba(0,0,0,0.15)',
            cardShadowHover: '0 12px 28px rgba(0,0,0,0.2)',
        },
        white: {
            cardBg:          '#ffffff',
            cardBgHover:     '#ffffff',
            cardBorder:      '#e1e8ed',
            cardBorderHover: '#cbd5e0',
            cardShadow:      '0 2px 8px rgba(0,0,0,0.08)',
            cardShadowHover: '0 8px 20px rgba(0,0,0,0.12)',
        },
    };

    // ============================================================
    // 2. 配置存储
    // ============================================================

    const STORAGE_KEY   = 'zhihu_collection_url';
    const SOFT_EYE_KEY  = 'zhihu_soft_eye';
    const ANIM_KEY      = 'zhihu_anim';

    const getCollectionUrl  = ()    => GM_getValue(STORAGE_KEY, '');
    const saveCollectionUrl = (url) => {
        if (url === null || url === undefined) return false;
        GM_setValue(STORAGE_KEY, url.trim());
        return true;
    };

    const getSoftEyeEnabled = ()        => GM_getValue(SOFT_EYE_KEY, true);
    const setSoftEyeEnabled = (enabled) => {
        GM_setValue(SOFT_EYE_KEY, enabled);
        applyTheme();
    };

    const getAnimEnabled = ()        => GM_getValue(ANIM_KEY, true);
    const setAnimEnabled = (enabled) => {
        GM_setValue(ANIM_KEY, enabled);
        applyTheme();
    };

    // ============================================================
    // 3. 核心:用 CSS 变量驱动主题切换
    //
    //    CSS 变量写在 document.documentElement(即 <html>)上,
    //    不依赖 body class,SPA 路由切换、框架重渲染都不会丢失,
    //    无需任何 MutationObserver 来"防御"。
    // ============================================================

    /**
     * 把对应色板的值写入 CSS 自定义属性。
     * 调用一次即永久生效,直到页面关闭或再次调用。
     */
    const applyTheme = () => {
        const softEye = getSoftEyeEnabled();
        const anim    = getAnimEnabled();
        const palette = COLOR_PALETTES[softEye ? 'softEye' : 'white'];
        const root    = document.documentElement;

        root.style.setProperty('--zh-card-bg',     palette.cardBg);
        root.style.setProperty('--zh-card-border', palette.cardBorder);
        root.style.setProperty('--zh-card-shadow', palette.cardShadow);

        if (anim) {
            root.style.setProperty('--zh-card-bg-hover',        palette.cardBgHover);
            root.style.setProperty('--zh-card-border-hover',    palette.cardBorderHover);
            root.style.setProperty('--zh-card-shadow-hover',    palette.cardShadowHover);
            root.style.setProperty('--zh-card-transform-hover', 'translateY(-2px)');
            root.style.setProperty('--zh-transition',           'all 0.3s ease');
        } else {
            root.style.setProperty('--zh-card-bg-hover',        palette.cardBg);
            root.style.setProperty('--zh-card-border-hover',    palette.cardBorder);
            root.style.setProperty('--zh-card-shadow-hover',    palette.cardShadow);
            root.style.setProperty('--zh-card-transform-hover', 'translateY(0)');
            root.style.setProperty('--zh-transition',           'none');
        }
    };
    const applySoftEye = () => applyTheme();

    // ============================================================
    // 4. 设置面板
    // ============================================================

    const showSettingsPanel = () => {
        if (document.getElementById('zh-settings-panel')) return;

        const currentUrl = getCollectionUrl();

        const overlay = document.createElement('div');
        overlay.id = 'zh-settings-overlay';
        overlay.style.cssText = `
            position: fixed;
            top: 0; left: 0;
            width: 100%; height: 100%;
            background: rgba(0,0,0,0.4);
            z-index: 10000;
            display: flex;
            align-items: center;
            justify-content: center;
        `;

        const panel = document.createElement('div');
        panel.id = 'zh-settings-panel';
        panel.style.cssText = `
            background: #fff;
            border-radius: 16px;
            box-shadow: 0 8px 24px rgba(0,0,0,0.15);
            width: 360px;
            padding: 24px 20px;
            text-align: left;
            font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif;
        `;
        panel.innerHTML = `
            <h3 style="margin: 0 0 20px 0; font-size: 20px; font-weight: 600; text-align: center;">知乎++ 设置</h3>
            <div style="margin-bottom: 20px;">
                <label style="display: block; font-size: 14px; font-weight: 500; margin-bottom: 8px;">📁 收藏夹地址</label>
                <input type="text" id="collection-url-input" value="${currentUrl.replace(/"/g, '&quot;')}" style="
                    width: 100%;
                    padding: 8px 12px;
                    border: 1px solid #d0d7de;
                    border-radius: 8px;
                    font-size: 14px;
                    box-sizing: border-box;
                ">
                <div style="font-size: 12px; color: #6c757d; margin-top: 6px;">
                    例如:https://www.zhihu.com/collection/12345678<br>
                    留空并点击确认可清除地址
                </div>
            </div>
            <label style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
                <span style="font-size: 14px;">🌿 护眼色模式</span>
                <input type="checkbox" id="soft-eye-checkbox" style="width: 18px; height: 18px;">
            </label>
            <label style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px;">
                <span style="font-size: 14px;">✨ 悬浮动画效果</span>
                <input type="checkbox" id="anim-checkbox" style="width: 18px; height: 18px;">
            </label>
            <div style="display: flex; gap: 12px;">
                <button id="confirm-settings-btn" style="
                    background: #3D7CD4;
                    border: none; color: white;
                    padding: 8px 16px;
                    border-radius: 8px;
                    font-size: 14px;
                    cursor: pointer; flex: 1;
                ">确认</button>
                <button id="close-settings-btn" style="
                    background: #e9ecef;
                    border: none; color: #495057;
                    padding: 8px 16px;
                    border-radius: 8px;
                    font-size: 14px;
                    cursor: pointer; flex: 1;
                ">取消</button>
            </div>
        `;
        overlay.appendChild(panel);
        document.body.appendChild(overlay);

        const input      = document.getElementById('collection-url-input');
        const checkbox     = document.getElementById('soft-eye-checkbox');
        const animCheckbox = document.getElementById('anim-checkbox');
        const confirmBtn   = document.getElementById('confirm-settings-btn');
        const cancelBtn    = document.getElementById('close-settings-btn');

        checkbox.checked     = getSoftEyeEnabled();
        animCheckbox.checked = getAnimEnabled();

        confirmBtn.addEventListener('click', () => {
            saveCollectionUrl(input.value.trim());
            GM_setValue(SOFT_EYE_KEY, checkbox.checked);
            GM_setValue(ANIM_KEY, animCheckbox.checked);
            applyTheme();
            overlay.remove();
        });
        cancelBtn.addEventListener('click', () => overlay.remove());
    };

    // ============================================================
    // 5. 全局标题清理
    // ============================================================

    const cleanTitle = () => {
        const titleElem = document.getElementsByTagName('title')[0];
        if (!titleElem) return;
        const newTitle = titleElem.innerText.replace(/^\(\d+(?:[^)]*)?\)\s*/, '');
        if (newTitle !== titleElem.innerText) titleElem.innerText = newTitle;
    };
    cleanTitle();

    const titleElem = document.querySelector('title');
    if (titleElem) {
        const titleObserver = new MutationObserver(() => {
            titleObserver.disconnect();
            cleanTitle();
            titleObserver.observe(titleElem, { childList: true });
        });
        titleObserver.observe(titleElem, { childList: true });
    }

    // ============================================================
    // 6. 知乎专属功能
    // ============================================================

    if (window.location.hostname.includes('zhihu.com')) {

        // ---------- 6.1 解除复制版权限制 ----------
        document.addEventListener('copy', (e) => {
            const selection = window.getSelection();
            if (selection && selection.toString().trim() !== '') {
                const plainText = selection.toString();
                e.clipboardData.setData('text/plain', plainText);
                e.clipboardData.setData('text/html', plainText.replace(/\n/g, '<br>'));
                e.preventDefault();
                e.stopPropagation();
                console.log('[知乎定制] 已拦截复制,仅保留纯净文本');
            }
        }, { capture: true });

        // ---------- 6.2 样式注入 ----------
        //
        //  所有卡片颜色属性统一引用 CSS 变量(--zh-card-*)。
        //  切换护眼色时只需改变量值,这段 CSS 无需修改。
        //  新增颜色属性:在 COLOR_PALETTES 里加字段,
        //  在 applySoftEye 里加一行 setProperty,在这里加一处 var() 引用即可。
        //
        const style = document.createElement('style');
        style.textContent = `
            /* 隐藏消息/私信弹窗 */
            .PushNotifications-menuContainer,
            .Messages-menuContainer,
            [id^="Popover"][class*="PushNotifications"],
            [id^="Popover"][class*="Messages"] {
                display: none !important;
            }

            /* 隐藏右侧边栏 */
            .Post-Row-Content-right,
            .css-1qyytj7,
            div[data-za-detail-view-path-module="RightSideBar"] {
                display: none !important;
            }

            /* 隐藏顶部导航栏的"付费咨询"和"知学堂"按钮 */
            a[href="https://www.zhihu.com/consult"],
            a[href="https://www.zhihu.com/education/learning"] {
                display: none !important;
            }

            /* 内容宽度自适应 */
            .Post-Row-Content-left {
                max-width: 100% !important;
                width: 90% !important;
                margin-left: 0 !important;
                margin-right: auto !important;
            }
            .Post-Row-Content-left-article {
                max-width: 100% !important;
            }

            /* 首页:只保留"推荐"选项卡 */
            a[aria-controls="Topstory-hot"],
            a[aria-controls="Topstory-zvideo"],
            ul[class~="AppHeader-Tabs"] li:not(:first-child) {
                display: none !important;
            }

            /* 隐藏搜索框占位符 */
            ::placeholder {
                color: transparent !important;
            }

            /* 推荐页过滤干扰内容 */
            .TopstoryItem--advertCard,
            .TopstoryItem-isRecommend:has(.VideoAnswerPlayer-video),
            .TopstoryItem-isRecommend:has(.ZVideoItem-video),
            .TopstoryItem-isRecommend:has(.RichText-video),
            .TopstoryItem-isRecommend:has(.CopyrightRichText-richTex),
            .TopstoryItem-isRecommend:has(.RichText-LinkCardContainer),
            .TopstoryItem-isRecommend:has(.RichText-EduCardContainer),
            .TopstoryItem-isRecommend:has(div[data-za-extra-module*="Post"]),
            .TopstoryItem-isRecommend:has(.RichText-Ecommerce),
            .TopstoryItem-isRecommend:has(.ecommerce-ad-box) {
                display: none !important;
            }

            /* 问题页隐藏视频答案 */
            .AnswerItem:has(.VideoCard-video-content),
            .AnswerItem:has(.VideoAnswerPlayer) {
                display: none !important;
            }

            /* 主列宽度调整 */
            .Topstory-container,
            .Topstory-mainColumn,
            .Question-main,
            .Question-mainColumn,
            .Search-container,
            .SearchMain {
                width: 900px !important;
            }

            /* 问题页显示修改时间 */
            meta[itemprop="dateModified"] {
                display: block;
                height: 20px;
                padding: 10px 0;
                margin-top: 10px;
            }
            meta[itemprop="dateModified"]::after {
                content: "修改时间: " attr(content);
                color: #8590a6;
                font-size: 14px;
            }

            /* 问题页标题字体加大 */
            .ContentItem-title {
                font-size: x-large !important;
            }

            /* ===== 卡片样式:全部使用 CSS 变量,护眼/白色均由变量决定 ===== */
            .TopstoryItem:not(.TopstoryItem-feedList),
            .SearchResult-Card,
            .Question-mainColumn .AnswerItem,
            .RelevantQuery,
            .HotItem,
            .css-i83kfi,
            .TopicFeedItem,
            .Post-Main.Post-NormalMain,
            .CollectionDetailPageItem {
                background:    var(--zh-card-bg)     !important;
                border:    1px solid var(--zh-card-border)   !important;
                border-radius: 12px                  !important;
                box-shadow:    var(--zh-card-shadow)  !important;
                margin-bottom: 20px                  !important;
                padding:       20px 24px             !important;
                transition:    var(--zh-transition)  !important;
            }
            .TopstoryItem:not(.TopstoryItem-feedList):hover,
            .SearchResult-Card:hover,
            .Question-mainColumn .AnswerItem:hover,
            .RelevantQuery:hover,
            .HotItem:hover,
            .css-i83kfi:hover,
            .TopicFeedItem:hover,
            .Post-Main.Post-NormalMain:hover,
            .CollectionDetailPageItem:hover {
                background:  var(--zh-card-bg-hover)     !important;
                border-color: var(--zh-card-border-hover) !important;
                box-shadow:  var(--zh-card-shadow-hover)  !important;
                transform:   var(--zh-card-transform-hover) !important;
            }
            /* 问题页答案卡片特殊处理 */
            .Question-mainColumn .AnswerItem {
                margin-bottom: 0px !important;
            }
            /* 专栏页已更内容特殊处理 */
            .css-1a2rdla,
            .css-oarhve {
                margin-bottom: 20px !important;
            }

            /* 拼接答案卡片:与现有卡片共享 CSS 变量,hover 效果一致 */
            .zh-injected-answer-card {
                background:    var(--zh-card-bg)     !important;
                border:    1px solid var(--zh-card-border)   !important;
                border-radius: 12px                  !important;
                box-shadow:    var(--zh-card-shadow)  !important;
                margin-bottom: 20px                  !important;
                padding:       20px 24px             !important;
                transition:    var(--zh-transition)  !important;
            }
            .zh-injected-answer-card:hover {
                background:  var(--zh-card-bg-hover)     !important;
                border-color: var(--zh-card-border-hover) !important;
                box-shadow:  var(--zh-card-shadow-hover)  !important;
                transform:   var(--zh-card-transform-hover) !important;
            }

            /* 相关搜索模块内部样式 */
            .RelevantQuery h2 {
                font-size: 18px !important;
                font-weight: 600 !important;
                margin-bottom: 16px !important;
            }
            .RelevantQuery ul {
                display: flex !important;
                flex-wrap: wrap !important;
                gap: 12px !important;
                list-style: none !important;
                padding-left: 0 !important;
            }
            .RelevantQuery li { margin: 0 !important; }
            .RelevantQuery li a {
                display: inline-block !important;
                padding: 6px 14px !important;
                background: #ffffff !important;
                border-radius: 20px !important;
                color: #3476d2 !important;
                text-decoration: none !important;
                font-size: 14px !important;
                transition: all 0.2s ease !important;
            }
            .RelevantQuery li a:hover {
                background: #eef2f6 !important;
                color: #0f4c81 !important;
                transform: scale(1.02) !important;
            }

            /* 隐藏搜索结果页右侧边栏 */
            .css-knqde { display: none !important; }

            /* 浏览历史页主内容区域加宽至900px */
            .css-9511cm {
                width: 900px !important;
                max-width: 900px !important;
                margin: 0 auto !important;
            }

            /* 收藏页主内容区域加宽至900px */
            .CollectionsDetailPage-mainColumn {
                width: 900px !important;
                max-width: 900px !important;
                margin: 0 auto !important;
            }

            /* 专栏主页内容区域加宽至900px(仅 html.zh-column 生效) */
            html.zh-column .css-1pariuy,
            html.zh-column .css-jqhguc,
            html.zh-column .css-44kk6u,
            html.zh-column .css-1u9sxdg{
                width: 900px !important;
                max-width: 900px !important;
            }

            /* 专栏主页文章卡片样式(仅 html.zh-column 生效,不影响其他页面) */
            html.zh-column .css-9w3zhd {
                width: 860px !important;
                background:    var(--zh-card-bg)     !important;
                border:    1px solid var(--zh-card-border)   !important;
                border-radius: 12px                  !important;
                box-shadow:    var(--zh-card-shadow)  !important;
                margin-bottom: 20px                  !important;
                margin-left: 20px                  !important;
                padding:       20px 24px             !important;
                transition:    var(--zh-transition)  !important;
            }
            html.zh-column .css-9w3zhd:hover {
                background:  var(--zh-card-bg-hover)     !important;
                border-color: var(--zh-card-border-hover) !important;
                box-shadow:  var(--zh-card-shadow-hover)  !important;
                transform:   var(--zh-card-transform-hover) !important;
            }

            /* 专题页文章外壳容器:仅保留上下间距,不加卡片装饰 */
            html.zh-column .ContentItem.ArticleItem {
                margin-bottom: 20px !important;
                background:    transparent !important;
                border:        none !important;
                box-shadow:    none !important;
                padding:       0 !important;
            }

            /* 个人主页:页头宽度 900px */
            html.zh-people .ProfileHeader,
            html.zh-people .Profile-main,
            html.zh-people .css-16mzn5c,
            html.zh-people .Profile-mainColumn {
                max-width: 900px !important;
                width:     900px !important;
                margin:    0 auto !important;
            }

            /* 个人主页:回答卡片样式 */
            html.zh-people .ContentItem.AnswerItem,
            html.zh-people .ContentItem.ArticleItem,
            html.zh-people .ContentItem.PinItem {
                background:    var(--zh-card-bg)     !important;
                border:    1px solid var(--zh-card-border)   !important;
                border-radius: 12px                  !important;
                box-shadow:    var(--zh-card-shadow)  !important;
                margin-bottom: 0px                  !important;
                padding:       20px 24px             !important;
                transition:    var(--zh-transition)  !important;
            }
            html.zh-people .ContentItem.AnswerItem:hover,
            html.zh-people .ContentItem.ArticleItem:hover,
            html.zh-people .ContentItem.PinItem:hover {
                background:  var(--zh-card-bg-hover)     !important;
                border-color: var(--zh-card-border-hover) !important;
                box-shadow:  var(--zh-card-shadow-hover)  !important;
                transform:   var(--zh-card-transform-hover) !important;
            }


            /* 圈子页面:页头宽度 900px */
            html.zh-ring .css-1g878q7 {
                max-width: 900px !important;
                width:     900px !important;
                margin:    0 auto !important;
            }


            /* 圈子页面(/ring-feeds):PinItem 卡片样式 */
            html.zh-ring .ContentItem.PinItem {
                background:    var(--zh-card-bg)     !important;
                border:    1px solid var(--zh-card-border)   !important;
                border-radius: 12px                  !important;
                box-shadow:    var(--zh-card-shadow)  !important;
                margin-bottom: 0px                  !important;
                padding:       20px 24px             !important;
                transition:    var(--zh-transition)  !important;
            }
            html.zh-ring .ContentItem.PinItem:hover {
                background:  var(--zh-card-bg-hover)     !important;
                border-color: var(--zh-card-border-hover) !important;
                box-shadow:  var(--zh-card-shadow-hover)  !important;
                transform:   var(--zh-card-transform-hover) !important;
            }
        `;
        document.head.appendChild(style);

        // 页面加载时立即把对应色板写入 CSS 变量,一次搞定
        applyTheme();

        // ---------- 6.2.1 动态更新页面类型类(解决 SPA 路由切换后类残留问题) ----------
        /**
         * 根据当前 URL 给 <html> 添加对应的类(zh-column / zh-people / zh-ring),
         * 并移除旧的类,确保样式只作用于正确页面。
         */
        const updatePageClass = () => {
            const host = window.location.hostname;
            const path = window.location.pathname;

            // 先移除所有可能已有的类(避免累积)
            document.documentElement.classList.remove('zh-column', 'zh-people', 'zh-ring');

            // zh-column:专栏/专题/文章详情相关页面
            if (
                // zhuanlan.zhihu.com/c_xxx(专栏主页)、/p/xxx(文章详情)
                (host === 'zhuanlan.zhihu.com' && /^\/(c_|p\/)/.test(path))
                // www.zhihu.com/column/c_xxx(专题主页)、/column-square(专栏广场)
                || (host === 'www.zhihu.com' && /^\/(column\/|column-square)/.test(path))
            ) {
                document.documentElement.classList.add('zh-column');
            }

            // zh-people:个人主页
            if (host === 'www.zhihu.com' && /^\/people\//.test(path)) {
                document.documentElement.classList.add('zh-people');
            }

            // zh-ring:圈子页面(/ring-feeds 和 /ring/host/xxx)
            if (host === 'www.zhihu.com' && /^\/ring/.test(path)) {
                document.documentElement.classList.add('zh-ring');
            }
        };

        // 初始调用一次
        updatePageClass();

        // 劫持 history.pushState 和 replaceState,以便在 SPA 路由切换时更新类
        const originalPushState = history.pushState;
        const originalReplaceState = history.replaceState;

        history.pushState = function(...args) {
            originalPushState.apply(this, args);
            // 延迟更新,等待 DOM 变化完成
            setTimeout(updatePageClass, 100);
        };

        history.replaceState = function(...args) {
            originalReplaceState.apply(this, args);
            setTimeout(updatePageClass, 100);
        };

        // 监听浏览器前进/后退事件
        window.addEventListener('popstate', () => setTimeout(updatePageClass, 100));

        // 补充:监听 title 变化作为"页面已渲染"信号(比 setTimeout 更准确)
        // 知乎每次 SPA 路由完成后都会更新 <title>,此时 DOM 已稳定
        const titleElemForRoute = document.querySelector('title');
        if (titleElemForRoute) {
            new MutationObserver(() => updatePageClass()).observe(titleElemForRoute, { childList: true });
        }

        // ---------- 6.3 答案文章页:原地注入全部回答 + 无限滚动 ----------
        //
        //  仅在单答案页 (/question/xxx/answer/xxx) 生效。
        //  - 拦截"查看全部回答"按钮,阻止跳转,改为在当前页尾部拼接答案列表。
        //  - 利用知乎 /api/v4/questions/{id}/feeds 接口分批加载,
        //    用 paging.next 游标驱动无限滚动,行为与问题页一致。
        //
        const answerPageMatch = window.location.pathname.match(/\/question\/(\d+)\/answer\/\d+/);
        if (answerPageMatch) {
            const questionId = answerPageMatch[1];
            let injected    = false;
            let nextFeedUrl = null;
            let loadingMore = false;

            // 当前答案 ID,用于过滤拼接列表中的重复项
            const currentAnswerId = String(window.location.pathname.match(/\/answer\/(\d+)/)[1]);

            // 修复懒加载图片:把 data-actualsrc 替换为真实 src
            const fixLazyImages = (container) => {
                // 方式一:处理 <img data-actualsrc="...">
                container.querySelectorAll('img[data-actualsrc]').forEach(img => {
                    img.src = img.dataset.actualsrc;
                    img.removeAttribute('data-actualsrc');
                });
                // 方式二:处理被 <noscript> 包裹的图片(知乎 SSR 场景)
                container.querySelectorAll('noscript').forEach(ns => {
                    const tmp = document.createElement('div');
                    tmp.innerHTML = ns.textContent;
                    const img = tmp.querySelector('img');
                    if (img) {
                        // 用 data-original 或 src 中第一个真实地址
                        const realSrc = img.dataset.original || img.src;
                        if (realSrc && !realSrc.startsWith('data:')) {
                            img.src = realSrc;
                            img.removeAttribute('data-original');
                            ns.replaceWith(img);
                        }
                    }
                });
                // 方式三:使用 IntersectionObserver 按需加载剩余懒图
                const lazyImgs = container.querySelectorAll('img[data-original]');
                if (lazyImgs.length === 0) return;
                const imgObserver = new IntersectionObserver((entries, obs) => {
                    entries.forEach(entry => {
                        if (!entry.isIntersecting) return;
                        const img = entry.target;
                        if (img.dataset.original) {
                            img.src = img.dataset.original;
                            img.removeAttribute('data-original');
                        }
                        obs.unobserve(img);
                    });
                }, { rootMargin: '200px' });
                lazyImgs.forEach(img => imgObserver.observe(img));
            };

            // 构建单条答案卡片 DOM(与现有卡片样式共享 CSS 变量)
            const buildAnswerCard = (ans) => {
                const author     = ans.author || {};
                const name       = author.name || '匿名用户';
                const avatar     = author.avatar_url || '';
                const profileUrl = author.url_token
                    ? `https://www.zhihu.com/people/${author.url_token}` : '#';
                const voteup     = (ans.voteup_count  || 0).toLocaleString();
                const comments   = ans.comment_count  || 0;
                const rawContent = ans.content        || ans.excerpt || '';
                const answerUrl  = `https://www.zhihu.com/question/${questionId}/answer/${ans.id}`;
                const timeStr    = ans.updated_time
                    ? new Date(ans.updated_time * 1000)
                        .toLocaleDateString('zh-CN', { year:'numeric', month:'2-digit', day:'2-digit' })
                    : '';

                const div = document.createElement('div');
                div.className = 'zh-injected-answer-card';

                // 头部:头像 + 作者信息 + 赞同数
                const header = document.createElement('div');
                header.style.cssText = 'display:flex;align-items:center;gap:10px;margin-bottom:14px';
                header.innerHTML = `
                    ${avatar
                        ? `<img src="${avatar}" style="width:36px;height:36px;border-radius:50%;flex-shrink:0" alt="${name}">`
                        : `<div style="width:36px;height:36px;border-radius:50%;background:#e0e7ef;flex-shrink:0"></div>`}
                    <div style="min-width:0">
                        <a href="${profileUrl}" target="_blank"
                           style="font-weight:600;color:inherit;text-decoration:none;display:block">${name}</a>
                        ${author.headline
                            ? `<div style="font-size:12px;color:#8590a6;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${author.headline}</div>`
                            : ''}
                    </div>
                    <span style="margin-left:auto;color:#8590a6;font-size:13px;white-space:nowrap">▲ ${voteup}</span>
                `;
                div.appendChild(header);

                // 正文:用 innerHTML 注入,然后再修复图片(避免 innerHTML 字符串拼接破坏内容)
                const body = document.createElement('div');
                body.className = 'RichText ztext';
                body.style.cssText = 'font-size:15px;line-height:1.75;word-break:break-word';
                body.innerHTML = rawContent;
                fixLazyImages(body);          // 注入后立刻修复图片
                div.appendChild(body);

                // 底部:时间 + 评论数 + 原答案链接
                const footer = document.createElement('div');
                footer.style.cssText = 'margin-top:14px;font-size:13px;color:#8590a6;display:flex;gap:16px;align-items:center;flex-wrap:wrap';
                footer.innerHTML = `
                    ${timeStr ? `<span>${timeStr}</span>` : ''}
                    <span>${comments} 条评论</span>
                    <a href="${answerUrl}" target="_blank" style="color:#3D7CD4;text-decoration:none;margin-left:auto">查看原答案 →</a>
                `;
                div.appendChild(footer);

                return div;
            };

            // 请求一批答案并追加到列表容器
            const fetchAndAppend = async (list, url) => {
                if (loadingMore) return;
                loadingMore = true;

                const loader = document.createElement('div');
                loader.style.cssText = 'text-align:center;padding:24px;color:#8590a6;font-size:14px';
                loader.textContent = '加载中…';
                list.appendChild(loader);

                try {
                    const resp = await fetch(url);
                    if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
                    const json = await resp.json();

                    loader.remove();

                    (json.data || []).forEach(item => {
                        const t = item.target;
                        // 跳过当前正在阅读的答案,避免重复展示
                        if (t && t.id && t.content !== undefined && String(t.id) !== currentAnswerId) {
                            list.appendChild(buildAnswerCard(t));
                        }
                    });

                    // paging.next 是下一页完整 URL,直接使用
                    nextFeedUrl = (json.paging && !json.paging.is_end)
                        ? json.paging.next : null;

                    if (!nextFeedUrl) {
                        const end = document.createElement('div');
                        end.style.cssText = 'text-align:center;padding:24px;color:#8590a6;font-size:14px';
                        end.textContent = '— 已加载全部回答 —';
                        list.appendChild(end);
                    }
                } catch (e) {
                    loader.textContent = '⚠️ 加载失败,请稍后重试';
                    console.error('[知乎定制] 答案加载失败', e);
                    nextFeedUrl = url; // 保留当前 URL,下次滚动可重试
                }

                loadingMore = false;
            };

            // 构建注入容器并启动首次加载
            const injectAnswerSection = async () => {
                if (injected) return;
                injected = true;

                // 找到当前答案卡片作为锚点
                const anchor = document.querySelector('.Card.AnswerCard')
                    || document.querySelector('.QuestionAnswer-content')?.closest('.Card');
                if (!anchor) { injected = false; return; }

                // 外层包装
                const wrapper = document.createElement('div');
                wrapper.id = 'zh-all-answers-wrapper';
                wrapper.style.marginTop = '24px';

                // 标题行
                const heading = document.createElement('div');
                heading.style.cssText = `
                    font-size: 18px; font-weight: 600;
                    margin-bottom: 16px; padding-bottom: 12px;
                    border-bottom: 2px solid var(--zh-card-border);
                    color: inherit;
                `;
                heading.textContent = '全部回答';
                wrapper.appendChild(heading);

                // 答案列表区域
                const list = document.createElement('div');
                list.id = 'zh-answer-list';
                wrapper.appendChild(list);

                // 滚动哨兵(位于列表最底部)
                const sentinel = document.createElement('div');
                sentinel.id = 'zh-answer-sentinel';
                sentinel.style.height = '4px';
                wrapper.appendChild(sentinel);

                anchor.parentNode.insertBefore(wrapper, anchor.nextSibling);

                // 首次加载
                const include = encodeURIComponent(
                    'data[*].content,is_normal,author,voteup_count,comment_count,updated_time,excerpt'
                );
                const firstUrl = `https://www.zhihu.com/api/v4/questions/${questionId}/feeds`
                    + `?include=${include}&limit=5&offset=0&platform=desktop&sort_by=default`;

                await fetchAndAppend(list, firstUrl);

                // IntersectionObserver 驱动无限滚动
                const scrollObserver = new IntersectionObserver((entries) => {
                    if (entries[0].isIntersecting && nextFeedUrl && !loadingMore) {
                        fetchAndAppend(list, nextFeedUrl);
                    }
                }, { rootMargin: '400px' });
                scrollObserver.observe(sentinel);
            };

            // 拦截"查看全部回答"按钮:阻止跳转,改为原地注入
            const interceptViewAllBtns = () => {
                document.querySelectorAll('.Card.ViewAll .QuestionMainAction.ViewAll-QuestionMainAction')
                    .forEach(btn => {
                        if (btn.dataset.zhIntercepted) return;
                        btn.dataset.zhIntercepted = 'true';
                        btn.addEventListener('click', (e) => {
                            e.preventDefault();
                            e.stopPropagation();
                            injectAnswerSection().then(() => {
                                setTimeout(() => {
                                    document.getElementById('zh-all-answers-wrapper')
                                        ?.scrollIntoView({ behavior: 'smooth', block: 'start' });
                                }, 100);
                            });
                        }, { capture: true });
                    });
            };

            // 自动触发(无需手动点按钮)
            const autoInject = () => {
                interceptViewAllBtns();
                injectAnswerSection();
            };

            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', autoInject);
            } else {
                autoInject();
            }

            // 按钮可能是异步渲染的,持续监听
            const viewAllObserver = new MutationObserver(interceptViewAllBtns);
            viewAllObserver.observe(document.body, { childList: true, subtree: true });
            setTimeout(() => viewAllObserver.disconnect(), 15 * 1000);
        }

        // ---------- 6.4 消息/私信按钮替换 ----------
        const configs = [
            {
                ariaLabel:    '通知',
                svgClass:     'Bell',
                getUrl:       () => getCollectionUrl(),
                newText:      '收藏',
                newAriaLabel: '收藏夹',
            },
            {
                ariaLabel:    '私信',
                svgClass:     'Chat',
                getUrl:       () => 'https://www.zhihu.com/recent-viewed',
                newText:      '最近浏览',
                newAriaLabel: '最近浏览',
            },
        ];

        const modifiedContainers = new Set();

        const findButton = (cfg) => {
            let btn = document.querySelector(`button[aria-label="${cfg.ariaLabel}"]`);
            if (!btn) {
                const svg = document.querySelector(`svg[class*="${cfg.svgClass}"]`);
                if (svg) btn = svg.closest('button');
            }
            return btn;
        };

        const hideBadge = (container) => {
            const badge = container.querySelector('.css-dkw0ir');
            if (badge) badge.style.display = 'none';
        };

        const modifyButton = (cfg) => {
            const btn = findButton(cfg);
            if (!btn) return null;
            const container = btn.closest('.Popover') || btn;
            if (modifiedContainers.has(container)) return container;

            const fix = () => {
                const span = container.querySelector('.css-vurnku');
                if (span) {
                    if (span.textContent !== cfg.newText) span.textContent = cfg.newText;
                } else {
                    for (let node of container.childNodes) {
                        if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === cfg.ariaLabel) {
                            if (node.textContent !== cfg.newText) node.textContent = cfg.newText;
                            break;
                        }
                    }
                }
                if (container.getAttribute('aria-label') !== cfg.newAriaLabel) {
                    container.setAttribute('aria-label', cfg.newAriaLabel);
                }
                hideBadge(container);
            };
            fix();

            container.addEventListener('click', (e) => {
                fix();
                e.stopPropagation();
                e.preventDefault();
                const targetUrl = cfg.getUrl();
                if (targetUrl && targetUrl.trim() !== '') {
                    window.location.href = targetUrl;
                } else if (cfg.ariaLabel === '通知') {
                    showSettingsPanel();
                } else {
                    console.warn('[知乎定制] 未获取到有效URL');
                }
            }, { capture: true });

            container.style.cursor = 'pointer';
            modifiedContainers.add(container);
            console.log(`[知乎定制] 已修改"${cfg.ariaLabel}"按钮为"${cfg.newText}"`);
            return container;
        };

        const initButtons = () => { for (const cfg of configs) modifyButton(cfg); };

        if (document.readyState === 'loading') {
            document.addEventListener('DOMContentLoaded', initButtons);
        } else {
            initButtons();
        }

        const btnObserver = new MutationObserver(() => {
            for (const cfg of configs) {
                const btn = findButton(cfg);
                if (!btn) continue;
                const container = btn.closest('.Popover') || btn;
                if (!modifiedContainers.has(container)) {
                    modifyButton(cfg);
                } else {
                    const span = container.querySelector('.css-vurnku');
                    let current = span ? span.textContent : '';
                    if (!span) {
                        for (let node of container.childNodes) {
                            if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === cfg.ariaLabel) {
                                current = node.textContent.trim();
                                break;
                            }
                        }
                    }
                    if (current !== cfg.newText) {
                        if (span) span.textContent = cfg.newText;
                        else {
                            for (let node of container.childNodes) {
                                if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === cfg.ariaLabel) {
                                    node.textContent = cfg.newText;
                                    break;
                                }
                            }
                        }
                        container.setAttribute('aria-label', cfg.newAriaLabel);
                        console.log(`[知乎定制] 修正了"${cfg.ariaLabel}"按钮文字(被重置)`);
                    }
                    hideBadge(container);
                }
            }
        });
        btnObserver.observe(document.body, { childList: true, subtree: true });
        setTimeout(() => btnObserver.disconnect(), 5 * 60 * 1000);

        // ---------- 6.5 创作中心改为"++设置"按钮 ----------
        const modifyCreator = () => {
            const creatorBtn = document.querySelector('a[href="https://www.zhihu.com/creator"]');
            if (!creatorBtn) return;
            if (creatorBtn.hasAttribute('data-customized')) return;
            creatorBtn.setAttribute('data-customized', 'true');

            const textSpan = creatorBtn.querySelector('.css-vurnku');
            if (textSpan) textSpan.textContent = '++设置';
            else {
                for (let node of creatorBtn.childNodes) {
                    if (node.nodeType === Node.TEXT_NODE && node.textContent.trim() === '创作中心') {
                        node.textContent = '++设置';
                        break;
                    }
                }
            }
            creatorBtn.setAttribute('aria-label', '设置');
            creatorBtn.style.cursor = 'pointer';
            creatorBtn.style.marginLeft = '12px';
            creatorBtn.removeAttribute('href');
            creatorBtn.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                showSettingsPanel();
            });

            console.log('[知乎定制] 创作中心已改为"++设置"按钮');
        };
        modifyCreator();

        const creatorObserver = new MutationObserver(() => modifyCreator());
        creatorObserver.observe(document.body, { childList: true, subtree: true });
        setTimeout(() => creatorObserver.disconnect(), 5 * 60 * 1000);
    }
})();