8chan Reports Page Enhancer

Enhances the usability and appearance of the 8chan board moderation reports page with a dynamic, compact grid layout, smaller image thumbnails, combined report labels in a single div, and styled Moderate buttons.

当前为 2025-04-24 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name 8chan Reports Page Enhancer
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.5
  5. // @description Enhances the usability and appearance of the 8chan board moderation reports page with a dynamic, compact grid layout, smaller image thumbnails, combined report labels in a single div, and styled Moderate buttons.
  6. // @author Grok
  7. // @match *://8chan.moe/openReports.js*
  8. // @grant GM_addStyle
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. // Inject CSS styles
  16. GM_addStyle(`
  17. /* General Layout */
  18. body {
  19. font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
  20. background-color: #f5f6f5;
  21. color: #333;
  22. line-height: 1.4;
  23. margin: 0;
  24. font-size: 0.9em;
  25. }
  26.  
  27. .titleWrapper {
  28. max-width: 100%;
  29. margin: 15px auto;
  30. padding: 0 10px;
  31. box-sizing: border-box;
  32. }
  33.  
  34. .titleFieldset {
  35. background: #fff;
  36. border: 1px solid #ddd;
  37. border-radius: 6px;
  38. padding: 15px;
  39. box-shadow: 0 1px 3px rgba(0,0,0,0.05);
  40. }
  41.  
  42. legend {
  43. font-size: 1.3em;
  44. font-weight: 600;
  45. color: #222;
  46. padding: 0 8px;
  47. }
  48.  
  49. /* Report Cells */
  50. #reportDiv {
  51. display: flex !important;
  52. flex-wrap: wrap !important;
  53. gap: 10px;
  54. margin-top: 15px;
  55. justify-content: space-between;
  56. width: 100%;
  57. box-sizing: border-box;
  58. }
  59.  
  60. .reportCell {
  61. background: #fff;
  62. border: 1px solid #e0e0e0;
  63. border-radius: 5px;
  64. padding: 10px;
  65. transition: box-shadow 0.2s;
  66. flex: 1 1 calc(50% - 8px);
  67. box-sizing: border-box;
  68. min-width: 250px;
  69. font-size: 0.85em;
  70. display: flex;
  71. flex-direction: column;
  72. }
  73.  
  74. .reportCell:hover {
  75. box-shadow: 0 3px 6px rgba(0,0,0,0.1);
  76. }
  77.  
  78. /* Combined Div */
  79. .reportCell .combined-div {
  80. display: flex;
  81. flex-wrap: nowrap;
  82. gap: 12px;
  83. align-items: center;
  84. font-size: 0.9em;
  85. margin-bottom: 5px;
  86. white-space: nowrap;
  87. }
  88.  
  89. .reportCell .combined-div span {
  90. display: inline-flex;
  91. align-items: center;
  92. }
  93.  
  94. /* Hide Reports and Category labels as fallback */
  95. .reportCell label:has(.totalLabel),
  96. .reportCell label.categoryDiv {
  97. display: none !important;
  98. }
  99.  
  100. .boardLabel, .totalLabel, .categoryLabel {
  101. font-weight: 500;
  102. color: #1a73e8;
  103. }
  104.  
  105. .reasonLabel {
  106. background: #f1f3f4;
  107. padding: 3px 6px;
  108. border-radius: 3px;
  109. display: inline-block;
  110. font-size: 0.9em;
  111. }
  112.  
  113. /* Checkbox Styles */
  114. .closureCheckbox {
  115. margin-right: 6px;
  116. vertical-align: middle;
  117. width: 16px;
  118. height: 16px;
  119. cursor: pointer;
  120. accent-color: #1a73e8;
  121. box-shadow: 0 0 6px rgba(26, 115, 232, 0.6);
  122. }
  123.  
  124. .deletionCheckBox {
  125. margin-right: 6px;
  126. vertical-align: middle;
  127. width: 14px;
  128. height: 14px;
  129. cursor: pointer;
  130. accent-color: #d32f2f;
  131. }
  132.  
  133. .link {
  134. color: #d32f2f;
  135. font-weight: 500;
  136. text-decoration: none;
  137. padding: 3px 6px;
  138. border-radius: 3px;
  139. font-size: 0.9em;
  140. background-color: #f0e0e0; /* Added background color */
  141. border: 1px solid #e03030; /* Added border stroke */
  142. transition: background-color 0.2s, border-color 0.2s;
  143. }
  144.  
  145. .link:hover {
  146. background-color: #ffebee;
  147. border-color: #888; /* Darker border on hover */
  148. text-decoration: none;
  149. }
  150.  
  151. /* Post Content */
  152. .postingDiv {
  153. flex: 1;
  154. min-width: 0;
  155. }
  156.  
  157. .postingDiv .innerPost {
  158. background: #fafafa;
  159. padding: 8px;
  160. border-radius: 3px;
  161. margin-top: 8px;
  162. font-size: 0.9em;
  163. min-width: 150px;
  164. }
  165.  
  166. .labelBoard {
  167. font-size: 1em;
  168. color: #0288d1;
  169. margin: 0 0 8px;
  170. }
  171.  
  172. .postInfo {
  173. font-size: 0.85em;
  174. color: #555;
  175. white-space: normal;
  176. }
  177.  
  178. .labelId {
  179. padding: 2px 5px;
  180. border-radius: 3px;
  181. color: #fff;
  182. font-size: 0.85em;
  183. }
  184.  
  185. .panelIp, .panelASN, .panelBypassId {
  186. font-size: 0.8em;
  187. color: #666;
  188. margin-top: 4px;
  189. word-break: break-all;
  190. }
  191.  
  192. .divMessage {
  193. margin-top: 8px;
  194. font-size: 0.9em;
  195. color: #333;
  196. padding: 6px;
  197. background: #f5f5f5;
  198. border-radius: 3px;
  199. min-width: 150px;
  200. word-wrap: break-word;
  201. }
  202.  
  203. .quoteLink {
  204. color: #388e3c;
  205. text-decoration: none;
  206. font-weight: 500;
  207. font-size: 0.9em;
  208. }
  209.  
  210. .quoteLink:hover {
  211. text-decoration: underline;
  212. }
  213.  
  214. /* Uploads */
  215. .panelUploads {
  216. margin-top: 8px;
  217. display: flex;
  218. flex-wrap: wrap;
  219. }
  220.  
  221. .uploadCell {
  222. margin-bottom: 8px;
  223. flex: 0 0 auto;
  224. }
  225.  
  226. .imgLink img {
  227. border-radius: 3px;
  228. max-width: 60px !important;
  229. max-height: 60px !important;
  230. width: auto !important;
  231. height: auto !important;
  232. object-fit: contain;
  233. display: block;
  234. margin: 0;
  235. }
  236.  
  237. .originalNameLink, .nameLink {
  238. color: #0288d1;
  239. text-decoration: none;
  240. font-size: 0.85em;
  241. }
  242.  
  243. .originalNameLink:hover, .nameLink:hover {
  244. text-decoration: underline;
  245. }
  246.  
  247. .uploadDetails, .divHash, .sizeLabel, .dimensionLabel {
  248. font-size: 0.8em;
  249. color: #555;
  250. }
  251.  
  252. .unlinkLink, .unlinkAndDeleteLink {
  253. font-size: 0.85em;
  254. }
  255.  
  256. /* Forms */
  257. #filterForm, form[action="/closeReports.js"] {
  258. background: #f9f9f9;
  259. padding: 10px;
  260. border-radius: 5px;
  261. margin-bottom: 15px;
  262. }
  263.  
  264. #filterForm label, form[action="/closeReports.js"] label {
  265. display: block;
  266. margin-bottom: 8px;
  267. font-weight: 500;
  268. color: #444;
  269. font-size: 0.9em;
  270. }
  271.  
  272. #filterCategoriesDiv {
  273. margin: 8px 0;
  274. }
  275.  
  276. .categoryCheckbox {
  277. margin-right: 6px;
  278. width: 14px;
  279. height: 14px;
  280. }
  281.  
  282. input[type="text"], select {
  283. padding: 6px;
  284. border: 1px solid #ccc;
  285. border-radius: 3px;
  286. font-size: 0.9em;
  287. width: 180px;
  288. margin-left: 8px;
  289. }
  290.  
  291. input[type="text"]:focus, select:focus {
  292. border-color: #1a73e8;
  293. outline: none;
  294. box-shadow: 0 0 0 2px rgba(26,115,232,0.2);
  295. }
  296.  
  297. button, #filterSubmitButton, #closeReportsFormButton {
  298. background: #1a73e8;
  299. color: #fff;
  300. border: none;
  301. padding: 6px 12px;
  302. border-radius: 3px;
  303. font-size: 0.9em;
  304. cursor: pointer;
  305. transition: background 0.2s;
  306. }
  307.  
  308. button:hover, #filterSubmitButton:hover, #closeReportsFormButton:hover {
  309. background: #1565c0;
  310. }
  311.  
  312. /* Checkboxes */
  313. .closeReportsField {
  314. margin-right: 6px;
  315. vertical-align: middle;
  316. width: 14px;
  317. height: 14px;
  318. }
  319.  
  320. /* Navigation */
  321. .navHeader {
  322. background: #fff;
  323. border-bottom: 1px solid #ddd;
  324. padding: 8px 15px;
  325. position: sticky;
  326. top: 0;
  327. z-index: 1000;
  328. }
  329.  
  330. .navLinkSpan a {
  331. color: #0288d1;
  332. text-decoration: none;
  333. margin: 0 4px;
  334. font-size: 0.9em;
  335. }
  336.  
  337. .navLinkSpan a:hover {
  338. text-decoration: underline;
  339. }
  340.  
  341. /* Responsive Adjustments for More Columns */
  342. @media (min-width: 900px) {
  343. .reportCell {
  344. flex: 1 1 calc(33.33% - 8px);
  345. }
  346. }
  347.  
  348. @media (min-width: 1200px) {
  349. .reportCell {
  350. flex: 1 1 calc(25% - 8px);
  351. }
  352. }
  353.  
  354. @media (max-width: 600px) {
  355. .reportCell {
  356. flex: 1 1 100% !important;
  357. min-width: 100% !important;
  358. max-width: 100% !important;
  359. }
  360.  
  361. input[type="text"], select {
  362. width: 100%;
  363. }
  364.  
  365. .divMessage, .innerPost {
  366. min-width: 100% !important;
  367. }
  368.  
  369. .reportCell .combined-div {
  370. flex-direction: column;
  371. gap: 5px;
  372. align-items: flex-start;
  373. white-space: normal;
  374. }
  375. }
  376. `);
  377.  
  378. // JavaScript to combine labels into a single div
  379. function processReportCells() {
  380. document.querySelectorAll('.reportCell').forEach(cell => {
  381. const labels = cell.querySelectorAll('label:not([for])');
  382. if (labels.length >= 3) {
  383. const boardSpan = labels[0].querySelector('.boardLabel');
  384. const totalSpan = labels[1].querySelector('.totalLabel');
  385. const categorySpan = labels[2].querySelector('.categoryLabel');
  386.  
  387. if (boardSpan && totalSpan && categorySpan) {
  388. // Create new div
  389. const combinedDiv = document.createElement('div');
  390. combinedDiv.className = 'combined-div';
  391. combinedDiv.innerHTML = `
  392. <span class="boardLabel">/${boardSpan.textContent}/</span>
  393. Reports: <span class="totalLabel">${totalSpan.textContent}</span>
  394. Category: <span class="categoryLabel">${categorySpan.textContent}</span>
  395. `;
  396.  
  397. // Insert div and remove original labels
  398. cell.insertBefore(combinedDiv, labels[0]);
  399. labels[0].remove();
  400. labels[1].remove();
  401. labels[2].remove();
  402. }
  403. }
  404. });
  405. }
  406.  
  407. // Run initially
  408. document.addEventListener('DOMContentLoaded', processReportCells);
  409.  
  410. // Observe for dynamic content
  411. const observer = new MutationObserver((mutations) => {
  412. if (mutations.some(m => m.addedNodes.length)) {
  413. processReportCells();
  414. }
  415. });
  416. observer.observe(document.body, { childList: true, subtree: true });
  417. })();