Copy Markdown Quote Alt+C

Press Alt+C to copy title and url as markdown style link `> ${SELECTION} [${TITLE}]( ${URL} )`

// ==UserScript==
// @name            Copy Markdown Quote Alt+C
// @name:zh         Alt+C 复制Markdown格式标题和地址快速分享
// @name:en         Alt+C Copy Title and Link as Markdown Style
// @name:ar         Alt+C نسخ العنوان والرابط بتنسيق Markdown
// @name:es         Alt+C Copiar Título y Enlace en Formato Markdown
// @name:fr         Alt+C Copier le Titre et le Lien au Format Markdown
// @name:ru         Alt+C Копировать Заголовок и Ссылку в Формате Markdown
// @description     Press Alt+C to copy title and url as markdown style link `> ${SELECTION} [${TITLE}]( ${URL} )`
// @description:zh  按 Alt+C 复制 Markdown 格式的链接 `> ${SELECTION} [${TITLE}]( ${URL} )`
// @description:ar  اضغط Alt+C لنسخ العنوان والرابط بتنسيق Markdown `> ${SELECTION} [${TITLE}]( ${URL} )`
// @description:es  Presiona Alt+C para copiar título y URL como enlace en formato Markdown `> ${SELECTION} [${TITLE}]( ${URL} )`
// @description:fr  Appuyez sur Alt+C pour copier le titre et l'URL comme lien au format Markdown `> ${SELECTION} [${TITLE}]( ${URL} )`
// @description:ru  Нажмите Alt+C для копирования заголовка и URL как ссылки в формате Markdown `> ${SELECTION} [${TITLE}]( ${URL} )`
// @namespace       https://userscript.snomiao.com/
// @version         0.8.5
// @author          [email protected]
// @supportURL      https://github.com/snomiao/copy-markdown-title-alt-c.user.js
// @homepageURL     https://github.com/snomiao/copy-markdown-title-alt-c.user.js
// @match           *://*/*
// @grant           none
// ==/UserScript==

(function() {
    'use strict';

    // Initialize the script
    function init() {
        // Store cleanup function globally to allow re-initialization
        if (window.copyMarkdownQuoteCleanup) {
            window.copyMarkdownQuoteCleanup();
        }

        // Register the Alt+C hotkey
        window.copyMarkdownQuoteCleanup = registerHotkey('Alt+C', handleCopyMarkdown);
    }

    // Register a hotkey combination
    function registerHotkey(combination, callback) {
        const normalizedCombo = normalizeHotkeyCombination(combination);
        
        function keydownHandler(event) {
            if (isHotkeyMatch(event, normalizedCombo)) {
                event.preventDefault();
                event.stopPropagation();
                callback(event);
            }
        }

        document.addEventListener('keydown', keydownHandler, true);

        // Return cleanup function
        return function cleanup() {
            document.removeEventListener('keydown', keydownHandler, true);
        };
    }

    // Normalize hotkey combination string
    function normalizeHotkeyCombination(combo) {
        const parts = combo.toLowerCase().split('+').map(s => s.trim());
        const modifiers = {
            ctrl: false,
            alt: false,
            shift: false,
            meta: false
        };
        let key = '';

        parts.forEach(part => {
            switch(part) {
                case 'ctrl':
                case 'control':
                    modifiers.ctrl = true;
                    break;
                case 'alt':
                    modifiers.alt = true;
                    break;
                case 'shift':
                    modifiers.shift = true;
                    break;
                case 'meta':
                case 'cmd':
                case 'command':
                case 'win':
                case 'windows':
                    modifiers.meta = true;
                    break;
                default:
                    key = part;
            }
        });

        return { modifiers, key };
    }

    // Check if keyboard event matches the hotkey combination
    function isHotkeyMatch(event, combo) {
        const keyMatches = event.key.toLowerCase() === combo.key.toLowerCase();
        const modifiersMatch = 
            event.ctrlKey === combo.modifiers.ctrl &&
            event.altKey === combo.modifiers.alt &&
            event.shiftKey === combo.modifiers.shift &&
            event.metaKey === combo.modifiers.meta;

        return keyMatches && modifiersMatch;
    }

    // Handle the copy markdown action
    async function handleCopyMarkdown() {
        try {
            // Get selected text if any
            const selectedText = window.getSelection().toString().trim();
            
            // Format selected text as markdown quote
            const quotedText = selectedText ? 
                selectedText.split('\n').map(line => `> ${line}`).join('\n') : 
                '';

            // Get the best title for the page
            const title = getBestPageTitle();
            
            // Escape special markdown characters in title
            const escapedTitle = title.replace(/([[\]|])/g, '\\$1');
            
            // Get current URL
            const url = window.location.href;

            // Build the markdown content
            const markdownContent = quotedText ? 
                `- [${escapedTitle}]( ${url} )\n${quotedText}` :
                `- [${escapedTitle}]( ${url} )`;

            // Copy to clipboard
            await copyToClipboard(markdownContent);

            // Show success notification
            showNotification(`Copied:\n${markdownContent}`);

        } catch (error) {
            console.error('Failed to copy markdown:', error);
            showNotification('Failed to copy to clipboard. Please check permissions.');
        }
    }

    // Get the best title for the current page
    function getBestPageTitle() {
        const candidates = [
            document.title,
            document.querySelector('h1')?.innerText || ''
        ];

        // Clean up titles (remove line breaks)
        const cleanedCandidates = candidates.map(title => 
            title.replace(/\r?\n.*/g, '').trim()
        );

        // Return the longest title (usually more descriptive)
        return cleanedCandidates.sort((a, b) => b.length - a.length)[0] || 'Untitled';
    }

    // Copy text to clipboard
    async function copyToClipboard(text) {
        // Try modern clipboard API first
        if (navigator.clipboard && navigator.clipboard.writeText) {
            await navigator.clipboard.writeText(text);
        } else {
            // Fallback for older browsers
            const textarea = document.createElement('textarea');
            textarea.value = text;
            textarea.style.position = 'fixed';
            textarea.style.opacity = '0';
            document.body.appendChild(textarea);
            textarea.select();
            document.execCommand('copy');
            document.body.removeChild(textarea);
        }
    }

    // Show notification to user
    function showNotification(message) {
        // Use alert for simple notification
        // Could be enhanced with a custom toast notification in the future
        alert(message);
    }

    // Start the script
    init();
})();