您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds preview links of tracked files in GitHub pull requests to the Astro and Starlight documentation.
// ==UserScript== // @name Astro Docs Preview Links // @version 0.1.2 // @namespace https://hideoo.dev/ // @description Adds preview links of tracked files in GitHub pull requests to the Astro and Starlight documentation. // @tag productivity // @license MIT // @author HiDeoo (https://github.com/hideoo) // @homepageURL https://github.com/HiDeoo/userscript-astro-docs-preview-links // @supportURL https://github.com/HiDeoo/userscript-astro-docs-preview-links/issues // @iconURL https://raw.githubusercontent.com/primer/octicons/refs/heads/main/icons/link-24.svg // // @match https://github.com/* // @run-at document-end // ==/UserScript== ;(function () { 'use strict' const docsPullRequestRegex = /^https:\/\/github\.com\/withastro\/(?:docs|starlight)\/pull\/\d+\/?$/ const validExtensionsRegex = /\.mdx?$/ /** * @param {Element[]} comments * @param {string} author * @returns {Element[]} */ function getCommentsFromAuthor(comments, author) { return comments.filter((comment) => isElementTextEqual(comment.querySelector('.timeline-comment-header .author'), author), ) } /** * @param {Element | null} element * @param {string} text * @returns {boolean} */ function isElementTextEqual(element, text) { return element instanceof HTMLElement && element.innerText === text } /** * @param {string} path * @returns {string} */ function stripExtension(path) { const periodIndex = path.lastIndexOf('.') return path.slice(0, periodIndex > -1 ? periodIndex : undefined) } /** * @param {string} locale * @returns {boolean} */ function isRootLocale(locale) { return location.href.startsWith('https://github.com/withastro/starlight/pull/') && locale === 'en' } /** * @param {string} url * @returns {boolean} */ function isDocsPullRequestPage(url) { return docsPullRequestRegex.test(url) } /** * @returns {boolean} */ function addLinks() { const comments = [...document.querySelectorAll('.pull-discussion-timeline .timeline-comment')] const deployComment = getCommentsFromAuthor(comments, 'netlify').find((comment) => { const title = comment.querySelector('.comment-body > h3:first-child') return title instanceof HTMLElement && title.innerText.includes('Deploy Preview for') }) if (!deployComment) return false const deployPreviewRow = [...deployComment.querySelectorAll('.comment-body td')].find((cell) => isElementTextEqual(cell, '😎 Deploy Preview'), )?.parentElement if (!deployPreviewRow) return false const deployPreviewUrl = deployPreviewRow.querySelector('a')?.href if (!deployPreviewUrl) return false const lunariaComment = getCommentsFromAuthor(comments, 'astrobot-houston').find((comment) => isElementTextEqual(comment.querySelector('.comment-body > h2:first-child'), 'Lunaria Status Overview'), ) if (!lunariaComment) return false const trackedFileTable = lunariaComment.querySelector('.comment-body > h3 ~ markdown-accessiblity-table') if (!trackedFileTable) return true const trackedFilesRows = [...trackedFileTable.querySelectorAll('table > tbody > tr')] /** @type {Set<string>} */ const trackedFiles = new Set() for (const row of trackedFilesRows) { const [locale, path] = [...row.querySelectorAll('td')].map((cell) => cell.innerText) if (!locale || !path || !validExtensionsRegex.test(path)) continue trackedFiles.add(`${isRootLocale(locale) ? '' : `${locale}/`}${stripExtension(path)}/`) } if (trackedFiles.size === 0) return true const linksRow = document.createElement('tr') const linksTitleCell = document.createElement('td') linksTitleCell.setAttribute('align', 'center') linksTitleCell.setAttribute('style', 'vertical-align: top;') linksTitleCell.innerText = '⚡ Tracked links' const linksContentCell = document.createElement('td') linksContentCell.append( ...[...trackedFiles].flatMap((pathname, index) => { if (pathname.endsWith('index/')) pathname = pathname.replace(/index\/$/, '') const link = document.createElement('a') link.href = deployPreviewUrl + pathname link.innerText = `/${pathname}` return index < trackedFiles.size - 1 ? [link, document.createElement('br')] : link }), ) linksRow.append(linksTitleCell, linksContentCell) deployPreviewRow.after(linksRow) return true } function handlePagination() { const paginationButton = /** @type {HTMLButtonElement?} */ ( document.querySelector('.ajax-pagination-form button.ajax-pagination-btn') ) if (!paginationButton) return paginationButton.form?.addEventListener( 'click', () => paginationButton.form?.addEventListener('page:loaded', run, { once: true }), { once: true }, ) } function run() { if (isDocsPullRequestPage(location.href)) { const didAddLinks = addLinks() if (!didAddLinks) handlePagination() } } run() document.addEventListener('turbo:render', run) })()