// ==UserScript== // @name LeetRay // @namespace http://tampermonkey.net/ // @version 0.1.1 // @description Take beautiful screenshots of your code in Leetcode instantly with Ray.so // @updateURL https://git.sangeeth.dev/x/userscripts/raw/branch/main/leetray/LeetRay.user.js // @downloadURL https://git.sangeeth.dev/x/userscripts/raw/branch/main/leetray/LeetRay.user.js // @author Sangeeth Sudheer // @match https://leetcode.com/problems/* // @match https://www.leetcode.com/problems/* // @match https://www.ray.so/* // @match https://ray.so/* // @icon https://www.google.com/s2/favicons?sz=64&domain=leetcode.com // @grant GM_registerMenuCommand // @grant GM_openInTab // @grant GM_log // @grant GM_getTab // @grant GM_setValue // @grant GM_addValueChangeListener // @grant GM_removeValueChangeListener // @grant unsafeWindow // @grant window.close // @grant window.focus // ==/UserScript== (function () { "use strict"; let uwindow = unsafeWindow; function isLeetcode() { return location.hostname === "leetcode.com"; } function isRayso() { return location.hostname === "www.ray.so"; } function waitForRaysoLoad() { return new Promise((resolve) => { let id = GM_addValueChangeListener("leetray.rayso.loaded", () => { resolve(); GM_removeValueChangeListener(id); }); }); } function waitForRaysoExit() { return new Promise((resolve) => { let id = GM_addValueChangeListener("leetray.rayso.exit", () => { resolve(); GM_removeValueChangeListener(id); }); }); } function debug(stuff) { GM_log(`LeetRay DEBUG ${stuff}`); } function error(stuff) { GM_log(`LeetRay ERROR ${stuff}`); } function leetcodeMain() { const menu_command_id = GM_registerMenuCommand( "Copy screenshot", async function (event) { if (!uwindow.monaco?.editor) { error("monaco.editor not found"); return; } const monacoEditorEl = document.querySelector( ".monaco-editor[data-uri]" ); const editorKey = monacoEditorEl.dataset.uri; const monacoModel = uwindow.monaco.editor.getModel(editorKey); const colors = { rust: "sunset", python: "raindrop", python3: "raindrop", cpp: "midnight", scala: "sunset", javascript: "sunset", typescript: "raindrop", }; const sourceCode = monacoModel.getValue(); debug("Source code value from editor:"); debug(sourceCode); const language = monacoModel._languageId === "python3" ? "python" : monacoModel._languageId; const title = uwindow.__NEXT_DATA__.query.slug; const raysoURL = new URL("https://www.ray.so"); raysoURL.hash = "__leetray"; raysoURL.searchParams.append("code", btoa(sourceCode)); raysoURL.searchParams.append("title", title); raysoURL.searchParams.append("padding", 16); language && raysoURL.searchParams.append("language", language); language && raysoURL.searchParams.append("colors", colors[language]); debug(`Opening URL: ${raysoURL.toString()}`, { active: true, setParent: true, }); const handler = GM_openInTab(raysoURL.toString()); await waitForRaysoLoad(); GM_setValue("leetray.screenshot", Date.now()); await waitForRaysoExit(); window.focus(); handler.onclose = () => { alert("Copied screenshot"); }; }, "s" ); } async function raysoCopyScreenshot() { debug("Copying screenshot"); document.querySelector("#app div.setting.export a:nth-child(3)").click(); // TODO: flaky mostly await new Promise((resolve) => setTimeout(resolve, 1000)); } function raysoMain() { GM_setValue("leetray.rayso.loaded", Date.now()); let id = GM_addValueChangeListener("leetray.screenshot", async () => { window.focus(); await raysoCopyScreenshot(); GM_removeValueChangeListener(id); GM_setValue("leetray.rayso.exit", Date.now()); uwindow.close(); }); } debug("Starting"); if (isLeetcode()) { leetcodeMain(); } else if (isRayso()) { raysoMain(); } })();