GitHub Repo Size Display

Display repository size on GitHub repo pages

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

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

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         GitHub Repo Size Display
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  Display repository size on GitHub repo pages
// @license      MIT
// @author       Lainbo
// @match        https://github.com/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @icon         data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMTQgMTQiPjxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iY3VycmVudENvbG9yIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxyZWN0IHdpZHRoPSIxMSIgaGVpZ2h0PSIxMyIgeD0iMS41IiB5PSIuNSIgcng9IjEiLz48cGF0aCBkPSJtNC41IDEwLjVsMS43NS0yLjc1TTQuMDkgN0EyLjkzIDIuOTMgMCAwIDEgNCA1LjE2YTMgMyAwIDEgMSA0LjY3IDMuMjdNNy41IDExSDEwIi8+PC9nPjwvc3ZnPg==
// ==/UserScript==

(function () {
  'use strict'

  GM_addStyle(`
    .modal-overlay {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(0, 0, 0, 0.5);
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 9999;
    }
    .modal-content {
        background-color: #f6f8fa;
        padding: 24px;
        border-radius: 6px;
        width: 340px;
        box-shadow: 0 8px 24px rgba(140,149,159,0.2);
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
    }
    .modal-title {
        font-size: 20px;
        font-weight: 600;
        margin-bottom: 16px;
        color: #24292f;
    }
    .modal-description {
        font-size: 14px;
        color: #57606a;
        margin-bottom: 16px;
        line-height: 1.5;
    }
    .modal-content input {
        width: 100%;
        margin-bottom: 16px;
        padding: 5px 12px;
        font-size: 14px;
        line-height: 20px;
        color: #24292f;
        vertical-align: middle;
        background-color: #ffffff;
        background-repeat: no-repeat;
        background-position: right 8px center;
        border: 1px solid #d0d7de;
        border-radius: 6px;
        box-shadow: inset 0 1px 0 rgba(208,215,222,0.2);
    }
    .modal-content button {
        color: #ffffff;
        background-color: #2da44e;
        padding: 5px 16px;
        font-size: 14px;
        font-weight: 500;
        line-height: 20px;
        white-space: nowrap;
        vertical-align: middle;
        cursor: pointer;
        border: 1px solid;
        border-radius: 6px;
        appearance: none;
        user-select: none;
        margin-right: 8px;
    }
    .modal-content button.cancel {
        color: #24292f;
        background-color: #f6f8fa;
        border-color: rgba(27,31,36,0.15);
    }
    .modal-content button:hover {
        background-color: #2c974b;
    }
    .modal-content button.cancel:hover {
        background-color: #f3f4f6;
    }
    .repo-size-label.error {
        background-color: #ffdce0;
        color: #cf222e;
    }
`)

  let observer
  let repoTitleComponent

  function formatSize(sizeInKB) {
    if (sizeInKB >= 1024 * 1024) {
      return `${(sizeInKB / (1024 * 1024)).toFixed(2)} GB`
    }
    else if (sizeInKB >= 1024) {
      return `${(sizeInKB / 1024).toFixed(2)} MB`
    }
    else {
      return `${sizeInKB} KB`
    }
  }

  function updateRepoSize(content, isError = false) {
    if (!repoTitleComponent) {
      repoTitleComponent = document.querySelector('#repo-title-component')
      if (!repoTitleComponent) {
        return
      }
    }

    let sizeElement = repoTitleComponent.querySelector('.repo-size-label')
    if (!sizeElement) {
      sizeElement = document.createElement('span')
      sizeElement.className = 'Label Label--secondary v-align-middle ml-1 repo-size-label'
      repoTitleComponent.appendChild(sizeElement)
    }

    sizeElement.textContent = content
    sizeElement.classList.toggle('error', isError)
  }

  function fetchRepoSize() {
    const repoRegex = /^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/?$/
    const match = location.href.match(repoRegex)

    if (!match) { return }

    const [, owner, repo] = match
    const apiUrl = `https://api.github.com/repos/${owner}/${repo}`
    const token = GM_getValue('github_token', '')

    if (!token) {
      updateRepoSize('Token not set', true)
      return
    }

    fetch(apiUrl, {
      headers: { Authorization: `token ${token}` },
    })
      .then((response) => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }
        return response.json()
      })
      .then((data) => {
        if (data.size !== undefined) {
          updateRepoSize(formatSize(data.size))
        }
        else {
          throw new Error('Size information not available')
        }
      })
      .catch((error) => {
        console.error('Error fetching repo size:', error)
        updateRepoSize('Size fetch failed', true)
      })
  }

  function createModal() {
    const tokenCreationUrl = new URL('https://github.com/settings/tokens/new')
    tokenCreationUrl.searchParams.append('description', 'GitHub Repo Size Display')
    tokenCreationUrl.searchParams.append('scopes', 'repo')

    const modalHTML = `
        <div class="modal-overlay">
            <div class="modal-content">
                <h2 class="modal-title">Set GitHub Token</h2>
                <p class="modal-description">
                    Enter your GitHub personal access token with "repo" scope.
                    <a href="${tokenCreationUrl.toString()}" target="_blank" rel="noopener noreferrer">
                        Click here to create a new token
                    </a>
                </p>
                <input type="text" id="github-token-input" placeholder="Enter your GitHub personal access token">
                <button id="save-token">Save</button>
                <button id="cancel-token" class="cancel">Cancel</button>
            </div>
        </div>
    `

    const modalContainer = document.createElement('div')
    modalContainer.innerHTML = modalHTML
    document.body.appendChild(modalContainer)

    const input = document.getElementById('github-token-input')
    input.value = GM_getValue('github_token', '')

    document.getElementById('save-token').addEventListener('click', () => {
      const token = input.value.trim()
      if (token) {
        GM_setValue('github_token', token)
        modalContainer.remove()
        location.reload()
      }
    })

    document.getElementById('cancel-token').addEventListener('click', () => modalContainer.remove())
  }

  function observePageChanges() {
    const targetNode = document.body
    const config = { childList: true, subtree: true }

    observer = new MutationObserver((mutationsList) => {
      for (const mutation of mutationsList) {
        if (mutation.type === 'childList') {
          const newRepoTitleComponent = document.querySelector('#repo-title-component')
          if (newRepoTitleComponent && newRepoTitleComponent !== repoTitleComponent) {
            repoTitleComponent = newRepoTitleComponent
            fetchRepoSize()
            break
          }
        }
      }
    })

    observer.observe(targetNode, config)
  }

  function init() {
    observePageChanges()
  }

  // Register the menu command to set the GitHub token
  GM_registerMenuCommand('Set GitHub Token', createModal)

  init()
})()