// ==UserScript== // @name GitHub 增强工具栏 // @namespace https://github.com/txy-sky // @icon https://github.com/favicons/favicon.svg // @version 1.5.0 // @description 在 Github 网站顶部显示 Github.dev 和 DeepWiki 和 ZreadAi 按钮,方便更好地查看代码。当按钮过多时自动切换为图标模式。 // @author Txy-Sky // @match https://github.com/* // @run-at document-end // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/544826/GitHub%20%E5%A2%9E%E5%BC%BA%E5%B7%A5%E5%85%B7%E6%A0%8F.user.js // @updateURL https://update.greasyfork.icu/scripts/544826/GitHub%20%E5%A2%9E%E5%BC%BA%E5%B7%A5%E5%85%B7%E6%A0%8F.meta.js // ==/UserScript== (function () { 'use strict'; // 添加全局样式以修复按钮样式 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; } .custom-github-button.icon-only .octicon { margin-right: 0; } .pagehead-actions > li { margin-right: 8px; } `; // 注入样式到页面 const styleElement = document.createElement('style'); styleElement.textContent = style; document.head.appendChild(styleElement); // 等待元素出现的函数 function waitForElement(selector, timeout = 10000) { return new Promise((resolve, reject) => { const element = document.querySelector(selector); if (element) { resolve(element); return; } const observer = new MutationObserver((mutations, obs) => { const element = document.querySelector(selector); if (element) { obs.disconnect(); resolve(element); } }); observer.observe(document.body, { childList: true, subtree: true }); // 超时处理 setTimeout(() => { observer.disconnect(); reject(new Error(`Element ${selector} not found within ${timeout}ms`)); }, timeout); }); } // 查找按钮容器的函数,提高兼容性 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 element = document.querySelector(selector); if (element) return element; } return null; } // 检测现有按钮数量的函数 function countExistingButtons(container) { if (!container) return 0; // 计算现有按钮数量,排除我们即将添加的按钮 const existingButtons = container.querySelectorAll('li:not(#githubdevButton):not(#zreadaiButton):not(#deepwikiButton)'); return existingButtons.length; } // 统一按钮创建函数,支持仅图标模式 function createCustomButton(id, url, iconHtml, text, iconOnly = false) { 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 = iconOnly ? 'btn btn-sm custom-github-button icon-only' : 'btn btn-sm custom-github-button'; a.target = '_blank'; a.rel = 'noopener noreferrer'; if (iconOnly) { a.innerHTML = iconHtml; a.title = text; // 添加 tooltip 显示完整文本 } else { a.innerHTML = `${iconHtml}${text}`; } li.appendChild(a); return li; } // 创建 Github.dev 按钮 function createGithubDevButton(iconOnly = false) { const githubdevUrl = `https://github.dev${location.pathname}${location.search}${location.hash}`; const iconHtml = ``; return createCustomButton('githubdevButton', githubdevUrl, iconHtml, 'Github.dev', iconOnly); } // 创建 ZreadAi 按钮 function createZreadAiButton(iconOnly = false) { const zreadAiUrl = `https://zread.ai${location.pathname}${location.search}${location.hash}`; const iconHtml = ``; return createCustomButton('zreadaiButton', zreadAiUrl, iconHtml, 'ZreadAi', iconOnly); } // 创建 DeepWiki 按钮 function createDeepWikiButton(iconOnly = false) { const deepwikiUrl = `https://deepwiki.com${location.pathname}${location.search}${location.hash}`; const iconHtml = ` `; return createCustomButton('deepwikiButton', deepwikiUrl, iconHtml, 'DeepWiki', iconOnly); } // 统一处理按钮创建和添加的函数 async function addButtons() { try { // 查找合适的按钮容器 const buttonContainer = await findButtonContainer(); if (!buttonContainer) { console.log('GitHub按钮脚本:找不到合适的按钮容器'); return; } // 移除可能已存在的按钮 const existingButtons = document.querySelectorAll('#githubdevButton, #zreadaiButton, #deepwikiButton'); existingButtons.forEach(btn => btn.remove()); // 检测现有按钮数量,决定是否使用仅图标模式 const existingButtonCount = countExistingButtons(buttonContainer); const iconOnly = existingButtonCount > 3; // 创建按钮并添加到容器 const deepWikiButton = createDeepWikiButton(iconOnly); const zreadAiButton = createZreadAiButton(iconOnly); const githubDevButton = createGithubDevButton(iconOnly); // 使用正确的顺序添加按钮,保证它们显示在最前面 buttonContainer.insertBefore(deepWikiButton, buttonContainer.firstChild); buttonContainer.insertBefore(zreadAiButton, buttonContainer.firstChild); buttonContainer.insertBefore(githubDevButton, buttonContainer.firstChild); } catch (error) { console.log('GitHub按钮脚本:添加按钮时发生错误', error); } } // 防抖函数 function debounce(func, wait) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } // 使用防抖的按钮添加函数 const debouncedAddButtons = debounce(addButtons, 300); // 页面加载完成后执行 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', debouncedAddButtons); } else { // 页面已经加载完成,延迟执行以确保所有元素都渲染完成 setTimeout(debouncedAddButtons, 100); } // 监听 PJAX/Turbo 导航事件 document.addEventListener("pjax:end", debouncedAddButtons); document.addEventListener("turbo:load", debouncedAddButtons); // 增加 Turbo 事件支持 // 监听 URL 变化 let currentUrl = location.href; const observer = new MutationObserver(() => { if (location.href !== currentUrl) { currentUrl = location.href; setTimeout(debouncedAddButtons, 500); } }); observer.observe(document.body, { childList: true, subtree: true }); })();