Jira Enhance

Enhance Jira Features: Copy Commit Message

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey, Greasemonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey किंवा Violentmonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला Tampermonkey यासारखे एक्स्टेंशन इंस्टॉल करावे लागेल..

ही स्क्रिप्ट इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्क्रिप्ट व्यवस्थापक एक्स्टेंशन इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्क्रिप्ट व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला Stylus सारखे एक्स्टेंशन इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

ही स्टाईल इंस्टॉल करण्यासाठी तुम्हाला एक युझर स्टाईल व्यवस्थापक इंस्टॉल करावे लागेल.

(माझ्याकडे आधीच युझर स्टाईल व्यवस्थापक आहे, मला इंस्टॉल करू द्या!)

// ==UserScript==
// @name         Jira Enhance
// @namespace    https://github.com/codeshareman/useful-scripts.git
// @version      2025-05-11
// @description  Enhance Jira Features: Copy Commit Message
// @author       zz_captain_zz
// @match        // TODO: match your jira issue link
// @include      // TODO: match your jira issue link
// @icon         data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAAHXRFWHRqaXJhLXN5c3RlbS1pbWFnZS10eXBlAGF2YXRhcuQCGmEAAAT1SURBVHja7Vj7U1R1FPevSBNQJMCSSHkoOmZOWtlMU2mvGWd0QlkhGZCXUmrAIpSigwiRmRmgJCK+ISRQWBgVSwFXWFNEIclYaJNdiKWFZTndc5u1/d5z9+6rH2zmnpkzs7Nz7rnn8/2ex+fcKfA/lykyABmADEAGIAOQAcgAniQAV5rvgDKrgtHcfZUwOTnptNPR0THI+vwk8aPV6h/bDA2NQlV1C3y26xQkbymBxJRiyMyugNNnf4JHj/50H8ANdQ94+UbDU17rH6uX7wZo73jgtNOaH9qY51HDF30MRqMJxsbMkJtXCf7PxREbq870/wjSM4/z9i4DMJsnYPkKJXG6c/dppwHExn9Dnk9XloPJZAZFzH6Y6r3ebvC2uur93TA8POp6DeQXVhNni5ZsA4vFcRoZhowQNC+ReXaqdxSob/bA/q9r+d/OBG/VNA64VPaKAui6pwVf/42Mo6d9FFx6dTsEUFunJkG8/GoGf5IhC7Yw/3vPioaMHcf59Lzb1QcHDtaC3+xYxiYwKB60/XrXAFgsFv76hIFgkTmSuIRD5Lm8/CpQt/8C02duYP7fllZGmkPx4QZyS7UX1K630SPfNdJCXJgqmUZ6wwi8EJbM3twMBfT0DICqSUPS6uqPncRH/4ABvARAS482uQ6gr28QAufEExDXW+/ZdVZ38Saxf+udXTzothvd3A0oGADXrncRHwO/G/iuZ+vjfE2b6wDwatcpCklAn2Ycs+tsU9K3xL74sOqf4jYYITgkSdDZzhAf5RWXmRSaFRjL36Bbk7jq+xYSUChXiNjLSffhApwXvpkUqe1QytlzhgnON2AjV7h10Kcd5E/+xMlmeDZ4E+MjIblIcohKAtDrRyA4NJmAaL5Kc/difTuxW7uugLH5yzTOzYGvYJpPFKkTYdrgrMD0wxg84kI44oWBpW4tJXYJKUUkgLPnroHYoMS0mvFMjGT/x/Y64s4kFsqlK7eJ87nhKTzf+ZfXGCFkPps+AXPiRAPAbrTwxa0OBxgOQ2ypExMWzwBgoBEiL8RArFLf0EHoAeauUCq5mvLxiyHps3R5Gry0LI2kEWo2RwqlQDhFp3dknyCOMbWskrSZTbNp3NRubLrF+NDphgnFwJvQ3OrlA0T99eEfsPK9HIGvKKiuafUMgEbTS6YjFjfSA54iCNInNIJ2qoOHLjA207mTx7khFJ1uCILmJpJZgrXjNoDx8Ql45fVMcgs44htUHaSrKLkCFMoHq3MZmwWLPxFtx2KNAweq7S7h1kZW8OV5AgBpMy4j7O4QzU9doURwAdvavf1ujt137eW4kzCNfr790DMAD3p1hIzh0MFuI2SeYkUXxvEoW7vFS7fzpFFMtqeXkULvvPubZwCQz4gxVKHmf1Et+vybq3Yydn4cRbjTSYNC8CveyHKaUru01CMrlAoeqQHyejHZk3uOTurIAn43tj2kklIVaRhWQugxABzrwj5uqyu5vLbHW+5390OAyB687DUl7N1XBUUlDRAde4BPFyHtxkX/P/usEinCUK2KO4SU4MYlDNCRRkYVgslOt3ILADJUsb0WWx3uEFKCt4M1InWLtlxq9Zo8GPSUzIlRi9nPx4uelLPS0nof1nyYT7qaVcMiUrmUqufmj/nJ/jKHNaVq1EBZ+SUoOaLi2St2Jlc+osnfRmUAMgAZgAxABiADkAF4IH8DHJ9HZXOkDL4AAAAASUVORK5CYII=
// @grant        GM.setClipboard
// @grant        GM.addStyle
// @run-at       document-end
// @license      MIT

