DeepWiki Code Copy

Add copy button to code blocks on DeepWiki

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         DeepWiki Code Copy
// @namespace    deepwiki_code_copy
// @description  Add copy button to code blocks on DeepWiki
// @version      1.1.0
// @author       roojay(https://github.com/roojay520)
// @license      http://opensource.org/licenses/MIT
// @match        https://deepwiki.com/*
// @run-at       document-end
// @grant        GM_addStyle
// ==/UserScript==

GM_addStyle(`
  .code-copy-button {
    position: absolute;
    top: 5px;
    right: 5px;
    padding: 4px 8px;
    background-color: rgba(0, 0, 0, 0.6);
    color: #fff;
    font-size: 12px;
    border-radius: 4px;
    cursor: pointer;
    opacity: 1;
    z-index: 100;
    border: none;
    outline: none;
  }
  
  .code-copy-button:hover {
    background-color: rgba(0, 0, 0, 0.8);
  }
  
  .code-copy-button.copied {
    background-color: #4caf50;
  }
  
  pre {
    position: relative;
  }
`);

(function() {
  /** Store processed code blocks to avoid duplicate processing */
  const processedPre = new WeakSet();

  /** Add copy buttons to code blocks */
  function addCopyButtonsToCodeBlocks() {
    /** Get all top-level pre elements (not nested inside other pre elements) */
    const preElements = Array.from(document.querySelectorAll('pre')).filter(pre => {
      // Check if this pre is nested inside another pre
      const parentPre = pre.closest('pre:not(:scope)');
      return !parentPre; // If no parent pre, it's a top-level pre
    });
    
    preElements.forEach(pre => {
      // Skip if already processed
      if (processedPre.has(pre)) {
        return;
      }
      
      // Mark as processed
      processedPre.add(pre);
      
      // Create copy button
      const copyButton = document.createElement('button');
      copyButton.className = 'code-copy-button';
      copyButton.textContent = 'Copy';
      
      // Add copy functionality
      copyButton.addEventListener('click', function(e) {
        e.stopPropagation();
        
        // Get clean code content by cloning the pre element, removing the button, and getting text
        const preClone = pre.cloneNode(true);
        const buttonInClone = preClone.querySelector('.code-copy-button');
        if (buttonInClone) {
          buttonInClone.remove();
        }
        const codeContent = preClone.textContent || '';
        
        // Copy to clipboard
        navigator.clipboard.writeText(codeContent).then(() => {
          // Visual feedback on success
          copyButton.classList.add('copied');
          copyButton.textContent = 'Copied!';
          
          // Reset after 2 seconds
          setTimeout(() => {
            copyButton.classList.remove('copied');
            copyButton.textContent = 'Copy';
          }, 2000);
        }).catch(err => {
          console.error('Copy failed:', err);
          
          // Fallback copy method
          const textarea = document.createElement('textarea');
          textarea.value = codeContent;
          textarea.style.position = 'fixed';
          textarea.style.opacity = '0';
          document.body.appendChild(textarea);
          textarea.select();
          
          try {
            document.execCommand('copy');
            copyButton.classList.add('copied');
            copyButton.textContent = 'Copied!';
            setTimeout(() => {
              copyButton.classList.remove('copied');
              copyButton.textContent = 'Copy';
            }, 2000);
          } catch (err) {
            console.error('Fallback copy failed:', err);
            copyButton.textContent = 'Copy failed';
          }
          
          document.body.removeChild(textarea);
        });
      });
      
      // Add button to code block
      pre.appendChild(copyButton);
    });
  }

  /** Initialize script */
  function initScript() {
    // Process initially loaded code blocks
    addCopyButtonsToCodeBlocks();
    
    // Watch for DOM changes to handle dynamically loaded code blocks
    const observer = new MutationObserver((mutations) => {
      let hasNewPre = false;
      
      mutations.forEach(mutation => {
        if (mutation.type === 'childList') {
          const addedNodes = Array.from(mutation.addedNodes);
          
          // Check for new pre elements or nodes that might contain pre elements
          const hasPreNodes = addedNodes.some(node => {
            if (node.nodeName === 'PRE') return true;
            if (node.nodeType === 1 && node.querySelector('pre')) return true;
            return false;
          });
          
          if (hasPreNodes) {
            hasNewPre = true;
          }
        }
      });
      
      if (hasNewPre) {
        addCopyButtonsToCodeBlocks();
      }
    });
    
    // Start observing document body
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }

  // Start script when page is loaded
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', initScript);
  } else {
    initScript();
  }
})();