// ==UserScript== // @name Universal-Markdown-Copy // @namespace http://tampermonkey.net/ // @version 1.5.0 // @description 自由选择网页区域并复制为 Markdown 格式 // @author shenfangda // @match https://*.shenfangda.cn/* // @grant GM_setClipboard // @license MIT // @downloadURL none // ==/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; } `; // 添加样式 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 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; copyBtn.textContent = '正在选择... (再次点击取消)'; document.body.style.cursor = 'crosshair'; log('进入选择模式'); } else { resetSelection(); log('取消选择模式'); } }); // 鼠标事件 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); log(`选择开始:(${startX}, ${startY})`); }); 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) { try { GM_setClipboard(markdownContent); alert('Markdown 已复制到剪贴板!'); log('复制成功'); } catch (err) { alert('复制失败,请检查控制台'); log(`复制失败:${err.message}`); } } else { alert('未检测到有效内容,请重新选择'); log('未找到有效内容'); } 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(); log('通过 Esc 键取消选择'); } }); } // 等待 DOM 就绪后执行 waitForDOM(() => { try { initScript(); } catch (err) { log(`初始化失败:${err.message}`); } }); })();