GitHub to DeepWiki Link

一个快速在github仓库页面跳转到DeepWiki的脚本;A script to quickly jump to DeepWiki from the GitHub repository page

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GitHub to DeepWiki Link
// @name_zh      GitHub 到 DeepWiki 链接
// @namespace    https://github.com/worryzyy/fast2deepwiki
// @version      0.1
// @description  一个快速在github仓库页面跳转到DeepWiki的脚本;A script to quickly jump to DeepWiki from the GitHub repository page
// @author       weilei
// @match        https://github.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

;(function () {
	'use strict'

	// Generate a unique page instance ID to ensure each page's script runs independently
	const PAGE_INSTANCE_ID =
		'deepwiki_' + Math.random().toString(36).substring(2, 15)

	// Add page ID to debug logs to distinguish between multiple pages
	function debugLog(message, obj = null) {
		const debug = false; // Set to true to enable debugging
		if (debug) {
			console.log(`[DeepWiki Debug ${PAGE_INSTANCE_ID}] ${message}`, obj || '')
		}
	}

	// Add page ID prefix to ensure DOM element IDs are unique across pages
	const BUTTON_ID = `deepwiki-button-${PAGE_INSTANCE_ID}`
	const STYLES_ID = `deepwiki-styles-${PAGE_INSTANCE_ID}`

	// Main function - add DeepWiki link to the page
	function addDeepWikiLink() {
		// First check if we're on a GitHub repository page
		const repoInfo = getRepositoryInfo()
		if (!repoInfo) {
			debugLog('Not a valid repository page')
			return
		}

		debugLog('Repository info detected', repoInfo)

		// Create DeepWiki link URL
		const deepWikiUrl = `https://deepwiki.com/${repoInfo.owner}/${repoInfo.repo}`

		// Add custom CSS styles to the page
		addCustomCSS()

		// Check if button already exists (avoid duplicate buttons)
		if (document.getElementById(BUTTON_ID)) {
			debugLog('Button already exists, skipping')
			return
		}

		// Create DeepWiki button
		const deepWikiButton = createDeepWikiButton(deepWikiUrl)

		// Find README link as insertion point
		const readmeLink = document.querySelector('a[href*="readme-ov-file"]')
		if (readmeLink) {
			debugLog('README link found')
			// Insert button after README link
			deepWikiButton.style.marginLeft = '10px'
			readmeLink.insertAdjacentElement('afterend', deepWikiButton)
		}
		debugLog('No suitable insertion point found')
	}

	// Create DeepWiki button element
	function createDeepWikiButton(url) {
		// Create button link directly
		const link = document.createElement('a')
		link.id = BUTTON_ID
		link.href = url
		link.target = '_blank'
		link.className = 'btn btn-sm'
		link.style.display = 'inline-flex'
		link.style.alignItems = 'center'
		link.style.justifyContent = 'center'
		link.style.position = 'relative'
		link.style.overflow = 'hidden'
		link.style.background = 'linear-gradient(90deg, #2188ff 0%, #43e97b 100%)'
		link.style.border = 'none'
		link.style.transition = 'all 0.2s cubic-bezier(.4,0,.2,1)'
		link.style.marginLeft = '8px'
		link.style.borderRadius = '6px'
		link.style.padding = '4px 14px'
		link.style.color = '#fff'
		link.style.fontWeight = '500'
		link.style.boxShadow = '0 1px 2px rgba(33,136,255,0.08)'

		// Add hover and click effects to the button
		link.addEventListener('mouseover', () => {
			link.style.background = 'linear-gradient(90deg, #0366d6 0%, #3bce6f 100%)'
			link.style.boxShadow = '0 4px 12px rgba(33,136,255,0.18)'
			link.style.transform = 'translateY(-1px)'
		})

		link.addEventListener('mouseout', () => {
			link.style.background = 'linear-gradient(90deg, #2188ff 0%, #43e97b 100%)'
			link.style.boxShadow = '0 1px 2px rgba(33,136,255,0.08)'
			link.style.transform = 'translateY(0)'
		})

		link.addEventListener('mousedown', () => {
			link.style.transform = 'scale(0.98) translateY(0)'
			link.style.boxShadow = '0 1px 3px rgba(33,136,255,0.12)'
		})

		link.addEventListener('mouseup', () => {
			link.style.transform = 'scale(1) translateY(-1px)'
			link.style.boxShadow = '0 4px 12px rgba(33,136,255,0.18)'
		})

		// Set button content
		link.innerHTML = `
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16" style="margin-right: 8px;" class="deepwiki-icon">
                <path fill="currentColor" d="M12 0C5.372 0 0 5.372 0 12s5.372 12 12 12 12-5.372 12-12S18.628 0 12 0zm0 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2zm-3 5v2h3.586L7.293 14.293l1.414 1.414L14 10.414V14h2V7H9z"/>
            </svg>
            <span>DeepWiki</span>
        `
		return link
	}

	// Get repository information from current page
	function getRepositoryInfo() {
		// Extract repository info from URL
		const path = window.location.pathname.substring(1).split('/')

		// Check if there are enough path segments to represent a repository
		if (path.length < 2) return null

		// Check path parts are not GitHub special pages
		const nonRepoPathParts = [
			'settings',
			'trending',
			'new',
			'organizations',
			'marketplace',
			'explore',
			'topics'
		]
		if (
			nonRepoPathParts.includes(path[0]) ||
			nonRepoPathParts.includes(path[1])
		) {
			return null
		}

		return {
			owner: path[0],
			repo: path[1]
		}
	}

	// Add custom CSS to the page
	function addCustomCSS() {
		if (document.getElementById(STYLES_ID)) return

		const styleElement = document.createElement('style')
		styleElement.id = STYLES_ID
		styleElement.textContent = `
            #${BUTTON_ID}::after {
                content: '';
                position: absolute;
                bottom: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: linear-gradient(to right, transparent, rgba(255, 255, 255, 0.1), transparent);
                transform: translateX(-100%);
                transition: transform 0.6s ease;
            }
            
            #${BUTTON_ID}:hover::after {
                transform: translateX(100%);
            }
            
            @keyframes pulse {
                0% { transform: scale(1); opacity: 1; }
                50% { transform: scale(1.1); opacity: 0.8; }
                100% { transform: scale(1); opacity: 1; }
            }
            
            .deepwiki-icon {
                animation: pulse 2s infinite;
                filter: drop-shadow(0 0 1px rgba(255,255,255,0.5));
            }
        `

		document.head.appendChild(styleElement)
	}

	// Enhanced page observation
	function observePageChanges() {
		// Monitor URL changes - for SPA page navigation
		let lastUrl = location.href
		const urlChecker = setInterval(() => {
			if (location.href !== lastUrl) {
				lastUrl = location.href
				debugLog('URL changed, attempting to add button')
				setTimeout(addDeepWikiLink, 500) // Delay execution to wait for DOM updates
			}
		}, 1000)

		// Monitor DOM changes - for asynchronously loaded content
		const domObserver = new MutationObserver((mutations) => {
			if (!document.getElementById(BUTTON_ID)) {
				// Determine if changes are significant enough to retry adding the button
				const significantChanges = mutations.some((mutation) => {
					// Only retry when new nodes are added
					return mutation.addedNodes.length > 0
				})

				if (significantChanges) {
					debugLog('DOM changes detected, attempting to add button')
					addDeepWikiLink()
				}
			}
		})

		domObserver.observe(document.body, {
			childList: true,
			subtree: true
		})

		// Initial execution
		addDeepWikiLink()

		// Delayed retry in case DOM is not fully ready on initial load
		setTimeout(addDeepWikiLink, 1000)
	}

	// Start monitoring when page is loaded
	window.addEventListener('load', observePageChanges)

	// Also execute once when DOM is loaded
	if (document.readyState === 'loading') {
		document.addEventListener('DOMContentLoaded', addDeepWikiLink)
	} else {
		addDeepWikiLink()
	}
})()