GitHub Freshness

通过颜色高亮的方式,帮助你快速判断一个 GitHub 仓库是否在更新。

  1. // ==UserScript==
  2. // @name GitHub Freshness
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.1.5
  5. // @description 通过颜色高亮的方式,帮助你快速判断一个 GitHub 仓库是否在更新。
  6. // @author 向前 https://docs.rational-stars.top/ https://github.com/rational-stars/GitHub-Freshness https://home.rational-stars.top/
  7. // @license MIT
  8. // @icon https://raw.githubusercontent.com/rational-stars/picgo/refs/heads/main/avatar.jpg
  9. // @match https://github.com/*/*
  10. // @match https://github.com/search?*
  11. // @match https://github.com/*/*/tree/*
  12. // @require https://code.jquery.com/jquery-3.6.0.min.js
  13. // @require https://cdn.jsdelivr.net/npm/sweetalert2@11
  14. // @require https://cdn.jsdelivr.net/npm/@simonwep/pickr@1.9.1/dist/pickr.min.js
  15. // @require https://cdn.jsdelivr.net/npm/luxon@3.4.3/build/global/luxon.min.js
  16. // @grant GM_registerMenuCommand
  17. // @grant GM_setValue
  18. // @grant GM_getValue
  19. // @grant GM_addStyle
  20. // ==/UserScript==
  21.  
  22. ; (function () {
  23. // 引入 Luxon
  24. const DateTime = luxon.DateTime
  25. // 解析日期(指定格式和时区)
  26. ; ('use strict')
  27. // 引入 Pickr CSS
  28. GM_addStyle(`@import url('https://cdn.jsdelivr.net/npm/@simonwep/pickr@1.9.1/dist/themes/classic.min.css');`)
  29. GM_addStyle(`
  30. .swal2-popup.swal2-modal.swal2-show{
  31. color: #FFF;
  32. border-radius: 20px;
  33. background: #31b96c;
  34. box-shadow: 8px 8px 16px #217e49,
  35. -8px -8px 16px #41f48f;
  36. #swal2-title a{
  37. display: inline-block;
  38. height: 40px;
  39. margin-right: 10px;
  40. border-radius: 10px;
  41. overflow: hidden;
  42. color: #fff;
  43. }
  44. #swal2-title {
  45. display: flex !important;
  46. justify-content: center;
  47. align-items: center;
  48. }
  49. .row-box select {
  50. border:unset;
  51. border-radius: .15em;
  52. }
  53. .row-box {
  54. display: flex;
  55. margin: 25px;
  56. align-items: center;
  57. justify-content: space-between;
  58. }
  59. .row-box .swal2-input {
  60. height: 40px;
  61. }
  62. .row-box label {
  63. margin-right: 10px;
  64. }
  65. .row-box main input{
  66. background: rgba(15, 172, 83, 1);
  67. }
  68. .row-box main {
  69. display: flex;
  70. align-items: center;
  71. }
  72. .row-box main input{
  73. width: 70px;
  74. border: unset;
  75. box-shadow: unset;
  76. text-align: right;
  77. margin:0;
  78. }
  79. `)
  80. const PanelDom = `
  81. <div class="row-box">
  82. <label for="rpcPort">主题设置:</label>
  83. <main>
  84. <select tabindex="-1" id="THEME-select" class="swal2-input">
  85. <option value="light">light</option>
  86. <option value="dark">dark</option>
  87. </select>
  88. </main>
  89. </div>
  90. <div class="row-box">
  91. <label id="TIME_BOUNDARY-label" for="rpcPort">时间阈值:</label>
  92. <main>
  93. <input id="TIME_BOUNDARY-number" type="number" class="swal2-input" value="" maxlength="3" pattern="\d{1,3}">
  94. <select tabindex="-1" id="TIME_BOUNDARY-select" class="swal2-input">
  95. <option value="day">日</option>
  96. <option value="week">周</option>
  97. <option value="month">月</option>
  98. <option value="year">年</option>
  99. </select>
  100. </main>
  101. </div>
  102. <div class="row-box">
  103. <div>
  104. <label id="BGC-label">背景颜色:</label>
  105. <input type="checkbox" id="BGC-enabled">
  106. </div>
  107. <main>
  108. <span id="BGC-highlight-color-value">
  109. <div id="BGC-highlight-color-pickr"></div>
  110. </span>
  111. <span id="BGC-grey-color-value">
  112. <div id="BGC-grey-color-pickr"></div>
  113. </span>
  114. </main>
  115. </div>
  116. <div class="row-box">
  117. <div>
  118. <label id="FONT-label">字体颜色:</label>
  119. <input type="checkbox" id="FONT-enabled">
  120. </div>
  121. <main>
  122. <span id="FONT-highlight-color-value">
  123. <div id="FONT-highlight-color-pickr"></div>
  124. </span>
  125. <span id="FONT-grey-color-value">
  126. <div id="FONT-grey-color-pickr"></div>
  127. </span>
  128. </main>
  129. </div>
  130.  
  131. <div class="row-box">
  132. <div>
  133. <label id="DIR-label">文件夹颜色:</label>
  134. <input type="checkbox" id="DIR-enabled">
  135. </div>
  136. <main>
  137. <span id="DIR-highlight-color-value">
  138. <div id="DIR-highlight-color-pickr"></div>
  139. </span>
  140. <span id="DIR-grey-color-value">
  141. <div id="DIR-grey-color-pickr"></div>
  142. </span>
  143. </main>
  144. </div>
  145. <div class="row-box">
  146. <div>
  147. <label id="TIME_FORMAT-label">时间格式化:</label>
  148. <input type="checkbox" id="TIME_FORMAT-enabled">
  149. </div>
  150. </div>
  151. <div class="row-box">
  152. <div>
  153. <label id="SORT-label">文件排序:</label>
  154. <input type="checkbox" id="SORT-enabled">
  155. </div>
  156. <main>
  157. <select tabindex="-1" id="SORT-select" class="swal2-input">
  158. <option value="asc">时间正序</option>
  159. <option value="desc">时间倒序</option>
  160. </select>
  161. </main>
  162. </div>
  163.  
  164. <div class="row-box">
  165. <label for="rpcPort">当前主题:</label>
  166. <main>
  167. <select tabindex="-1" id="CURRENT_THEME-select" class="swal2-input">
  168. <option value="auto">auto</option>
  169. <option value="light">light</option>
  170. <option value="dark">dark</option>
  171. </select>
  172. </main>
  173. </div>
  174.  
  175. <div class="row-box">
  176. <div>
  177. <label id="AWESOME-label"><a target="_blank" href="https://github.com/settings/tokens">AWESOME token: </a></label>
  178. <input type="checkbox" id="AWESOME-enabled">
  179. </div>
  180. <main>
  181. <input id="AWESOME_TOKEN" type="password" class="swal2-input" value="">
  182. </main>
  183. </div>
  184. <p>当复选框切换到未勾选状态时,部分设置不会立即生效需重新刷新页面。AWESOME谨慎开启详细说明请看 <a target="_blank" href="https://docs.rational-stars.top/diy-settings/awesome-xxx.html"> 文档ℹ️</><p/>
  185.  
  186. `
  187. // === 配置项 ===
  188. let default_THEME = {
  189. BGC: {
  190. highlightColor: 'rgba(15, 172, 83, 1)', // 高亮颜色(示例:金色)
  191. greyColor: 'rgba(245, 245, 245, 0.24)', // 灰色(示例:深灰)
  192. isEnabled: true, // 是否启用背景色
  193. },
  194. TIME_BOUNDARY: {
  195. number: 30, // 时间阈值(示例:30)
  196. select: 'day', // 可能的值: "day", "week", "month", "year"
  197. },
  198. FONT: {
  199. highlightColor: 'rgba(252, 252, 252, 1)', // 文字高亮颜色(示例:橙红色)
  200. greyColor: 'rgba(0, 0, 0, 1)', // 灰色(示例:标准灰)
  201. isEnabled: true, // 是否启用字体颜色
  202. },
  203. DIR: {
  204. highlightColor: 'rgba(15, 172, 83, 1)', // 目录高亮颜色(示例:道奇蓝)
  205. greyColor: 'rgba(154, 154, 154, 1)', // 灰色(示例:暗灰)
  206. isEnabled: true, // 是否启用文件夹颜色
  207. },
  208. SORT: {
  209. select: 'desc', // 排序方式(可能的值:"asc", "desc")
  210. isEnabled: true, // 是否启用排序
  211. },
  212. AWESOME: {
  213. isEnabled: false, // AWESOME项目是否启用
  214. },
  215. TIME_FORMAT: {
  216. isEnabled: true, // 是否启用时间格式化
  217. },
  218. }
  219. let CURRENT_THEME = GM_getValue('CURRENT_THEME', 'light')
  220. let AWESOME_TOKEN = GM_getValue('AWESOME_TOKEN', '')
  221. let THEME_TYPE = getThemeType()
  222. const config_JSON = JSON.parse(
  223. GM_getValue('config_JSON', JSON.stringify({ light: default_THEME }))
  224. )
  225. let THEME = config_JSON[THEME_TYPE] // 当前主题
  226.  
  227. const configPickr = {
  228. theme: 'monolith', // 使用经典主题
  229. components: {
  230. preview: true,
  231. opacity: true,
  232. hue: true,
  233. interaction: {
  234. rgba: true,
  235. // hex: true,
  236. // hsla: true,
  237. // hsva: true,
  238. // cmyk: true,
  239. input: true,
  240. clear: true,
  241. save: true,
  242. },
  243. },
  244. }
  245. function getThemeType() {
  246. let themeType = CURRENT_THEME
  247. if (CURRENT_THEME === 'auto') {
  248. if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
  249. // console.log('当前系统是深色模式 🌙')
  250. themeType = 'dark'
  251. } else {
  252. // console.log('当前系统是浅色模式 ☀️')
  253. themeType = 'light'
  254. }
  255. }
  256. window.console.log("%c✅向前:" + "如果您觉得GitHub-Freshness好用,点击下方 github链接 给个 star 吧。非常感谢你!!!\n[https://github.com/rational-stars/GitHub-Freshness]", "color:green")
  257. return themeType
  258. }
  259. function initPickr(el_default) {
  260. const pickr = Pickr.create({ ...configPickr, ...el_default })
  261. watchPickr(pickr)
  262. }
  263. function watchPickr(pickrName, el) {
  264. pickrName.on('save', (color, instance) => {
  265. pickrName.hide()
  266. })
  267. }
  268. const preConfirm = () => {
  269. // 遍历默认主题配置,更新设置
  270. const updated_THEME = getUpdatedThemeConfig(default_THEME)
  271. CURRENT_THEME = $('#CURRENT_THEME-select').val()
  272. AWESOME_TOKEN = $('#AWESOME_TOKEN').val()
  273. // 保存到油猴存储
  274. GM_setValue(
  275. 'config_JSON',
  276. JSON.stringify({
  277. ...config_JSON,
  278. [$('#THEME-select').val()]: updated_THEME,
  279. })
  280. )
  281. GM_setValue('CURRENT_THEME', CURRENT_THEME)
  282. GM_setValue('AWESOME_TOKEN', AWESOME_TOKEN)
  283. THEME = updated_THEME // 更新当前主题
  284. GitHub_Freshness(updated_THEME)
  285. Swal.fire({
  286. position: 'top-center',
  287. background: '#4ab96f',
  288. icon: 'success',
  289. title: '设置已保存',
  290. showConfirmButton: false,
  291. timer: 800,
  292. })
  293. }
  294. function initSettings(theme) {
  295. initPickr({
  296. el: '#BGC-highlight-color-pickr',
  297. default: theme.BGC.highlightColor,
  298. })
  299. initPickr({ el: '#BGC-grey-color-pickr', default: theme.BGC.greyColor })
  300. initPickr({
  301. el: '#FONT-highlight-color-pickr',
  302. default: theme.FONT.highlightColor,
  303. })
  304. initPickr({ el: '#FONT-grey-color-pickr', default: theme.FONT.greyColor })
  305. initPickr({
  306. el: '#DIR-highlight-color-pickr',
  307. default: theme.DIR.highlightColor,
  308. })
  309. initPickr({ el: '#DIR-grey-color-pickr', default: theme.DIR.greyColor })
  310. $('#THEME-select').val(getThemeType())
  311. $('#CURRENT_THEME-select').val(CURRENT_THEME)
  312. $('#AWESOME_TOKEN').val(AWESOME_TOKEN)
  313. handelData(theme)
  314. }
  315. function getUpdatedThemeConfig() {
  316. // 创建一个新的对象,用于存储更新后的主题配置
  317. let updatedTheme = {}
  318.  
  319. // 遍历默认主题配置,更新需要的键值
  320. for (const [themeKey, themeVal] of Object.entries(default_THEME)) {
  321. updatedTheme[themeKey] = {} // 创建每个主题键名的嵌套对象
  322.  
  323. for (let [key, val] of Object.entries(themeVal)) {
  324. switch (key) {
  325. case 'highlightColor':
  326. // 获取高亮颜色(示例:金色、道奇蓝等)
  327. val = $(`#${themeKey}-highlight-color-value .pcr-button`).css(
  328. '--pcr-color'
  329. )
  330. break
  331. case 'greyColor':
  332. // 获取灰色调(示例:深灰、标准灰、暗灰等)
  333. val = $(`#${themeKey}-grey-color-value .pcr-button`).css(
  334. '--pcr-color'
  335. )
  336. break
  337. case 'isEnabled':
  338. // 判断该主题项是否启用
  339. val = $(`#${themeKey}-enabled`).prop('checked')
  340. break
  341. case 'number':
  342. // 获取时间阈值(示例:30)
  343. val = $(`#${themeKey}-number`).val()
  344. break
  345. case 'select':
  346. // 获取时间单位(可能的值:"day", "week", "month")
  347. val = $(`#${themeKey}-select`).val()
  348. break
  349. default:
  350. // 其他未定义的情况
  351. break
  352. }
  353.  
  354. // 更新当前键名对应的值
  355. updatedTheme[themeKey][key] = val
  356. }
  357. }
  358.  
  359. return updatedTheme
  360. }
  361. function handelData(theme) {
  362. for (const [themeKey, themeVal] of Object.entries(theme)) {
  363. for (const [key, val] of Object.entries(themeVal)) {
  364. switch (key) {
  365. case 'highlightColor':
  366. $(`#${themeKey}-highlight-color-value .pcr-button`).css(
  367. '--pcr-color',
  368. val
  369. )
  370. break
  371. case 'greyColor':
  372. $(`#${themeKey}-grey-color-value .pcr-button`).css(
  373. '--pcr-color',
  374. val
  375. )
  376. break
  377. case 'isEnabled':
  378. $(`#${themeKey}-enabled`).prop('checked', val) // 选中
  379. break
  380. case 'number':
  381. $(`#${themeKey}-number`).val(val)
  382. break
  383. case 'select':
  384. $(`#${themeKey}-select`).val(val)
  385. break
  386. default:
  387. break
  388. }
  389. }
  390. }
  391. }
  392. // === 创建设置面板 ===
  393. function createSettingsPanel() {
  394. Swal.fire({
  395. title: `<a target="_blank" tabindex="-1" id="swal2-title-div" href="https://home.rational-stars.top/"><img src="https://raw.githubusercontent.com/rational-stars/picgo/refs/heads/main/avatar.jpg" alt="向前" width="40"></a><a tabindex="-1" target="_blank" href="https://github.com/rational-stars/GitHub-Freshness">GitHub Freshness 设置</a>`,
  396. html: PanelDom,
  397. focusConfirm: false,
  398. preConfirm,
  399. heightAuto: false,
  400. showCancelButton: true,
  401. cancelButtonText: '取消',
  402. confirmButtonText: '保存设置',
  403. })
  404.  
  405. initSettings(THEME)
  406.  
  407. $('#THEME-select').on('change', function () {
  408. let selectedTheme = $(this).val() // 获取选中的值
  409. let theme = config_JSON[selectedTheme]
  410. console.log('主题设置变更:', selectedTheme)
  411. handelData(theme)
  412. })
  413. }
  414. function setElementBGC(el, BGC, timeResult) {
  415. // el是元素 BGC是 theme BGC配置对象
  416. if (el.length && BGC.isEnabled) {
  417. if (timeResult) {
  418. el[0].style.setProperty('background-color', BGC.highlightColor, 'important')
  419. } else {
  420. el[0].style.setProperty('background-color', BGC.greyColor, 'important')
  421. }
  422. }
  423. }
  424. function setElementDIR(el, DIR, timeResult) {
  425. if (el.length && DIR.isEnabled) {
  426. if (timeResult) {
  427. el.attr('fill', DIR.highlightColor)
  428. } else {
  429. el.attr('fill', DIR.greyColor)
  430. }
  431. }
  432. }
  433. function setElementTIME_FORMAT(el, TIME_FORMAT, datetime) {
  434. if (TIME_FORMAT.isEnabled && el.css('display') !== 'none') {
  435. el.css('display', 'none')
  436. const formattedDate = formatDate(datetime)
  437. el.before(`<span>${formattedDate}</span>`)
  438. } else if (TIME_FORMAT.isEnabled === false) {
  439. el.parent().find('span').remove()
  440. el.css('display', 'block')
  441. }
  442. }
  443. // 设置字体颜色
  444. function setElementFONT(el, FONT, timeResult) {
  445. // el是元素 FONT是 theme FONT配置对象
  446. if (FONT.isEnabled) {
  447. if (timeResult) {
  448. el.css('color', FONT.highlightColor)
  449. } else {
  450. el.css('color', FONT.greyColor)
  451. }
  452. }
  453. }
  454. function handelTime(time, time_boundary, type = 'ISO8601') {
  455. const { number, select } = time_boundary
  456. let days = 0
  457. // 根据 select 计算相应的天数
  458. switch (select) {
  459. case 'day':
  460. days = number
  461. break
  462. case 'week':
  463. days = number * 7
  464. break
  465. case 'month':
  466. days = number * 30
  467. break
  468. case 'year':
  469. days = number * 365
  470. break
  471. default:
  472. console.warn('无效的时间单位:', select)
  473. return false // 遇到无效单位直接返回 false
  474. }
  475.  
  476. const now = new Date() // 当前时间
  477. const targetDate = new Date(now) // 复制当前时间
  478. targetDate.setDate(now.getDate() - days) // 计算指定时间范围的起点
  479. let inputDate = new Date(time) // 传入的时间转换为 Date 对象
  480. if (type === 'UTC') {
  481. // 解析日期(指定格式和时区)
  482. const dt = DateTime.fromFormat(time, "yyyy年M月d日 'GMT'Z HH:mm", {
  483. zone: 'UTC',
  484. }).setZone('Asia/Shanghai')
  485. const formattedDate = dt.toJSDate()
  486. inputDate = new Date(formattedDate)
  487. }
  488. return inputDate >= targetDate // 判断输入时间是否在 time_boundary 以内
  489. }
  490. // 检查 href 是否符合 https://github.com/*/* 但不是 https://github.com/*/*/ 格式
  491. const pattern = /^https:\/\/github\.com\/[^\/]+\/[^\/]+\/?$/;
  492. function isValidHref(href) {
  493. return pattern.test(href);
  494. }
  495. function toAPIUrl(href) {
  496. // 使用正则表达式从 href 中提取 owner 和 repo
  497. const githubPattern = /^https:\/\/github\.com\/([^\/]+)\/([^\/]+)/;
  498. const match = href.match(githubPattern);
  499. // 如果匹配成功,则生成 API URL
  500. if (match) {
  501. let owner = match[1]; // GitHub 仓库所有者
  502. let repo = match[2]; // GitHub 仓库名称
  503.  
  504. // 返回转换后的 GitHub API URL
  505. return 'https://api.github.com/repos/' + owner + '/' + repo;
  506. } else {
  507. console.log("无效的 GitHub 链接:", href);
  508. return null;
  509. }
  510. }
  511. // === 核心函数 ===
  512. function GitHub_FreshnessSearchPage(theme = THEME) {
  513. const elements = $('.Text__StyledText-sc-17v1xeu-0.hWqAbU')
  514. if (elements.length === 0) return console.log('没有找到日期元素')
  515. let themeType = getThemeType()
  516. elements.each(function () {
  517. const title = $(this).attr('title')
  518. if (title) {
  519. const timeResult = handelTime(title, theme.TIME_BOUNDARY, 'UTC')
  520. const BGC_element = $(this).closest(
  521. `.Box-sc-g0xbh4-0 .${themeType === 'dark' ? 'iwUbcA' : 'flszRz'}`
  522. )
  523. // 背景色
  524. setElementBGC(BGC_element, theme.BGC, timeResult)
  525. // 字体颜色
  526. setElementFONT($(this), theme.FONT, timeResult)
  527. // 时间格式化
  528. if (theme.TIME_FORMAT.isEnabled) {
  529. // 解析日期(指定格式和时区)
  530. const dt = DateTime.fromFormat(title, "yyyy年M月d日 'GMT'Z HH:mm", {
  531. zone: 'UTC',
  532. }).setZone('Asia/Shanghai')
  533.  
  534. // 格式化成 YYYY-MM-DD
  535. const formattedDate = dt.toFormat('yyyy-MM-dd')
  536. $(this).text(formattedDate)
  537. }
  538. }
  539. })
  540. }
  541. function GitHub_FreshnessAwesome(theme = THEME) {
  542. // 选择符合条件的 <a> 标签
  543. let elementsToObserve = [];
  544. $('.Box-sc-g0xbh4-0.csrIcr a').each(function () {
  545. let href = $(this).attr('href');
  546. // 只处理符合 href 条件的 <a> 标签
  547. if (isValidHref(href)) {
  548. elementsToObserve.push(this); // 存储符合条件的元素
  549. }
  550. });
  551.  
  552. // 使用 IntersectionObserver 监听元素是否进入/离开视口
  553. const observer = new IntersectionObserver(function (entries, observer) {
  554. entries.forEach(el => {
  555. const href = $(el.target).attr('href');
  556. const apiHref = toAPIUrl(href)
  557. if (el.isIntersecting && el.target.getAttribute('request') !== 'true' && apiHref) {
  558. $.ajax({
  559. url: apiHref, // API 地址
  560. method: 'GET', // 请求方式
  561. headers: {
  562. 'Authorization': `token ${AWESOME_TOKEN}` || '' // 替换为你的个人访问令牌
  563. },
  564. success: function (data) {
  565. const stars = data.stargazers_count; // 获取星标数
  566. const time = data.updated_at; // 获取星标数
  567. const timeResult = handelTime(time, theme.TIME_BOUNDARY);
  568. // 添加标签
  569. if (theme.AWESOME.isEnabled && el.target.getAttribute('request') !== 'true') {
  570. $(el.target).after(`<span class="stars" style="padding: 8px">★${stars}</span><span class="updated-at">📅${formatDate(time)}</span>`);
  571. el.target.setAttribute('request', 'true')
  572. }
  573. setElementBGC($(el.target), theme.BGC, timeResult)
  574. // 字体颜色
  575. setElementFONT($(el.target), theme.FONT, timeResult)
  576. $(el.target).css('padding', '0 12px')
  577. },
  578. error: function (err) {
  579. if (err.status === 403) {
  580. Swal.fire({
  581. position: 'top-center',
  582. icon: 'warning',
  583. title: '检测到AWESOME API 速率限制超出!',
  584. confirmButtonText: '查看详情',
  585. showConfirmButton: true,
  586. background: '#4ab96f',
  587. preConfirm: () => {
  588. window.open("https://home.rational-stars.top/", "_blank")
  589. }
  590. })
  591. }
  592. }
  593. });
  594.  
  595. } else {
  596. // console.log('元素离开视口:', href);
  597. }
  598. });
  599. }, { threshold: 0.5 }); // 当元素至少 50% 进入视口时触发回调
  600. // 开始监听所有符合条件的元素
  601. elementsToObserve.forEach(function (el) {
  602. observer.observe(el);
  603. });
  604.  
  605. }
  606. function GitHub_Freshness(theme = THEME) {
  607. const matchUrl = isMatchedUrl()
  608. if (!matchUrl) return
  609. if (matchUrl === 'matchSearchPage') return GitHub_FreshnessSearchPage(theme)
  610. const elements = $('.sc-aXZVg')
  611. if (elements.length === 0) return console.log('没有找到日期元素');
  612. console.log("向前🇨🇳 ====> GitHub_Freshness ====> elements:", elements.length)
  613.  
  614. let trRows = []
  615. elements.each(function () {
  616. const datetime = $(this).attr('datetime')
  617. if (datetime) {
  618. const timeResult = handelTime(datetime, theme.TIME_BOUNDARY)
  619. const trElement = $(this).closest('tr.react-directory-row')
  620. trRows.push(trElement[0])
  621. // 背景颜色和字体
  622. const BGC_element = $(this).closest('td')
  623. // 在 tr 元素中查找 SVG 元素
  624. const DIR_element = trElement.find('.icon-directory')
  625. const FILE_element = trElement.find('.color-fg-muted')
  626. // 背景色
  627. setElementBGC(BGC_element, theme.BGC, timeResult)
  628. // 文件夹颜色和文件图标
  629. setElementDIR(DIR_element, theme.DIR, timeResult)
  630. setElementDIR(FILE_element, theme.DIR, timeResult)
  631. // 时间格式化
  632. setElementTIME_FORMAT($(this), theme.TIME_FORMAT, datetime)
  633. // 字体颜色
  634. setElementFONT($(this).parent(), theme.FONT, timeResult)
  635. }
  636. })
  637. // 文件排序
  638. if (theme.SORT.isEnabled) {
  639. // 将 tr 元素按日期排序
  640. trRows.sort((a, b) => {
  641. // 获取 datetime 属性
  642. let dateA = new Date(a.querySelector('relative-time').getAttribute('datetime'));
  643. let dateB = new Date(b.querySelector('relative-time').getAttribute('datetime'));
  644. // 根据 isAscending 变量控制排序顺序
  645. return theme.SORT.select === 'asc' ? dateA - dateB : dateB - dateA;
  646. });
  647. $('.Box-sc-g0xbh4-0.fdROMU tbody').append(trRows);
  648. }
  649.  
  650. if (theme.AWESOME.isEnabled && $('#repo-title-component a').text().toLowerCase().includes('awesome')) {
  651. GitHub_FreshnessAwesome()
  652. }
  653. }
  654. function formatDate(isoDateString) {
  655. return DateTime.fromISO(isoDateString).toFormat('yyyy-MM-dd');
  656. }
  657. function isMatchedUrl() {
  658. const currentUrl = window.location.href
  659.  
  660. // 判断是否符合 @match 的 URL 模式
  661. const matchRepoPage =
  662. /^https:\/\/github\.com\/[^/]+\/[^/]+(?:\?.*)?$|^https:\/\/github\.com\/[^/]+\/[^/]+\/tree\/.+$/.test(
  663. currentUrl
  664. )
  665. // 判断是否符合 @match 的 URL 模式
  666. const matchSearchPage = /^https:\/\/github\.com\/search\?.*$/.test(
  667. currentUrl
  668. )
  669. // 如果当前是仓库页面,返回变量名
  670. if (matchRepoPage) return 'matchRepoPage'
  671.  
  672. // 如果当前是搜索页面,返回变量名
  673. if (matchSearchPage) return 'matchSearchPage'
  674.  
  675. // 如果没有匹配,返回 null 或空字符串
  676. return null
  677. }
  678.  
  679. function debounce(func, wait) {
  680. let timeout;
  681. return function (...args) {
  682. clearTimeout(timeout);
  683. timeout = setTimeout(() => func.apply(this, args), wait);
  684. };
  685. }
  686.  
  687. const runScript = debounce(() => {
  688. if (!isMatchedUrl()) return;
  689. GitHub_Freshness(); // 页面内容加载完成后执行
  690. }, 350); // 设置合适的延迟,避免频繁执行
  691. // 页面加载完成后执行
  692. window.addEventListener('load', () => {
  693. console.log("页面加载完成 => 执行 runScript");
  694. runScript(); // 页面加载完成后执行 GitHub_Freshness
  695. });
  696.  
  697. // 监听页面是否从不可见切换到可见
  698. document.addEventListener('visibilitychange', () => {
  699. if (document.visibilityState === 'visible') {
  700. console.log("页面切换到前台 => 执行 runScript");
  701. runScript(); // 页面切换到前台时执行
  702. }
  703. });
  704.  
  705. // 监听 pjax:end 事件,确保页面内容完全加载
  706. document.addEventListener('pjax:end', () => {
  707. console.log('GitHub PJAX 跳转,页面内容已加载');
  708. runScript(); // 页面内容加载完成后执行 GitHub_Freshness
  709. });
  710.  
  711. // 重写 history.pushState 和 history.replaceState 来处理 URL 变化
  712. (function (history) {
  713. const pushState = history.pushState;
  714. const replaceState = history.replaceState;
  715.  
  716. // 监听 pushState 事件,确保 URL 变化时执行
  717. history.pushState = function (state, title, url) {
  718. pushState.apply(history, arguments); // 调用原始的 pushState
  719. console.log('pushState 触发,URL 变化:', url);
  720. setTimeout(runScript, 350); // 页面内容加载完成后执行 runScript
  721. };
  722.  
  723. // 监听 replaceState 事件,确保 URL 变化时执行
  724. history.replaceState = function (state, title, url) {
  725. replaceState.apply(history, arguments); // 调用原始的 replaceState
  726. console.log('replaceState 触发,URL 变化:', url);
  727. setTimeout(runScript, 350); // 页面内容加载完成后执行 runScript
  728. };
  729.  
  730. // 监听浏览器的前进/后退按钮 (popstate)
  731. window.addEventListener('popstate', () => {
  732. console.log('popstate 触发,URL 变化:', window.location.href);
  733. setTimeout(runScript, 500); // 页面内容加载完成后执行 runScript
  734. });
  735. })(window.history);
  736. // === 初始化设置面板 ===
  737. // createSettingsPanel()
  738.  
  739. // === 使用油猴菜单显示/隐藏设置面板 ===
  740. GM_registerMenuCommand('⚙️ 设置面板', createSettingsPanel)
  741. // 监听主题变化
  742. window
  743. .matchMedia('(prefers-color-scheme: dark)')
  744. .addEventListener('change', (e) => {
  745. if (e.matches) {
  746. THEME = config_JSON['dark']
  747. // console.log('系统切换到深色模式 🌙')
  748. GitHub_Freshness(THEME)
  749. } else {
  750. THEME = config_JSON['light']
  751. // console.log('系统切换到浅色模式 ☀️')
  752. GitHub_Freshness(THEME)
  753. }
  754. })
  755. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址