// ==UserScript==
// @name GitHub Label Color Picker
// @version 1.0.9
// @description A userscript that adds a color picker to the label color input
// @license MIT
// @author Rob Garrison
// @contributor darkred
// @namespace https://github.com/Mottie
// @match https://github.com/*
// @run-at document-idle
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @require https://greasyfork.org/scripts/23181-colorpicker/code/colorPicker.js?version=147862
// @require https://greasyfork.org/scripts/398877-utils-js/code/utilsjs.js?version=1079637
// @icon https://github.githubassets.com/pinned-octocat.svg
// @supportURL https://github.com/Mottie/GitHub-userscripts/issues
// ==/UserScript==
/* global jsColorPicker $ on */
(() => {
"use strict";
GM_addStyle(`
div.cp-app { margin:100px 0 0 -7px; z-index:10; }
.js-new-label-color-icon { pointer-events:none; }
.js-new-label-color-icon.color-scale-black { color:#000 !important; }
`);
function addPicker() {
if ($(".js-new-label-color")) {
jsColorPicker(".js-new-label-color-input", {
customBG: "#222",
noAlpha: true,
renderCallback: function(colors) {
const input = this && this.input;
if (input) {
updateSwatch(input, colors);
}
}
});
}
}
function updateSwatch(input, colors) {
input.value = colors.HEX;
const colorStyle = calcStyle(colors.rgb, colors.hsl);
// Update color swatch next to input
const inputSwatch = $(".js-new-label-color", input.closest("dd"));
inputSwatch.style = colorStyle;
// Update label preview
const labelSwatch = $(
".js-label-preview .IssueLabel--big",
input.closest(".Box-row")
);
labelSwatch.style = colorStyle;
}
function calcStyle(rgb, hsl) {
// GitHub adds CSS variables to the wrapper
// rgb is used as the foreground (text) color
// hsl is used to calculate a color variant for the background
const multiplier = { h: 360, s: 100, l: 100 };
const fg = Object.entries(rgb).map(
([c, v]) => `--label-${c}:${(v * 255).toFixed(0)}`
);
const bg = Object.entries(hsl).map(
([c, v]) => `--label-${c}:${(v * multiplier[c]).toFixed(0)}`
);
// --label-r:255; --label-g:255; --label-b:255; --label-h:15; --label-s:0; --label-l:100;
return `${fg.join("; ")}; ${bg.join("; ")}`;
}
/* replace colorPicker storage */
window.ColorPicker.docCookies = (key, val) => {
if (typeof val === "undefined") {
return GM_getValue(key);
}
GM_setValue(key, val);
};
/* colorPickerMemosNoAlpha *MUST* follow this format
"'rgba(83,25,231,1)','rgba(86,66,66,1)','rgba(22,20,223,1)'"
*/
function convertColorsToRgba(values) {
// see http://stackoverflow.com/a/26196012/145346
return values
.replace(/['"]/g, "")
.split(/\s*,(?![^()]*(?:\([^()]*\))?\))\s*/g)
.map(val => {
const rgb = hexToRgb(val);
if (rgb) {
return `'rgba(${rgb.r},${rgb.g},${rgb.b},1)'`;
} else if (rgb === null && val.indexOf("rgba(") > -1) {
// allow adding rgba() definitions
return`'${val}'`;
}
})
.filter(Boolean)
.join(",");
}
// Modified code from http://stackoverflow.com/a/5624139/145346
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
function hexToRgb(hex) {
const modHex = hex.replace(shorthandRegex, (_, r, g, b) => {
return r + r + g + g + b + b;
});
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(modHex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
// Add GM options
GM_registerMenuCommand(
"Set label ColorPicker swatches (8 HEX or RGBA Max)",
() => {
const colors = GM_getValue("colorPickerMemosNoAlpha", "#000000,#ffffff"),
val = prompt("Set label default colors (8 max):", colors);
if (val !== null && typeof val === "string") {
GM_setValue("colorPickerMemosNoAlpha", convertColorsToRgba(val));
}
}
);
on(document.body, "click", event => {
// initialize if "Edit" or "New label" button clicked
// because "Save changes" updates the entire item
if (
event.target?.matches(".js-edit-label, .js-details-target")
) {
addPicker();
}
});
addPicker();
})();