发布一个油猴脚本-Github增强工具栏

🚀 GitHub 增强工具栏 – 让仓库浏览“一键到位”的油猴插件

GitHub 增强工具栏 是一段只有几百行的用户脚本(Tampermonkey / Violentmonkey),它会在每个 GitHub 仓库页面的右上角注入 3 个实用按钮,帮你瞬间打开 VS Code Web、查询 DeepWiki 文档、或用 ZreadAi 对仓库进行 AI 分析。
安装链接 👉 https://update.greasyfork.org/scripts/544826/GitHub%20%E5%A2%9E%E5%BC%BA%E5%B7%A5%E5%85%B7%E6%A0%8F.user.js


1️⃣ 为何要写这玩意儿?

在日常的开源阅读、代码评审、面试准备中,我经常遇到同样的痛点:

痛点 现象 结果
切换工具太多 想在 VS Code Web 里编辑 → 再打开 DeepWiki 看文档 → 再去 ZreadAi 让 AI 给项目总结 页面切换、复制 URL、手动拼接,浪费十几秒甚至几分钟
GitHub 原生功能不够 . 可以打开 github.dev,但没有文档视图、没有 AI 分析 功能碎片化,要么装一大堆 Chrome 扩展,要么手动跳转
PJAX/Turbo 导航失效 切换仓库时按钮消失,需要刷新页面 使用体验不友好

于是我决定 把这三件事浓缩成个按钮栏,只要装好油猴脚本,GitHub 页面一刷新就能看到。
这就是 GitHub 增强工具栏的初衷——“少点点,省几秒”。

2️⃣ 适合谁?

角色 场景 为什么爱上它
开发者 / 代码审查者 快速打开仓库代码 → 直接在浏览器里编辑 Github.dev 一键打开,省去 git clone、本地 IDE 步骤
新人 / 学生党 面试前要快速了解陌生项目的结构 DeepWiki 自动生成目录树,ZreadAi 10 s 给出项目概览
运营 / DevOps 需要临时查看文档或 audit 代码 按钮随时可点,不必装额外的企业内部插件
技术布道者 想把好用的“油猴+GitHub”技巧分享给团队 只需发送一个链接,所有人秒装生效

3️⃣ 核心优势一览

优势 解释
零依赖、即装即用 仅依赖油猴 (Tampermonkey / Violentmonkey),不需要任何后端或 Node 环境
与 GitHub UI 完美融合 使用 GitHub 官方的 btn btn-sm 样式 + 自动暗黑模式适配
高兼容性 按钮容器通过 4 条备选 selector 自动定位,兼容未来 UI 细微改动
防抖 + PJAX/Turbo 兼容 debounce 防止多次渲染;监听 pjax:endturbo:load 以及 URL 变化,保持按钮始终可见
体积极小 < 4 KB(gzip ≈ 1.8 KB),加载几乎没有感知
安全透明 只做 window.open 跳转,不收集任何用户数据,符合 CSP 要求
可自行定制 代码结构化,所有按钮属性(顺序、文字、图标、目标 URL)在函数内部可直接改动

4️⃣ 代码剖析(逐块解释)

以下每段代码均对应博客后面的 进阶魔改 小节,按需复制粘贴即可。

4.1 元信息 & IIFE

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ==UserScript==
// @name GitHub 增强工具栏
// @namespace https://github.com/txy-sky
// @icon https://github.com/favicons/favicon.svg
// @version 1.4.0
// @description 在 Github 网站顶部显示 Github.dev、DeepWiki、ZreadAi 按钮,方便更好地查看代码。
// @match https://github.com/*
// @grant none
// @license MIT
// ==/UserScript==

(function () {
'use strict';
// ……后续代码
})();
  • @match 限定脚本只在 https://github.com/* 生效,避免对其他站点造成干扰。
  • 'use strict' 防止意外的全局变量泄漏。
  • 整个脚本被封装成 IIFE(Immediately‑Invoked Function Expression),确保变量不会污染页面全局空间。

4.2 样式注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const style = `
.custom-github-button {
margin: 0 4px;
display: flex;
align-items: center;
height: 28px;
}
.custom-github-button .octicon {
margin-right: 4px;
vertical-align: text-bottom;
}
.pagehead-actions > li {
margin-right: 8px;
}
`;
const styleElement = document.createElement('style');
styleElement.textContent = style;
document.head.appendChild(styleElement);
  • 使用 <style> 动态注入自定义 CSS,确保按钮在不同主题(Light / Dark)下都有合适的间距与对齐。
  • 只给自定义按钮加 .custom-github-button,不影响已有的 GitHub UI。

4.3 按钮容器定位(兼容性神器)

1
2
3
4
5
6
7
8
9
10
11
12
13
async function findButtonContainer() {
const selectors = [
'ul.pagehead-actions',
'.pagehead-actions',
'.file-navigation .d-flex',
'nav[aria-label="Repository"] .d-flex'
];
for (const selector of selectors) {
const el = document.querySelector(selector);
if (el) return el;
}
return null;
}
  • GitHub 时常改动页面结构,上面的四个 selector 覆盖了 仓库首页、文件列表页、PR/Issues 页 中常见的按钮区域。
  • 若全部匹配失败,脚本会在控制台报错(后面会提到调试技巧)。

