在GitHub仓库页面上添加一个与原生UI无缝集成的DeepWiki搜索框。
// ==UserScript==
// @name GitHub DeepWiki Search (Visually Integrated)
// @namespace http://tampermonkey.net/
// @version 2025-08-07.3
// @description 在GitHub仓库页面上添加一个与原生UI无缝集成的DeepWiki搜索框。
// @author Your Name
// @match https://github.com/*/*
// @grant GM_xmlhttpRequest
// @grant GM_openInTab
// @connect api.devin.ai
// @run-at document-end
// @license MIT
// ==/UserScript==
(function() {
'use strict';
const observer = new MutationObserver(() => {
// **CHANGED**: 直接定位到操作按钮的<ul>列表,这是最稳定的父容器。
const actionsList = document.querySelector('ul.pagehead-actions');
if (actionsList && !document.getElementById('deepwiki-search-container')) {
console.log('Found actions list, adding integrated DeepWiki search UI...');
addSearchUI(actionsList);
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
/**
* @param {HTMLElement} targetList The <ul> element (pagehead-actions) to append to.
*/
function addSearchUI(targetList) {
// 1. 创建最外层的 <li> 元素,这是符合 GitHub 布局的关键
const listItem = document.createElement('li');
// 2. 创建一个 div 作为按钮组 (BtnGroup),让输入框和按钮看起来像一个整体
const btnGroup = document.createElement('div');
btnGroup.id = 'deepwiki-search-container';
btnGroup.className = 'BtnGroup d-flex'; // 使用 GitHub 的 BtnGroup 类
// 3. 创建输入框
const input = document.createElement('input');
input.type = 'text';
input.id = 'deepwiki-search-input';
input.placeholder = 'DeepWiki Search';
// 复用GitHub样式,并作为按钮组的一部分
input.className = 'form-control input-sm BtnGroup-item';
// 样式微调,使其与按钮无缝连接
input.style.borderTopRightRadius = '0';
input.style.borderBottomRightRadius = '0';
input.style.zIndex = '1'; // 确保边框不会被旁边的按钮覆盖
// 4. 创建搜索按钮
const button = document.createElement('button');
button.innerText = 'Search';
button.id = 'deepwiki-search-button';
// 复用GitHub样式,并作为按钮组的一部分
button.className = 'btn btn-sm BtnGroup-item';
// 负外边距,让按钮和输入框紧紧贴在一起
button.style.marginLeft = '-1px';
// 绑定事件
button.addEventListener('click', performSearch);
input.addEventListener('keyup', (event) => {
if (event.key === "Enter") {
performSearch();
}
});
// 5. 按正确的层级组装 DOM
btnGroup.appendChild(input);
btnGroup.appendChild(button);
listItem.appendChild(btnGroup);
// 6. 将最终的 <li> 添加到操作列表中
targetList.appendChild(listItem);
}
function performSearch() {
const inputElement = document.getElementById('deepwiki-search-input');
const keyword = inputElement.value.trim();
if (!keyword) {
alert('请输入搜索关键词!');
return;
}
const searchButton = document.getElementById('deepwiki-search-button');
searchButton.disabled = true;
searchButton.innerText = '...'; // 使用更简洁的加载提示
const repoNameMatch = window.location.pathname.match(/^\/([^/]+\/[^/]+)/);
if (!repoNameMatch) {
console.error('无法从URL中解析仓库名称。');
alert('无法识别当前 GitHub 仓库!');
searchButton.disabled = false;
searchButton.innerText = 'Search';
return;
}
const repoName = repoNameMatch[1];
const queryId = generateUUID();
const searchUrl = `https://deepwiki.com/search/_${queryId}`;
const requestBody = {
engine_id: "multihop",
user_query: `${keyword}`,
keywords: [],
repo_names: [repoName],
additional_context: "",
query_id: `_${queryId}`,
use_notes: false,
generate_summary: false
};
GM_xmlhttpRequest({
method: "POST",
url: "https://api.devin.ai/ada/query",
headers: {
"accept": "*/*",
"content-type": "application/json",
"Referer": "https://deepwiki.com/"
},
data: JSON.stringify(requestBody),
onload: function(response) {
console.log('DeepWiki API 请求成功:', response);
GM_openInTab(searchUrl, { active: true });
inputElement.value = '';
},
onerror: function(error) {
console.error('DeepWiki API 请求失败:', error);
alert('请求 DeepWiki API 失败,请查看浏览器控制台获取更多信息。');
},
ontimeout: function() {
console.error('DeepWiki API 请求超时');
alert('请求 DeepWiki API 超时。');
},
onloadend: function() {
searchButton.disabled = false;
searchButton.innerText = 'Search';
}
});
}
function generateUUID() {
if (self.crypto && self.crypto.randomUUID) {
return self.crypto.randomUUID();
}
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
})();