AlphaXiv to ArXiv

Add a button to jump from alphaxiv.org to arxiv.org

// ==UserScript==
// @name         AlphaXiv to ArXiv
// @namespace    https://github.com/pangahn/arxiv-navigator
// @version      0.1
// @description  Add a button to jump from alphaxiv.org to arxiv.org
// @author       pangahn
// @match        https://www.alphaxiv.org/abs/*
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    function addArxivButton() {
        // Use more precise selectors to find the target div container
        // Prioritize the most specific selector, then use backup selectors
        let targetDiv = document.querySelector('[data-sentry-component="RightSection"] .flex.items-center.space-x-2');

        // Backup selector 1: Locate through div containing the download button
        if (!targetDiv) {
            const downloadButton = document.querySelector('button[aria-label="Download from arXiv"]');
            if (downloadButton) {
                targetDiv = downloadButton.closest('.flex.items-center.space-x-2');
            }
        }

        // Backup selector 2: Locate through button containing thumbs-up
        if (!targetDiv) {
            const thumbsUpButton = document.querySelector('svg.lucide-thumbs-up');
            if (thumbsUpButton) {
                targetDiv = thumbsUpButton.closest('.flex.items-center.space-x-2');
            }
        }

        // Backup selector 3: Locate through div containing bookmark
        if (!targetDiv) {
            const bookmarkDiv = document.querySelector('[data-sentry-component="PaperFeedBookmarks"]');
            if (bookmarkDiv) {
                targetDiv = bookmarkDiv.closest('.flex.items-center.space-x-2');
            }
        }

        // Backup selector 4: Last generic selector
        if (!targetDiv) {
            targetDiv = document.querySelector('.flex.items-center.space-x-2');
        }

        if (!targetDiv) {
            console.log('Target div not found, retrying...');
            return false;
        }

        // Check if button has already been added
        if (targetDiv.querySelector('.arxiv-jump-button')) {
            return true;
        }

        // Get paper ID from current URL
        const currentUrl = window.location.href;
        const match = currentUrl.match(/\/abs\/(.+)$/);

        if (!match) {
            console.log('Paper ID not found in URL');
            return true;
        }

        const paperId = match[1];
        const arxivUrl = `https://www.arxiv.org/abs/${paperId}`;

        // Create ArXiv jump button
        const arxivButton = document.createElement('button');
        arxivButton.className = 'arxiv-jump-button inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm ring-offset-white transition-all duration-200 outline-hidden focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-neutral-950 hover:bg-[#9a20360a] hover:text-custom-red dark:text-white dark:hover:bg-custom-red/25 enabled:active:ring-2 enabled:active:ring-[#9a20360a] size-10 rounded-full! h-8 w-8';
        arxivButton.setAttribute('aria-label', 'Jump to ArXiv');
        arxivButton.setAttribute('title', 'Jump to ArXiv');

        // Add ArXiv icon (using external link icon)
        arxivButton.innerHTML = `
            <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-external-link size-4" aria-hidden="true">
                <path d="M15 3h6v6"></path>
                <path d="M10 14 21 3"></path>
                <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path>
            </svg>
        `;

        // Add click event
        arxivButton.addEventListener('click', function(e) {
            e.preventDefault();
            window.open(arxivUrl, '_blank');
        });

        // Insert button at the first position in div
        targetDiv.insertBefore(arxivButton, targetDiv.firstChild);

        console.log('ArXiv jump button added successfully');
        return true;
    }

    // Try to add button after page loads
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            setTimeout(addArxivButton, 100);
        });
    } else {
        // If page is already loaded, try immediately
        setTimeout(addArxivButton, 100);
    }

    // Use MutationObserver to monitor DOM changes, in case button container is dynamically loaded
    const observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                // Delay execution to ensure DOM is fully updated
                setTimeout(function() {
                    if (addArxivButton()) {
                        observer.disconnect(); // Stop observing after button is successfully added
                    }
                }, 100);
            }
        });
    });

    // Start observing DOM changes
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // Stop observing after 5 seconds (to prevent infinite observation)
    setTimeout(function() {
        observer.disconnect();
    }, 5000);

})();