// ==/UserScript==

const DELAY_MILLI_SECONDS = 1000;

const style = `
  #jira-enhance-copy-button {
    position: relative;
    color: var(--jira-issue-status-default-color);
    background-color: var(--jira-issue-status-default-bgcolor);
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 3px 12px;
    margin-top: 10px;
    border-radius: 3.01px;
    cursor: pointer;
    transition: background-color 0.3s ease;
  }

 #jira-enhance-copy-button:hover {
    background-color: var(--jira-issue-status-hover-default-bgcolor);
 }

 #jira-enhance-copy-button-message {
    position: absolute;
    padding: 4px 8px;
    border-radius: 3px;
    bottom: -100%;
    left: 50%;
    transform: translateX(-50%);
    background-color: #e6e6e6;
    font-size: 12px;
    color: var(--jira-issue-status-default-color);
    z-index: 1000;
    color: var(--jira-issue-status-default-color);;
    opacity: 0;
    transition: opacity 0.3s ease;
 }
 #jira-enhance-copy-button-message.active {
    opacity: 1;
 }
 #jira-enhance-copy-button-message::before {
    content: "";
    position: absolute;
    top: -5px;
    left: 50%;
    width: 0;
    height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-bottom: 5px solid#e6e6e6;
    transform: translateX(-50%);
 }
`;

GM.addStyle(style);

(function () {
  "use strict";

  function runScript(
    config = {
      summaryId: "#summary-val",
      issueKeyId: "#key-val",
      toolbarPrimaryId:
        "#stalker > div > div.command-bar > div > div > div > div.aui-toolbar2-primary",
    }
  ) {
    try {
      // Your code here...
      const $summary = document.querySelector(config.summaryId);
      const $issue_key = document.querySelector(config.issueKeyId);
      const $toolbar_primary = document.querySelector(config.toolbarPrimaryId);

      const metadata = {
        issue_key: $issue_key.textContent,
        summary: $summary.textContent,
      };

      /**
       * Git Enhance
       */
      const git = {
        generateCommitMsg(issueKey, summary, options = { prefix: "fix:" }) {
          const { prefix } = options;
          const commitMsg = `${prefix} [${issueKey}] ${summary}`;
          return commitMsg;
        },
      };

      /**
       * Jira Enhance
       */
      const jira = {
        getIssueInfo(metadata) {
          return {
            issue_key: metadata.issue_key,
            summary: metadata.summary,
            commit_msg: git.generateCommitMsg(
              metadata.issue_key,
              metadata.summary
            ),
          };
        },
        logIssueInfo(info) {
          group("=== Jira Enhance Message ===");
          Object.entries(info).forEach(([key, value]) => {
            log("info")(key, value);
          });
          groupEnd();
        },
      };

      const issueInfo = jira.getIssueInfo(metadata);

      // 添加复制按钮
      appendCopyButton($toolbar_primary, issueInfo.commit_msg);

      // 打印issue信息
      jira.logIssueInfo(issueInfo);
    } catch (error) {
      log("error")(error);
    }
  }

  window.addEventListener("load", () => {
    runScript();
  });
})();

