Greasy Fork

来自缓存

Greasy Fork is available in English.

Discourse Markdown 主题导出

将任意 Discourse 论坛的主题导出,支持动态页面加载

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Discourse Markdown 主题导出
// @namespace    http://innjay.cn
// @version      1.1.0
// @description  将任意 Discourse 论坛的主题导出,支持动态页面加载
// @author       Hebaodan
// @match        http://*/*
// @match        https://*/*
// @license      MIT
// @icon         https://img.innjay.cn/i/2024/10/01/66fbf6914fe72.png
// @grant        GM_setClipboard
// @require      https://unpkg.com/[email protected]/dist/turndown.js
// @downloadURL
// @updateURL
// ==/UserScript==

(function() {
    'use strict';

    let buttonContainer = null;

    // 检查当前页面是否是 Discourse 论坛
    function isDiscourseForum() {
        return document.querySelector('meta[name="generator"][content*="Discourse"]') !== null;
    }

    // 检查是否在主题页面
    function isTopicPage() {
        return window.location.pathname.match(/\/t\/.*\/\d+/);
    }

    // 创建按钮容器
    function createButtonContainer() {
        if (buttonContainer) {
            buttonContainer.remove();
        }

        const container = document.createElement('div');
        container.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            z-index: 9999;
            display: flex;
            flex-direction: column;
            gap: 10px;
        `;
        document.body.appendChild(container);
        buttonContainer = container;
        return container;
    }

    // 创建按钮
    function createButton(text, onClick) {
        const button = document.createElement('button');
        button.textContent = text;
        button.style.cssText = `
            padding: 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        `;
        button.addEventListener('click', onClick);
        return button;
    }

    // 获取文章内容
    function getArticleContent() {
        const titleElement = document.querySelector('#topic-title h1');
        const contentElement = document.querySelector('#post_1 .cooked');

        if (!titleElement || !contentElement) {
            console.error('无法找到文章标题或内容');
            return null;
        }

        return {
            title: titleElement.textContent.trim(),
            content: contentElement.innerHTML
        };
    }

    // 转换为Markdown
    function convertToMarkdown(article) {
        const turndownService = new TurndownService({
            headingStyle: 'atx',
            codeBlockStyle: 'fenced'
        });

        // 自定义规则处理图片和链接
        turndownService.addRule('images_and_links', {
            filter: ['a', 'img'],
            replacement: function (content, node) {
                // 处理图片
                if (node.nodeName === 'IMG') {
                    const alt = node.alt || '';
                    const src = node.getAttribute('src') || '';
                    const title = node.title ? ` "${node.title}"` : '';
                    return `![${alt}](${src}${title})`;
                }
                // 处理链接
                else if (node.nodeName === 'A') {
                    const href = node.getAttribute('href');
                    const title = node.title ? ` "${node.title}"` : '';
                    // 检查链接是否包含图片
                    const img = node.querySelector('img');
                    if (img) {
                        const alt = img.alt || '';
                        const src = img.getAttribute('src') || '';
                        const imgTitle = img.title ? ` "${img.title}"` : '';
                        return `[![${alt}](${src}${imgTitle})](${href}${title})`;
                    }
                    // 普通链接
                    return `[${node.textContent}](${href}${title})`;
                }
            }
        });

        return `# ${article.title}\n\n${turndownService.turndown(article.content)}`;
    }

    // 下载为Markdown文件
    function downloadAsMarkdown() {
        const article = getArticleContent();
        if (!article) {
            alert('无法获取文章内容,请检查网页结构是否变更。');
            return;
        }

        const markdown = convertToMarkdown(article);

        const blob = new Blob([markdown], { type: 'text/markdown;charset=utf-8' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = `${article.title}.md`;
        a.click();
        URL.revokeObjectURL(url);

        showNotification('Markdown 文件已下载');
    }

    // 复制到剪贴板
    function copyToClipboard() {
        const article = getArticleContent();
        if (!article) {
            alert('无法获取文章内容,请检查网页结构是否变更。');
            return;
        }

        const markdown = convertToMarkdown(article);
        GM_setClipboard(markdown);

        showNotification('内容已复制到剪贴板');
    }

    // 显示通知
    function showNotification(message) {
        const notification = document.createElement('div');
        notification.textContent = message;
        notification.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background-color: #333;
            color: white;
            padding: 10px;
            border-radius: 5px;
            z-index: 10000;
        `;
        document.body.appendChild(notification);
        setTimeout(() => notification.remove(), 3000);
    }

    // 主函数
    function main() {
        // 首先检查是否是 Discourse 论坛
        if (!isDiscourseForum()) {
            return;
        }

        if (isTopicPage()) {
            const container = createButtonContainer();
            const downloadButton = createButton('下载 Markdown', downloadAsMarkdown);
            const copyButton = createButton('复制到剪贴板', copyToClipboard);
            container.appendChild(downloadButton);
            container.appendChild(copyButton);
        } else {
            if (buttonContainer) {
                buttonContainer.remove();
                buttonContainer = null;
            }
        }
    }

    // 延迟执行主函数,确保页面完全加载
    setTimeout(main, 1000);

    // 监听 URL 变化
    let lastUrl = location.href;
    new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
            lastUrl = url;
            setTimeout(main, 1000);
        }
    }).observe(document, { subtree: true, childList: true });
})();