Universal-Markdown-Copy

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

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

    // 日志函数
    const log = (msg) => console.log(`[Markdown-Copy] ${msg}`);

    // 检查 DOM 是否就绪
    function waitForDOM(callback) {
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            callback();
        } else {
            document.addEventListener('DOMContentLoaded', callback);
            setTimeout(callback, 2000); // 2秒后强制执行
        }
    }

    // 主逻辑
    function initScript() {
        log(`脚本开始初始化于 ${window.location.href}`);

        // 样式定义
        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;
            }
            .div-highlight {
                border: 2px solid #4CAF50;
                background-color: rgba(76, 175, 80, 0.1);
                transition: all 0.2s;
            }
        `;

        // 添加样式
        if (!document.head) {
            log('错误:document.head 未找到');
            return;
        }
        const styleSheet = document.createElement('style');
        styleSheet.textContent = STYLES;
        document.head.appendChild(styleSheet);
        log('样式已加载');

        // 创建按钮
        if (!document.body) {
            log('错误:document.body 未找到');
            return;
        }
        const copyBtn = document.createElement('button');
        copyBtn.className = 'markdown-copy-btn';
        copyBtn.textContent = '选择区域复制 Markdown';
        document.body.appendChild(copyBtn);
        log('按钮已添加');

        let isSelecting = false;
        let isDivMode = false; // 新增:是否为 div 选择模式
        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') {
                    const content = htmlToMarkdown(el);
                    log(`自由选择内容:${content.substring(0, 50)}...`);
                    return content;
                }
            }
            return '';
        }

        // 按钮点击切换模式
        copyBtn.addEventListener('click', () => {
            if (!isSelecting) {
                isSelecting = true;
                isDivMode = !isDivMode; // 切换模式
                copyBtn.textContent = isDivMode ? '按 Div 选择 (再次切换)' : '正在选择... (再次切换)';
                document.body.style.cursor = isDivMode ? 'pointer' : 'crosshair';
                log(`进入${isDivMode ? 'Div 选择' : '自由选择'}模式`);
            } else {
                resetSelection();
                log('取消选择模式');
            }
        });

        // 自由选择模式:鼠标事件
        document.addEventListener('mousedown', (e) => {
            if (!isSelecting || isDivMode || 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);
            log(`自由选择开始:(${startX}, ${startY})`);
        });

        document.addEventListener('mousemove', (e) => {
            if (!isSelecting || isDivMode || !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 || isDivMode || !selectionBox) return;
            const endX = e.clientX;
            const endY = e.clientY;
            const markdownContent = getSelectedContent(startX, startY, endX, endY);
            if (markdownContent) {
                try {
                    GM_setClipboard(markdownContent);
                    alert('Markdown 已复制到剪贴板!');
                    log('自由选择复制成功');
                } catch (err) {
                    alert('复制失败,请检查控制台');
                    log(`复制失败:${err.message}`);
                }
            } else {
                alert('未检测到有效内容,请重新选择');
                log('自由选择未找到有效内容');
            }
            resetSelection();
        });

        // Div 选择模式:鼠标悬停高亮和点击复制
        let highlightedDiv = null;
        document.addEventListener('mouseover', (e) => {
            if (!isSelecting || !isDivMode) return;
            const target = e.target.closest('div');
            if (target && target !== highlightedDiv) {
                if (highlightedDiv) highlightedDiv.classList.remove('div-highlight');
                highlightedDiv = target;
                highlightedDiv.classList.add('div-highlight');
                log(`高亮 Div: ${target.outerHTML.substring(0, 50)}...`);
            }
        });

        document.addEventListener('click', (e) => {
            if (!isSelecting || !isDivMode || e.target === copyBtn) return;
            e.preventDefault(); // 防止点击触发其他事件
            const target = e.target.closest('div');
            if (target) {
                const markdownContent = htmlToMarkdown(target);
                if (markdownContent) {
                    try {
                        GM_setClipboard(markdownContent);
                        alert('Markdown 已复制到剪贴板!');
                        log('Div 选择复制成功');
                    } catch (err) {
                        alert('复制失败,请检查控制台');
                        log(`复制失败:${err.message}`);
                    }
                } else {
                    alert('未检测到有效内容');
                    log('Div 选择未找到有效内容');
                }
                resetSelection();
            }
        });

        // 离开页面时清理高亮
        document.addEventListener('mouseout', (e) => {
            if (!isSelecting || !isDivMode || !highlightedDiv) return;
            if (!e.relatedTarget || !highlightedDiv.contains(e.relatedTarget)) {
                highlightedDiv.classList.remove('div-highlight');
                highlightedDiv = null;
            }
        });

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

        // Esc 键取消
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape' && isSelecting) {
                resetSelection();
                log('通过 Esc 键取消选择');
            }
        });
    }

    // 等待 DOM 就绪后执行
    waitForDOM(() => {
        try {
            initScript();
        } catch (err) {
            log(`初始化失败:${err.message}`);
        }
    });
})();

QingJ © 2025

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