Greasy Fork 还支持 简体中文。

GitHub Freshness

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

目前為 2025-02-19 提交的版本,檢視 最新版本

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