// ==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
`, (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 = `
`;
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();
}