Double-click on a formula on a webpage to copy it to the clipboard, then paste it into Microsoft Word
// ==UserScript==
// @name Copy a math equation to Word
// @namespace http://tampermonkey.net/
// @version 1.0
// @license GPLv3
// @description Double-click on a formula on a webpage to copy it to the clipboard, then paste it into Microsoft Word
// @author Bui Quoc Dung
// @match *://*.wikipedia.org/*
// @match *://*.chatgpt.com/*
// @match *://*.stackexchange.com/*
// @match *://*.deepseek.com/*
// ==/UserScript==
(function () {
'use strict';
// Insert CSS styles
const css = `
.mathml-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; }
.mathml-copy-success { position: absolute; background-color: rgba(0, 0, 0, 0.7); color: #fff; padding: 5px 10px; border-radius: 5px; font-size: 12px; z-index: 1000; opacity: 1; transition: opacity 0.5s; pointer-events: none; }
`;
const styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = css;
document.head.appendChild(styleSheet);
// Create tooltip element
const tooltip = document.createElement('div');
tooltip.classList.add('mathml-tooltip');
document.body.appendChild(tooltip);
function getTarget(url) {
let target = { elementSelector: '', getMathMLString: null };
function extractMathML(element) {
let mathML = element.querySelector('math');
return mathML ? mathML.outerHTML : null;
}
if (url.includes('wikipedia.org')) {
target.elementSelector = 'span.mwe-math-element';
target.getMathMLString = extractMathML;
} else if (url.includes('chatgpt.com') || url.includes('deepseek.com')) {
target.elementSelector = 'span.katex';
target.getMathMLString = extractMathML;
} else if (url.includes('stackexchange.com')) {
target.elementSelector = 'span.math-container';
target.getMathMLString = extractMathML;
}
return target.elementSelector ? target : null;
}
function addHandler() {
let target = getTarget(window.location.href);
if (!target) return;
let tooltipTimeout;
document.querySelectorAll(target.elementSelector).forEach(element => {
element.addEventListener('mouseenter', function () {
element.style.cursor = "pointer";
tooltipTimeout = setTimeout(function () {
tooltip.textContent = "Double click to copy MathML";
const rect = element.getBoundingClientRect();
tooltip.style.left = `${rect.left}px`;
tooltip.style.top = `${rect.top - tooltip.offsetHeight - 5}px`;
tooltip.style.display = 'block';
tooltip.style.opacity = '0.8';
}, 1000);
});
element.addEventListener('mouseleave', function () {
element.style.cursor = "auto";
clearTimeout(tooltipTimeout);
tooltip.style.display = 'none';
tooltip.style.opacity = '0';
});
element.ondblclick = function (event) {
const mathMLString = target.getMathMLString(element);
if (mathMLString !== null) {
navigator.clipboard.writeText(mathMLString).then(() => {
showCopySuccessTooltip(event);
});
}
window.getSelection().removeAllRanges();
};
});
}
function showCopySuccessTooltip(event) {
const copyTooltip = document.createElement("div");
copyTooltip.className = "mathml-copy-success";
copyTooltip.innerText = "Copied";
document.body.appendChild(copyTooltip);
const rect = event.target.getBoundingClientRect();
copyTooltip.style.left = `${rect.left + window.scrollX}px`;
copyTooltip.style.top = `${rect.top + window.scrollY - 25}px`;
setTimeout(() => {
copyTooltip.style.opacity = "0";
setTimeout(() => {
document.body.removeChild(copyTooltip);
}, 500);
}, 1000);
}
document.addEventListener('DOMContentLoaded', addHandler);
new MutationObserver(addHandler).observe(document.documentElement, { childList: true, subtree: true });
})();