GitHub Label Color Picker

A userscript that adds a color picker to the label color input

目前为 2022-10-24 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name GitHub Label Color Picker
  3. // @version 1.0.9
  4. // @description A userscript that adds a color picker to the label color input
  5. // @license MIT
  6. // @author Rob Garrison
  7. // @contributor darkred
  8. // @namespace https://github.com/Mottie
  9. // @include https://github.com/*
  10. // @run-at document-idle
  11. // @grant GM_addStyle
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_registerMenuCommand
  15. // @require https://greasyfork.org/scripts/23181-colorpicker/code/colorPicker.js?version=147862
  16. // @require https://greasyfork.org/scripts/398877-utils-js/code/utilsjs.js?version=1079637
  17. // @icon https://github.githubassets.com/pinned-octocat.svg
  18. // @supportURL https://github.com/Mottie/GitHub-userscripts/issues
  19. // ==/UserScript==
  20. /* global jsColorPicker $ on */
  21. (() => {
  22. "use strict";
  23.  
  24. GM_addStyle(`
  25. div.cp-app { margin:100px 0 0 -7px; z-index:10; }
  26. .js-new-label-color-icon { pointer-events:none; }
  27. .js-new-label-color-icon.color-scale-black { color:#000 !important; }
  28. `);
  29.  
  30. function addPicker() {
  31. if ($(".js-new-label-color")) {
  32. jsColorPicker(".js-new-label-color-input", {
  33. customBG: "#222",
  34. noAlpha: true,
  35. renderCallback: function(colors) {
  36. const input = this && this.input;
  37. if (input) {
  38. updateSwatch(input, colors);
  39. }
  40. }
  41. });
  42. }
  43. }
  44.  
  45. function updateSwatch(input, colors) {
  46. input.value = colors.HEX;
  47. const colorStyle = calcStyle(colors.rgb, colors.hsl);
  48.  
  49. // Update color swatch next to input
  50. const inputSwatch = $(".js-new-label-color", input.closest("dd"));
  51. inputSwatch.style = colorStyle;
  52.  
  53. // Update label preview
  54. const labelSwatch = $(
  55. ".js-label-preview .IssueLabel--big",
  56. input.closest(".Box-row")
  57. );
  58. labelSwatch.style = colorStyle;
  59. }
  60.  
  61. function calcStyle(rgb, hsl) {
  62. // GitHub adds CSS variables to the wrapper
  63. // rgb is used as the foreground (text) color
  64. // hsl is used to calculate a color variant for the background
  65. const multiplier = { h: 360, s: 100, l: 100 };
  66. const fg = Object.entries(rgb).map(
  67. ([c, v]) => `--label-${c}:${(v * 255).toFixed(0)}`
  68. );
  69. const bg = Object.entries(hsl).map(
  70. ([c, v]) => `--label-${c}:${(v * multiplier[c]).toFixed(0)}`
  71. );
  72. // --label-r:255; --label-g:255; --label-b:255; --label-h:15; --label-s:0; --label-l:100;
  73. return `${fg.join("; ")}; ${bg.join("; ")}`;
  74. }
  75.  
  76. /* replace colorPicker storage */
  77. window.ColorPicker.docCookies = (key, val) => {
  78. if (typeof val === "undefined") {
  79. return GM_getValue(key);
  80. }
  81. GM_setValue(key, val);
  82. };
  83.  
  84. /* colorPickerMemosNoAlpha *MUST* follow this format
  85. "'rgba(83,25,231,1)','rgba(86,66,66,1)','rgba(22,20,223,1)'"
  86. */
  87. function convertColorsToRgba(values) {
  88. // see http://stackoverflow.com/a/26196012/145346
  89. return values
  90. .replace(/['"]/g, "")
  91. .split(/\s*,(?![^()]*(?:\([^()]*\))?\))\s*/g)
  92. .map(val => {
  93. const rgb = hexToRgb(val);
  94. if (rgb) {
  95. return `'rgba(${rgb.r},${rgb.g},${rgb.b},1)'`;
  96. } else if (rgb === null && val.indexOf("rgba(") > -1) {
  97. // allow adding rgba() definitions
  98. return`'${val}'`;
  99. }
  100. })
  101. .filter(Boolean)
  102. .join(",");
  103. }
  104.  
  105. // Modified code from http://stackoverflow.com/a/5624139/145346
  106. // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
  107. const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
  108. function hexToRgb(hex) {
  109. const modHex = hex.replace(shorthandRegex, (_, r, g, b) => {
  110. return r + r + g + g + b + b;
  111. });
  112. const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(modHex);
  113. return result ? {
  114. r: parseInt(result[1], 16),
  115. g: parseInt(result[2], 16),
  116. b: parseInt(result[3], 16)
  117. } : null;
  118. }
  119.  
  120. // Add GM options
  121. GM_registerMenuCommand(
  122. "Set label ColorPicker swatches (8 HEX or RGBA Max)",
  123. () => {
  124. const colors = GM_getValue("colorPickerMemosNoAlpha", "#000000,#ffffff"),
  125. val = prompt("Set label default colors (8 max):", colors);
  126. if (val !== null && typeof val === "string") {
  127. GM_setValue("colorPickerMemosNoAlpha", convertColorsToRgba(val));
  128. }
  129. }
  130. );
  131.  
  132. on(document.body, "click", event => {
  133. // initialize if "Edit" or "New label" button clicked
  134. // because "Save changes" updates the entire item
  135. if (
  136. event.target?.matches(".js-edit-label, .js-details-target")
  137. ) {
  138. addPicker();
  139. }
  140. });
  141. addPicker();
  142. })();