// ==UserScript== // @name Github Web IDE // @name:zh-CN Github Web IDE // @name:zh-TW Github Web IDE // @name:en Github Web IDE // @version 2.0.3 // @author zvizvi (migrated by Cesaryuan) // @description ⚡ Open GitHub repositories in online web IDE (Migrated from https://github.com/zvizvi/GitHub-Web-IDE) // @description:zh-CN ⚡ Open GitHub repositories in online web IDE (Migrated from https://github.com/zvizvi/GitHub-Web-IDE) // @description:zh-TW ⚡ Open GitHub repositories in online web IDE (Migrated from https://github.com/zvizvi/GitHub-Web-IDE) // @description:en ⚡ Open GitHub repositories in online web IDE (Migrated from https://github.com/zvizvi/GitHub-Web-IDE) // @match *://github.com/* // @match *://gitlab.com/* // @resource css https://unpkg.com/@primer/css@^20.2.4/dist/primer.css // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iODAiIGhlaWdodD0iODAiIHZpZXdCb3g9IjAsIDAsIDQwMCw0MDAiPjxnPjxwYXRoIGQ9Ik0xNzQuMjE5IDEuMjI4IEMgOC44NzUgMjIuNDM2LC01OS4yMzggMjI2Ljk1OCw2MC4yNjYgMzQzLjM5MiBDIDg3LjcwMiAzNzAuMTI0LDEzMi42ODAgMzk0LjYxOSwxNDQuOTMwIDM4OS41MDAgQyAxNTAuNDU5IDM4Ny4xOTAsMTUwLjc3NSAzODUuNzQ5LDE1MC43NzggMzYyLjgwOCBMIDE1MC43ODEgMzQyLjQxMyAxNDQuNzI3IDM0My41MzAgQyAxMTQuMTQ4IDM0OS4xNzUsOTMuNDQ1IDMzOS45OTIsODEuMzgwIDMxNS40MzMgQyA3NC45MDEgMzAyLjI0Niw2OS44ODQgMjk1LjgwOCw2MS43MTUgMjkwLjE5OCBDIDUwLjM0NyAyODIuMzkyLDUwLjYyMSAyNzguMzQ3LDYyLjUwMCAyNzguNjIyIEMgNzMuMTkwIDI3OC44NzEsODMuNzAzIDI4Ni4yNDYsOTIuOTcyIDMwMC4wMDAgQyAxMDIuODA5IDMxNC41OTcsMTEzLjA4NCAzMjAuNTk2LDEyOC4xMjUgMzIwLjUyNCBDIDEzOS40NzEgMzIwLjQ2OSwxNTEuNTYyIDMxNi44NTUsMTUxLjU2MyAzMTMuNTE5IEMgMTUxLjU2MyAzMDguMDk4LDE1Ni43MjAgMjk2LjYyMCwxNjEuNTczIDI5MS4yNDIgQyAxNjQuNjAyIDI4Ny44ODUsMTY1LjI1NyAyODguMjk1LDE1NC4yOTcgMjg2LjY5MiBDIDEwOC40NTcgMjc5Ljk5MCw4Mi4wODcgMjU2LjIyMiw3NS4wMTYgMjE1LjIzNCBDIDY5Ljc5NCAxODQuOTcwLDc0LjA2OSAxNjEuMzU1LDg4LjEzMyAxNDIuNzc0IEMgOTAuMjQwIDEzOS45OTAsOTIuMjEyIDEzNy4zMTEsOTIuNTE1IDEzNi44MjAgQyA5Mi44MTkgMTM2LjMyOSw5Mi4yODMgMTMyLjg1Myw5MS4zMjUgMTI5LjA5NiBDIDg4LjMyNCAxMTcuMzI5LDg4Ljk4OCAxMDIuODM0LDkzLjEyMyA4OS44NDQgQyA5NS40NDggODIuNTQwLDk1LjQ2MSA4Mi41MzAsMTAyLjE5OSA4My4wOTkgQyAxMTIuNDk0IDgzLjk3MCwxMjkuNDg0IDkwLjkxMCwxNDMuODQzIDEwMC4xMTEgTCAxNDkuNzk1IDEwMy45MjUgMTU3LjkwNSAxMDEuOTk0IEMgMTg0LjExMCA5NS43NTUsMjE2LjA1OCA5NS43NTEsMjQyLjA5NSAxMDEuOTg0IEwgMjUwLjIwNSAxMDMuOTI1IDI1Ni4yMTcgMTAwLjA2OSBDIDI3Ny40MDMgODYuNDc4LDMwMi4yMTUgNzkuMDQzLDMwNS4yOTQgODUuMzYyIEMgMzEwLjMxNCA5NS42NjcsMzExLjkyNiAxMTYuNDU1LDMwOC42ODQgMTI5LjA5NiBDIDMwNy43MjEgMTMyLjg1MywzMDcuMTgxIDEzNi4zMjksMzA3LjQ4NSAxMzYuODIwIEMgMzA3Ljc4OCAxMzcuMzExLDMwOS43NjAgMTM5Ljk5MCwzMTEuODY3IDE0Mi43NzQgQyAzMjUuOTMxIDE2MS4zNTUsMzMwLjIwNiAxODQuOTcwLDMyNC45ODQgMjE1LjIzNCBDIDMxNy43MDkgMjU3LjQwMiwyODguNjUxIDI4Mi4zMTUsMjQxLjA1NSAyODcuMTkxIEMgMjM1LjA1NyAyODcuODA2LDIzNS4xNTUgMjg3LjYxNywyMzguNzU1IDI5MS42MDEgQyAyNDcuODU3IDMwMS42NzYsMjQ5LjIxNiAzMDkuMjg0LDI0OS4yMzAgMzUwLjI0NiBDIDI0OS4yNDMgMzg1LjQ5OCwyNDkuMjQ2IDM4NS41MjksMjUzLjIzMSAzODguNDg3IEMgMjU4LjUxMiAzOTIuNDA3LDI2Ny4wMzUgMzkwLjI3NywyODguNzM0IDM3OS42MTYgQyAzNjQuOTMyIDM0Mi4xNzgsNDA5LjI4NyAyNTkuNzM0LDM5OC44MDEgMTc1LjAzMiBDIDM4NS4yMjIgNjUuMzQ5LDI4NC4xNDggLTEyLjg3MiwxNzQuMjE5IDEuMjI4IE0yMTcuMjA5IDE0NS4xMTggQyAyMTQuMjgyIDE0Ni41OTIsMjE0LjQyMCAxNDYuMzY3LDE4OS4wOTUgMTkxLjAxNiBDIDE4Mi41MTUgMjAyLjYxNywxNzQuNzIxIDIxNi4zNTQsMTcxLjc3NiAyMjEuNTQzIEMgMTY0LjMyMSAyMzQuNjc4LDE2NC4yMTkgMjM5LjM2NiwxNzEuMzMyIDI0Mi4wODAgQyAxNzguMDQ3IDI0NC42NDEsMTc2LjI2OCAyNDcuMDkyLDIwNi45ODEgMTkyLjk2OSBDIDIxNC40MTggMTc5Ljg2MywyMjIuMTk4IDE2Ni4xNTIsMjI0LjI3MSAxNjIuNTAwIEMgMjMwLjA2MiAxNTIuMjk3LDIyOS45NTQgMTQ3LjQzMSwyMjMuODgxIDE0NC44OTQgQyAyMjAuNDY2IDE0My40NjcsMjIwLjQ5MSAxNDMuNDY2LDIxNy4yMDkgMTQ1LjExOCBNMTQxLjAxNiAxNjIuMDQxIEMgMTM5LjcyNyAxNjIuNjE0LDEzNi41NzcgMTY1LjMyNSwxMzQuMDE3IDE2OC4wNjUgQyAxMzEuNDU3IDE3MC44MDUsMTI1LjU2OSAxNzYuOTc1LDEyMC45MzEgMTgxLjc3NSBDIDExNS41NjUgMTg3LjMzMSwxMTIuNTAwIDE5MS4xMTEsMTEyLjUwMCAxOTIuMTczIEMgMTEyLjUwMCAxOTMuMDkxLDExMi4xNDggMTkzLjYyNSwxMTEuNzE5IDE5My4zNTkgQyAxMTEuMjg5IDE5My4wOTQsMTEwLjkzOCAxOTMuMjI4LDExMC45MzggMTkzLjY1OCBDIDExMC45MzggMTk0LjA4NywxMTEuMjQ0IDE5NC42MjgsMTExLjYxOCAxOTQuODYwIEMgMTExLjk5MiAxOTUuMDkxLDExMi41MTMgMTk2LjI1NCwxMTIuNzc2IDE5Ny40NDUgQyAxMTMuMDgxIDE5OC44MjUsMTE4LjE3NCAyMDQuNzAyLDEyNi44MzggMjEzLjY3MiBMIDE0MC40MjEgMjI3LjczNCAxNDQuMTAzIDIyNy43MzQgQyAxNDkuMTc0IDIyNy43MzQsMTUyLjM0NCAyMjQuMzgxLDE1Mi4zNDQgMjE5LjAxNSBDIDE1Mi4zNDQgMjE1LjUwMCwxNTIuMTI5IDIxNS4xNzYsMTQ0LjcyMiAyMDcuNTA0IEMgMTQwLjUzMCAyMDMuMTYyLDEzNi4wOTAgMTk4LjQ4OCwxMzQuODU1IDE5Ny4xMTggTCAxMzIuNjExIDE5NC42MjYgMTQyLjQ3NyAxODQuMDc3IEMgMTUxLjk3MiAxNzMuOTI1LDE1Mi4zNDQgMTczLjQwMSwxNTIuMzQ0IDE3MC4xNDEgQyAxNTIuMzQ0IDE2My40MjIsMTQ2LjgxMCAxNTkuNDY1LDE0MS4wMTYgMTYyLjA0MSBNMjUxLjAyNiAxNjIuNDYxIEMgMjQzLjYxNiAxNjYuOTgwLDI0NC43NDUgMTcxLjI1MSwyNTYuNzQxIDE4NC4wNzcgTCAyNjYuNjA4IDE5NC42MjYgMjY0LjM2NCAxOTcuMTE4IEMgMjYzLjEyOSAxOTguNDg4LDI1OC42ODkgMjAzLjE2MiwyNTQuNDk3IDIwNy41MDQgQyAyNDcuMDkwIDIxNS4xNzYsMjQ2Ljg3NSAyMTUuNTAwLDI0Ni44NzUgMjE5LjAxNSBDIDI0Ni44NzUgMjI0LjM4MSwyNTAuMDQ0IDIyNy43MzQsMjU1LjExNiAyMjcuNzM0IEwgMjU4Ljc5OCAyMjcuNzM0IDI3Mi4zODEgMjEzLjY3MiBDIDI4MS4wNDUgMjA0LjcwMiwyODYuMTM4IDE5OC44MjUsMjg2LjQ0MyAxOTcuNDQ1IEMgMjg2LjcwNSAxOTYuMjU0LDI4Ny4yMjcgMTk1LjA5MSwyODcuNjAxIDE5NC44NjAgQyAyODcuOTc1IDE5NC42MjgsMjg4LjI4MSAxOTQuMDg3LDI4OC4yODEgMTkzLjY1OCBDIDI4OC4yODEgMTkzLjIyOCwyODcuOTMwIDE5My4wOTQsMjg3LjUwMCAxOTMuMzU5IEMgMjg3LjA3MCAxOTMuNjI1LDI4Ni43MTkgMTkzLjA5MSwyODYuNzE5IDE5Mi4xNzMgQyAyODYuNzE5IDE5MS4xMTEsMjgzLjY1NCAxODcuMzMxLDI3OC4yODcgMTgxLjc3NSBDIDI3My42NTAgMTc2Ljk3NSwyNjcuNzYxIDE3MC44MDQsMjY1LjIwMSAxNjguMDY0IEMgMjU4LjY4MCAxNjEuMDgyLDI1NS40MDkgMTU5Ljc4OSwyNTEuMDI2IDE2Mi40NjEiIHN0cm9rZT0ibm9uZSIgZmlsbD0iIzAwMDAwMCIgZmlsbC1ydWxlPSJldmVub2RkIj48L3BhdGg+PC9nPjwvc3ZnPgo= // @grant GM_registerMenuCommand // @grant GM_getResourceText // @grant GM_getValue // @grant GM_setValue // @license none // @run-at document-end // @namespace https://github.com/zvizvi/GitHub-Web-IDE // @supportURL https://github.com/zvizvi/GitHub-Web-IDE // @homepageURL https://github.com/zvizvi/GitHub-Web-IDE // @downloadURL https://update.greasyfork.icu/scripts/454972/Github%20Web%20IDE.user.js // @updateURL https://update.greasyfork.icu/scripts/454972/Github%20Web%20IDE.meta.js // ==/UserScript== (function () { GM_registerMenuCommand("Options", () => { const dialogId = "open-in-web-ide-options"; const dialog = document.getElementById(dialogId) || addFrameDialog(dialogId, ` Options

