您需要先安装一个扩展,例如 篡改猴、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(); })();