// ==UserScript==
// @name GitHub CodeWiki Button
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 在 GitHub 仓库页面添加 CodeWiki 按钮. 点击跳转到 https://codewiki.google/github.com/{user}/{repo}
// @author You
// @match https://github.com/*/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
// @grant none
// @license MIT
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
// 日志函数
const log = (...args) => console.log('[CodeWiki Button]', ...args);
// 判断是否为仓库页面
function isRepoPage() {
try {
const pathname = window.location.pathname;
return document.querySelector('main#js-repo-pjax-container') !== null ||
document.querySelector('div[data-pjax="#repo-content-pjax-container"]') !== null ||
(pathname.split('/').filter(Boolean).length >= 2 &&
!pathname.includes('/settings') &&
!pathname.includes('/issues'));
} catch (e) {
log('检查仓库页面时出错:', e);
return false;
}
}
// 获取用户名和仓库名
function getUserAndRepo() {
try {
const pathParts = window.location.pathname.split('/').filter(part => part.length > 0);
if (pathParts.length >= 2) {
return {
user: pathParts[0],
repo: pathParts[1]
};
}
} catch (e) {
log('获取用户和仓库信息时出错:', e);
}
return null;
}
// 创建 SVG 图标元素
function createSVGIconElement() {
const svgNS = 'http://www.w3.org/2000/svg';
const svg = document.createElementNS(svgNS, 'svg');
svg.setAttribute('class', 'octicon');
svg.setAttribute('width', '16');
svg.setAttribute('height', '16');
svg.setAttribute('viewBox', '0 0 16 16');
svg.setAttribute('style', 'margin-right:4px;vertical-align:text-bottom;');
// 简单 C 图标
svg.innerHTML = `
`;
return svg;
}
// 创建 CodeWiki 按钮
function createCodeWikiButton(user, repo) {
try {
// 这里按你提供的实际规则来拼接
// 例如 https://codewiki.google/github.com/google-gemini/gemini-cli
const codeWikiUrl = `https://codewiki.google/github.com/${user}/${repo}`;
const existingButtons = document.querySelectorAll('.btn-sm, [data-hydro-click]');
let templateButton = null;
for (const btn of existingButtons) {
const text = btn.textContent || '';
if (text.includes('Fork') || text.includes('Star') ||
text.includes('Watch') || text.includes('Code')) {
templateButton = btn;
break;
}
}
const button = document.createElement('a');
button.href = codeWikiUrl;
button.id = 'codewiki-button';
button.target = '_blank';
button.rel = 'noopener noreferrer';
button.title = `查看 ${user}/${repo} 的 CodeWiki 页面`;
button.setAttribute('data-user', user);
button.setAttribute('data-repo', repo);
button.setAttribute('aria-label', `打开 CodeWiki 页面: ${user}/${repo}`);
if (templateButton) {
const classNames = Array.from(templateButton.classList)
.filter(cls =>
!cls.includes('selected') &&
!cls.includes('disabled') &&
!cls.includes('tooltipped') &&
!cls.includes('BtnGroup')
);
button.className = classNames.join(' ');
} else {
button.className = 'btn btn-sm';
}
const svgIcon = createSVGIconElement();
button.appendChild(svgIcon);
const text = document.createTextNode('CodeWiki');
button.appendChild(text);
if (!templateButton) {
button.style.backgroundColor = '#f6f8fa';
button.style.border = '1px solid rgba(27,31,36,0.15)';
button.style.borderRadius = '6px';
button.style.color = '#24292f';
button.style.padding = '3px 12px';
button.style.fontSize = '12px';
button.style.fontWeight = '500';
button.style.lineHeight = '20px';
button.style.textDecoration = 'none';
}
button.addEventListener('click', () => {
log(`点击 CodeWiki 按钮: ${user}/${repo}`);
});
return button;
} catch (e) {
log('创建按钮时出错:', e);
return null;
}
}
// 把按钮插入页面
function addCodeWikiButton() {
try {
if (!isRepoPage()) return;
const userAndRepo = getUserAndRepo();
if (!userAndRepo) return;
if (document.querySelector('#codewiki-button')) return;
const targetSelectors = [
'.file-navigation',
'.d-flex.mb-3.px-3.px-md-4.px-lg-5',
'#repository-container-header .d-flex'
];
for (const selector of targetSelectors) {
const targetElements = document.querySelectorAll(selector);
if (targetElements && targetElements.length > 0) {
const targetElement = targetElements[0];
const codeWikiButton = createCodeWikiButton(userAndRepo.user, userAndRepo.repo);
if (codeWikiButton) {
const container = document.createElement('div');
container.className = 'codewiki-button-container';
container.style.marginLeft = 'auto';
container.appendChild(codeWikiButton);
if (selector === '.file-navigation') {
const actionsContainer = targetElement.querySelector('.d-flex');
if (actionsContainer) {
actionsContainer.appendChild(container);
} else {
targetElement.appendChild(container);
}
} else {
targetElement.appendChild(container);
}
log(`成功添加 CodeWiki 按钮: ${userAndRepo.user}/${userAndRepo.repo}`);
return;
}
}
}
const actionButtons = document.querySelectorAll('.pagehead-actions li, .flex-1 nav ul');
if (actionButtons && actionButtons.length > 0) {
const lastAction = actionButtons[actionButtons.length - 1];
const codeWikiButton = createCodeWikiButton(userAndRepo.user, userAndRepo.repo);
if (codeWikiButton) {
const wrapper = document.createElement('li');
if (lastAction.tagName === 'LI') {
wrapper.className = lastAction.className;
} else {
wrapper.style.marginLeft = '8px';
}
wrapper.appendChild(codeWikiButton);
lastAction.parentNode.appendChild(wrapper);
log(`成功添加 CodeWiki 按钮到操作区域: ${userAndRepo.user}/${userAndRepo.repo}`);
return;
}
}
const repoNavLinks = document.querySelector('nav[aria-label="Repository"], .pagehead-actions');
if (repoNavLinks) {
const codeWikiButton = createCodeWikiButton(userAndRepo.user, userAndRepo.repo);
if (codeWikiButton) {
const wrapper = document.createElement('div');
wrapper.style.display = 'inline-block';
wrapper.style.marginLeft = '8px';
wrapper.appendChild(codeWikiButton);
repoNavLinks.appendChild(wrapper);
log(`成功添加 CodeWiki 按钮到仓库导航区: ${userAndRepo.user}/${userAndRepo.repo}`);
return;
}
}
log('未找到合适的位置添加 CodeWiki 按钮');
} catch (e) {
log('添加按钮时出错:', e);
}
}
// 处理页面动态加载
function setupMutationObserver() {
try {
const observer = new MutationObserver(function (mutations) {
let shouldCheck = false;
for (const mutation of mutations) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
shouldCheck = true;
break;
}
}
if (shouldCheck) {
setTimeout(addCodeWikiButton, 300);
}
});
observer.observe(document.body, { childList: true, subtree: true });
log('成功设置 MutationObserver');
} catch (e) {
log('设置 MutationObserver 时出错:', e);
}
}
function init() {
try {
log('初始化脚本...');
addCodeWikiButton();
setTimeout(addCodeWikiButton, 500);
setTimeout(addCodeWikiButton, 1000);
setTimeout(addCodeWikiButton, 2000);
setupMutationObserver();
window.addEventListener('popstate', () => {
setTimeout(addCodeWikiButton, 500);
});
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
setTimeout(addCodeWikiButton, 500);
}
}).observe(document, { subtree: true, childList: true });
setInterval(addCodeWikiButton, 3000);
} catch (e) {
log('初始化时出错:', e);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();