// ==UserScript== // @name Alphaxiv Assistant Enhancer // @namespace https://github.com/kagani // @version 1.0 // @description Copy and download buttons for Alphaxiv Assistant. // @author Kagan Ilbak // @match https://www.alphaxiv.org/* // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3MTguNDEgNTA0LjQ3IiB3aWR0aD0iNzE4LjQxIiBoZWlnaHQ9IjUwNC40NyIgY2xhc3M9InRleHQtY3VzdG9tLXJlZCBzaXplLThoIGRhcms6dGV4dC13aGl0ZSIgZGF0YS1zZW50cnktZWxlbWVudD0ic3ZnIiBkYXRhLXNlbnRyeS1zb3VyY2UtZmlsZT0iQWxwaFhpdkxvZ28udHN4IiBkYXRhLXNlbnRyeS1jb21wb25lbnQ9IkFscGhhWGl2TG9nbyI+PHBvbHlnb24gZmlsbD0iY3VycmVudENvbG9yIiBwb2ludHM9IjU5MS4xNSAgMjU4LjU0IDcxOC40MSAzODUuNzMgNjYzLjcyIDQ0MC4yOCA1MzYuNTcgMzEzLjYyIDU5MS4xNSAyNTguNTQiIGRhdGEtc2VudHlyLWVsZW1lbnQ9InBvbHlnb24iIGRhdGEtc2VudHktc291cmNlLWZpbGU9IkFscGhhWGl2TG9nby50c3giPjwvcG9seWdvbj48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik0yNzMuODYuM2MzNC41Ni0yLjQxLDY3LjY2LDkuNzMsOTIuNTEsMzMuNTRsOTQuNjQsOTQuNjMtNTUuMTEsNTQuNTUtOTYuNzYtOTYuNTVjLTE2LjAyLTEyLjctMzcuNjctMTIuMS01My4xOSwxLjExTDU0LjYyLDI4OC44MiwwLDIzNC4yMywyMDQuNzYsMjkuNTdDMjIzLjEyLDEzLjMxLDI0OS4yNywyLjAyLDI3My44Ni4zWiIgZGF0YS1zZW50cnktZWxlbWVudD0icGF0aCIgZGF0YS1zZW50cnktc291cmNlLWZpbGU9IkFscGhhWGl2TG9nby50c3giPjwvcGF0aD48cGF0aCBmaWxsPSJjdXJyZW50Q29sb3IiIGQ9Ik02NjMuNzksMS4yOWw1NC42Miw1NC41OC00MTguMTEsNDE3LjljLTExNC40Myw5NS45NC0yNjMuNTctNTMuNDktMTY3LjA1LTE2Ny41MmwxNjAuNDYtMTYwLjMzLDU0LjYyLDU0LjU4LTE1Ny44OCwxNTcuNzdjLTMzLjE3LDQwLjMyLDE4LjkzLDkxLjQxLDU4LjY2LDU3LjQ4TDY2My43OSwxLjI5WiIgZGF0YS1zZW50cnktZWxlbWVudD0icGF0aCIgZGF0YS1zZW50cnktc291cmNlLWZpbGU9IkFscGhhWGl2TG9nby50c3giPjwvcGF0aD48L3N2Zz4= // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/538221/Alphaxiv%20Assistant%20Enhancer.user.js // @updateURL https://update.greasyfork.icu/scripts/538221/Alphaxiv%20Assistant%20Enhancer.meta.js // ==/UserScript== (function() { const style = document.createElement('style'); style.textContent = ` .__msg-wrapper { display: flex; flex-direction: column; margin-bottom: 0.75em; } .__msg-btn-container { display: flex; gap: 8px; align-self: flex-start; margin-top: 4px; } .__msg-copy-btn, .__msg-download-btn { background: transparent; border: none; padding: 4px; cursor: pointer; opacity: 0.6; transition: opacity 0.2s ease; } .__msg-copy-btn:hover, .__msg-download-btn:hover { opacity: 1; } .__msg-copy-btn svg, .__msg-download-btn svg { width: 1rem; height: 1rem; fill: currentColor; } .__msg-copied-indicator { align-self: flex-start; background: rgba(0, 0, 0, 0.75); color: #fff; font-size: 0.75rem; padding: 2px 6px; border-radius: 3px; opacity: 0; transition: opacity 0.2s ease; margin-top: 4px; } .__msg-copied-indicator.show { opacity: 1; } .__code-wrapper { position: relative; margin-bottom: 1em; } .__copy-container { position: sticky; top: 0.25rem; z-index: 10; display: flex; justify-content: flex-end; background: inherit; } .__code-wrapper > pre { margin-top: -2.5rem; padding-top: 1rem; overflow: auto; } .__copy-btn { margin-right: 0.5rem; margin-bottom: 0.25rem; background: #444; color: #fff; border: none; border-radius: 3px; padding: 0.25em 0.6em; font-size: 0.8em; cursor: pointer; opacity: 0.75; transition: opacity 0.2s ease; } .__copy-btn:hover { opacity: 1; } `; document.head.appendChild(style); const copyIconSVG = ` `; const downloadIconSVG = ` `; const checkmarkSVG = ` `; function wrapMessage(el) { if (el.parentElement.classList.contains('__msg-wrapper')) return; const wrapper = document.createElement('div'); wrapper.classList.add('__msg-wrapper'); el.parentNode.insertBefore(wrapper, el); wrapper.appendChild(el); const markdownContainer = el.querySelector('.markdown-content'); const textContent = markdownContainer ? null : el.innerText; const btnContainer = document.createElement('div'); btnContainer.classList.add('__msg-btn-container'); wrapper.appendChild(btnContainer); const copyBtn = document.createElement('button'); copyBtn.classList.add('__msg-copy-btn'); copyBtn.innerHTML = copyIconSVG; btnContainer.appendChild(copyBtn); const downloadBtn = document.createElement('button'); downloadBtn.classList.add('__msg-download-btn'); downloadBtn.innerHTML = downloadIconSVG; btnContainer.appendChild(downloadBtn); const originalCopyHTML = copyIconSVG; const originalDownloadHTML = downloadIconSVG; copyBtn.addEventListener('click', async () => { try { const toCopy = markdownContainer ? markdownContainer.innerText : textContent; await navigator.clipboard.writeText(toCopy); copyBtn.innerHTML = checkmarkSVG; setTimeout(() => { copyBtn.innerHTML = originalCopyHTML; }, 1200); } catch { } }); downloadBtn.addEventListener('click', () => { const toDownload = markdownContainer ? markdownContainer.innerText : textContent; const blob = new Blob([toDownload], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const timestamp = Date.now(); a.download = `message_${timestamp}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); downloadBtn.innerHTML = checkmarkSVG; setTimeout(() => { downloadBtn.innerHTML = originalDownloadHTML; }, 1200); }); } function processMessages() { document.querySelectorAll('[data-sentry-component="ChatMessage"]').forEach(wrapMessage); } function addStickyCopyButtons() { document.querySelectorAll('pre > code').forEach((codeEl) => { if (codeEl.closest('.__code-wrapper')) return; const preEl = codeEl.parentElement; const wrapper = document.createElement('div'); wrapper.classList.add('__code-wrapper'); preEl.parentNode.insertBefore(wrapper, preEl); const copyContainer = document.createElement('div'); copyContainer.classList.add('__copy-container'); const btn = document.createElement('button'); btn.classList.add('__copy-btn'); btn.innerText = 'Copy'; copyContainer.appendChild(btn); wrapper.appendChild(copyContainer); wrapper.appendChild(preEl); btn.addEventListener('click', async () => { const text = codeEl.innerText; try { await navigator.clipboard.writeText(text); btn.innerText = 'Copied!'; setTimeout(() => { btn.innerText = 'Copy'; }, 1200); } catch { btn.innerText = 'Error'; setTimeout(() => { btn.innerText = 'Copy'; }, 1200); } }); }); } processMessages(); addStickyCopyButtons(); const observer = new MutationObserver(() => { processMessages(); addStickyCopyButtons(); }); observer.observe(document.body, { childList: true, subtree: true }); })();