diff --git a/.gitignore b/.gitignore index a547bf3..30bc162 100644 --- a/.gitignore +++ b/.gitignore @@ -1,24 +1 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? +/node_modules \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index bf5819a..0000000 --- a/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# 🚀 GitRepo Zip Fetcher - -Effortlessly download GitHub repositories and folders as ZIP files with just a few clicks! 🖱️ - -## ✨ Features - -- 📦 Fetch entire GitHub repos or specific folders -- 🗜️ Download repositories as ZIP files -- 🔒 Support for both public and private repositories -- 🔑 Optional GitHub Personal Access Token integration -- 📊 Real-time progress tracking -- 📱 Responsive design for all your devices - -## 🚀 How to Use - -1. 🌐 Open the app in your favorite web browser -2. 📝 Enter a GitHub repository URL -3. 🔑 (Optional) Click "Use Token" to enter your GitHub Personal Access Token -4. 🚀 Hit the "Fetch" button or press Enter -5. 📊 Watch the progress in the status output area -6. 💾 When it's done, your ZIP file will start downloading automatically! - -## 🛠️ Dependencies - -- [JSZip](https://stuk.github.io/jszip/): ZIP file wizardry 🧙‍♂️ -- [FileSaver.js](https://github.com/eligrey/FileSaver.js/): Download magic 🪄 -- [Octokit](https://github.com/octokit/octokit.js): GitHub API sorcery 🔮 - -## 📝 Note - -Your privacy is our top priority! 🛡️ This app runs entirely in your browser and doesn't collect or send any personal data. All operations happen on your device for maximum security. 🔒 - -Enjoy fetching your GitHub repositories with ease! 🎉 diff --git a/bun.lockb b/bun.lockb deleted file mode 100644 index 11539cb..0000000 Binary files a/bun.lockb and /dev/null differ diff --git a/github/fetcher.js b/fetcher.js similarity index 100% rename from github/fetcher.js rename to fetcher.js diff --git a/github/index.html b/github/index.html deleted file mode 100644 index 8f7bf14..0000000 --- a/github/index.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - GitRepo Zip Fetcher - - - - - - - - -
-
-

GitRepo Zip Fetcher

-

Effortlessly download GitHub repositories and folders

