// ==UserScript== // @name 复制LaTex公式,支持ChatGPT/DeepSeek/wikipedia/知乎等多个网站 // @namespace http://tampermonkey.net/ // @version 1.1.3 // @license GPLv3 // @description 单击网页中的LaTeX公式,将其复制到剪切板,可以选择直接复制成mathtype对象或者是latex源码 // @description:en Click on the LaTeX formula on the webpage and copy it to the clipboard. You can choose to directly copy it as a mathtype object or the LaTeX source code // @author S1gn // @match *://*.wikipedia.org/* // @match *://*.zhihu.com/* // @match *://*.chatgpt.com/* // @match *://*.moonshot.cn/* // @match *://*.stackexchange.com/* // @match *://*.deepseek.com/* // @icon https://i.miji.bid/2025/02/16/97ff538f8b0b1b5a3c2f02ad179fd6ea.png // @require https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.jst // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 插入样式表 const css = ` .latex-tooltip { position: fixed; background-color: rgba(0, 0, 0, 0.7); color: #fff; padding: 5px 10px; border-radius: 5px; font-size: 11px; z-index: 1000; opacity: 0; transition: opacity 0.2s; pointer-events: none; } .latex-copy-success { position: fixed; bottom: 10%; left: 50%; transform: translateX(-50%); background-color: rgba(0, 0, 0, 0.7); color: #fff; padding: 10px 20px; border-radius: 5px; font-size: 12px; z-index: 1000; opacity: 0; transition: opacity 0.2s; pointer-events: none; } .formula-buttons { position: fixed; top: 20px; right: 20px; z-index: 1000; } .formula-buttons button { margin: 5px; padding: 10px; font-size: 14px; cursor: pointer; } `; const styleSheet = document.createElement("style"); styleSheet.type = "text/css"; styleSheet.innerText = css; document.head.appendChild(styleSheet); // 创建选择按钮 const buttonContainer = document.createElement('div'); buttonContainer.classList.add('formula-buttons'); document.body.appendChild(buttonContainer); const types = ['LaTeX', 'MathML']; types.forEach(type => { const button = document.createElement('button'); button.textContent = `copy ${type}`; button.addEventListener('click', () => { currentCopyType = type; alert(`selected:${type}`); }); buttonContainer.appendChild(button); }); let currentCopyType = 'LaTeX'; // 默认选择 LaTeX // 创建提示框元素 const tooltip = document.createElement('div'); tooltip.classList.add('latex-tooltip'); document.body.appendChild(tooltip); // 创建复制功能 function copyToClip(text) { const input = document.createElement("input"); input.setAttribute("value", text); document.body.appendChild(input); input.select(); document.execCommand("copy"); document.body.removeChild(input); } // 创建 LaTeX 复制功能 function copyMLToClip(latexInput) { // Reset MathJax MathJax.texReset(); // Convert LaTeX to MathML MathJax.tex2mmlPromise(latexInput).then(function(mathML) { // Copy the MathML to clipboard copyToClip(mathML); showCopySuccessTooltip(); }).catch(function(error) { console.error(error); }); } // 获取对象和公式方法 function getTarget(url) { const targets = { 'wikipedia.org': { elementSelector: 'span.mwe-math-element', getLatexString: el => { const math = el.querySelector('math'); return math ? math.getAttribute('alttext') : '' } }, 'zhihu.com': { elementSelector: 'span.ztext-math', getLatexString: el => el.getAttribute('data-tex') || '' }, 'chatgpt.com': { elementSelector: 'span.katex', getLatexString: el => { const annotation = el.querySelector('annotation'); return annotation ? annotation.textContent : ''; } }, 'deepseek.com': { elementSelector: 'span.katex', getLatexString: el => { const annotation = el.querySelector('.katex-mathml annotation'); return annotation ? annotation.textContent : '' } }, 'moonshot.cn': { elementSelector: 'span.katex', getLatexString: el => { const annotation = el.querySelector('.katex-html annotation'); return annotation ? annotation.textContent : '' } }, 'stackexchange.com': { elementSelector: 'span.math-container', getLatexString: el => { const script = el.querySelector('script'); return script ? script.textContent : '' } } // 其他网站的规则同样类似 }; for (const [key, value] of Object.entries(targets)) { if (url.includes(key)) { return value; } } return null; } // 绑定事件到元素 function addHandler() { const target = getTarget(window.location.href); if (!target) return; document.querySelectorAll(target.elementSelector).forEach(element => { const latexString = target.getLatexString(element); element.addEventListener('mouseenter', function () { element.style.cursor = "pointer"; tooltip.textContent = latexString; const rect = element.getBoundingClientRect(); tooltip.style.left = `${rect.left}px`; tooltip.style.top = `${rect.top - tooltip.offsetHeight - 5}px`; tooltip.style.opacity = '0.8'; }); element.addEventListener('mouseleave', function () { element.style.cursor = "auto"; tooltip.style.opacity = '0'; }); element.addEventListener('click', function() { if (currentCopyType === 'LaTeX') { copyToClip(latexString); } else if (currentCopyType === 'MathML') { copyMLToClip(latexString); } showCopySuccessTooltip(); window.getSelection().removeAllRanges(); }); }); } // 显示复制成功提示 function showCopySuccessTooltip() { const copyTooltip = document.createElement("div"); copyTooltip.className = "latex-copy-success"; copyTooltip.innerText = `copy ${currentCopyType} success!`; document.body.appendChild(copyTooltip); // 强制浏览器重绘,确保样式应用 requestAnimationFrame(() => { copyTooltip.style.opacity = "1"; // 激活显示 }); setTimeout(() => { copyTooltip.style.opacity = "0"; setTimeout(() => { document.body.removeChild(copyTooltip); }, 200); }, 1000); } // 监听页面加载或变化,绑定事件 document.addEventListener('DOMContentLoaded', addHandler); new MutationObserver(addHandler).observe(document.documentElement, { childList: true, subtree: true }); })();