您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Convert Dedao articles to Markdown format
// ==UserScript== // @name Dedao article2markdown // @namespace http://tampermonkey.net/ // @version 2025-06-02 // @description Convert Dedao articles to Markdown format // @author AA // @match https://www.dedao.cn/course/** // @icon https://www.google.com/s2/favicons?sz=64&domain=dedao.cn // @grant GM_setClipboard // @grant GM_notification // @license MIT // ==/UserScript== (function() { 'use strict'; function htmlToMarkdown(htmlElement) { let markdown = ''; // Process each child node for (const child of htmlElement.childNodes) { if (child.nodeType === Node.ELEMENT_NODE) { // Handle headers if (child.classList.contains('header-2')) { markdown += '### ' + child.textContent.trim() + '\n'; } else if (child.classList.contains('header-3')) { markdown += '#### ' + child.textContent.trim() + '\n'; } else if (child.tagName === 'P') { // 处理段落内容,包括<b>标签 let paragraphText = ''; for (const node of child.childNodes) { if (node.nodeType === Node.TEXT_NODE) { paragraphText += node.textContent; } else if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'B') { paragraphText += ' **' + node.textContent + '** '; } } // 检查<p>是否只包含<br>或空白 const hasOnlyBr = child.children.length === 1 && child.children[0].tagName === 'BR' && child.textContent.trim() === ''; if (hasOnlyBr) { // 对于只包含<br>的<p>,转换为单个换行 markdown += '\n\n'; } else { // 普通段落处理 const text = paragraphText.trim(); if (text) { markdown += text + '\n'; } else { // 完全空的段落 markdown += '\n'; } } } else if (child.classList.contains('original-block-quote')) { // Blockquote const quoteText = child.querySelector('blockquote').textContent.trim(); markdown += '> ' + quoteText.replace(/\n/g, '\n> ') + '\n\n'; } // 直接处理独立的<b>标签(非段落内) else if (child.tagName === 'B') { markdown += '**' + child.textContent + '**'; } } } // Clean up multiple consecutive newlines markdown = markdown.replace(/\n{3,}/g, '\n\n'); return markdown.trim(); } function showMarkdownModal(markdown) { // Create modal container const modal = document.createElement('div'); modal.style.position = 'fixed'; modal.style.top = '50px'; modal.style.left = '50%'; modal.style.transform = 'translateX(-50%)'; modal.style.width = '80%'; modal.style.maxWidth = '800px'; modal.style.maxHeight = '80vh'; modal.style.backgroundColor = 'white'; modal.style.border = '1px solid #ddd'; modal.style.borderRadius = '5px'; modal.style.boxShadow = '0 4px 8px rgba(0,0,0,0.2)'; modal.style.zIndex = '9999'; modal.style.padding = '20px'; modal.style.overflow = 'auto'; // Create title const title = document.createElement('h3'); title.textContent = 'Markdown Conversion'; title.style.marginTop = '0'; modal.appendChild(title); // Create code block const pre = document.createElement('pre'); pre.style.backgroundColor = '#f5f5f5'; pre.style.padding = '15px'; pre.style.borderRadius = '4px'; pre.style.overflow = 'auto'; pre.style.whiteSpace = 'pre-wrap'; const code = document.createElement('code'); code.textContent = markdown; pre.appendChild(code); modal.appendChild(pre); // Create copy button const copyBtn = document.createElement('button'); copyBtn.textContent = 'Copy to Clipboard'; copyBtn.style.marginTop = '10px'; copyBtn.style.padding = '8px 15px'; copyBtn.style.backgroundColor = '#4CAF50'; copyBtn.style.color = 'white'; copyBtn.style.border = 'none'; copyBtn.style.borderRadius = '4px'; copyBtn.style.cursor = 'pointer'; copyBtn.addEventListener('click', () => { navigator.clipboard.writeText(markdown).then(() => { GM_notification({ title: 'Copied!', text: 'Markdown content copied to clipboard', timeout: 2000 }); }).catch(err => { console.error('Failed to copy:', err); }); }); modal.appendChild(copyBtn); // Create close button const closeBtn = document.createElement('button'); closeBtn.textContent = 'Close'; closeBtn.style.marginLeft = '10px'; closeBtn.style.padding = '8px 15px'; closeBtn.style.backgroundColor = '#f44336'; closeBtn.style.color = 'white'; closeBtn.style.border = 'none'; closeBtn.style.borderRadius = '4px'; closeBtn.style.cursor = 'pointer'; closeBtn.addEventListener('click', () => { document.body.removeChild(modal); }); modal.appendChild(closeBtn); // Add modal to document document.body.appendChild(modal); } // Wait for content to load const convertBtn = document.createElement('button'); convertBtn.innerHTML = '转换为<br>Markdown'; convertBtn.style.position = 'fixed'; convertBtn.style.top = '5vh'; convertBtn.style.right = '20px'; convertBtn.style.zIndex = '9999'; convertBtn.style.padding = '8px 16px'; convertBtn.style.backgroundColor = '#4CAF50'; convertBtn.style.color = 'white'; convertBtn.style.border = 'none'; convertBtn.style.borderRadius = '4px'; convertBtn.style.cursor = 'pointer'; convertBtn.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)'; convertBtn.style.fontSize = '14px'; // 添加悬停效果 convertBtn.addEventListener('mouseover', () => { convertBtn.style.backgroundColor = '#45a049'; }); convertBtn.addEventListener('mouseout', () => { convertBtn.style.backgroundColor = '#4CAF50'; }); // 按钮点击事件处理 convertBtn.addEventListener('click', () => { const editorShow = document.querySelector('.editor-show'); if (editorShow) { const markdown = htmlToMarkdown(editorShow); showMarkdownModal(markdown); } else { GM_notification({ title: '转换失败', text: '未找到文章内容,请确认是否在文章页面', timeout: 3000 }); } }); // 将按钮添加到页面 setTimeout(() => { document.body.appendChild(convertBtn); }, 1000); })();