知识星球文章去水印、复制为 Markdown

知识星球辅助,文章去水印、可手动复制,可以一键复制为 Markdown

// ==UserScript==
// @name         知识星球文章去水印、复制为 Markdown
// @namespace    http://tampermonkey.net/
// @version      0.0.1
// @description  知识星球辅助,文章去水印、可手动复制,可以一键复制为 Markdown
// @author       Loner1024
// @match        https://articles.zsxq.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=zsxq.com
// @require      https://cdnjs.cloudflare.com/ajax/libs/turndown/7.1.1/turndown.min.js
// @grant        GM_setClipboard
// @grant        GM_addStyle
// @license MIT
// ==/UserScript==

(function() {
    'use strict';
    let body = document.getElementsByTagName('body')[0];
    body.classList.remove("js-disable-copy");
    let post = document.getElementsByClassName('post')[0];
    post.style.removeProperty('background-image');
    post.style.removeProperty('background-repeat');
    post.style.removeProperty("background-size");

      // 添加样式
    GM_addStyle(`
        #md-copy-btn {
            position: fixed;
            bottom: 20px;
            right: 20px;
            padding: 10px 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
            z-index: 9999;
            font-family: Arial, sans-serif;
            font-size: 14px;
        }
        #md-copy-btn:hover {
            background-color: #45a049;
        }
        #md-status {
            position: fixed;
            bottom: 70px;
            right: 20px;
            padding: 8px 12px;
            background-color: rgba(0,0,0,0.7);
            color: white;
            border-radius: 5px;
            font-family: Arial, sans-serif;
            font-size: 14px;
            z-index: 9999;
            display: none;
        }
    `);

    // 创建复制按钮
    function createCopyButton() {
        const button = document.createElement('button');
        button.id = 'md-copy-btn';
        button.textContent = '复制文章为Markdown';
        button.addEventListener('click', extractAndCopyArticle);

        const status = document.createElement('div');
        status.id = 'md-status';
        status.textContent = '';

        document.body.appendChild(button);
        document.body.appendChild(status);
    }

    // 显示状态消息
    function showStatus(message, isError = false) {
        const status = document.getElementById('md-status');
        status.textContent = message;
        status.style.backgroundColor = isError ? 'rgba(255,0,0,0.7)' : 'rgba(0,0,0,0.7)';
        status.style.display = 'block';

        setTimeout(() => {
            status.style.display = 'none';
        }, 3000);
    }

    // 在转换前预处理HTML,保留空白
    // 在turndown处理前运行此函数
    function preserveCodeIndentation() {
        // 找到所有代码块容器
        const codeContainers = document.querySelectorAll('.ql-code-block-container');

        codeContainers.forEach(container => {
            // 处理每个代码行
            const codeBlocks = container.querySelectorAll('.ql-code-block');

            codeBlocks.forEach(block => {
                // 获取原始HTML
                const originalHtml = block.innerHTML;

                // 检查是否有前导空格
                const leadingWhitespaceMatch = originalHtml.match(/^(\s| )+/);

                if (leadingWhitespaceMatch) {
                    // 计算空格数量
                    const whitespace = leadingWhitespaceMatch[0]
                    .replace(/ /g, ' ')
                    .replace(/\s/g, ' ');

                    // 创建用于显示空格的元素
                    const spacesSpan = document.createElement('span');
                    spacesSpan.className = 'preserved-indent';
                    spacesSpan.setAttribute('data-spaces', whitespace.length);
                    spacesSpan.textContent = whitespace;

                    // 替换原始HTML中的空格
                    const contentWithoutIndent = originalHtml.substring(leadingWhitespaceMatch[0].length);
                    block.innerHTML = '';
                    block.appendChild(spacesSpan);

                    // 添加剩余内容
                    const contentSpan = document.createElement('span');
                    contentSpan.innerHTML = contentWithoutIndent;
                    block.appendChild(contentSpan);
                }
            });
        });
    }


    // 提取文章内容并复制到剪贴板
    function extractAndCopyArticle() {
        const articles = document.getElementsByClassName('content');

        if (articles.length === 0) {
            showStatus('未找到内容', true);
            return;
        }
        preserveCodeIndentation()

        // 创建Turndown实例
        const turndownService = new TurndownService({
            headingStyle: 'atx',
            codeBlockStyle: 'fenced',
            emDelimiter: '*'
        });

        // 增强Turndown以更好地处理代码块
        turndownService.addRule('codeBlocks', {
            filter: function (node, options) {
                return (
                    node.nodeName === 'DIV' &&
                    node.classList.contains('ql-code-block-container')
                );
            },
            replacement: function(content, node) {
                // 提取代码块内容
                let codeLines = [];
                const codeBlocks = node.querySelectorAll('.ql-code-block');

                // 检测语言
                let language = 'go'; // 假设是Go语言,可根据实际情况调整

                // 处理每一行代码
                codeBlocks.forEach(block => {
                    // 查找缩进元素
                    const indentSpan = block.querySelector('.preserved-indent');
                    let indentSpaces = 0;

                    // 如果找到缩进元素,获取缩进数量
                    if (indentSpan) {
                        indentSpaces = parseInt(indentSpan.getAttribute('data-spaces') || '0', 10);
                        // 移除缩进元素以便获取纯文本内容
                        indentSpan.remove();
                    }

                    // 获取纯文本内容
                    let textContent = block.textContent;

                    // 添加缩进和内容
                    codeLines.push(' '.repeat(indentSpaces) + textContent);
                });

                // 将所有行组合
                const codeContent = codeLines.join('\n');

                // 返回Markdown格式的代码块
                return '```' + language + '\n' + codeContent + '\n```\n\n';
            }
        });

        // 增强对图片的处理
        turndownService.addRule('images', {
            filter: 'img',
            replacement: function(content, node) {
                const alt = node.alt || '';
                let src = node.getAttribute('src') || '';

                // 处理相对URL
                if (src && !src.match(/^(https?:)?\/\//)) {
                    if (src.startsWith('/')) {
                        // 域名根路径
                        const domain = window.location.origin;
                        src = domain + src;
                    } else {
                        // 相对当前路径
                        const base = window.location.href.split('/').slice(0, -1).join('/');
                        src = base + '/' + src;
                    }
                }

                return '![' + alt + '](' + src + ')';
            }
        });

        let allMarkdown = '';
        const title = document.getElementsByClassName('title')[0];
        allMarkdown += '## ' + title.textContent + '\n\n'

        for (let i = 0; i < articles.length; i++) {
            // 创建一个新的文档片段作为工作区域
            const tempContainer = document.createElement('div');
            // 克隆节点以避免修改原始DOM
            tempContainer.appendChild(articles[i].cloneNode(true));

            // 移除其他可能不需要的元素
            const elementsToRemove = tempContainer.querySelectorAll('.comments, .related-posts, .share-buttons, nav, .navigation, .ads, script, style');
            elementsToRemove.forEach(el => el.remove());

            // 获取第一个article元素(在tempContainer中)
            const processedArticle = tempContainer.getElementsByClassName('content')[0];
            if (processedArticle) {
                // 转换为Markdown
                const markdown = turndownService.turndown(processedArticle.innerHTML);
                allMarkdown += markdown + '';
            }
        }

        if (allMarkdown) {
            // 复制到剪贴板
            GM_setClipboard(allMarkdown);
            showStatus('文章已转换为Markdown并复制');
        } else {
            showStatus('处理文章内容失败', true);
        }
    }

    // 添加快捷键(Alt+M)
    document.addEventListener('keydown', function(e) {
        if (e.altKey && e.key === 'm') {
            extractAndCopyArticle();
        }
    });

    // 初始化
    function init() {
        createCopyButton();
    }

    // 等待页面完全加载
    window.addEventListener('load', init);
})();

QingJ © 2025

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