Add GitHub DeepWiki Button

Add a DeepWiki button to GitHub repository navigation

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

// ==UserScript==
// @name         Add GitHub DeepWiki Button
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  Add a DeepWiki button to GitHub repository navigation
// @author       heakjo
// @match        https://github.com/*/*
// @grant        none
// @run-at      document-idle
// @icon         https://www.google.com/s2/favicons?sz=64&domain=github.com
// ==/UserScript==
(function() {
    'use strict';

    // 配置
    const config = {
        maxRetries: 10,
        retryDelay: 500,
        observerConfig: {
            childList: true,
            subtree: true,
            attributes: false,
            characterData: false
        }
    };

    // 日志函数
    function log(message) {
        console.log('[DeepWiki Button]', message);
    }

    // 等待元素出现的函数
    function waitForElement(selector, callback, retryCount = 0) {
        const element = document.querySelector(selector);
        if (element) {
            callback(element);
            return;
        }

        if (retryCount < config.maxRetries) {
            setTimeout(() => {
                waitForElement(selector, callback, retryCount + 1);
            }, config.retryDelay);
        } else {
            log(`Element not found after ${config.maxRetries} retries: ${selector}`);
        }
    }

    // 添加DeepWiki按钮的主函数
    function addDeepWikiButton() {
        // 尝试多个可能的选择器
        const selectors = [
            'a[data-tab-item="i7insights-tab"]',  // 原始选择器
            'a[data-selected-links^="repo_graphs"]',  // 备用选择器1
            'nav[aria-label="Repository"] a[href$="/pulse"]',  // 备用选择器2
            'nav[aria-label="Repository"] a[href$="/insights"]',  // 备用选择器3
            '.UnderlineNav-item[href$="/insights"]',  // 备用选择器4
            '.js-responsive-underlinenav-item[href$="/insights"]'  // 备用选择器5
        ];

        let insightsButton = null;
        let usedSelector = '';

        // 尝试每个选择器
        for (const selector of selectors) {
            insightsButton = document.querySelector(selector);
            if (insightsButton) {
                usedSelector = selector;
                break;
            }
        }

        if (!insightsButton) {
            log('Insights button not found with any selector');
            return false;
        }

        log(`Found Insights button using selector: ${usedSelector}`);

        // 检查是否已经添加了DeepWiki按钮
        const existingButton = document.querySelector('a[data-tab-item="deepwiki-tab"]');
        if (existingButton) {
            log('DeepWiki button already exists');
            return true;
        }

        // 获取用户名和仓库名
        const currentUrl = window.location.href;
        const urlParts = currentUrl.split('/');
        const username = urlParts[3];
        const repoName = urlParts[4];

        if (!username || !repoName) {
            log('Could not extract username or repo name from URL');
            return false;
        }

        // 创建DeepWiki按钮
        const deepWikiUrl = `https://deepwiki.com/${username}/${repoName}`;
        const deepWikiButtonHtml = `
            <li data-view-component="true" class="d-inline-flex">
                <a href="${deepWikiUrl}" data-tab-item="deepwiki-tab" data-selected-links="" data-pjax="#repo-content-pjax-container" data-turbo-frame="repo-content-turbo-frame" data-analytics-event="{&quot;category&quot;:&quot;Underline navbar&quot;,&quot;action&quot;:&quot;Click tab&quot;,&quot;label&quot;:&quot;DeepWiki&quot;,&quot;target&quot;:&quot;UNDERLINE_NAV.TAB&quot;}" data-view-component="true" class="UnderlineNav-item no-wrap js-responsive-underlinenav-item">
                    <svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-book UnderlineNav-octicon d-none d-sm-inline">
                        <path d="M0 1.75A.75.75 0 0 1 .75 1h4.25a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75H.75a.75.75 0 0 1-.75-.75ZM6.5 1.75a.75.75 0 0 1 .75-.75h8a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-8a.75.75 0 0 1-.75-.75Z"></path>
                    </svg>
                    <span data-content="DeepWiki">DeepWiki</span>
                </a>
            </li>
        `;

        // 插入按钮
        try {
            insightsButton.parentElement.insertAdjacentHTML('afterend', deepWikiButtonHtml);
            log('DeepWiki button added successfully');
            return true;
        } catch (error) {
            log('Error adding DeepWiki button:', error);
            return false;
        }
    }

    // 设置MutationObserver来监听DOM变化
    function setupObserver() {
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                if (mutation.type === 'childList') {
                    // 检查是否有新的导航元素被添加
                    const hasNavChanges = Array.from(mutation.addedNodes).some(node => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            return node.querySelector && (
                                node.querySelector('nav[aria-label="Repository"]') ||
                                node.querySelector('.UnderlineNav') ||
                                node.querySelector('.js-responsive-underlinenav-item')
                            );
                        }
                        return false;
                    });

                    if (hasNavChanges) {
                        log('DOM change detected, attempting to add DeepWiki button');
                        setTimeout(addDeepWikiButton, 100); // 小延迟确保DOM完全更新
                    }
                }
            }
        });

        // 开始观察整个文档
        observer.observe(document.body, config.observerConfig);
        log('MutationObserver setup complete');
    }

    // 初始化函数
    function init() {
        log('Initializing DeepWiki button script');

        // 立即尝试添加按钮
        addDeepWikiButton();

        // 设置MutationObserver
        setupObserver();

        // 监听GitHub的导航事件
        document.addEventListener('pjax:end', () => {
            log('pjax:end event detected');
            setTimeout(addDeepWikiButton, 100);
        });

        document.addEventListener('turbo:render', () => {
            log('turbo:render event detected');
            setTimeout(addDeepWikiButton, 100);
        });

        document.addEventListener('turbo:load', () => {
            log('turbo:load event detected');
            setTimeout(addDeepWikiButton, 100);
        });

        // 监听hash变化(单页应用导航)
        window.addEventListener('hashchange', () => {
            log('hashchange event detected');
            setTimeout(addDeepWikiButton, 100);
        });
    }

    // 等待DOM ready后初始化
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();