Github - Inactive Development Warning

display yellow banner if project's last commit over 6 months ago and red banner if over 1 year ago

  1. // ==UserScript==
  2. // @name Github - Inactive Development Warning
  3. // @namespace https://openuserjs.org/users/zachhardesty7
  4. // @author Zach Hardesty <zachhardesty7@users.noreply.github.com> (https://github.com/zachhardesty7)
  5. // @description display yellow banner if project's last commit over 6 months ago and red banner if over 1 year ago
  6. // @copyright 2019-2025, Zach Hardesty (https://zachhardesty.com/)
  7. // @license GPL-3.0-only; http://www.gnu.org/licenses/gpl-3.0.txt
  8. // @version 1.4.0
  9.  
  10. // @homepageURL https://github.com/zachhardesty7/tamper-monkey-scripts-collection/raw/master/github-inactive-dev-warning.user.js
  11. // @homepage https://github.com/zachhardesty7/tamper-monkey-scripts-collection/raw/master/github-inactive-dev-warning.user.js
  12. // @homepageURL https://openuserjs.org/scripts/zachhardesty7/Github_-_Inactive_Development_Warning
  13. // @homepage https://openuserjs.org/scripts/zachhardesty7/Github_-_Inactive_Development_Warning
  14. // @supportURL https://github.com/zachhardesty7/tamper-monkey-scripts-collection/issues
  15.  
  16.  
  17. // @match https://github.com/*/*
  18. // @require https://github.com/zachhardesty7/tamper-monkey-scripts-collection/raw/refs/tags/onElementReady@0.10.0/utils/onElementReady.js
  19. // ==/UserScript==
  20.  
  21. /* global onElementReady */
  22.  
  23. onElementReady(
  24. "[data-testid='latest-commit-details'] relative-time",
  25. { findOnce: false },
  26. (el) => {
  27. if (document.querySelector("#zh-inactive-dev-warning")) return;
  28.  
  29. const date = new Date(el.getAttribute("datetime") || "");
  30. const daysSinceLastCommit = (Date.now() - date.getTime()) / 1000 / 60 / 60 / 24;
  31. if (daysSinceLastCommit > 365) {
  32. const years = Math.floor(daysSinceLastCommit / 365);
  33. renderWarning(years);
  34. } else if (daysSinceLastCommit > 182.5) {
  35. renderCaution();
  36. }
  37. }
  38. );
  39.  
  40. function displayMessage(el) {
  41. const container = document.querySelector("#js-repo-pjax-container");
  42. if (container) {
  43. const existingBanner = document.querySelector("#zh-inactive-dev-warning");
  44. if (existingBanner) existingBanner.remove();
  45. container.insertAdjacentElement("beforebegin", el);
  46. }
  47. }
  48.  
  49. function getThemeSettings(isSevereWarning = false) {
  50. const colorMode = document.documentElement.getAttribute('data-color-mode');
  51. const darkTheme = document.documentElement.getAttribute('data-dark-theme');
  52. const isLight = colorMode === 'light' ||
  53. (colorMode === 'auto' && !window.matchMedia('(prefers-color-scheme: dark)').matches);
  54.  
  55. // Check for specific dark themes
  56. const isSoftDark = darkTheme === 'dark_dimmed';
  57. const isColorblindDark = darkTheme && (
  58. darkTheme.includes('colorblind') ||
  59. darkTheme.includes('tritanopia') ||
  60. darkTheme.includes('deuteranopia') ||
  61. darkTheme.includes('protanopia')
  62. );
  63.  
  64. if (isSevereWarning) {
  65. // Red tint for severe warning (1+ years)
  66. if (isLight) {
  67. return {
  68. bg: '#ffebee',
  69. text: '#b71c1c',
  70. border: '#ef9a9a',
  71. warningColor: '#c62828'
  72. };
  73. } else if (isColorblindDark) {
  74. return {
  75. bg: '#231010',
  76. text: '#E47F7F',
  77. border: '#753535',
  78. warningColor: '#ff5252'
  79. };
  80. } else if (isSoftDark) {
  81. return {
  82. bg: '#3a1e1e',
  83. text: '#ff9e9e',
  84. border: '#874242',
  85. warningColor: '#ff5252'
  86. };
  87. } else {
  88. // Default dark theme
  89. return {
  90. bg: '#231010',
  91. text: '#E47F7F',
  92. border: '#753535',
  93. warningColor: '#ff5252'
  94. };
  95. }
  96. }
  97.  
  98. // Original colors for caution (6+ months)
  99. if (isLight) {
  100. return {
  101. bg: '#fef7c5',
  102. text: '#1e2227',
  103. border: '#edd789',
  104. warningColor: '#996606'
  105. };
  106. } else if (isColorblindDark) {
  107. return {
  108. bg: '#262014',
  109. text: '#f0f0f0',
  110. border: '#614612',
  111. warningColor: '#d19826'
  112. };
  113. } else if (isSoftDark) {
  114. return {
  115. bg: '#36342c',
  116. text: '#f0f0f0',
  117. border: '#665122',
  118. warningColor: '#c58f29'
  119. };
  120. } else {
  121. // Default dark theme
  122. return {
  123. bg: '#262014',
  124. text: '#f0f0f0',
  125. border: '#614612',
  126. warningColor: '#d19826'
  127. };
  128. }
  129. }
  130.  
  131. function createWarningIcon(theme) {
  132. const icon = document.createElement("div");
  133. icon.innerHTML = `
  134. <svg width="16" height="16" viewBox="0 0 16 16" fill="${theme.warningColor}" style="vertical-align: middle;">
  135. <path d="M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z"></path>
  136. </svg>
  137. `;
  138. return icon;
  139. }
  140.  
  141. function renderWarning(years) {
  142. const theme = getThemeSettings(true);
  143. const banner = document.createElement("div");
  144. banner.id = "zh-inactive-dev-warning";
  145. banner.setAttribute("style", `
  146. background-color: ${theme.bg};
  147. color: ${theme.text};
  148. height: 50px;
  149. margin-bottom: 20px;
  150. display: flex;
  151. justify-content: center;
  152. align-items: center;
  153. font-size: 18px;
  154. border-top: 1px solid ${theme.border};
  155. border-bottom: 1px solid ${theme.border};
  156. padding: 0 16px;
  157. position: relative;
  158. `);
  159.  
  160. const message = document.createElement("div");
  161. message.style.flex = "1";
  162. message.style.textAlign = "center";
  163. message.style.display = "flex";
  164. message.style.alignItems = "center";
  165. message.style.justifyContent = "center";
  166. message.style.gap = "8px";
  167.  
  168. message.appendChild(createWarningIcon(theme));
  169.  
  170. const text = document.createElement("span");
  171. text.textContent = `repo hasn't been updated in ${years} year${years > 1 ? 's' : ''}`;
  172. message.appendChild(text);
  173.  
  174. message.appendChild(createWarningIcon(theme));
  175.  
  176. banner.appendChild(message);
  177.  
  178. displayMessage(banner);
  179. }
  180.  
  181. function renderCaution() {
  182. const theme = getThemeSettings();
  183. const banner = document.createElement("div");
  184. banner.id = "zh-inactive-dev-warning";
  185. banner.setAttribute("style", `
  186. background-color: ${theme.bg};
  187. color: ${theme.text};
  188. height: 50px;
  189. margin-bottom: 20px;
  190. display: flex;
  191. justify-content: center;
  192. align-items: center;
  193. font-size: 18px;
  194. border-top: 1px solid ${theme.border};
  195. border-bottom: 1px solid ${theme.border};
  196. padding: 0 16px;
  197. position: relative;
  198. `);
  199.  
  200. const message = document.createElement("div");
  201. message.style.flex = "1";
  202. message.style.textAlign = "center";
  203. message.style.display = "flex";
  204. message.style.alignItems = "center";
  205. message.style.justifyContent = "center";
  206. message.style.gap = "8px";
  207.  
  208. message.appendChild(createWarningIcon(theme));
  209.  
  210. const text = document.createElement("span");
  211. text.textContent = "repo hasn't been updated in 6+ months";
  212. message.appendChild(text);
  213.  
  214. message.appendChild(createWarningIcon(theme));
  215.  
  216. banner.appendChild(message);
  217.  
  218. displayMessage(banner);
  219. }