您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a "Copy commit reference" button to every commit page on GitLab.
当前为
- // ==UserScript==
- // @name GitLab: copy commit reference
- // @namespace https://andrybak.dev
- // @license AGPL-3.0-only
- // @version 3
- // @description Adds a "Copy commit reference" button to every commit page on GitLab.
- // @homepageURL https://gitlab.com/andrybak/copy-commit-reference-userscript
- // @supportURL https://gitlab.com/andrybak/copy-commit-reference-userscript/-/issues
- // @author Andrei Rybak
- // @match https://gitlab.com/*/-/commit/*
- // @match https://invent.kde.org/*/-/commit/*
- // @match https://gitlab.gnome.org/*/-/commit/*
- // @icon https://gitlab.com/assets/favicon-72a2cad5025aa931d6ea56c3201d1f18e68a8cd39788c7c80d5b2b82aa5143ef.png
- // @require https://cdn.jsdelivr.net/gh/rybak/userscript-libs@e86c722f2c9cc2a96298c8511028f15c45180185/waitForElement.js
- // @require https://cdn.jsdelivr.net/gh/rybak/copy-commit-reference-userscript@c7f2c3b96fd199ceee46de4ba7eb6315659b34e3/copy-commit-reference-lib.js
- // @grant none
- // ==/UserScript==
- /*
- * Copyright (C) 2023 Andrei Rybak
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
- (function () {
- 'use strict';
- /*
- * Implementation for GitLab.
- *
- * Example URLs for testing:
- * - https://gitlab.com/andrybak/resoday/-/commit/b82824ec6dc3f14c3711104bf0ffd792c86d19ba
- * - https://invent.kde.org/education/kturtle/-/commit/8beecff6f76a4afc74879c46517d00657d8426f9
- */
- class GitLab extends GitHosting {
- static #HEADER_SELECTOR = 'main#content-body .page-content-header > .header-main-content';
- getTargetSelector() {
- return GitLab.#HEADER_SELECTOR;
- }
- getButtonTagName() {
- return 'button'; // like GitLab's "Copy commit SHA"
- }
- wrapButton(button) {
- const copyShaButtonIcon = document.querySelector(`${GitLab.#HEADER_SELECTOR} > button > svg[data-testid="copy-to-clipboard-icon"]`);
- const icon = copyShaButtonIcon.cloneNode(true);
- button.replaceChildren(icon); // is just icon enough?
- button.classList.add('btn-sm', 'btn-default', 'btn-default-tertiary', 'btn-icon', 'btn', 'btn-clipboard', 'gl-button');
- button.setAttribute('data-toggle', 'tooltip'); // this is needed to have a fancy tooltip in style of other UI
- button.setAttribute('data-placement', 'bottom'); // this is needed so that the fancy tooltip appears below the button
- button.style = 'border: 1px solid darkgray;';
- button.title = this.getButtonText() + " to clipboard";
- return button;
- }
- getFullHash() {
- const copyShaButton = document.querySelector(`${GitLab.#HEADER_SELECTOR} > button`);
- return copyShaButton.getAttribute('data-clipboard-text');
- }
- getDateIso(hash) {
- // careful not to select <time> tag for "Committed by"
- const authorTimeTag = document.querySelector(`${GitLab.#HEADER_SELECTOR} > .d-sm-inline + time`);
- return authorTimeTag.getAttribute('datetime').slice(0, 'YYYY-MM-DD'.length);
- }
- getCommitMessage(hash) {
- /*
- * Even though vast majority will only need `subj`, gather everything and
- * let downstream code handle paragraph splitting.
- */
- const subj = document.querySelector('.commit-box .commit-title').innerText;
- const maybeBody = document.querySelector('.commit-box .commit-description');
- if (maybeBody == null) { // some commits have only a single-line message
- return subj;
- }
- const body = maybeBody.innerText;
- return subj + '\n\n' + body;
- }
- addButtonContainerToTarget(target, buttonContainer) {
- const authoredSpanTag = target.querySelector('span.d-sm-inline');
- target.insertBefore(buttonContainer, authoredSpanTag);
- // add spacer to make text "authored" not stick to the button
- target.insertBefore(document.createTextNode(" "), authoredSpanTag);
- }
- /*
- * GitLab has a complex interaction with library ClipboardJS:
- *
- * - https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/helpers/button_helper.rb#L31-68
- * - https://gitlab.com/gitlab-org/gitlab/-/blob/master/app/assets/javascripts/behaviors/copy_to_clipboard.js#L63-94
- *
- * and the native tooltips are even more complicated.
- */
- createCheckmark() {
- const checkmark = super.createCheckmark();
- checkmark.style.left = 'calc(100% + 0.3rem)';
- checkmark.style.lineHeight = '1.5';
- checkmark.style.padding = '0.5rem 1.5rem';
- checkmark.style.textAlign = 'center';
- checkmark.style.width = 'auto';
- checkmark.style.borderRadius = '3px';
- checkmark.style.fontSize = '0.75rem';
- checkmark.style.fontFamily = '"Segoe UI", Roboto, "Noto Sans", Ubuntu, Cantarell, "Helvetica Neue", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"';
- if (document.body.classList.contains('gl-dark')) {
- checkmark.style.backgroundColor = '#dcdcde';
- checkmark.style.color = '#1f1e24';
- } else {
- checkmark.style.backgroundColor = '#000';
- checkmark.style.color = '#fff';
- }
- return checkmark;
- }
- /**
- * @returns {string}
- */
- static #getIssuesUrl() {
- const issuesLink = document.querySelector('aside a[href$="/issues"]');
- return issuesLink.href;
- }
- convertPlainSubjectToHtml(plainTextSubject) {
- if (!plainTextSubject.includes('#')) {
- return plainTextSubject;
- }
- const issuesUrl = GitLab.#getIssuesUrl();
- return plainTextSubject.replaceAll(/#([0-9]+)/g, `<a href="${issuesUrl}/\$1">#\$1</a>`);
- }
- }
- CopyCommitReference.runForGitHostings(new GitLab());
- })();