Gitea: copy commit reference

Adds a "Copy commit reference" button to every commit page on Gitea websites.

目前为 2023-10-06 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Gitea: copy commit reference
  3. // @namespace https://andrybak.dev
  4. // @version 2
  5. // @license AGPL-3.0-only
  6. // @author Andrei Rybak
  7. // @description Adds a "Copy commit reference" button to every commit page on Gitea websites.
  8. // @icon https://about.gitea.com/favicon.ico
  9. // @homepageURL https://github.com/rybak/copy-commit-reference-userscript
  10. // @supportURL https://github.com/rybak/copy-commit-reference-userscript/issues
  11. // @match https://gitea.com/*/commit/*
  12. // @match https://git.plastiras.org/*/commit/*
  13. // @match https://projects.blender.org/*/commit/*
  14. // @require https://cdn.jsdelivr.net/gh/rybak/userscript-libs@e86c722f2c9cc2a96298c8511028f15c45180185/waitForElement.js
  15. // @require https://cdn.jsdelivr.net/gh/rybak/copy-commit-reference-userscript@c7f2c3b96fd199ceee46de4ba7eb6315659b34e3/copy-commit-reference-lib.js
  16. // @grant none
  17. // ==/UserScript==
  18.  
  19. /*
  20. * Copyright (C) 2023 Andrei Rybak
  21. *
  22. * This program is free software: you can redistribute it and/or modify
  23. * it under the terms of the GNU Affero General Public License as published
  24. * by the Free Software Foundation, version 3.
  25. *
  26. * This program is distributed in the hope that it will be useful,
  27. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  28. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  29. * GNU Affero General Public License for more details.
  30. *
  31. * You should have received a copy of the GNU Affero General Public License
  32. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  33. */
  34.  
  35. (function () {
  36. 'use strict';
  37.  
  38. /**
  39. * Implementation for Gitea, which is a fork of Gogs.
  40. *
  41. * Example URLs for testing:
  42. * - https://git.plastiras.org/Tha_14/Antidote/commit/f84a08f1ac5312acc9ccedff25e6957e575f03ff
  43. */
  44. class Gitea extends GitHosting {
  45. getTargetSelector() {
  46. return '.commit-header h3 + div';
  47. }
  48.  
  49. wrapButtonContainer(container) {
  50. container.style.marginRight = '0.5rem';
  51. return container;
  52. }
  53.  
  54. wrapButton(button) {
  55. /*
  56. * Mimicking Gitea's "Browse Source" button, but without class 'primary',
  57. * because there shouldn't be too many primary buttons. Class 'basic' is
  58. * for styling like most of the buttons on the commit pages.
  59. */
  60. button.classList.add('ui', 'tiny', 'button', 'basic');
  61. const maybeNativeIcon = document.querySelector('.svg.octicon-copy');
  62. if (maybeNativeIcon) {
  63. /*
  64. * Some instances of Gitea don't have the copy icons,
  65. * e.g. https://projects.blender.org
  66. */
  67. const icon = maybeNativeIcon.cloneNode(true);
  68. icon.style.verticalAlign = 'middle';
  69. icon.style.marginTop = '-4px';
  70. button.insertBefore(document.createTextNode(" "), button.childNodes[0]);
  71. button.insertBefore(icon, button.childNodes[0]);
  72. }
  73. return button;
  74. }
  75.  
  76. addButtonContainerToTarget(target, buttonContainer) {
  77. // to the left of Gitea's "Browse Source" button
  78. target.insertBefore(buttonContainer, target.querySelector('.ui.primary.tiny.button'));
  79. }
  80.  
  81. /**
  82. * Styles adapted from GitHub's CSS classes ".tooltipped::before"
  83. * and ".tooltipped-s::before".
  84. *
  85. * @returns {HTMLElement}
  86. */
  87. #createTooltipTriangle() {
  88. const triangle = document.createElement('div');
  89. triangle.style.position = 'absolute';
  90. triangle.style.zIndex = '1000001';
  91. triangle.style.bottom = '-15px'; // not -16px to look better at different zoom levels
  92. triangle.style.left = '14px'; // to align with .left of `checkmark`
  93. triangle.style.height = '0';
  94. triangle.style.width = '0';
  95. /*
  96. * Borders connect at 45° angle => when only top border is colored,
  97. * it's a trapezoid. But with width=0, the bottom edge of trapezoid
  98. * has length 0, so it's a downwards triangle.
  99. *
  100. * bgColor from Gitea CSS classes
  101. */
  102. triangle.style.border = '8px solid transparent';
  103. triangle.style.borderTopColor = 'var(--color-tooltip-bg)';
  104. return triangle;
  105. }
  106.  
  107. createCheckmark() {
  108. const checkmark = super.createCheckmark();
  109. checkmark.style.left = '0.2rem'; // to put emoji right above the button's icon
  110. checkmark.style.bottom = 'calc(100% + 1.2rem)'; // to mimic native tooltips shown above the buttons
  111. /*
  112. * Look and feel from CSS classes of Tippy -- a library (?)
  113. * used by Gitea.
  114. */
  115. checkmark.style.zIndex = '9999';
  116. checkmark.style.backgroundColor = 'var(--color-tooltip-bg)';
  117. checkmark.style.color = 'var(--color-tooltip-text)';
  118. checkmark.style.borderRadius = 'var(--border-radius)';
  119. checkmark.style.fontSize = '1rem';
  120. checkmark.style.padding = '.5rem 1rem';
  121. checkmark.appendChild(this.#createTooltipTriangle());
  122. return checkmark;
  123. }
  124.  
  125. getFullHash() {
  126. const browseButton = document.querySelector('.commit-header h3 + div > a');
  127. const lastSlashIndex = browseButton.href.lastIndexOf('/');
  128. return browseButton.href.slice(lastSlashIndex + 1);
  129. }
  130.  
  131. getDateIso(hash) {
  132. const timeTag = document.querySelector('#authored-time relative-time');
  133. return timeTag.datetime.slice(0, 'YYYY-MM-DD'.length);
  134. }
  135.  
  136. getCommitMessage(hash) {
  137. const subj = document.querySelector('.commit-summary').innerText;
  138. const bodyElement = document.querySelector('.commit-body');
  139. if (!bodyElement) {
  140. return subj;
  141. }
  142. const body = bodyElement.childNodes[0].innerText;
  143. return subj + '\n\n' + body;
  144. }
  145.  
  146. static #getIssuesUrl() {
  147. return document.querySelector('.header-wrapper > .ui.tabs.container > .tabular.menu.navbar a[href$="/issues"]').href;
  148. }
  149.  
  150. convertPlainSubjectToHtml(plainTextSubject, hash) {
  151. if (!plainTextSubject.includes('#')) {
  152. return plainTextSubject;
  153. }
  154. const issuesUrl = Gitea.#getIssuesUrl();
  155. return plainTextSubject.replaceAll(/#([0-9]+)/g, `<a href="${issuesUrl}/\$1">#\$1</a>`);
  156. }
  157. }
  158.  
  159. CopyCommitReference.runForGitHostings(new Gitea());
  160. })();