您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在 GitHub 仓库页面的 Star/Watch/Fork 按钮旁添加 DeepWiki 按钮及新窗口打开图标,方便一键跳转到对应仓库的 DeepWiki 页面。
- // ==UserScript==
- // @name GitHub DeepWiki Button
- // @namespace http://tampermonkey.net/
- // @version 0.12
- // @description 在 GitHub 仓库页面的 Star/Watch/Fork 按钮旁添加 DeepWiki 按钮及新窗口打开图标,方便一键跳转到对应仓库的 DeepWiki 页面。
- // @author nuttycc
- // @match https://github.com/*/*
- // @icon https://deepwiki.com/favicon.ico
- // @grant none
- // @license MIT
- // ==/UserScript==
- (function () {
- "use strict";
- // 配置
- const CONFIG = {
- DEBUG: false, // 设置为 true 开启调试日志
- DEBOUNCE_DELAY: 300, // 防抖延迟时间(毫秒)
- };
- // 缓存常用数据
- const CACHE = {
- excludedPaths: [
- "settings",
- "marketplace",
- "explore",
- "topics",
- "trending",
- "collections",
- "events",
- "sponsors",
- "notifications",
- ],
- // 预定义 SVG 图标
- svgIcon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="margin-right:4px">
- <path d="M21 4H3a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h18a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1zm-1 14H4V6h16v12z"></path>
- <path d="M7 9h10v1H7zm0 4h10v1H7z"></path>
- </svg>`,
- // 新窗口打开图标
- newWindowIcon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" style="vertical-align:middle">
- <path d="M19 19H5V5h7V3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"></path>
- </svg>`,
- };
- // 日志函数,只在调试模式下输出
- function log(...args) {
- if (CONFIG.DEBUG) {
- console.debug("[DeepWiki]", ...args);
- }
- }
- // 防抖函数
- function debounce(func, delay) {
- let timer;
- return function (...args) {
- clearTimeout(timer);
- timer = setTimeout(() => func.apply(this, args), delay);
- };
- }
- // 获取仓库路径
- function getRepoPath() {
- const pathParts = window.location.pathname.split("/").filter(Boolean);
- if (pathParts.length < 2) return null;
- return `${pathParts[0]}/${pathParts[1]}`;
- }
- // 检查当前页面是否是仓库页面
- function isRepoPage() {
- // 快速检查 URL 格式
- const pathParts = window.location.pathname.split("/").filter(Boolean);
- // 必须至少有用户名和仓库名两部分
- if (pathParts.length < 2) {
- return false;
- }
- // 排除特殊页面
- if (CACHE.excludedPaths.includes(pathParts[0])) {
- return false;
- }
- // 检查是否存在 Star/Watch/Fork 按钮容器
- const buttonContainer = document.querySelector("ul.pagehead-actions");
- const isRepo = !!buttonContainer;
- log(
- `[isRepoPage] 检测仓库页面: 路径:${pathParts.join(
- "/"
- )}, 按钮容器是否存在:${isRepo}`
- );
- return isRepo;
- }
- // 创建 DeepWiki 按钮
- function createDeepWikiButton(repoPath) {
- // 使用 DocumentFragment 提高性能
- const fragment = document.createDocumentFragment();
- // 创建列表项
- const listItem = document.createElement("li");
- listItem.className = "deepwiki-button d-flex";
- listItem.style.display = "flex";
- listItem.style.alignItems = "center";
- // 创建主按钮
- const button = document.createElement("a");
- button.href = `https://deepwiki.com/${repoPath}`;
- button.className = "btn btn-sm";
- button.target = "_blank";
- button.rel = "noopener noreferrer";
- button.style.display = "flex";
- button.style.alignItems = "center";
- button.style.gap = "4px";
- button.style.borderTopRightRadius = "0";
- button.style.borderBottomRightRadius = "0";
- button.style.marginRight = "0";
- button.style.height = "28px"; // 固定高度
- // 使用预定义的 SVG 图标
- button.innerHTML = CACHE.svgIcon;
- // 添加文本
- const text = document.createElement("span");
- text.textContent = "DeepWiki";
- button.appendChild(text);
- // 创建新窗口打开按钮
- const newWindowButton = document.createElement("a");
- newWindowButton.href = `https://deepwiki.com/${repoPath}`;
- newWindowButton.className = "btn btn-sm";
- newWindowButton.target = "_blank";
- newWindowButton.rel = "noopener noreferrer";
- newWindowButton.title = "在新窗口中打开";
- newWindowButton.setAttribute("aria-label", "在新窗口中打开");
- newWindowButton.style.display = "flex";
- newWindowButton.style.alignItems = "center";
- newWindowButton.style.justifyContent = "center";
- newWindowButton.style.borderTopLeftRadius = "0";
- newWindowButton.style.borderBottomLeftRadius = "0";
- newWindowButton.style.borderLeft = "1px solid var(--color-border-default)";
- newWindowButton.style.padding = "5px 8px";
- newWindowButton.style.height = "28px"; // 确保与主按钮高度一致
- // 添加新窗口图标
- newWindowButton.innerHTML = CACHE.newWindowIcon;
- // 组装
- listItem.appendChild(button);
- listItem.appendChild(newWindowButton);
- fragment.appendChild(listItem);
- return fragment;
- }
- // 添加 DeepWiki 按钮
- function addDeepWikiButton() {
- // 如果按钮已存在,则不再添加
- if (document.querySelector(".deepwiki-button")) {
- log("按钮已存在,跳过添加。");
- return;
- }
- // 获取仓库路径
- const repoPath = getRepoPath();
- if (!repoPath) {
- log("路径不符合要求,跳过添加。");
- return;
- }
- log(`[addDeepWikiButton] 检测到仓库路径: ${repoPath}`);
- // 获取按钮容器
- const buttonContainer = document.querySelector("ul.pagehead-actions");
- if (!buttonContainer) {
- log("未找到 ul.pagehead-actions 容器,跳过添加。");
- return;
- }
- // 创建并添加按钮
- const buttonFragment = createDeepWikiButton(repoPath);
- buttonContainer.insertBefore(buttonFragment, buttonContainer.firstChild);
- log("🎉 按钮添加成功。");
- }
- // 处理页面变化的统一函数
- const handlePageChange = debounce(() => {
- if (isRepoPage()) {
- addDeepWikiButton();
- }
- }, CONFIG.DEBOUNCE_DELAY);
- // 初始化函数
- function init() {
- // 页面加载完成时检查
- window.addEventListener("load", () => {
- log("[event] load 事件触发");
- handlePageChange();
- });
- // 监听 PJAX 结束事件
- document.addEventListener("pjax:end", () => {
- log("[event] pjax:end 事件触发");
- handlePageChange();
- });
- // 监听 turbo:render 事件
- document.addEventListener("turbo:render", () => {
- log("[event] turbo:render 事件触发");
- handlePageChange();
- });
- // 使用 turbo:render 监听变化已经足够。故移除下面内容。
- // 使用更精确的 MutationObserver 监听 DOM 变化。
- // let lastUrl = location.href;
- // const urlObserver = new MutationObserver(() => {
- // const url = location.href;
- // if (url !== lastUrl) {
- // lastUrl = url;
- // log("[Observer CallBack] URL 变化:", url);
- // handlePageChange();
- // }
- // });
- // // 只观察 body 元素,减少不必要的回调
- // const observeTarget = document.querySelector("body");
- // if (observeTarget) {
- // urlObserver.observe(observeTarget, {
- // childList: true,
- // subtree: true,
- // });
- // }
- // 初始检查
- log("[init] 初始检查。");
- handlePageChange();
- }
- // 启动脚本
- init();
- })();