【油猴脚本】Github 文件夹下载器
分享下 Gemini 搓的一个 Github 文件夹下载器,妈妈再也不用担心我为了一个文件夹下载整个仓库啦
精修样式,完美融入原生页面:
脚本源码:
// ==UserScript==
// @name GitHub Folder Downloader
// @name:zh-CN GitHub 文件夹下载器
// @version 0.7.0.33
// @author 叁月柒
// @match *://github.com/*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
const isFolder = () => {
const path = window.location.pathname.split('/').filter(Boolean);
return path.length >= 2 && (path.length === 2 || path[2] === 'tree');
};
const injectToMenu = () => {
const portalRoot = document.querySelector('#__primerPortalRoot__');
if (!portalRoot) return;
const menu = portalRoot.querySelector('ul[role="menu"]');
if (!menu || menu.querySelector('.gh-download-integrated')) return;
const menuText = menu.innerText;
// 确保是操作菜单
if (!menuText.includes('Copy path') && !menuText.includes('Delete directory')) return;
// 1. 分割线
const dividerHtml = `<li role="none" class="ActionList-sectionDivider gh-download-integrated"></li>`;
// 2. 标题
const headerHtml = `
<li class="ActionList-sectionHeader gh-download-integrated" style="padding: 8px 16px 4px 16px;">
<span class="ActionList-sectionHeader-label" style="color: #9198a1; font-size: 12px; font-weight: 500; display: block; line-height: 1.5;">
Download folder
</span>
</li>`;
// 3. 子选项
const createItem = (text, url) => `
<li role="none" class="ActionList-item gh-download-integrated">
<a role="menuitem" class="ActionList-content ActionList-content--visual16" target="_blank" rel="noopener noreferrer" href="${url}" style="text-decoration: none; padding-left: 16px;">
<span class="ActionList-item-label" style="padding-left: 24px; font-weight: 400; font-size: 14px; color: var(--fgColor-default, #adbac7);">
${text}
</span>
</a>
</li>`;
const downloadDirUrl = `https://download-directory.github.io?url=${window.location.href}`;
const downGitUrl = `https://downgit.github.io/#/home?url=${window.location.href}`;
const fragment = dividerHtml +
headerHtml +
createItem('by Download-Directory', downloadDirUrl) +
createItem('by DownGit', downGitUrl);
menu.insertAdjacentHTML('beforeend', fragment);
};
const observer = new MutationObserver((mutations) => {
if (!isFolder()) return;
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0) {
injectToMenu();
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
})();