Enabled IDE list

Opening options

Made with by @zvizvi
`, (iframe) => { // other methods run javascript in iframe // 1. iframe.contentWindow.eval; // 2. inline script in html // however, both of them are blocked by CSP with policy "script-src github.githubassets.com;" optionFrameJS.call(iframe.contentWindow) }); dialog.showModal(); }); const platform = location.host.split(".")[0]; const ideWebsitesList = [ { title: "GitHub Dev", name: "gitHubDev", baseurl: "https://github.dev/", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "VSCode Dev", name: "vsCodeDev", baseurl: "https://vscode.dev/github/", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "CodeSandbox", name: "codeSandbox", baseurl: `https://codesandbox.io/s/${platform}/`, platforms: ["github"], openInNewTab: true, icon: '', }, { title: "GitHub1s", name: "gitHub1s", baseurl: "https://github1s.com/", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "GitLab1s", name: "gitLab1s", baseurl: "https://gitlab1s.com/", platforms: ["gitlab"], openInNewTab: true, icon: '', }, { title: "Repl.it", name: "replit", baseurl: `https://repl.it/${platform}/`, platforms: ["github"], openInNewTab: true, icon: '', }, { title: "StackBlitz", name: "stackBlitz", baseurl: "https://githubblitz.com/", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "Gitpod", name: "gitpod", baseurl: `https://gitpod.io/#https://${platform}.com/`, platforms: ["github", "gitlab"], openInNewTab: true, icon: '', }, { title: "Glitch", name: "glitch", baseurl: "https://glitch.com/edit/#!/import/github/", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "Sourcegraph", name: "sourcegraph", baseurl: `https://sourcegraph.com/${platform}.com/`, platforms: ["github", "gitlab"], openInNewTab: true, icon: '', }, { title: "Active Forks", name: "activeForks", baseurl: "https://techgaun.github.io/active-forks/index.html#", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "Gitpop2", name: "gitpop2", baseurl: "http://gitpop2.herokuapp.com/", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "GitHub Memory", name: "gitHubMemory", baseurl: "https://githubmemory.com/repo/", platforms: ["github"], openInNewTab: true, icon: '', }, { title: "Clone in VSCode", name: "cloneInVSCode", baseurl: `vscode://vscode.git/clone?url=https://${platform}.com/`, platforms: ["github"], icon: '', class: "d-none d-md-block", }, // { // title: 'VSCode Remote Repositories', // name: 'vsCodeRemoteRepositories', // platforms: ['github'], // baseurl: 'vscode://GitHub.remotehub/open?url=https://github.com/', // icon: '', // class: 'd-none d-md-block' // } ]; const defaultOptions = { gitHubDev: true, vsCodeDev: true, codeSandbox: true, gitHub1s: true, replit: true, stackBlitz: true, gitLab1s: true, gitpod: true, glitch: true, sourcegraph: true, activeForks: false, gitpop2: false, gitHubMemory: false, cloneInVSCode: true, vsCodeRemoteRepositories: false, openInNewTab: true, }; let repoUrlPath = getRepoUrlPath(); const hasPackageJson = [ ...document.querySelectorAll( ".Details > .js-active-navigation-container > .Box-row a.js-navigation-open" ), ].some((el) => el.innerText === "package.json"); let options; function init() { const storage = GM_getValue("options"); options = storage || defaultOptions; switch (platform) { case "github": { const itemWithBorderOnTop = ideWebsitesList.find( // first item only (item) => (item.name === "cloneInVSCode" || item.name === "vsCodeRemoteRepositories") && filterItems(item) ); if (itemWithBorderOnTop) { itemWithBorderOnTop.class += " border-top"; } addGitHubSelectMenu(); document.addEventListener("soft-nav:success", () => { console.log("soft-nav:success"); document.getElementById("open-in-web-ide")?.remove(); repoUrlPath = getRepoUrlPath(); addGitHubSelectMenu(); }); break; } case "gitlab": { addGitLabSelectMenu(); localStorage.setItem("gl-web-ide-button-selected", "webide"); break; } default: { break; } } } function getRepoUrlPath() { return location.pathname.split("/").slice(1, 3).join("/"); } function filterItems(item) { if (item.title === "StackBlitz" && !hasPackageJson) { return false; } return (!options || options[item.name]) && item.platforms.includes(platform); } function addGitHubSelectMenu() { const menuElement = document.querySelector("#repo-content-turbo-frame .file-navigation"); if (!menuElement || menuElement.querySelector("#open-in-web-ide")) { return; } const githubHtml = ` Open In Web IDE IDE
`; const detailsElement = document.createElement("details"); detailsElement.setAttribute( "class", "details-overlay details-reset position-relative d-block" ); detailsElement.setAttribute("id", "open-in-web-ide"); detailsElement.innerHTML = githubHtml; menuElement.appendChild(detailsElement); } function addGitLabSelectMenu() { const webIDEDropdown = document.querySelector( ".tree-controls .gl-new-dropdown .dropdown-menu .gl-new-dropdown-contents" ); if (!webIDEDropdown || document.querySelector("#open-in-web-ide")) { return; } const gitLabHtml = `${ideWebsitesList .filter(filterItems) .map( (item) => `
  • ${item.icon} ${item.title}
  • ` ) .join("")}`; webIDEDropdown.setAttribute("id", "open-in-web-ide"); webIDEDropdown.setAttribute("style", "--color-fg-default: currentColor"); webIDEDropdown.innerHTML = gitLabHtml; } window.addEventListener("load", () => { init(); }); init(); })(); function addFrameDialog(dialogId, html, frameOnload) { const dialog = document.createElement("dialog"); dialog.setAttribute("id", dialogId); dialog.setAttribute( "style", "width: 60%; background-color: #fff; border: 1px solid #ccc; border-radius: 5px; padding: 10px; box-sizing: border-box;" ); dialog.innerHTML = `

    Options

    `; const iframe = document.createElement("iframe"); iframe.setAttribute("style", "width: 100%; height: 950px; border: none; overflow: hidden;"); dialog.appendChild(iframe); document.body.appendChild(dialog); iframe.autofocus = true; iframe.contentWindow.document.open(); iframe.contentWindow.document.write(html); iframe.contentWindow.document.close(); frameOnload(iframe); let closeBtn = dialog.querySelector("#dialog-close-gwi"); closeBtn.addEventListener("click", () => { dialog.close(); }); // close dialog when click outside dialog.addEventListener("click", (e) => { if (e.target === dialog) { dialog.close(); } }); return dialog; } function optionFrameJS() { let document = this.document; console.log("optionFrameJS", document); const defaultOptions = { gitHubDev: true, vsCodeDev: true, codeSandbox: true, gitHub1s: true, gitLab1s: true, replit: true, stackBlitz: true, gitpod: true, glitch: true, sourcegraph: true, activeForks: false, gitpop2: false, gitHubMemory: false, cloneInVSCode: true, vsCodeRemoteRepositories: false, openInNewTab: true, }; let options = { ...defaultOptions }; // Load options from storage const load = function () { const storage = GM_getValue("options"); options = storage || defaultOptions; show(); }; // Save options to storage const save = function (object = options) { if (!Object.keys(object).some((key) => key !== "openInNewTab" && options[key])) { object = { gitHubDev: true, openInNewTab: object.openInNewTab, }; } GM_setValue("options", object); load(); }; // Show options const show = function () { for (const key in defaultOptions) { const ele = document.getElementById(key); if (ele) { switch (typeof defaultOptions[key]) { case "boolean": { ele.checked = options[key]; break; } case "string": { ele.value = options[key]; break; } } } } }; // On checkbox changed document.addEventListener("input", (event) => { if (event.target.type === "checkbox") { for (const key in defaultOptions) { const ele = document.getElementById(key); if(!ele)continue; switch (typeof defaultOptions[key]) { case "boolean": { options[key] = !!ele.checked; break; } case "string": { options[key] = ele.value; break; } } } save(options); } }); // Reset to defaults const reset = function () { save(defaultOptions); }; // On reset button clicked document.addEventListener("click", (event) => { if (event.target.id === "reset-button") { reset(); } }); load(); }