您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Convert selected HTML Content to Markdown with filtering
// ==UserScript== // @name HTML Content to Markdown // @name:zh 网页内容转Markdown // @namespace https://github.com/ChuwuYo // @homepageURL https://github.com/ChuwuYo/misc-files/blob/main/userscripts/HTML%20Content%20to%20Markdown.user.js // @supportURL https://github.com/ChuwuYo/misc-files/issues // @version 0.1.3 // @description Convert selected HTML Content to Markdown with filtering // @description:zh 将选定的HTML内容转换为Markdown(规则过滤) // @author ChuwuYo // @match *://*/* // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_setClipboard // @grant GM_setValue // @grant GM_getValue // @icon https://litera-reader.com/favicon.png?v=2 // @require https://code.jquery.com/jquery-3.6.0.min.js // @require https://unpkg.com/turndown/dist/turndown.js // @require https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.0/marked.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js // @require https://unpkg.com/@guyplusplus/turndown-plugin-gfm/dist/turndown-plugin-gfm.js // @license AGPL-3.0 // @TODO 1.消息国际化 zh-cn/en-us // ==/UserScript== (function () { 'use strict'; // --- User Config Defaults --- const DEFAULT_SHORTCUT_CONFIG = { "Shift": false, "Ctrl": true, "Alt": false, "Key": "m" }; const DEFAULT_FILTER_CONFIG = { removeTags: ['script', 'style', 'link', 'meta', 'iframe', 'noscript', 'object', 'embed', 'button', 'input', 'textarea', 'select', 'option', 'form', 'video', 'audio', 'canvas', 'map', 'area', 'track', 'applet', 'bgsound', 'blink', 'isindex', 'keygen', 'marquee', 'menuitem', 'nextid', 'noembed', 'param', 'source'], removeAttributes: [ 'style', 'onclick', 'onload', 'onerror', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'target', 'contenteditable', 'draggable', 'tabindex', 'spellcheck', 'translate', 'dir', 'lang', 'aria-\\w+', 'data-\\w+' ], keepAttributesOnTags: { 'img': ['src', 'alt', 'title', 'width', 'height'], 'a': ['href', 'title', 'rel'], 'code': ['class'], 'pre': ['class'], 'table': ['class'], 'th': ['scope', 'colspan', 'rowspan'], 'td': ['colspan', 'rowspan'] }, removeElementsWithClasses: ['advertisement', 'ads', 'sidebar', 'footer', 'header', 'nav', 'menu'], removeElementsWithIds: ['advertisement', 'ads', 'sidebar', 'footer', 'header', 'nav', 'menu'], smartContentDetection: true, preserveCodeBlocks: true }; // --- User-Provided Config (can be empty) --- const shortCutUserConfig = { /* Example: "Key": "s" */ }; const filterUserConfig = { /* Example: removeTags: ['script', 'style', 'header'] */ }; // --- Global Variables --- let isSelecting = false; let isMultiSelectMode = false; let hoveredElement = null; let selectedElements = []; let shortCutConfig; let filterConfig; const closeButtonSvgIcon = '<svg viewBox="0 0 16 16" aria-hidden="true"><path fill-rule="evenodd" d="M2.343 13.657A8 8 0 1 1 13.658 2.343 8 8 0 0 1 2.343 13.657M6.03 4.97a.75.75 0 0 0-1.042.018.75.75 0 0 0-.018 1.042L6.94 8 4.97 9.97a.749.749 0 0 0 .326 1.275.75.75 0 0 0 .734-.215L8 9.06l1.97 1.97a.749.749 0 0 0 1.275-.326.75.75 0 0 0-.215-.734L9.06 8l1.97-1.97a.749.749 0 0 0-.326-1.275.75.75 0 0 0-.734.215L8 6.94Z"/></svg>'; // --- Helper Functions --- function loadConfig(storageKey, defaultConfig, userProvidedConfig) { let mergedConfig = { ...defaultConfig }; const storedConfigStr = GM_getValue(storageKey); if (storedConfigStr) { try { const storedConfig = storedConfigStr ? JSON.parse(storedConfigStr) : {}; mergedConfig = { ...defaultConfig, ...storedConfig }; } catch (e) { console.error(`[HTML to MD] Error parsing stored config for ${storageKey}:`, e, "\nStored string was:", storedConfigStr); GM_setValue(storageKey, JSON.stringify(defaultConfig)); // Reset to default if parsing fails mergedConfig = { ...defaultConfig }; } } else { GM_setValue(storageKey, JSON.stringify(defaultConfig)); } if (userProvidedConfig && Object.keys(userProvidedConfig).length > 0) { mergedConfig = { ...mergedConfig, ...userProvidedConfig }; GM_setValue(storageKey, JSON.stringify(mergedConfig)); } return mergedConfig; } // --- Initialize Configurations --- try { shortCutConfig = loadConfig('shortCutConfig', DEFAULT_SHORTCUT_CONFIG, shortCutUserConfig); filterConfig = loadConfig('filterConfig', DEFAULT_FILTER_CONFIG, filterUserConfig); } catch (e) { console.error("[HTML to MD] Critical error loading configuration:", e); shortCutConfig = { ...DEFAULT_SHORTCUT_CONFIG }; // Fallback to defaults filterConfig = { ...DEFAULT_FILTER_CONFIG }; // Fallback to defaults alert("Error loading script configuration. Using default settings. Please check console for details."); } // --- Turndown Service Setup --- const turndownService = new TurndownService({ codeBlockStyle: 'fenced', headingStyle: 'atx', hr: '---', bulletListMarker: '-', emDelimiter: '*', strongDelimiter: '**', linkStyle: 'inlined', linkReferenceStyle: 'full' }); TurndownPluginGfmService.gfm(turndownService); if (filterConfig && filterConfig.removeTags && Array.isArray(filterConfig.removeTags)) { turndownService.remove(filterConfig.removeTags); } turndownService.remove((node) => node.nodeType === Node.COMMENT_NODE); // Enhanced image handling turndownService.addRule('enhancedImages', { filter: 'img', replacement: function (content, node) { const alt = node.getAttribute('alt') || ''; const src = node.getAttribute('src') || ''; const title = node.getAttribute('title'); if (!src) return alt; return title ? `` : ``; } }); // Enhanced link handling turndownService.addRule('enhancedLinks', { filter: function (node) { return node.nodeName === 'A' && node.getAttribute('href'); }, replacement: function (content, node) { const href = node.getAttribute('href'); const title = node.getAttribute('title'); if (!href || href.startsWith('javascript:') || href === '#') return content; return title ? `[${content}](${href} "${title}")` : `[${content}](${href})`; } }); // --- Core Functions --- function convertToMarkdown(element) { if (!element) return ''; const clonedElement = element.cloneNode(true); if (filterConfig) { clonedElement.querySelectorAll('*').forEach(el => { const tagName = el.tagName.toLowerCase(); const attributesToKeep = (filterConfig.keepAttributesOnTags && filterConfig.keepAttributesOnTags[tagName]) || []; if (filterConfig.removeAttributes && Array.isArray(filterConfig.removeAttributes)) { Array.from(el.attributes).forEach(attr => { const attrName = attr.name.toLowerCase(); if (attributesToKeep.includes(attrName)) { return; } let shouldRemove = false; for (const pattern of filterConfig.removeAttributes) { if (pattern.includes('\\w+')) { const regex = new RegExp('^' + pattern.replace('\\w+', '\\w+') + '$', 'i'); if (regex.test(attrName)) { shouldRemove = true; break; } } else if (attrName === pattern.toLowerCase()) { shouldRemove = true; break; } } if (shouldRemove) { el.removeAttribute(attr.name); } }); } }); if (filterConfig.removeElementsWithClasses && Array.isArray(filterConfig.removeElementsWithClasses)) { filterConfig.removeElementsWithClasses.forEach(className => { const selector = className.startsWith('.') ? className : '.' + className; const escapedSelector = selector.replace(/([.#\[\](){}*+?^$|\\])/g, '\\$1'); clonedElement.querySelectorAll(`[class*="${className}"], ${escapedSelector}`).forEach(elToRemove => elToRemove.remove()); }); } if (filterConfig.removeElementsWithIds && Array.isArray(filterConfig.removeElementsWithIds)) { filterConfig.removeElementsWithIds.forEach(idName => { const selector = idName.startsWith('#') ? idName : '#' + idName; const escapedSelector = selector.replace(/([.#\[\](){}*+?^$|\\])/g, '\\$1'); const elToRemove = clonedElement.querySelector(escapedSelector); if (elToRemove) elToRemove.remove(); }); } // Smart content detection if (filterConfig.smartContentDetection) { clonedElement.querySelectorAll('*').forEach(el => { const classList = Array.from(el.classList).join(' ').toLowerCase(); const id = (el.id || '').toLowerCase(); const commonNoisePatterns = /\b(ad|ads|advertisement|banner|popup|modal|overlay|sidebar|footer|header|nav|menu|social|share|comment|related|recommend)\b/; if (commonNoisePatterns.test(classList) || commonNoisePatterns.test(id)) { el.remove(); } }); } } const html = clonedElement.outerHTML; let turndownMd = turndownService.turndown(html); // Enhanced post-processing Markdown cleanup turndownMd = turndownMd.replace(/\[\s*]\(\s*\)/g, ''); // Remove completely empty links turndownMd = turndownMd.replace(/\[\s*]\((#|javascript:[^)]*|mailto:|tel:)\)/g, ''); // Remove empty/junk links turndownMd = turndownMd.replace(/\[([^\]]+)]\(\s*\)/g, '$1'); // Remove links with text but no href turndownMd = turndownMd.replace(/\[([^\]]+)]\(\1\)/g, '$1'); // Remove redundant links where text equals URL turndownMd = turndownMd.replace(/!\[\s*]\(\s*\)/g, ''); // Remove empty images turndownMd = turndownMd.replace(/\n{3,}/g, '\n\n'); // Consolidate multiple blank lines turndownMd = turndownMd.replace(/^\s*\n+|\n+\s*$/g, ''); // Trim leading/trailing whitespace turndownMd = turndownMd.replace(/(\*\*|__)\s*\1/g, ''); // Remove empty bold/italic markers turndownMd = turndownMd.replace(/`\s*`/g, ''); // Remove empty code spans return turndownMd.trim(); } function showMarkdownModal(markdown) { const $modal = $(` <div class="h2m-modal-overlay"> <div class="h2m-modal"> <div class="h2m-modal-body"> <textarea class="h2m-markdown-area" spellcheck="false"></textarea> <div class="h2m-preview"></div> </div> <div class="h2m-modal-footer"> <button class="h2m-copy">Copy to clipboard</button> <button class="h2m-download">Download as MD</button> </div> <button class="h2m-close">${closeButtonSvgIcon}</button> </div> </div> `); const $markdownArea = $modal.find('.h2m-markdown-area'); const $previewArea = $modal.find('.h2m-preview'); const $copyButton = $modal.find('.h2m-copy'); const $downloadButton = $modal.find('.h2m-download'); const $closeButton = $modal.find('.h2m-close'); $markdownArea.val(markdown); // Set initial value $previewArea.html(marked.parse(markdown)); $markdownArea.on('input', function () { $previewArea.html(marked.parse($(this).val())); }); const closeModal = () => { $modal.remove(); $(document).off('keydown.h2mModalGlobal'); }; $modal.on('keydown', function (e) { if (e.key === 'Escape') closeModal(); }); $(document).on('keydown.h2mModalGlobal', function (e) { if (e.key === 'Escape' && $('.h2m-modal-overlay').length > 0) closeModal(); }); $copyButton.on('click', function () { GM_setClipboard($markdownArea.val()); $(this).text('Copied!'); setTimeout(() => $(this).text('Copy to clipboard'), 1000); }); $downloadButton.on('click', function () { const md = $markdownArea.val(); const blob = new Blob([md], { type: 'text/markdown;charset=utf-8' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const safeTitle = (document.title.replace(/[\\/:*?"<>|]/g, '_') || 'untitled'); a.download = `${safeTitle}-${new Date().toISOString().replace(/:/g, '-')}.md`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }); $closeButton.on('click', closeModal); let isScrolling = false; function syncScroll(source, target) { if (isScrolling) { isScrolling = false; return; } isScrolling = true; const sh = source.scrollHeight - source.offsetHeight; if (sh <= 0) { isScrolling = false; return; } const scrollPercentage = source.scrollTop / sh; target.scrollTop = scrollPercentage * (target.scrollHeight - target.offsetHeight); setTimeout(() => isScrolling = false, 50); } $markdownArea.on('scroll', () => syncScroll($markdownArea[0], $previewArea[0])); $previewArea.on('scroll', () => syncScroll($previewArea[0], $markdownArea[0])); $('body').append($modal); $markdownArea.trigger('input'); // Trigger input to ensure preview is initially synced if markdown is complex } function updateTip() { let message; if (isMultiSelectMode) { message = `<b>Multi-Select Mode (${selectedElements.length} selected)</b><br><b>Click</b> to add/remove element.<br>Release <b>Shift</b> to exit.<br>Press <b>Enter</b> to convert.<br><b>Esc</b> to cancel.` } else { message = '<b>Single-Select Mode</b><br>Navigate with mouse/arrows. <b>Click</b> to convert.<br>Hold <b>Shift</b> for Multi-Select.<br><b>Esc</b> to cancel.'; } tip(message); } function processSelection() { try { let finalElements = isMultiSelectMode ? selectedElements : [hoveredElement]; if (finalElements.length === 0 || (finalElements.length === 1 && !finalElements[0])) { tip('No element selected.', 2000); return; } // Sort elements by their document order (top-to-bottom) finalElements.sort((a, b) => { const position = a.compareDocumentPosition(b); if (position & Node.DOCUMENT_POSITION_FOLLOWING) { return 1; // a is after b } else if (position & Node.DOCUMENT_POSITION_PRECEDING) { return -1; // a is before b } return 0; }); const markdown = finalElements.map(el => convertToMarkdown(el)).join('\n\n---\n\n'); if (markdown.trim()) { showMarkdownModal(markdown); } else { tip('所选元素没有有效内容', 2000); } } catch (err) { console.error("[HTML to MD] Error during conversion or showing modal:", err); alert("Error processing selection. Check console for details."); } finally { endSelecting(); } } function startSelecting() { if (isSelecting) return; $('body').addClass('h2m-no-scroll'); isSelecting = true; isMultiSelectMode = false; selectedElements = []; hoveredElement = document.body.firstElementChild || document.body; if (hoveredElement) { $(hoveredElement).addClass('h2m-selection-box'); } updateTip(); } function endSelecting() { if (!isSelecting) return; isSelecting = false; isMultiSelectMode = false; $('.h2m-selection-box').removeClass('h2m-selection-box'); $('.h2m-selected-item').removeClass('h2m-selected-item'); $('body').removeClass('h2m-no-scroll'); $('#h2m-tip-instance').remove(); hoveredElement = null; selectedElements = []; } function isContentElement(el) { const contentTags = ['P', 'DIV', 'ARTICLE', 'SECTION', 'MAIN', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'UL', 'OL', 'LI', 'BLOCKQUOTE', 'PRE', 'CODE', 'TABLE']; return contentTags.includes(el.tagName) || el.textContent.trim().length > 20; } function isValidElement(el) { if (!el || ['SCRIPT', 'STYLE', 'NOSCRIPT'].includes(el.tagName)) return false; const rect = el.getBoundingClientRect(); return rect.width > 0 && rect.height > 0; } // Use an ID for the tip element to increase specificity without !important. function tip(message, timeout = null) { $('#h2m-tip-instance').remove(); // Remove any existing tip by its unique ID const $t = $('<div>') .attr('id', 'h2m-tip-instance') // Assign a unique ID for high-specificity styling .html(message) .appendTo('body') .hide() .fadeIn(200); if (timeout !== null) { setTimeout(() => { $t.fadeOut(200, () => $t.remove()); }, timeout); } } function handleKeyboardNavigation(e) { if (!isSelecting || !hoveredElement) return; e.preventDefault(); let newEl = hoveredElement; switch (e.key) { case 'Escape': endSelecting(); return; case 'Enter': if (isMultiSelectMode) { processSelection(); } return; case 'ArrowUp': newEl = hoveredElement.parentElement || hoveredElement; if (['HTML', 'BODY'].includes(newEl.tagName)) { newEl = newEl.firstElementChild || newEl; } break; case 'ArrowDown': newEl = hoveredElement.firstElementChild || hoveredElement; break; case 'ArrowLeft': { let p = hoveredElement.previousElementSibling; if (p) { newEl = p; while (newEl.lastElementChild && !isContentElement(newEl)) { newEl = newEl.lastElementChild; } } else if (hoveredElement.parentElement && !['BODY', 'HTML'].includes(hoveredElement.parentElement.tagName)) { newEl = hoveredElement.parentElement; } break; } case 'ArrowRight': { let n = hoveredElement.nextElementSibling; if (n) { newEl = n; while (newEl.firstElementChild && !isContentElement(newEl)) { newEl = newEl.firstElementChild; } } else if (hoveredElement.parentElement && !['BODY', 'HTML'].includes(hoveredElement.parentElement.tagName)) { newEl = hoveredElement.parentElement; } break; } default: return; } if (newEl && newEl !== hoveredElement && isValidElement(newEl)) { $(hoveredElement).removeClass('h2m-selection-box'); hoveredElement = newEl; $(hoveredElement).addClass('h2m-selection-box'); } } function handleMouseWheelNavigation(e) { if (!isSelecting || !hoveredElement) return; e.preventDefault(); let newEl = hoveredElement; if (e.originalEvent.deltaY < 0) { newEl = hoveredElement.parentElement || hoveredElement; if (['HTML', 'BODY'].includes(newEl.tagName)) newEl = newEl.firstElementChild || newEl; } else { newEl = hoveredElement.firstElementChild || hoveredElement; } if (newEl && newEl !== hoveredElement) { $(hoveredElement).removeClass('h2m-selection-box'); hoveredElement = newEl; $(hoveredElement).addClass('h2m-selection-box'); } } $(document).on('keydown.h2m', function (e) { if (e.key.toUpperCase() === shortCutConfig.Key.toUpperCase() && e.ctrlKey === shortCutConfig.Ctrl && e.altKey === shortCutConfig.Alt && e.shiftKey === shortCutConfig.Shift) { e.preventDefault(); if (isSelecting) { endSelecting(); } else { startSelecting(); } return; } if (isSelecting) { if (e.key === 'Shift' && !isMultiSelectMode) { isMultiSelectMode = true; updateTip(); } handleKeyboardNavigation(e); } }).on('keyup.h2m', function(e) { if (isSelecting && e.key === 'Shift') { isMultiSelectMode = false; updateTip(); } }).on('mouseover.h2m', function (e) { if (isSelecting && hoveredElement !== e.target && !$(e.target).closest('#h2m-tip-instance, .h2m-modal-overlay').length && isValidElement(e.target)) { $(hoveredElement).removeClass('h2m-selection-box'); hoveredElement = e.target; $(hoveredElement).addClass('h2m-selection-box'); } }).on('wheel.h2m', function (e) { if (isSelecting) handleMouseWheelNavigation(e); }).on('mousedown.h2m', function (e) { if (isSelecting && hoveredElement && $(e.target).closest('#h2m-tip-instance, .h2m-modal-overlay').length === 0) { e.preventDefault(); e.stopPropagation(); if (isMultiSelectMode) { const index = selectedElements.indexOf(hoveredElement); if (index > -1) { selectedElements.splice(index, 1); $(hoveredElement).removeClass('h2m-selected-item'); } else { selectedElements.push(hoveredElement); $(hoveredElement).addClass('h2m-selected-item'); } updateTip(); } else { processSelection(); } } }); GM_registerMenuCommand('开始选择 / Start Selection', startSelecting); GM_registerMenuCommand('配置过滤规则 / Configure Filters', () => { const currentFilters = JSON.stringify(filterConfig || DEFAULT_FILTER_CONFIG, null, 2); const newFiltersStr = prompt("编辑过滤规则 (JSON):\n支持的配置项:\n- removeTags: 要移除的HTML标签\n- removeAttributes: 要移除的属性\n- keepAttributesOnTags: 特定标签保留的属性\n- removeElementsWithClasses: 要移除的CSS类\n- removeElementsWithIds: 要移除的ID\n- smartContentDetection: 智能内容检测\n- preserveCodeBlocks: 保留代码块", currentFilters); if (newFiltersStr) { try { const newFilters = JSON.parse(newFiltersStr); filterConfig = { ...DEFAULT_FILTER_CONFIG, ...newFilters }; GM_setValue('filterConfig', JSON.stringify(filterConfig)); alert("过滤规则已更新!页面将刷新以应用新规则。"); location.reload(); } catch (err) { alert("无效的JSON格式!规则未更新。\n" + err.message); } } }); GM_registerMenuCommand('重置为默认配置 / Reset to Default', () => { if (confirm('确定要重置所有配置为默认值吗?')) { GM_setValue('filterConfig', JSON.stringify(DEFAULT_FILTER_CONFIG)); GM_setValue('shortCutConfig', JSON.stringify(DEFAULT_SHORTCUT_CONFIG)); alert('配置已重置!页面将刷新。'); location.reload(); } }); // --- CSS Styles --- GM_addStyle(` .h2m-selection-box { outline: 2px dashed #0B57D0 !important; background-color: rgba(11, 87, 208, 0.1) !important; box-shadow: 0 0 0 9999px rgba(0,0,0,0.05), inset 0 0 0 1px rgba(11, 87, 208, 0.3) !important; position: relative; z-index: 9999998; transition: all 0.2s ease-in-out !important; } .h2m-selected-item { outline: 2px solid #D00B0B !important; /* Solid red outline for selected items */ background-color: rgba(208, 11, 11, 0.15) !important; /* Light red background */ box-shadow: 0 0 0 9999px rgba(0,0,0,0.05), inset 0 0 0 1px rgba(208, 11, 11, 0.4) !important; } .h2m-selection-box::before { content: attr(tagName) ' - ' attr(class); position: absolute; top: -25px; left: 0; background: #0B57D0; color: white; padding: 2px 8px; font-size: 12px; border-radius: 3px; z-index: 10000000; font-family: monospace; white-space: nowrap; max-width: 200px; overflow: hidden; text-overflow: ellipsis; } .h2m-no-scroll { overflow: hidden !important; } .h2m-modal-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.6); z-index: 9999999; display: flex; align-items: center; justify-content: center; } .h2m-modal { width: 90%; height: 85%; max-width: 1600px; max-height: 95vh; background: #FFFFFF; border-radius: 16px; box-shadow: 0 8px 12px rgba(0,0,0,0.15), 0 4px 8px rgba(0,0,0,0.1); display: flex; flex-direction: column; padding: 0; position: relative; overflow: hidden; } .h2m-modal-body { flex-grow: 1; display: flex; flex-direction: row; overflow: hidden; border-top-left-radius: 16px; border-top-right-radius: 16px; } .h2m-modal-footer { flex-shrink: 0; padding: 12px 24px; background-color: #F8F9FA; border-top: 1px solid #DEE2E6; display: flex; justify-content: flex-end; align-items: center; gap: 12px; /* Ensure vertical alignment and gap */ border-bottom-left-radius: 16px; border-bottom-right-radius: 16px; position: relative; } .h2m-modal textarea.h2m-markdown-area, .h2m-modal .h2m-preview { flex: 1; height: 100%; padding: 20px 24px; box-sizing: border-box; overflow-y: auto; border: none; font-size: 14px; line-height: 1.6; margin: 0; } .h2m-modal textarea.h2m-markdown-area { font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; border-right: 1px solid #DCDCDC; resize: none; color: #333; background-color: #FAFAFA; } .h2m-modal textarea.h2m-markdown-area:focus { outline: none; box-shadow: none; } .h2m-modal .h2m-preview { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; background-color: #FFFFFF !important; color: #1C1B1F !important; } .h2m-modal .h2m-preview * { color: inherit !important; background-color: transparent !important; font-family: inherit !important; font-size: inherit !important; line-height: inherit !important; margin: 0; padding: 0; border: 0; } .h2m-modal .h2m-preview p { margin-bottom: 1em; } .h2m-modal .h2m-preview h1, .h2m-modal .h2m-preview h2, .h2m-modal .h2m-preview h3, .h2m-modal .h2m-preview h4, .h2m-modal .h2m-preview h5, .h2m-modal .h2m-preview h6 { margin-top: 1.5em; margin-bottom: 0.5em; font-weight: 600; line-height: 1.2; } .h2m-modal .h2m-preview h1 { font-size: 2em; } .h2m-modal .h2m-preview h2 { font-size: 1.75em; } .h2m-modal .h2m-preview h3 { font-size: 1.5em; } .h2m-modal .h2m-preview h4 { font-size: 1.25em; } .h2m-modal .h2m-preview h5 { font-size: 1.125em; } .h2m-modal .h2m-preview h6 { font-size: 1em; } .h2m-modal .h2m-preview a, .h2m-modal .h2m-preview a:visited { color: #0B57D0 !important; text-decoration: none !important; } .h2m-modal .h2m-preview a:hover, .h2m-modal .h2m-preview a:focus { text-decoration: underline !important; } .h2m-modal .h2m-preview ul, .h2m-modal .h2m-preview ol { margin-bottom: 1em; padding-left: 2em; } .h2m-modal .h2m-preview li { margin-bottom: 0.25em; } .h2m-modal .h2m-preview ul li::marker, .h2m-modal .h2m-preview ol li::marker { color: #1C1B1F; } .h2m-modal .h2m-preview blockquote { border-left: 4px solid #CAC4D0; padding: 0.5em 1em; margin: 1em 0; color: #49454F !important; background-color: #F5F3F7 !important; } .h2m-modal .h2m-preview blockquote p { margin-bottom: 0.5em; } .h2m-modal .h2m-preview blockquote p:last-child { margin-bottom: 0; } .h2m-modal .h2m-preview code { font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; background-color: #E8DEF8 !important; color: #1D192B !important; padding: 0.2em 0.4em; border-radius: 4px; font-size: 0.9em; } .h2m-modal .h2m-preview pre { font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace; background-color: #202124 !important; color: #E8EAED !important; padding: 1em; margin: 1em 0; border-radius: 8px; overflow-x: auto; font-size: 0.9em; line-height: 1.45; } .h2m-modal .h2m-preview pre code { background-color: transparent !important; color: inherit !important; padding: 0; border-radius: 0; font-size: inherit; } .h2m-modal .h2m-preview table { width: auto; max-width: 100%; border-collapse: collapse; margin: 1em 0; border: 1px solid #CAC4D0; } .h2m-modal .h2m-preview th, .h2m-modal .h2m-preview td { border: 1px solid #CAC4D0; padding: 0.5em 0.75em; text-align: left; } .h2m-modal .h2m-preview th { background-color: #F5F3F7 !important; font-weight: 600; } .h2m-modal .h2m-preview hr { border: none; border-top: 1px solid #CAC4D0; margin: 2em 0; } .h2m-modal .h2m-preview img { max-width: 100%; height: auto; border-radius: 8px; margin: 1em 0; display: block; } .h2m-modal-footer button, .h2m-modal-footer button.h2m-copy, .h2m-modal-footer button.h2m-download { position: static !important; display: inline-flex !important; background-color: #0B57D0 !important; color: #FFFFFF !important; border: none; border-radius: 20px; padding: 0 24px; font-size: 14px; font-weight: 500; line-height: 1; text-align: center; text-decoration: none; align-items: center; justify-content: center; height: 40px; min-width: 80px; box-sizing: border-box; cursor: pointer; box-shadow: 0 1px 2px rgba(0,0,0,0.15), 0 1px 3px rgba(0,0,0,0.1); transition: background-color 0.2s ease-in-out, box-shadow 0.2s ease-in-out; margin: 0; } .h2m-modal-footer button:hover, .h2m-modal-footer button.h2m-copy:hover, .h2m-modal-footer button.h2m-download:hover { background-color: #0A50BF !important; box-shadow: 0 2px 4px rgba(0,0,0,0.15), 0 2px 6px rgba(0,0,0,0.1); } .h2m-modal .h2m-close { position: absolute; top: 12px; right: 12px; width: 40px; height: 40px; background-color: transparent !important; border-radius: 50%; border: none; display: flex; justify-content: center; align-items: center; cursor: pointer; padding: 0; box-shadow: none !important; z-index: 20; transition: opacity 0.2s ease-in-out; } .h2m-modal .h2m-close svg { width: 24px; height: 24px; display: block; } .h2m-modal .h2m-close svg path { fill: #B3261E !important; transition: fill 0.2s ease-in-out; } .h2m-modal .h2m-close:hover svg path { fill: #9E221A !important; } .h2m-modal .h2m-close:hover { opacity: 0.85; } /* Use a high-specificity ID selector to style the tip box, avoiding !important */ #h2m-tip-instance { position: fixed; top: 20px; right: 20px; background-color: rgba(255,255,255,0.95); color: #202124; /* High specificity from ID selector makes !important unnecessary */ border: 1px solid #DCDCDC; padding: 10px 15px; z-index: 10000000; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.2); max-width: 300px; font-family: sans-serif; font-size: 14px; } #h2m-tip-instance h1, #h2m-tip-instance h2, #h2m-tip-instance h3 { margin-top: 0.5em; margin-bottom: 0.2em; font-weight: 600; } #h2m-tip-instance ul { margin-left: 20px; padding-left: 0; } #h2m-tip-instance li { margin-bottom: 0.3em; } `); console.log('[HTML Content to Markdown] Script loaded. Version 0.2.0. Shortcut:', shortCutConfig, "Filters:", filterConfig); if (!TurndownPluginGfmService || typeof TurndownPluginGfmService.gfm !== 'function') { console.error("[HTML to MD] Turndown GFM plugin not loaded correctly!"); alert("[HTML to MD] Error: GFM plugin failed to load. Some Markdown features might not work correctly."); } if (typeof marked === 'undefined' || typeof marked.parse !== 'function') { console.error("[HTML to MD] Marked library not loaded correctly!"); alert("[HTML to MD] Error: Markdown preview library (Marked) failed to load."); } })();