Magic Markdown Copy (Ctrl+C & Button)

Automatically converts Markdown to rich HTML on copy, both from user selection (Ctrl+C) and from website "Copy" buttons.

// ==UserScript==
// @name         Magic Markdown Copy (Ctrl+C & Button)
// @namespace    http://tampermonkey.net/
// @version      3.0
// @description  Automatically converts Markdown to rich HTML on copy, both from user selection (Ctrl+C) and from website "Copy" buttons.
// @author       Tertium
// @match        *://*/*
// @require      https://cdnjs.cloudflare.com/ajax/libs/marked/5.1.2/marked.min.js
// @grant        GM_notification
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // --- Configuration ---
    const SMART_DETECTION = true; // Set to false to attempt conversion on ALL copied text.
    const MD_CHARS = /[\*\_#\`\[\]\->\!]/; // Regex to detect Markdown characters

    /**
     * The core conversion function. Takes Markdown text and writes rich HTML to the clipboard.
     * @param {string} markdownText - The text to convert.
     * @returns {Promise<void>}
     */
    async function convertAndCopyToClipboard(markdownText) {
        console.log('Attempting to convert Markdown:', markdownText.substring(0, 80) + '...');
        try {
            // Convert the Markdown to HTML using the 'marked' library
            const generatedHtml = marked.parse(markdownText, { gfm: true, breaks: true });

            // Create a rich text clipboard item that has both HTML and plain text fallbacks
            const blobHtml = new Blob([generatedHtml], { type: 'text/html' });
            const blobText = new Blob([markdownText], { type: 'text/plain' });
            const clipboardItem = new ClipboardItem({
                'text/html': blobHtml,
                'text/plain': blobText,
            });

            // Write the new rich text content to the clipboard
            await navigator.clipboard.write([clipboardItem]);
            console.log('Markdown successfully converted and copied as rich text.');
            GM_notification({
                text: 'Converted to HTML and copied!',
                title: 'Markdown Magic Copy',
                timeout: 2000
            });
        } catch (err) {
            console.error('Failed to convert or copy:', err);
            GM_notification({
                text: 'Error during conversion.',
                title: 'Markdown Magic Copy',
                timeout: 4000
            });
        }
    }


    // --- FEATURE 1: Intercept User Selection (Ctrl+C) ---
    document.addEventListener('copy', (event) => {
        const selectedText = window.getSelection().toString();
        if (selectedText.trim().length === 0) return;

        // If smart detection is on, only proceed if text looks like Markdown
        if (SMART_DETECTION && !MD_CHARS.test(selectedText)) {
            console.log('No Markdown characters in selection. Performing normal copy.');
            return;
        }

        // Prevent the default copy action and run our conversion
        event.preventDefault();
        convertAndCopyToClipboard(selectedText);
    });


    // --- FEATURE 2: Intercept Website "Copy" Buttons ---
    // We "monkey-patch" the clipboard API to intercept programmatic copies.
    if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
        const originalWriteText = navigator.clipboard.writeText;

        navigator.clipboard.writeText = async function(textToCopy) {
            // If smart detection is on, check if the text looks like Markdown
            if (SMART_DETECTION && (typeof textToCopy !== 'string' || !MD_CHARS.test(textToCopy))) {
                // If not, use the original, unmodified function
                return originalWriteText.apply(this, arguments);
            }

            // If it looks like Markdown, run our custom conversion function instead!
            // We don't need to prevent a default action here because we are replacing the function.
            try {
                await convertAndCopyToClipboard(textToCopy);
            } catch (err) {
                // If our function fails for any reason, fall back to the original to avoid breaking the site.
                console.error("Conversion failed, falling back to original copy function.", err);
                return originalWriteText.apply(this, arguments);
            }
        };
        console.log('Magic Markdown Copy: Intercepting copy buttons.');
    }

    console.log('Magic Markdown Copy script is fully active.');

})();