Eink Polish

优化网页与浏览器插件在 eink 设备上的显示

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Eink Polish
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  优化网页与浏览器插件在 eink 设备上的显示
// @author       chen
// @match        https://*/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict'; // 开启严格模式,让浏览器更规范地执行代码

    // 1. 定义我们要强制生效的 CSS 样式
    const css = `
        p, h1, h2, h3, h4, h5, h6, a, div, span, li, table, small, sup, select {
            /* --- 基础清理:禁用动画和特效 --- */
            animation: none !important;     /* 禁用所有动画 */
            transition: none !important;    /* 禁用所有渐变过渡 */
            box-shadow: none !important;    /* 禁用所有元素阴影 */
            filter: none !important;        /* 禁用所有滤镜(比如模糊效果) */

            /* --- 文字 --- */
            font-weight: bold !important;   /* 加粗,提高墨水屏上的辨识度 */
            color: #000 !important;       /* 强制纯黑文字 */
            text-shadow:                    /* 给文字加物理描边,对抗发虚 */
                -0.5px -0.5px 0 #FFF,
                0.5px -0.5px 0 #FFF,
                -0.5px  0.5px 0 #FFF,
                0.5px  0.5px 0 #FFF;

            /* --- 控件 --- */
            border-color: #333333 !important;      /* 让边框变成深色,防止按钮背景变白后找不到按钮在哪里 */
            
        }
    `;

    // 2. 核心动作:把上面的 CSS 注入到指定的“区域”
    function injectStyle(target) {
        // 如果这个区域已经打过我们留下的标记,说明注入过了,直接跳过,防止重复添加
        if (target.__styleInjected) return;

        // 创建一个 <style> 标签,并把我们的 CSS 文本塞进去
        const style = document.createElement('style');
        style.textContent = css;

        // 把 <style> 标签正式插入到目标区域中
        target.appendChild(style);

        // 给这个区域打上标记,表示“我已经处理过啦”
        target.__styleInjected = true;
    }

    // 3. 扫描网页节点:寻找并处理那些带有 Shadow DOM 的元素
    // 因为你提到“不存在嵌套情况”,所以我们只需要查一次表面即可,不需要无限往下深挖
    function scanAndInject(node) {
        // 第一步:如果当前检查的这个元素自身就是一个带有 Shadow DOM 的节点,直接注入
        if (node.shadowRoot) {
            injectStyle(node.shadowRoot);
        }

        // 第二步:如果这个元素是个正常的 HTML 标签(比如 <div>),找找它肚子里有没有带 Shadow DOM 的子元素
        if (node.querySelectorAll) {
            // querySelectorAll('*') 会把当前节点下的所有子孙元素都找出来
            node.querySelectorAll('*').forEach(el => {
                if (el.shadowRoot) {
                    injectStyle(el.shadowRoot);
                }
            });
        }
    }

    // ==========================================
    // 下面是脚本的执行主流程
    // ==========================================

    // 【新增修复】原代码漏掉了最关键的一步:给网页本身(主 DOM)注入样式!
    // 我们必须先给主网页注入,这样网页大部分内容的动画和阴影才会被消除
    injectStyle(document.head || document.documentElement);

    // 页面结构刚加载完时,进行一次全局扫描(主要是扫出已经存在的 Shadow DOM)
    window.addEventListener('DOMContentLoaded', () => {
        scanAndInject(document.body);
    });

    // 创建一个“监视器” (MutationObserver)
    // 作用:现在很多网页(比如 Substack)往下滑动时会动态加载新文章或评论
    // 这个监视器就是盯着网页,一旦有新元素加进来,立刻对新元素进行扫描和去动画处理
    const observer = new MutationObserver(mutations => {
        mutations.forEach(m => {
            m.addedNodes.forEach(newNode => {
                // nodeType === 1 代表它是一个正常的 HTML 标签元素(排除了普通的纯文本文字)
                if (newNode.nodeType === 1) {
                    scanAndInject(newNode);
                }
            });
        });
    });

    // 启动监视器,盯着整个网页 (document)
    // childList: true 监视子元素的增加或删除
    // subtree: true 不仅监视儿子,连孙子、曾孙子(所有后代)的变动也要监视
    observer.observe(document, {
        childList: true,
        subtree: true
    });

})();