github在线编辑器-快速跳转

在GitHub仓库页面添加快速跳转到各种在线编辑器的按钮

// ==UserScript==
// @name         github在线编辑器-快速跳转
// @namespace    http://tampermonkey.net/
// @version      0.7
// @description  在GitHub仓库页面添加快速跳转到各种在线编辑器的按钮
// @author       Jiabin
// @match        https://github.com/*/*
// @grant        none
// @license MIT
// ==/UserScript==

(function () {
  "use strict";

  // 配置所有支持的在线编辑器
  const EDITORS = [
    {
      name: "StackBlitz",
      color: "#5C6BC0",
      textColor: "white",
      getUrl: (owner, repo) => `https://stackblitz.com/github/${owner}/${repo}`,
    },
    {
      name: "CodeSandbox",
      color: "#151515",
      textColor: "white",
      getUrl: (owner, repo) => `https://codesandbox.io/s/github/${owner}/${repo}`,
    },
    {
      name: "GitHub.dev",
      color: "#0969da",
      textColor: "white",
      getUrl: (owner, repo) => `https://github.dev/${owner}/${repo}`,
    },
    {
      name: "Gitpod",
      color: "#FFAE33",
      textColor: "black",
      getUrl: (owner, repo) => `https://gitpod.io/#https://github.com/${owner}/${repo}`,
    },
    {
      name: "Replit",
      color: "#F26207",
      textColor: "white",
      getUrl: (owner, repo) => `https://replit.com/github/${owner}/${repo}`,
    },
    {
      name: "Glitch",
      color: "#3333FF",
      textColor: "white",
      getUrl: (owner, repo) => `https://glitch.com/edit/#!/import/github/${owner}/${repo}`,
    },
  ];

  // 存储状态
  let currentUrl = window.location.href;
  let editorPanel = null;
  const STYLE_ID = "github-online-editors-style";
  let isPageLoaded = false;

  // 添加样式表
  function addStyles() {
    // 如果已存在样式表,则不重复添加
    if (document.getElementById(STYLE_ID)) return;

    const styleElement = document.createElement("style");
    styleElement.id = STYLE_ID;
    styleElement.textContent = `
            .gh-online-editors-container {
                display: grid;
                grid-template-columns: 50% 50%;
                gap: 8px;
                margin-bottom: 16px;
            }

            .gh-online-editor-button {
                width: 100%;
                display: flex;
                align-items: center;
                justify-content: center;
                padding: 6px 12px;
                font-size: 14px;
                font-weight: 500;
                line-height: 20px;
                white-space: nowrap;
                vertical-align: middle;
                cursor: pointer;
                user-select: none;
                border: 1px solid;
                border-radius: 6px;
                appearance: none;
                text-decoration: none;
                transition: background-color 0.2s cubic-bezier(0.3, 0, 0.5, 1);
            }

            .gh-online-editors-panel {
                width: 100%;
            }

            .gh-online-editors-title {
                margin-bottom: 12px;
                font-size: 16px;
                font-weight: 600;
            }
        `;

    document.head.appendChild(styleElement);
  }

  // 主函数
  function init() {
    // 如果页面还没加载完成,则不执行
    if (!isPageLoaded) return;

    removePanel();
    addStyles();

    if (!isRepoPage()) return;

    const repoInfo = getRepoInfo();
    if (!repoInfo) return;

    createPanel(repoInfo);
  }

  // 移除现有面板
  function removePanel() {
    // 首先尝试使用已存储的引用删除
    if (editorPanel && editorPanel.parentNode) {
      editorPanel.parentNode.removeChild(editorPanel);
      editorPanel = null;
    }

    // 再次查找并删除可能存在的面板(以防引用失效)
    const existingPanel = document.querySelector(".gh-online-editors-panel");
    if (existingPanel && existingPanel.parentNode) {
      existingPanel.parentNode.removeChild(existingPanel);
    }
  }

  // 检查是否在仓库页面
  function isRepoPage() {
    const path = window.location.pathname.split("/").filter((p) => p);

    if (path.length < 2) return false;

    const nonRepoPages = [
      "issues",
      "pull",
      "pulls",
      "wiki",
      "settings",
      "actions",
      "security",
      "pulse",
      "people",
      "network",
    ];

    if (path.length === 2) return true;

    return !nonRepoPages.includes(path[2]);
  }

  // 获取仓库信息
  function getRepoInfo() {
    const path = window.location.pathname.split("/").filter((p) => p);
    if (path.length < 2) return null;

    return {
      owner: path[0],
      repo: path[1],
    };
  }

  // 创建面板
  function createPanel(repoInfo) {
    const sidebar = document.querySelector(".BorderGrid");
    if (!sidebar) {
      console.warn("GitHub侧边栏未找到,无法插入在线编辑器面板");
      return;
    }

    // 查找About部分
    const aboutElement = findSidebarSection(sidebar, "About");

    // 创建面板容器
    const panel = document.createElement("div");
    panel.className = "BorderGrid-row gh-online-editors-panel";

    // 创建标题部分
    const header = document.createElement("div");
    header.className = "BorderGrid-cell";

    // 添加标题
    const title = document.createElement("h2");
    title.className = "h4 gh-online-editors-title";
    title.textContent = "在线编辑器";
    header.appendChild(title);

    // 创建按钮容器
    const buttonsContainer = document.createElement("div");
    buttonsContainer.className = "gh-online-editors-container";

    // 添加所有编辑器按钮
    EDITORS.forEach((editor) => {
      const button = createEditorButton(editor, repoInfo);
      buttonsContainer.appendChild(button);
    });

    header.appendChild(buttonsContainer);
    panel.appendChild(header);

    // 插入面板
    if (aboutElement) {
      sidebar.insertBefore(panel, aboutElement);
    } else {
      sidebar.insertBefore(panel, sidebar.firstChild);
    }

    editorPanel = panel;
  }

  // 查找侧边栏中的特定部分
  function findSidebarSection(sidebar, title) {
    return Array.from(sidebar.children).find(
      (el) => el.textContent.includes(title) || el.querySelector("h2")?.textContent.includes(title)
    );
  }

  // 创建编辑器按钮
  function createEditorButton(editor, repoInfo) {
    const button = document.createElement("a");
    button.href = editor.getUrl(repoInfo.owner, repoInfo.repo);
    button.target = "_blank";
    button.className = "btn gh-online-editor-button";
    button.style.backgroundColor = editor.color;
    button.style.color = editor.textColor;
    button.textContent = editor.name;
    return button;
  }

  // 监听URL变化
  function checkUrlChange() {
    if (currentUrl !== window.location.href) {
      currentUrl = window.location.href;
      init();
    }
  }

  // 等待页面加载完成
  function waitForPageLoad() {
    // 当DOM内容加载完成后
    if (document.readyState === "loading") {
      document.addEventListener("DOMContentLoaded", onPageLoaded);
    } else {
      onPageLoaded();
    }
  }

  // 页面加载完成后的处理
  function onPageLoaded() {
    isPageLoaded = true;
    init();

    // 设置URL变化检测
    setInterval(checkUrlChange, 1000);

    // 监听GitHub的导航事件
    setupNavigationListeners();
  }

  // 设置导航事件监听
  function setupNavigationListeners() {
    const originalPushState = history.pushState;
    const originalReplaceState = history.replaceState;

    history.pushState = function () {
      originalPushState.apply(this, arguments);
      setTimeout(init, 300);
    };

    history.replaceState = function () {
      originalReplaceState.apply(this, arguments);
      setTimeout(init, 300);
    };

    // 监听popstate事件
    window.addEventListener("popstate", () => {
      setTimeout(init, 300);
    });
  }

  // 启动脚本
  waitForPageLoad();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址