Go to Github Releases Page

Add a quick link button to navigate to GitHub repository releases page

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name                    Go to Github Releases Page
// @name:zh-CN              跳转到 Github Releases 页面
// @namespace               https://github.com/xlsama/tampermonkey-scripts
// @version                 0.1.0
// @author                  xlsama
// @description             Add a quick link button to navigate to GitHub repository releases page
// @description:zh-CN       在 GitHub 仓库页面添加快速跳转到 Releases 页面的按钮
// @homepageURL             https://github.com/xlsama/tampermonkey-scripts
// @supportURL              https://github.com/xlsama/tampermonkey-scripts/issues
// @match                   *://github.com/*
// @include                 *://*github*
// @grant                   none
// @license                 MIT
// ==/UserScript==

// 判断当前path是否是一个 github repo,且位于项目的主页面
function isGithubRepo(path) {
  path = path.slice(0, -1)
  return path.split('/').length === 3
}

// 创建 package 图标 SVG 的辅助函数
function createPackageIcon() {
  const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
  svg.setAttribute('aria-hidden', 'true')
  svg.setAttribute('focusable', 'false')
  svg.setAttribute('class', 'octicon octicon-package')
  svg.setAttribute('viewBox', '0 0 16 16')
  svg.setAttribute('width', '16')
  svg.setAttribute('height', '16')
  svg.setAttribute('fill', 'currentColor')
  svg.setAttribute('display', 'inline-block')
  svg.setAttribute('overflow', 'visible')
  svg.setAttribute('style', 'vertical-align: text-bottom;')

  const path = document.createElementNS('http://www.w3.org/2000/svg', 'path')
  path.setAttribute(
    'd',
    'm8.878.392 5.25 3.045c.54.314.872.89.872 1.514v6.098a1.75 1.75 0 0 1-.872 1.514l-5.25 3.045a1.75 1.75 0 0 1-1.756 0l-5.25-3.045A1.75 1.75 0 0 1 1 11.049V4.951c0-.624.332-1.2.872-1.514L7.122.392a1.75 1.75 0 0 1 1.756 0ZM7.875 1.69l-4.63 2.685L8 7.133l4.755-2.758-4.63-2.685a.248.248 0 0 0-.25 0ZM2.5 4.775v5.913c0 .09.047.171.125.216l4.625 2.683V7.653Zm6.25 8.832 4.625-2.683a.25.25 0 0 0 .125-.216V4.775L8.75 7.653Z'
  )

  svg.appendChild(path)
  return svg
}

// 创建 releases 按钮(带文本)
function createReleasesButton() {
  const el = document.querySelector('[class^="OverviewContent-module__Box_4"]')

  if (!el) {
    return
  }

  // 检查是否已经存在 releases 按钮
  if (el.querySelector('a[href*="/releases"]')) {
    return
  }

  // 创建按钮链接
  const a = document.createElement('a')
  a.type = 'button'
  a.href = `${window.location.pathname}/releases`
  a.className = 'prc-Button-ButtonBase-c50BI OverviewContent-module__Button--MDoYP'
  a.setAttribute('data-loading', 'false')
  a.setAttribute('data-size', 'medium')
  a.setAttribute('data-variant', 'invisible')

  // 创建按钮内容容器
  const buttonContent = document.createElement('span')
  buttonContent.setAttribute('data-component', 'buttonContent')
  buttonContent.setAttribute('data-align', 'center')
  buttonContent.className = 'prc-Button-ButtonContent-HKbr-'

  // 创建图标容器
  const visualWrap = document.createElement('span')
  visualWrap.setAttribute('data-component', 'leadingVisual')
  visualWrap.className = 'prc-Button-Visual-2epfX prc-Button-VisualWrap-Db-eB'

  const svg = createPackageIcon()
  visualWrap.appendChild(svg)

  // 创建文本标签
  const label = document.createElement('span')
  label.setAttribute('data-component', 'text')
  label.className = 'prc-Button-Label-pTQ3x'
  label.textContent = 'Releases'

  // 组装按钮
  buttonContent.appendChild(visualWrap)
  buttonContent.appendChild(label)
  a.appendChild(buttonContent)

  // 添加到容器
  el.appendChild(a)
}

// 创建 releases 按钮(仅图标)
function createReleasesButtonMobile() {
  const el = document.querySelector('[class^="OverviewContent-module__Box_5"]')

  if (!el) {
    return
  }

  // 检查是否已经存在 releases 按钮
  if (el.querySelector('a[href*="/releases"]')) {
    return
  }

  // 创建按钮链接
  const a = document.createElement('a')
  a.type = 'button'
  a.href = `${window.location.pathname}/releases`
  a.setAttribute('aria-label', 'Go to Releases page')
  a.className = 'prc-Button-ButtonBase-c50BI OverviewContent-module__Button_1--_1Ng2'
  a.setAttribute('data-loading', 'false')
  a.setAttribute('data-no-visuals', 'true')
  a.setAttribute('data-size', 'medium')
  a.setAttribute('data-variant', 'invisible')

  const svg = createPackageIcon()
  a.appendChild(svg)

  // 添加到容器
  el.appendChild(a)
}

function initButtons() {
  if (!isGithubRepo(window.location.pathname)) {
    return
  }

  createReleasesButton()
  createReleasesButtonMobile()
}

;(function () {
  'use strict'

  // 初始化
  initButtons()

  // 监听页面变化
  new MutationObserver(() => {
    // 检查两个容器是否存在
    if (
      document.querySelector('[class^="OverviewContent-module__Box_4"]') ||
      document.querySelector('[class^="OverviewContent-module__Box_5"]')
    ) {
      initButtons()
    }
  }).observe(document, {
    childList: true,
    subtree: true,
  })
})()