Readwise 阅读优化

使用innerHTML保留原始格式与符号位置,同时分句、对▍特殊处理,并将<br>转换为新段落实现缩进

目前为 2024-12-18 提交的版本。查看 最新版本

// ==UserScript==
// @name         Readwise 阅读优化
// @namespace    readwise.reader
// @version      2.0
// @description  使用innerHTML保留原始格式与符号位置,同时分句、对▍特殊处理,并将<br>转换为新段落实现缩进
// @match        https://read.readwise.io/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 添加自定义字体与段落样式
    const styleSheet = document.createElement('style');
    styleSheet.textContent = `
        @font-face {
            font-family: "仓耳华新体";
            src: url("https://ziti-beta.vercel.app/fonts/仓耳华新体.ttf") format("truetype");
            font-weight: normal;
            font-style: normal;
        }

        body, body * {
            font-family: "仓耳华新体", sans-serif !important;
        }

        p {
            text-indent: 2em !important;
            margin-bottom: 1em !important;
            line-height: 1.8 !important;
            display: block !important;
        }

        /* 处理链接样式 */
        p em {
            display: block !important;
            margin: 0.5em 0 !important;
            text-indent: 0 !important;
        }

        /* 处理以br开头的段落 */
        p[data-formatted="true"] br:first-child {
            display: none !important;
        }

        li p {
            text-indent: 0 !important;
        }
    `;
    document.head.appendChild(styleSheet);

    function splitParagraphsOnBr(p) {
        const html = p.innerHTML;
        if (html.includes('<br')) {
            // 按 <br> 分割文本,并过滤空白
            const parts = html.split(/<br\s*\/?>/i).map(s => s.trim()).filter(s => s);
            if (parts.length > 1) {
                const fragment = document.createDocumentFragment();
                parts.forEach(part => {
                    const newP = document.createElement('p');
                    newP.innerHTML = part;
                    newP.dataset.formatted = 'true';
                    fragment.appendChild(newP);
                });
                p.parentNode.replaceChild(fragment, p);
            }
        }
    }

    function formatAllParagraphs() {
        const paras = document.querySelectorAll('p:not([data-formatted])');
        let formattedCount = 0;

        paras.forEach(para => {
            let html = para.innerHTML.trim();
            if (!html) {
                para.dataset.formatted = 'true';
                return;
            }

            // 处理链接段落
            if (html.includes('[') && html.includes(']') && html.includes('http')) {
                const links = html.match(/\[.*?\].*?(?=\[|$)/g);
                if (links) {
                    const fragment = document.createDocumentFragment();
                    links.forEach(link => {
                        const newP = document.createElement('p');
                        newP.innerHTML = link.trim();
                        newP.dataset.formatted = 'true';
                        fragment.appendChild(newP);
                    });
                    para.parentNode.replaceChild(fragment, para);
                    return;
                }
            }

            // 在句子末尾插入<split>
            html = html.replace(/([。!?!?])([^"'])/g, '$1<split>$2');

            let segments = html.split(/<split>/).map(s => s.trim()).filter(s => s);

            if (segments.length <= 1) {
                para.dataset.formatted = 'true';
                return;
            }

            const fragment = document.createDocumentFragment();

            segments.forEach(segment => {
                if (segment.includes('▍')) {
                    let parts = segment.split('▍').map(part => part.trim()).filter(p => p);
                    if (parts.length > 0) {
                        const firstP = document.createElement('p');
                        firstP.innerHTML = parts[0];
                        firstP.dataset.formatted = 'true';
                        fragment.appendChild(firstP);

                        for (let i = 1; i < parts.length; i++) {
                            const newP = document.createElement('p');
                            newP.innerHTML = '▍' + parts[i];
                            newP.dataset.formatted = 'true';
                            fragment.appendChild(newP);
                        }
                    }
                } else {
                    const newP = document.createElement('p');
                    newP.innerHTML = segment;
                    newP.dataset.formatted = 'true';
                    fragment.appendChild(newP);
                }
            });

            para.parentNode.replaceChild(fragment, para);
            formattedCount++;
        });

        if (formattedCount > 0) {
            console.log(`分句成功:${formattedCount} 个段落已重新分段`);
        }

        // 对已格式化的段落进行 <br> 分割
        // 这不会造成之前逻辑问题,因为只对 data-formatted 的内容操作
        document.querySelectorAll('p[data-formatted="true"]').forEach(p => {
            splitParagraphsOnBr(p);
        });

        return formattedCount;
    }

    const observer = new MutationObserver(() => {
        formatAllParagraphs();
    });
    observer.observe(document.body, { childList: true, subtree: true });

    document.addEventListener('DOMContentLoaded', () => {
        formatAllParagraphs();
        setTimeout(formatAllParagraphs, 500);
        setTimeout(formatAllParagraphs, 1000);
        setTimeout(formatAllParagraphs, 2000);
    });

    let attempts = 0;
    const maxAttempts = 20;
    const intervalId = setInterval(() => {
        const count = formatAllParagraphs();
        attempts++;
        if (count > 0 || attempts >= maxAttempts) {
            clearInterval(intervalId);
        }
    }, 500);

    window.addEventListener('load', () => {
        formatAllParagraphs();
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址