-
-
-
- - - - -
- - - -
- -
- - - - - - - - diff --git a/index.html b/index.html index 5e62a73..aa50d79 100644 --- a/index.html +++ b/index.html @@ -9,9 +9,13 @@ content="GitRepo Zip Fetcher is a sleek tool for effortlessly downloading GitHub repositories and folders as ZIP files." /> + - + +
diff --git a/package.json b/package.json deleted file mode 100644 index 4570039..0000000 --- a/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "github-repo-downloader", - "private": true, - "version": "1.0.0", - "type": "module", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview" - }, - "devDependencies": { - "@vercel/analytics": "^1.3.1", - "vite": "^6.1.0" - } -} diff --git a/src/fetcher.js b/src/fetcher.js deleted file mode 100644 index 919e145..0000000 --- a/src/fetcher.js +++ /dev/null @@ -1,150 +0,0 @@ -import { Octokit } from "https://cdn.skypack.dev/@octokit/rest"; - -document.addEventListener("DOMContentLoaded", () => { - const repoInput = document.getElementById("repoInput"); - const tokenInput = document.getElementById("tokenInput"); - const fetchBtn = document.getElementById("fetchBtn"); - const statusLog = document.getElementById("statusLog"); - const statusOutput = document.getElementById("statusOutput"); - const fileListElement = document.getElementById("fileList"); - const fileListWrapper = document.getElementById("fileListWrapper"); - const toggleTokenBtn = document.getElementById("toggleTokenBtn"); - const tokenInfo = document.getElementById("tokenInfo"); - - let fetchedFiles = []; - let progressPercentage = 0; - - const updateStatus = (message) => { - statusLog.textContent = message; - statusOutput.style.display = "block"; - }; - - const updateProgress = (percent) => { - progressPercentage = percent; - statusLog.style.background = `linear-gradient(to right, rgba(100, 255, 218, 0.3) ${percent}%, transparent ${percent}%)`; - statusLog.innerHTML = `${statusLog.textContent} ${percent}% Complete`; - }; - - const fetchFolderContents = async (octokit, owner, repo, path) => { - const response = await octokit.rest.repos.getContent({ - owner, - repo, - path, - }); - return response.data; - }; - - const fetchFileContent = async (octokit, owner, repo, path, fileName, totalSize) => { - const response = await octokit.rest.repos.getContent({ - owner, - repo, - path, - mediaType: { - format: "raw", - }, - }); - updateStatus(`Fetching ${fileName}`); - updateProgress(100); - return response.data; - }; - - const processFolderContents = async (octokit, owner, repo, folder, zip, path = "") => { - for (const item of folder) { - if (item.type === "file") { - updateStatus(`Queueing fetch of ${item.name}`); - const fileContent = await fetchFileContent(octokit, owner, repo, item.path, item.name, item.size); - fetchedFiles.push(item); - updateFileList(); - zip.file(item.name, fileContent); - } else if (item.type === "dir") { - const subFolder = await fetchFolderContents(octokit, owner, repo, item.path); - const subFolderZip = zip.folder(item.name); - await processFolderContents(octokit, owner, repo, subFolder, subFolderZip, item.path); - } - } - }; - - const fetchRepositoryContents = async (octokit, owner, repo, path) => { - try { - const folderData = await fetchFolderContents(octokit, owner, repo, path); - const zip = new JSZip(); - await processFolderContents(octokit, owner, repo, folderData, zip, path); - return zip; - } catch (error) { - console.error(`Error: ${error}`); - throw error; - } - }; - - const updateFileList = () => { - fileListElement.innerHTML = ""; - fetchedFiles.forEach((file) => { - const li = document.createElement("li"); - li.textContent = file.name; - fileListElement.appendChild(li); - }); - fileListWrapper.style.display = "block"; - fileListElement.scrollTop = fileListElement.scrollHeight; - }; - - const fetchRepo = async () => { - updateStatus(""); - fetchedFiles = []; - fileListWrapper.style.display = "none"; - - const repoUrl = repoInput.value; - const token = tokenInput.value.trim(); - - if (!repoUrl.includes("github.com")) { - updateStatus("Invalid URL. Please enter a valid GitHub repository URL."); - return; - } - - const [, , , owner, repo, , , ...dirParts] = repoUrl.split("/"); - const path = dirParts.join("/"); - - const octokit = new Octokit({ auth: token }); - - updateStatus("Fetching repository contents..."); - try { - const zip = await fetchRepositoryContents(octokit, owner, repo, path); - - updateStatus("Compressing files..."); - const content = await zip.generateAsync({ type: "blob" }); - saveAs(content, `${path ? path.replace(/\/|%20/g, "-") : repo}.zip`); - - const fileList = zip.file(/.*/); - - updateProgress(0); - updateStatus( - `Fetched ${fileList.length} files\nUser: ${owner}\nRepository: ${repoUrl}\nFolder: ${path}\nSize: ${( - content.size / - 1024 / - 1024 - ).toFixed(2)} MB` - ); - } catch (error) { - if (error.status === 403) { - updateStatus("Rate limit exceeded. Please try again in a few minutes or use a GitHub Personal Access Token."); - } else { - updateStatus(`Error: ${error.message}`); - } - } - }; - - fetchBtn.addEventListener("click", fetchRepo); - repoInput.addEventListener("keydown", (e) => { - if (e.key === "Enter") { - fetchRepo(); - } - }); - - toggleTokenBtn.addEventListener("click", () => { - const isHidden = tokenInput.style.display === "none"; - tokenInput.style.display = isHidden ? "block" : "none"; - tokenInfo.style.display = isHidden ? "block" : "none"; - toggleTokenBtn.textContent = isHidden ? "Hide Token" : "Use Token"; - }); - - repoInput.focus(); -}); diff --git a/src/styles.css b/src/styles.css deleted file mode 100644 index 7b75470..0000000 --- a/src/styles.css +++ /dev/null @@ -1,171 +0,0 @@ -:root { - --accent-color: #64ffda; - --bg-color: #0a192f; - --surface-color: #112240; - --text-color: #ccd6f6; - --error-color: #ff5370; -} - -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -body { - background: linear-gradient(135deg, #0a192f, #112240); - font-family: "Poppins", sans-serif; - color: var(--text-color); - min-height: 100vh; -} - -.container { - display: flex; - flex-direction: column; - min-height: 100vh; -} - -header { - background-color: var(--surface-color); - padding: 2rem; - text-align: center; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); -} - -h1 { - font-weight: 500; - margin-bottom: 0.5rem; - color: var(--accent-color); - font-size: 2.5rem; -} - -main { - flex-grow: 1; - padding: 2rem; - max-width: 800px; - margin: 0 auto; - width: 100%; -} - -.input-wrapper { - display: flex; - flex-direction: column; - gap: 1rem; - margin-bottom: 1rem; -} - -input { - flex-grow: 1; - padding: 0.75rem 1rem; - border: 2px solid var(--accent-color); - border-radius: 4px; - background-color: var(--surface-color); - color: var(--text-color); - font-size: 1rem; - font-family: "Poppins", sans-serif; -} - -button { - display: flex; - align-items: center; - justify-content: center; - gap: 0.3rem; - padding: 0.75rem 1.5rem; - border: none; - border-radius: 4px; - background-color: var(--accent-color); - color: var(--bg-color); - font-size: 1rem; - font-weight: 500; - cursor: pointer; - transition: background-color 0.3s ease, transform 0.1s ease; -} - -button:hover { - background-color: #45e0bc; - transform: translateY(-2px); -} - -.token-info { - font-size: 0.9rem; - color: var(--accent-color); - margin-bottom: 1rem; -} - -.toggle-token-btn { - align-self: flex-start; - padding: 0.5rem 1rem; - font-size: 0.9rem; - background-color: var(--surface-color); - color: var(--accent-color); - border: 1px solid var(--accent-color); - cursor: pointer; - transition: background-color 0.3s ease; -} - -.toggle-token-btn:hover { - background-color: rgba(100, 255, 218, 0.1); -} - -.status-output { - background-color: var(--surface-color); - border-radius: 4px; - padding: 1rem; - margin-bottom: 2rem; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.status-log { - font-family: "Courier New", monospace; - white-space: pre-wrap; - word-break: break-all; - font-size: 0.9rem; - line-height: 1.5; -} - -.file-list { - list-style-type: none; - max-height: 250px; - overflow-y: auto; -} - -.file-list li { - padding: 0.5rem 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - font-size: 0.9rem; -} - -footer { - background-color: var(--surface-color); - padding: 1rem; - text-align: center; - font-size: 0.9rem; -} - -footer a { - color: var(--accent-color); - text-decoration: none; - font-weight: 500; -} - -.note { - font-size: 0.8rem; - color: var(--error-color); - margin-top: 0.5rem; -} - -@media (max-width: 600px) { - h1 { - font-size: 1.8rem; - } - - p { - font-size: 0.9rem; - } - .input-wrapper { - flex-direction: column; - } - input { - font-size: 0.8rem; - } -} diff --git a/github/styles.css b/styles.css similarity index 100% rename from github/styles.css rename to styles.css