ScholarQA Enhanced Copy Tools

Adds copy button and double-click functionality to extract formatted text from scholarqa.allen.ai

// ==UserScript==
// @name         ScholarQA Enhanced Copy Tools
// @namespace    https://violentmonkey.github.io/
// @version      1.4
// @description  Adds copy button and double-click functionality to extract formatted text from scholarqa.allen.ai
// @author       Bui Quoc Dung
// @match        *://scholarqa.allen.ai/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // Define CSS selector variables for easier maintenance
    const ACCORDION_SUMMARY_SELECTOR    = '.MuiButtonBase-root.MuiAccordionSummary-root';
    const ACCORDION_ROOT_SELECTOR       = '.MuiAccordion-root';
    const ACCORDION_HEADING_SELECTOR    = '.MuiTypography-h5';
    const ACCORDION_BODY_SELECTOR       = '.MuiTypography-body1';
    const CITATION_SELECTOR             = '.MuiChip-root';
    const PARAGRAPH_SELECTOR            = '.MuiCollapse-root';
    const COPY_BUTTON_CLASS             = 'MuiButtonBase-root MuiIconButton-root MuiIconButton-sizeSmall';

    // Find the "Disclaimer" button dynamically based on its text content
    function findDisclaimerButton() {
        const buttons = document.querySelectorAll('button');
        for (let button of buttons) {
            if (button.textContent.includes('Disclaimer')) {
                return button;
            }
        }
        return null;
    }

    // Expands all collapsed sections for complete content extraction
    function expandSections() {
        console.log("Expanding all sections...");
        document.querySelectorAll(ACCORDION_SUMMARY_SELECTOR).forEach(btn => btn.click());
    }

    // Shows notification when content is copied
    function showNotification(x, y) {
        let notification = document.createElement('div');
        notification.innerText = "Copied!";
        Object.assign(notification.style, {
            position: 'absolute',
            left: `${x}px`,
            top: `${y}px`,
            background: 'rgba(0, 0, 0, 0.8)',
            color: 'white',
            padding: '8px 12px',
            borderRadius: '5px',
            fontSize: '14px',
            zIndex: '1000',
            transition: 'opacity 0.5s'
        });
        document.body.appendChild(notification);
        setTimeout(() => {
            notification.style.opacity = '0';
            setTimeout(() => document.body.removeChild(notification), 500);
        }, 1500);
    }

    // Copies formatted HTML content to clipboard
    function copyToClipboard(text) {
        let tempContainer = document.createElement('div');
        tempContainer.innerHTML = text;
        document.body.appendChild(tempContainer);
        let range = document.createRange();
        range.selectNode(tempContainer);
        let selection = window.getSelection();
        selection.removeAllRanges();
        selection.addRange(range);

        try {
            document.execCommand('copy');
            console.log("Copied formatted text.");
        } catch (err) {
            console.error("Copy failed", err);
        }

        selection.removeAllRanges();
        document.body.removeChild(tempContainer);
    }

    // Processes text from a single paragraph element
    function processText(paragraphElement, includeHeading = true) {
        let tempDiv = paragraphElement.cloneNode(true);

        let paragraphText = tempDiv.innerHTML;

        tempDiv.querySelectorAll(CITATION_SELECTOR).forEach(citation => {
            let linkElement = citation.closest('a');
            if (linkElement) {
                let hyperlink = linkElement.href;
                let citationText = citation.innerText;
                let formattedCitation = `<a href="${hyperlink}" target="_blank">${citationText}</a>`;
                paragraphText = paragraphText.replace(citation.outerHTML, formattedCitation);
            }
        });

        paragraphText = paragraphText.replace(/<div[^>]*>/g, ' ')
                                     .replace(/<\/div>/g, '')
                                     .replace(/\s+/g, ' ');

        paragraphText = paragraphText.replace(/Is this section helpful\? /g, ""); // Remove "Is this section helpful? "

        if (includeHeading) {
            let headingText = "";
            let intermediateText = "";

            let accordion = paragraphElement.closest(ACCORDION_ROOT_SELECTOR);
            if (accordion) {
                let headingElement = accordion.querySelector(ACCORDION_HEADING_SELECTOR);
                if (headingElement) {
                    headingText = `<strong><u>${headingElement.innerText.trim()}</u></strong>`.trim();
                }

                let intermediateElement = accordion.querySelector(ACCORDION_BODY_SELECTOR);
                if (intermediateElement) {
                    intermediateText = intermediateElement.innerText.trim();
                }

                if (headingText) {
                    let prefix = headingText + ':\n';
                    if (intermediateText) {
                        prefix += intermediateText + '\n';
                    }
                    paragraphText = prefix + paragraphText;
                }
            }
        }

        return paragraphText;
    }


    // Adds a copy button next to the "Disclaimer" button
    function addCopyButton() {
        let targetButton = findDisclaimerButton();
        if (!targetButton) {
            return;
        }

        if (document.querySelector('#custom-copy-button')) {
            return;
        }
        let copyButton = document.createElement('button');
        copyButton.id = 'custom-copy-button';
        copyButton.className = COPY_BUTTON_CLASS;
        copyButton.style.marginLeft = '10px';
        copyButton.style.color = 'black';
        copyButton.title = "Copy Text";
        copyButton.textContent = "Copy";

        targetButton.parentNode.insertBefore(copyButton, targetButton.nextSibling);

        copyButton.addEventListener('click', (event) => {
            console.log("Copy button clicked!");
            copyButton.textContent = "Coping";

            expandSections();

            setTimeout(() => {
                let copiedText = extractAllText();
                copyToClipboard(copiedText);
                copyButton.textContent = "Copy";
            }, 1500);
        });
    }

    // Extracts formatted text from all paragraphs
    function extractAllText() {
        let paragraphs = document.querySelectorAll(PARAGRAPH_SELECTOR);
        let copiedText = "";

        paragraphs = Array.from(paragraphs).slice(0, -1); // Loại bỏ phần tử cuối

        paragraphs.forEach(paragraph => {
            let processedText = processText(paragraph, true);
            copiedText += processedText + '\n\n';
        });

        return copiedText;
    }

    // Handles double-click event to copy individual paragraph
    function handleDoubleClick(event) {
        const targetDiv = event.target.closest(PARAGRAPH_SELECTOR);
        if (!targetDiv) return;

        let accordion = targetDiv.closest(ACCORDION_ROOT_SELECTOR);
        if (accordion) {
            let button = accordion.querySelector(ACCORDION_SUMMARY_SELECTOR);
            if (button) {
                button.click();
            }
        }

        setTimeout(() => {
            let textContent = processText(targetDiv, true);
            copyToClipboard(textContent);
            showNotification(event.pageX, event.pageY);
        }, 500);
    }

    // Initialize both features
    function initialize() {
        const observer = new MutationObserver(() => {
            addCopyButton();
        });

        observer.observe(document.body, { childList: true, subtree: true });

        document.body.addEventListener('dblclick', handleDoubleClick);

        setTimeout(addCopyButton, 2000);
    }

    initialize();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址