// DOM
function appendCopyButton(target, copyText = "") {
  if (!target) {
    throw new Error("target is required");
  }
  const $button = createToolbarButton({
    onClick: () => {
      GM.setClipboard(JSON.stringify(copyText));
      const $message = $button.querySelector(
        "#jira-enhance-copy-button-message"
      );
      $message.classList.add("active");
      setTimeout(() => {
        $message.classList.remove("active");
      }, DELAY_MILLI_SECONDS);
      log("info")("Copy to clipboard");
    },
  });
  appendToolbarButton(target, $button);
}

function createToolbarButton(buttonProperties) {
  const defaultProperties = {
    id: "jira-enhance-copy-button",
    icon: "fa-solid fa-clipboard",
    text: "复制提交信息",
    onClick: () => {},
  };
  const { id, icon, text, onClick } = {
    ...defaultProperties,
    ...buttonProperties,
  };
  const $button = document.createElement("div");
  const $message = document.createElement("div");
  $button.id = id;
  $button.innerHTML = `<i class="${icon}"></i> ${text}`;
  $button.addEventListener("click", onClick);
  $message.id = `${id}-message`;
  $message.innerHTML = `Copied`;
  $button.appendChild($message);
  return $button;
}

function appendToolbarButton(target, button) {
  if (!target) {
    throw new Error("target is required");
  }
  if (!button) {
    throw new Error("button is required");
  }
  const isCopyButtonExist = target.querySelector(`#${button.id}`);
  if (isCopyButtonExist) return;
  target.appendChild(button);
}

/**
 * Common Utils
 */
function debounce(
  fn,
  options = { delay: DELAY_MILLI_SECONDS, immediate: false }
) {
  const { delay, immediate } = options;
  let timer = null;

  return function (...args) {
    if (timer) {
      clearTimeout(timer);
    }

    if (immediate && !timer) {
      fn.apply(this, args);
      timer = setTimeout(() => {
        timer = null;
      }, delay);
    } else {
      timer = setTimeout(() => {
        fn.apply(this, args);
        timer = null;
      }, delay);
    }
  };
}

function throttle(
  fn,
  options = { delay: DELAY_MILLI_SECONDS, immediate: false }
) {
  const { delay, immediate } = options;
  let timer = null;
  let isFirst = true;
  return function (...args) {
    if (timer) return;
    if (immediate && isFirst) {
      fn.apply(this, args);
      isFirst = false;
    }
    timer = setTimeout(() => {
      fn.apply(this, args);
      timer = null;
    }, delay);
  };
}

/**
 * Console Utils
 */
function log(type) {
  const typeColor = {
    info: "#0000FF",
    warn: "#FFA500",
    error: "#FF0000",
  };
  return function (...args) {
    const color = typeColor[type] || "#000";
    const style = `background-color: ${color}; color: #fff;`;
    console.error(`%c[${type}]`, style, ...args);
  };
}

function group(...args) {
  const style = `background-color:#cf1fdc; color: #fff;`;
  console.group.call(console, `%c${args[0]}`, style, ...args.slice(1));
}

function groupEnd(...args) {
  console.groupEnd.call(console, ...args);
}