// ==UserScript== // @name 复制格式转换 (Markdown) // @namespace http://tampermonkey.net/ // @version 1.5 // @description 选中内容后浮现按钮在右上角,点击按钮自动复制为完整的 Markdown 格式,并确保排版正确。新增支持数学公式的复制。 // @author KiwiFruit // @match *://*/* // @grant GM_setClipboard // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/532490/%E5%A4%8D%E5%88%B6%E6%A0%BC%E5%BC%8F%E8%BD%AC%E6%8D%A2%20%EF%BC%88Markdown%EF%BC%89.user.js // @updateURL https://update.greasyfork.icu/scripts/532490/%E5%A4%8D%E5%88%B6%E6%A0%BC%E5%BC%8F%E8%BD%AC%E6%8D%A2%20%EF%BC%88Markdown%EF%BC%89.meta.js // ==/UserScript== (function () { 'use strict'; const config = { preserveEmptyLines: false, // 是否保留空行 addSeparators: true, // 是否自动添加分隔符 }; function createFloatingButton() { const button = document.createElement('button'); button.id = 'markdownCopyButton'; button.innerText = '复制为 Markdown 格式 (Ctrl+Shift+C)'; button.style.position = 'fixed'; button.style.top = '20px'; button.style.right = '20px'; button.style.padding = '10px 20px'; button.style.backgroundColor = '#007bff'; button.style.color = '#fff'; button.style.border = 'none'; button.style.borderRadius = '5px'; button.style.cursor = 'pointer'; button.style.zIndex = '9999'; button.style.display = 'none'; button.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)'; document.body.appendChild(button); return button; } let floatingButton = createFloatingButton(); function createPreviewWindow() { const preview = document.createElement('div'); preview.id = 'markdownPreview'; preview.style.position = 'fixed'; preview.style.top = '60px'; preview.style.right = '20px'; preview.style.width = '300px'; preview.style.maxHeight = '400px'; preview.style.overflowY = 'auto'; preview.style.padding = '10px'; preview.style.backgroundColor = '#fff'; preview.style.border = '1px solid #ddd'; preview.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)'; preview.style.zIndex = '9999'; preview.style.display = 'none'; document.body.appendChild(preview); return preview; } let previewWindow = createPreviewWindow(); function showFloatingButton() { floatingButton.style.display = 'block'; } function hideFloatingButton() { floatingButton.style.display = 'none'; previewWindow.style.display = 'none'; } function convertToMarkdown(node) { if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent.trim(); return config.preserveEmptyLines ? text : text || ''; } if (node.nodeType === Node.ELEMENT_NODE) { switch (node.tagName.toLowerCase()) { case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': { const level = node.tagName.toLowerCase().replace('h', ''); const content = processChildren(node); return `${'#'.repeat(parseInt(level))} ${content} `; } case 'p': { return ` ${processChildren(node)} `; } case 'ul': { let markdown = ''; for (const li of node.children) { markdown += `- ${processChildren(li)} `; } return `${markdown} `; } case 'ol': { let markdown = ''; let index = 1; for (const li of node.children) { markdown += `${index++}. ${processChildren(li)} `; } return `${markdown} `; } case 'blockquote': { const content = processChildren(node); return `> ${content.split('\n').join('\n> ')} `; } case 'pre': { return `\`\`\` ${node.textContent.trim()} \`\`\` `; } case 'code': { return `\`${node.textContent.trim()}\``; } case 'a': { const text = processChildren(node); const href = node.href || ''; return `[${text}](${href})`; } case 'img': { const alt = node.alt || 'Image'; const src = node.src || ''; return `![${alt}](${src})`; } case 'strong': case 'b': { return ` **${processChildren(node).trim()}** `; } case 'em': case 'i': { return ` *${processChildren(node).trim()}* `; } case 'del': { return ` ~~${processChildren(node).trim()}~~ `; } case 'hr': { return '---\n\n'; } case 'table': { const rows = Array.from(node.rows).map(row => { const cells = Array.from(row.cells).map(cell => `| ${processChildren(cell).trim()} `); return cells.join('') + '|'; }); return `${rows.join('\n')} `; } case 'span': case 'div': { if (node.classList.contains('math-formula')) { const formulaText = node.textContent.trim(); return config.addSeparators ? `$$ ${formulaText} $$ ` : `$${formulaText}$`; } return processChildren(node); } default: { return processChildren(node); } } } return ''; } function processChildren(node) { let result = ''; for (const child of node.childNodes) { result += convertToMarkdown(child); } return result.trim(); } function extractAndConvertToMarkdown(range) { return Array.from(range.cloneContents().childNodes) .map(node => convertToMarkdown(node)) .join('') .trim(); } async function copyToClipboard(text) { try { if (typeof GM_setClipboard === 'function') { GM_setClipboard(text); return true; } else { await navigator.clipboard.writeText(text); console.log('Markdown 已复制到剪贴板'); return true; } } catch (err) { console.error('复制失败:', err); return false; } } function showToast(message) { const toast = document.createElement('div'); toast.style.position = 'fixed'; toast.style.bottom = '20px'; toast.style.right = '20px'; toast.style.padding = '10px 20px'; toast.style.backgroundColor = '#333'; toast.style.color = '#fff'; toast.style.borderRadius = '5px'; toast.style.zIndex = '9999'; toast.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)'; toast.innerText = message; document.body.appendChild(toast); setTimeout(() => { toast.remove(); }, 2000); } document.addEventListener('mouseup', handleMouseUp); function handleMouseUp(event) { const selection = window.getSelection(); if (selection && !selection.isCollapsed) { showFloatingButton(); floatingButton.onclick = async () => { try { const range = selection.getRangeAt(0); const markdownContent = extractAndConvertToMarkdown(range); previewWindow.innerText = markdownContent; previewWindow.style.display = 'block'; const success = await copyToClipboard(markdownContent); if (success) { showToast('内容已复制为 Markdown 格式!'); } else { showToast('复制失败,请重试!'); } } catch (err) { console.error('处理内容时出错:', err); showToast(`发生错误:${err.message}`); } finally { hideFloatingButton(); } }; } else { hideFloatingButton(); } } document.addEventListener('keydown', (event) => { if (event.ctrlKey && event.shiftKey && event.code === 'KeyC') { handleMouseUp(event); } }); document.addEventListener('mousedown', (event) => { if (!floatingButton.contains(event.target) && !previewWindow.contains(event.target)) { hideFloatingButton(); } }); })();