View the compile time of your project
// ==UserScript==
// @name Overleaf - Compile time
// @namespace https://github.com/BLumbye/overleaf-userscripts
// @version 0.1
// @description View the compile time of your project
// @author Benjamin Lumbye
// @license GPL-3
// @match https://www.overleaf.com/project/*
// @grant none
// ==/UserScript==
'use strict';
function addStyle(css) {
const style =
document.getElementById('compile-time-style') ||
(function () {
const style = document.createElement('style');
style.id = 'compile-time-style';
document.head.appendChild(style);
return style;
})();
const sheet = style.sheet;
sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
}
function waitForElement(selector) {
return new Promise((resolve) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver((mutations) => {
if (document.querySelector(selector)) {
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
});
}
let timerElement;
let compiling = false;
let compileStart = 0;
let endTimeout;
function formatTime() {
return `${((Date.now() - compileStart) / 1000).toFixed(3)}s`;
}
function addTimerElement() {
timerElement = document.createElement('div');
timerElement.id = 'compile-time';
document.querySelector('.pdfjs-viewer.pdfjs-viewer-outer').appendChild(timerElement);
}
function updateTimerElement() {
if (!compiling) return;
timerElement.textContent = formatTime();
requestAnimationFrame(updateTimerElement);
}
(function () {
addStyle(`
#compile-time {
position: absolute;
bottom: 0;
left: 0;
background-color: #fff;
padding: .5em 1em;
border-radius: 9999px;
margin: 20px 12.5px;
background-color: #3e70bb;
color: #fff;
opacity: 0;
}
`);
addStyle(`
#compile-time.compiling, #compile-time:hover {
opacity: 1;
}
`);
window.addEventListener('UNSTABLE_editor:extensions', async (event) => {
// Wait for the PDF viewer to load, then add the timer element
const mutationTarget = await waitForElement('.pdf-viewer');
const observer = new MutationObserver((mutations) => {
if (
mutationTarget.querySelector('.pdfjs-viewer') !== null &&
mutationTarget.querySelector('#compile-time') === null
) {
addTimerElement();
timerElement.textContent = 'No compilation yet';
}
});
observer.observe(mutationTarget, { childList: true, subtree: true });
// Observe the recompile button to detect when compilation starts and ends
const recompileButton = await waitForElement('.toolbar-pdf-left > .split-menu > button > span');
const recompileObserver = new MutationObserver((mutations) => {
if (!timerElement) return;
if (recompileButton.textContent.startsWith('Compiling')) {
clearTimeout(endTimeout);
compiling = true;
compileStart = Date.now();
timerElement.classList.add('compiling');
timerElement.textContent = '0.000s';
updateTimerElement();
} else {
compiling = false;
timerElement.textContent = formatTime();
endTimeout = setTimeout(() => timerElement.classList.remove('compiling'), 1000);
}
});
recompileObserver.observe(recompileButton, { characterData: true, childList: true, subtree: true });
});
})();