4.4 统一的按钮生成函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function createCustomButton(id, url, iconHtml, text) {
const li = document.createElement('li');
li.id = id;
li.className = 'd-flex';
li.style.marginRight = '8px';

const a = document.createElement('a');
a.href = url;
a.className = 'btn btn-sm custom-github-button';
a.target = '_blank';
a.rel = 'noopener noreferrer';
a.innerHTML = `${iconHtml}<span>${text}</span>`;

li.appendChild(a);
return li;
}
  • id 用来在后续 remove 已有按钮,防止重复。
  • url 为目标跳转地址,**iconHtml** 与 text 负责视觉。
  • 通过 target="_blank" + rel="noopener noreferrer",安全打开新标签页。

4.5 三个具体按钮的实现

4.5.1 Github.dev

1
2
3
4
5
6
function createGithubDevButton() {
const githubdevUrl = `https://github.dev${location.pathname}${location.search}${location.hash}`;
const iconHtml = `<img class="octicon" width="16" height="16"
src="https://github.com/favicons/favicon-codespaces.svg" />`;
return createCustomButton('githubdevButton', githubdevUrl, iconHtml, 'Github.dev');
}
  • 直接把当前仓库路径拼进去,打开 VS Code Web(只读也能编辑)。
  • 用 GitHub 官方的 favicon-codespaces.svg 作为图标,视觉统一。

4.5.2 DeepWiki

1
2
3
4
5
function createDeepWikiButton() {
const deepwikiUrl = `https://deepwiki.com${location.pathname}${location.search}${location.hash}`;
const iconHtml = `<svg class="octicon" …>…</svg>`; // 省略的完整 SVG
return createCustomButton('deepwikiButton', deepwikiUrl, iconHtml, 'DeepWiki');
}
  • DeepWiki 用来快速展示仓库的文档树、Readme、Wiki 页面等。
  • 图标是自定义 SVG,颜色 rgb(33,193,154)(淡绿),在暗黑主题里也能看清。

4.5.3 ZreadAi

1
2
3
4
5
function createZreadAiButton() {
const zreadAiUrl = `https://zread.ai${location.pathname}${location.search}${location.hash}`;
const iconHtml = `<svg aria-hidden="true" …>…</svg>`; // 同上
return createCustomButton('zreadaiButton', zreadAiUrl, iconHtml, 'ZreadAi');
}
  • ZreadAi 将仓库内容喂给 AI,返回 项目概览、模块划分、技术栈 等信息。
  • 图标采用对称 “×” 形状,视觉上与前两者形成区分。

4.6 将按钮装进页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
async function addButtons() {
try {
const buttonContainer = await findButtonContainer();
if (!buttonContainer) {
console.log('GitHub按钮脚本:找不到合适的按钮容器');
return;
}

// 清理旧按钮(防止重复)
const existing = document.querySelectorAll('#githubdevButton, #zreadaiButton, #deepwikiButton');
existing.forEach(btn => btn.remove());

// 创建 & 按顺序插入(先 DeepWiki,再 ZreadAi,最后 Github.dev)
const deepWikiButton = createDeepWikiButton();
const zreadAiButton = createZreadAiButton();
const githubDevButton = createGithubDevButton();

buttonContainer.insertBefore(deepWikiButton, buttonContainer.firstChild);
buttonContainer.insertBefore(zreadAiButton, buttonContainer.firstChild);
buttonContainer.insertBefore(githubDevButton, buttonContainer.firstChild);
} catch (e) {
console.log('GitHub按钮脚本:添加按钮时发生错误', e);
}
}
  • 先清理创建,避免在 PJAX / Turbo 导航后出现重复按钮。
  • 使用 insertBefore(..., firstChild) 保证按钮永远排在最前面,紧贴 GitHub 原生的 “Watch / Star / Fork” 区域。

4.7 防抖(Debounce)

1
2
3
4
5
6
7
8
function debounce(func, wait) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
}
const debouncedAddButtons = debounce(addButtons, 300);
  • 当页面频繁触发 pjax:endturbo:load 或 URL 变化时,防抖 能有效避免 10 + 次的重复渲染,只保留最后一次执行。

4.8 页面加载 & 导航监听

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', debouncedAddButtons);
} else {
setTimeout(debouncedAddButtons, 100);
}

// PJAX / Turbo 迁移
document.addEventListener('pjax:end', debouncedAddButtons);
document.addEventListener('turbo:load', debouncedAddButtons);

// URL 变化(单页应用式跳转)监控
let currentUrl = location.href;
new MutationObserver(() => {
if (location.href !== currentUrl) {
currentUrl = location.href;
setTimeout(debouncedAddButtons, 500);
}
}).observe(document.body, { childList: true, subtree: true });
  • DOM 完成加载 → 初始化一次按钮。
  • 通过 PJAX(GitHub 内部的局部刷新)和 Turbo(GitHub 新版的渲染框架)双重监听,保持按钮在切页后仍然存在。
  • 额外的 MutationObserver 捕获 URL 手动变化(比如从地址栏敲入新路径)的情况。

小技巧:如果你在公司内部网络中使用,记得把 github.devdeepwiki.comzread.ai 替换为内部代理域名。只需要改 createGithubDevButtoncreateDeepWikiButtoncreateZreadAiButton 中的 url 拼接语句即可。