GitHub Commits Time Formatter

GitHub 仓库的 /commits 页面,每个 commit 的头像前面 增加时间 YYYY-MM-DD HH:MM

  1. // ==UserScript==
  2. // @name GitHub Commits Time Formatter
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.2
  5. // @description GitHub 仓库的 /commits 页面,每个 commit 的头像前面 增加时间 YYYY-MM-DD HH:MM
  6. // @author You
  7. // @match https://github.com/*/commits/*
  8. // @license MIT
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
  10. // @grant none
  11. // ==/UserScript==
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // 从 title 属性中提取并格式化时间的函数
  17. function formatDateTimeFromTitle(title) {
  18. const parts = title.match(/(\w+ \d+, \d{4}), (\d+:\d+ [AP]M)/);
  19. if (!parts) {
  20. return "";
  21. }
  22. const [, datePart, timePart] = parts;
  23. const fullDateStr = `${datePart} ${timePart}`;
  24. const date = new Date(fullDateStr);
  25. const year = date.getFullYear();
  26. const month = String(date.getMonth() + 1).padStart(2, '0');
  27. const day = String(date.getDate()).padStart(2, '0');
  28. const hours = String(date.getHours()).padStart(2, '0');
  29. const minutes = String(date.getMinutes()).padStart(2, '0');
  30. return `${year}-${month}-${day} ${hours}:${minutes}`;
  31. }
  32.  
  33. // 处理时间元素的函数
  34. function processTimeElements() {
  35. const relativeTimeElements = document.querySelectorAll('relative-time[class^="sc-aXZVg"][class*="pl-1"]');
  36. relativeTimeElements.forEach((timeElement) => {
  37. const title = timeElement.getAttribute('title');
  38. if (title) {
  39. const formattedTime = formatDateTimeFromTitle(title);
  40. const targetDiv = timeElement.closest('li').querySelector('div[data-testid="author-avatar"]');
  41. if (targetDiv) {
  42. // 检查是否已经存在格式化后的时间元素
  43. const existingTimeSpan = targetDiv.previousElementSibling;
  44. if (existingTimeSpan && existingTimeSpan.tagName === 'SPAN' && existingTimeSpan.textContent === formattedTime) {
  45. return; // 如果已经存在则跳过
  46. }
  47. // 创建新的 <span> 元素来显示格式化后的时间
  48. const timeSpan = document.createElement('span');
  49. timeSpan.textContent = formattedTime;
  50. timeSpan.style.whiteSpace = 'pre'; // 确保空格显示正常
  51. timeSpan.style.marginRight = '10px'; // 添加右边距
  52. // 在指定的 div 元素前面插入新的 <span> 元素
  53. targetDiv.parentNode.insertBefore(timeSpan, targetDiv);
  54. }
  55. }
  56. });
  57. }
  58.  
  59. // 节流函数
  60. function throttle(func, delay) {
  61. let timer = null;
  62. return function() {
  63. if (!timer) {
  64. func.apply(this, arguments);
  65. timer = setTimeout(() => {
  66. timer = null;
  67. }, delay);
  68. }
  69. };
  70. }
  71.  
  72. // 初始化 MutationObserver
  73. function initMutationObserver() {
  74. const throttledProcessTimeElements = throttle(processTimeElements, 500); // 每 500 毫秒最多执行一次
  75. const observer = new MutationObserver((mutationsList) => {
  76. for (const mutation of mutationsList) {
  77. if (mutation.type === 'childList') {
  78. throttledProcessTimeElements();
  79. }
  80. }
  81. });
  82.  
  83. const targetNode = document.body;
  84. const config = { childList: true, subtree: true };
  85.  
  86. observer.observe(targetNode, config);
  87. }
  88.  
  89. // 主函数
  90. function main() {
  91. processTimeElements();
  92. initMutationObserver();
  93. }
  94.  
  95. main();
  96.  
  97. })();

QingJ © 2025

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