// ==UserScript==
// @name 知乎摸鱼版
// @namespace http://tampermonkey.net/
// @version 1.05
// @icon https://static.zhihu.com/heifetz/assets/apple-touch-icon-152.81060cab.png
// @description 图形设置:隐藏标题|图片马赛克|去侧边|伪装标题|官方暗黑模式一键切换
// @author Jay628
// @match https://www.zhihu.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @run-at document-start
// @license MIT
// @downloadURL none
// ==/UserScript==
(function () {
'use strict';
/* ==================== 1. 默认配置 ==================== */
const DEFAULT = {
hideHeader: true, // 滚动隐藏问题标题栏
imgMosaic : true, // 图片马赛克
hideSide : true, // 隐藏右侧边栏
fakeTitle : true, // 伪装标题
titleText : 'Excel 在线文档 - 只读',
darkMode : false // 官方暗黑模式
};
/* ==================== 2. 配置读写 ==================== */
function getConf(key) { return GM_getValue(key, DEFAULT[key]); }
function setConf(key, val) { GM_setValue(key, val); }
/* ==================== 3. 官方主题切换(首次进入/手动切换) ==================== */
(function ensureTheme(){
const wantDark = getConf('darkMode');
if (location.search.includes(`theme=${wantDark?'dark':'light'}`)) return; // 已是指定主题
const url = new URL(location.href);
url.searchParams.set('theme', wantDark?'dark':'light');
location.replace(url); // 整页跳转,让知乎真正加载主题样式
})();
/* ==================== 4. 图形设置面板 ==================== */
function openSettingPanel() {
const box = document.createElement('div');
box.innerHTML = `
`;
document.body.appendChild(box);
box.querySelector('.save').onclick = () => {
['hideHeader','imgMosaic','hideSide','fakeTitle','darkMode'].forEach(k=> setConf(k,box.querySelector('#'+k).checked));
setConf('titleText',box.querySelector('#titleText').value.trim()||DEFAULT.titleText);
box.remove(); location.reload();
};
box.querySelector('.cancel').onclick = () => box.remove();
}
GM_registerMenuCommand('⚙️ 摸鱼脚本设置', openSettingPanel);
if (!GM_getValue('_inited',false)) { GM_setValue('_inited',true); openSettingPanel(); }
/* ==================== 5. 功能实现 ==================== */
const isQuestionPage = /^\/question\/\d+/.test(location.pathname);
/* ① 滚动隐藏标题栏(仅问题页) */
if (getConf('hideHeader') && isQuestionPage) {
let lastScrollTop = 0;
const headerSelector = '.QuestionHeader-main';
function handleScroll() {
const st = window.pageYOffset || document.documentElement.scrollTop;
const header = document.querySelector(headerSelector);
if (!header) return;
if (st > lastScrollTop && st > 100) {
header.style.transition = 'opacity 0.3s';
header.style.opacity = '0';
header.style.pointerEvents = 'none';
} else {
header.style.opacity = '1';
header.style.pointerEvents = 'auto';
}
lastScrollTop = st <= 0 ? 0 : st;
}
window.addEventListener('scroll', handleScroll);
}
/* ② 图片马赛克(仅问题页) */
if (getConf('imgMosaic') && isQuestionPage) {
const STYLE_ID = 'zh-mosaic-style';
const MOSAIC_CLASS = 'zh-mosaic-cover';
function addMosaicStyles() {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement('style');
style.id = STYLE_ID;
style.textContent = `
.${MOSAIC_CLASS}{position:relative;display:inline-block;}
.${MOSAIC_CLASS}::before{content:'';position:absolute;inset:0;background:rgba(0,0,0,.35);backdrop-filter:blur(12px) grayscale(80%);z-index:1;cursor:pointer;}
.${MOSAIC_CLASS}.revealed::before{display:none;}
`;
document.head.appendChild(style);
}
function applyMosaic(img) {
if (img.dataset.mosaicApplied) return;
img.dataset.mosaicApplied = '1';
const par = img.parentElement;
if (getComputedStyle(par).position === 'static') par.style.position = 'relative';
par.classList.add(MOSAIC_CLASS);
par.addEventListener('click', () => par.classList.add('revealed'), { once: true });
}
function processImages() {
document.querySelectorAll('.RichContent-inner img, .QuestionAnswer-content img').forEach(img => {
if (img.closest('video, [class*="Video"], [class*="video"], [class*="Player"], [class*="player"], [data-video], [data-zop-video], [class*="VideoPreview"], [class*="video-preview"], ._1fog6rx, ._1n8yr2n, ._w3ddjq, .css-bp306l')) return;
applyMosaic(img);
});
}
addMosaicStyles(); processImages();
new MutationObserver(() => processImages()).observe(document.body, { childList: true, subtree: true });
}
/* ③ 隐藏右侧边栏(全站) */
if (getConf('hideSide')) {
const style = document.createElement('style');
style.textContent = `
.Question-sideColumn, .Card.QuestionHeader-actions, .Card.QuestionHeader-side, .Card.Question-sideColumn--light,
[data-za-detail-view-path-module="RightSideBar"], .GlobalSideBar, .QuestionPage-sideBar { display:none !important; }
`;
document.head.appendChild(style);
}
/* ④ 伪装标题(全站) */
if (getConf('fakeTitle')) {
const fake = getConf('titleText');
document.title = fake;
new MutationObserver(() => { if (document.title !== fake) document.title = fake; })
.observe(document.querySelector('title'), { childList: true, characterData: true, subtree: true });
const originalPushState = history.pushState;
history.pushState = function (...args) { originalPushState.apply(this, args); document.title = fake; };
}
/* ⑥ 每日自动随机伪装标题 */
(function dailyRandomTitle() {
// 无害短语库,可继续追加
const POOL = [
'Excel 在线文档 - 只读',
'工作台 - 待办事项',
'邮件草稿箱 - Outlook',
'会议纪要 - 模板',
'数据透视表练习',
'周报 - 第 21 周',
'费用报销单',
'培训资料 - 2025',
'项目进度表 - 内部',
'考勤异常说明',
'VPN 使用指南',
'入职手续清单',
'离职交接表',
'资产领用单',
'印章申请流程',
'预算表 - v3',
'采购需求汇总',
'客户信息更新',
'合同审批单',
'发票抬头维护',
'OKR 复盘表',
'KPI 指标说明',
'SOP 标准文档',
'内部知识库',
'系统操作手册',
'测试用例 - 登录模块',
'需求文档 - PRD',
'版本发布记录',
'BUG 列表 - 本周',
'代码 Review 记录',
'接口文档 - Swagger',
'数据库变更记录',
'服务器巡检报告',
'安全漏洞扫描报告',
'运维值班表',
'上线 checklist',
'回滚方案 - 模板',
'灾备演练报告',
'带宽申请单',
'SSL 证书续费',
'域名备案资料',
'云资源申请',
'CDN 配置说明',
'Docker 镜像清单',
'Jenkins 构建日志',
'Git 提交规范',
'Sonar 扫描报告',
'单元测试覆盖率',
'性能压测报告',
'埋点需求文档',
'A/B 测试方案',
'用户调研问卷',
'竞品分析报告',
'PRD 评审记录',
'UI 走查意见',
'设计规范 - V1.2',
'图标库更新',
'字体授权说明',
'配色方案 - 2025',
'原型图 - 登录页',
'交互说明文档',
'需求变更记录',
'版本管理规范',
'API 限流策略',
'缓存更新方案',
'数据库优化记录',
'日志清理策略',
'监控告警配置',
'值班电话表 - 2025'
];
const KEY_MANUAL = 'titleManual'; // 用户是否手动改过
const KEY_DATE = 'titleDate'; // 最后一次随机日期
function isToday(ds) { return ds === new Date().toISOString().slice(0, 10); }
function randomItem(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
// 1. 如果用户手动输入过标题 → 不再自动随机
if (GM_getValue(KEY_MANUAL, false)) return;
// 2. 如果今天已随机过 → 直接用它
const lastDate = GM_getValue(KEY_DATE, '');
if (isToday(lastDate)) {
const todayTitle = GM_getValue('titleText', '');
if (todayTitle) { DEFAULT.titleText = todayTitle; return; }
}
// 3. 否则随机一条并写入配置
const newTitle = randomItem(POOL);
GM_setValue('titleText', newTitle);
GM_setValue(KEY_DATE, new Date().toISOString().slice(0, 10));
// 4. 立即生效(当前页)
if (getConf('fakeTitle')) document.title = newTitle;
})();
/* 监听用户“手动输入” → 停止自动随机 */
const _origSetTitle = setConf;
setConf = function (key, val) {
if (key === 'titleText' && val && typeof val === 'string' && val.trim() !== '') {
GM_setValue('titleManual', true); // 标记为手动
GM_setValue(KEY_DATE, ''); // 清空日期,方便以后想恢复随机
}
_origSetTitle.apply(this, arguments);
};
})();