Greasy Fork

Greasy Fork is available in English.

Universal-Markdown-Copy

自由选择网页区域并复制为 Markdown 格式

当前为 2025-03-11 提交的版本,查看 最新版本

// ==UserScript==
// @name         Universal-Markdown-Copy
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  自由选择网页区域并复制为 Markdown 格式
// @author       shenfangda
// @match        https://*.shenfangda.cn/*
// @grant        GM_setClipboard
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 样式定义
    const STYLES = `
        .markdown-copy-btn {
            position: fixed;
            top: 10px;
            right: 10px;
            z-index: 9999;
            padding: 8px 16px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            font-size: 14px;
            transition: all 0.3s;
        }
        .markdown-copy-btn:hover {
            background-color: #45a049;
            transform: scale(1.05);
        }
        .selection-box {
            position: absolute;
            border: 2px dashed #4CAF50;
            background-color: rgba(76, 175, 80, 0.1);
            z-index: 9998;
            pointer-events: none;
        }
    `;

    // 添加样式到页面
    const styleSheet = document.createElement('style');
    styleSheet.textContent = STYLES;
    document.head.appendChild(styleSheet);

    // 创建复制按钮
    const copyBtn = document.createElement('button');
    copyBtn.className = 'markdown-copy-btn';
    copyBtn.textContent = '选择区域复制 Markdown';
    document.body.appendChild(copyBtn);

    let isSelecting = false;
    let startX, startY;
    let selectionBox = null;

    // 将 HTML 转换为 Markdown 的函数
    function htmlToMarkdown(element) {
        let markdown = '';

        function processNode(node) {
            if (node.nodeType === Node.TEXT_NODE) {
                return node.textContent.trim();
            }
            if (node.nodeType !== Node.ELEMENT_NODE) return '';

            let result = '';
            const tag = node.tagName.toLowerCase();

            if (/h[1-6]/.test(tag)) {
                const level = parseInt(tag[1]);
                result += '#'.repeat(level) + ' ' + node.textContent.trim() + '\n\n';
            } else if (tag === 'p') {
                result += node.textContent.trim() + '\n\n';
            } else if (tag === 'ul' || tag === 'ol') {
                const items = Array.from(node.children).filter(child => child.tagName.toLowerCase() === 'li');
                items.forEach(item => {
                    result += (tag === 'ul' ? '- ' : '1. ') + item.textContent.trim() + '\n';
                });
                result += '\n';
            } else if (tag === 'pre' || tag === 'code') {
                result += '```\n' + node.textContent.trim() + '\n```\n\n';
            } else {
                // 处理子节点
                node.childNodes.forEach(child => {
                    result += processNode(child);
                });
            }
            return result;
        }

        if (element) {
            markdown = processNode(element);
        }
        return markdown.trim();
    }

    // 获取选区内容的函数
    function getSelectedContent(x1, y1, x2, y2) {
        const elements = document.elementsFromPoint((x1 + x2) / 2, (y1 + y2) / 2);
        for (let el of elements) {
            if (el.tagName.toLowerCase() !== 'body' && el.tagName.toLowerCase() !== 'html') {
                return htmlToMarkdown(el);
            }
        }
        return '';
    }

    // 开始选择
    copyBtn.addEventListener('click', () => {
        if (!isSelecting) {
            isSelecting = true;
            copyBtn.textContent = '正在选择... (再次点击取消)';
            document.body.style.cursor = 'crosshair';
        } else {
            resetSelection();
        }
    });

    // 鼠标按下
    document.addEventListener('mousedown', (e) => {
        if (!isSelecting || e.target === copyBtn) return;

        startX = e.clientX;
        startY = e.clientY;

        if (selectionBox) selectionBox.remove();
        selectionBox = document.createElement('div');
        selectionBox.className = 'selection-box';
        document.body.appendChild(selectionBox);
    });

    // 鼠标移动
    document.addEventListener('mousemove', (e) => {
        if (!isSelecting || !selectionBox) return;

        const currentX = e.clientX;
        const currentY = e.clientY;

        const left = Math.min(startX, currentX);
        const top = Math.min(startY, currentY);
        const width = Math.abs(currentX - startX);
        const height = Math.abs(currentY - startY);

        selectionBox.style.left = `${left}px`;
        selectionBox.style.top = `${top}px`;
        selectionBox.style.width = `${width}px`;
        selectionBox.style.height = `${height}px`;
    });

    // 鼠标松开
    document.addEventListener('mouseup', (e) => {
        if (!isSelecting || !selectionBox) return;

        const endX = e.clientX;
        const endY = e.clientY;

        const markdownContent = getSelectedContent(startX, startY, endX, endY);
        if (markdownContent) {
            GM_setClipboard(markdownContent);
            alert('Markdown 已复制到剪贴板!');
        } else {
            alert('未检测到有效内容,请重新选择');
        }

        resetSelection();
    });

    // 重置选择状态
    function resetSelection() {
        isSelecting = false;
        copyBtn.textContent = '选择区域复制 Markdown';
        document.body.style.cursor = 'default';
        if (selectionBox) {
            selectionBox.remove();
            selectionBox = null;
        }
    }

    // 快捷键:按 Esc 取消选择
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape' && isSelecting) {
            resetSelection();
        }
    });
})();