From ab8eaf572bc5dc9c19ed66f2f27729a49f428878 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Wed, 17 Dec 2025 21:31:07 -0600
Subject: [PATCH 01/12] Create colors.js
---
extensions/LincolnX/colors.js | 999 ++++++++++++++++++++++++++++++++++
1 file changed, 999 insertions(+)
create mode 100644 extensions/LincolnX/colors.js
diff --git a/extensions/LincolnX/colors.js b/extensions/LincolnX/colors.js
new file mode 100644
index 0000000000..52715f5379
--- /dev/null
+++ b/extensions/LincolnX/colors.js
@@ -0,0 +1,999 @@
+// Name: Colors
+// ID: lxColors
+// Description: Adds a variety of new blocks that manipulates and utilizes colors.
+// By: LincolnX
+// License: MPL-2.0
+
+const { abs, round, floor, sqrt } = Math;
+
+const toDec = (hex) => parseInt(hex, 16);
+const toHex = (dec) => dec.toString(16);
+const limitHex = (hex, mi, ma) => toHex(Math.min(Math.max(toDec(hex), mi), ma));
+const clamp = (n, mi, ma) => Math.min(Math.max(n, mi), ma);
+const fixHex = (hex) => limitHex(hex, 0, 255).padStart(2, '0');
+const toFixHex = (dec) => fixHex(dec.toString(16));
+const lerp = (a, b, t) => a + (b - a) * t;
+function interpolateHexColorsHsv(color1, color2, factor) {
+ const hsv1 = hexToHsv(color1);
+ const hsv2 = hexToHsv(color2);
+ factor = Math.min(1, Math.max(0, factor));
+ // Handle hue interpolation for the shortest path around the color wheel
+ let h1 = hsv1.h;
+ let h2 = hsv2.h;
+ let hueDiff = h2 - h1;
+ if (hueDiff > 180) {
+ h1 += 360;
+ } else if (hueDiff < -180) {
+ h2 += 360;
+ }
+
+ const h = h1 + factor * (h2 - h1);
+ const s = hsv1.s + factor * (hsv2.s - hsv1.s);
+ const v = hsv1.v + factor * (hsv2.v - hsv1.v);
+
+ const interpolatedRgb = hsvToRgb({ h, s, v });
+ return rgbToHex(interpolatedRgb);
+}
+function overlayHex(hex1, hex2) {
+ let a = toDec(hex1) / 255;
+ let b = toDec(hex2) / 255;
+ let overlay;
+ if (a < 0.5) {
+ overlay = 2 * a * b;
+ } else {
+ overlay = 1 - (2 * (1 - a) * (1 - b));
+ }
+ return toFixHex(overlay * 255)
+}
+
+function hexToRgb(hex) {
+ let r = 0, g = 0, b = 0;
+ // Handle 3-digit shorthand
+ if (hex.length === 4) {
+ r = parseInt(hex[1] + hex[1], 16);
+ g = parseInt(hex[2] + hex[2], 16);
+ b = parseInt(hex[3] + hex[3], 16);
+ } else if (hex.length === 7) {
+ r = parseInt(hex.substring(1, 3), 16);
+ g = parseInt(hex.substring(3, 5), 16);
+ b = parseInt(hex.substring(5, 7), 16);
+ }
+ return { r, g, b };
+}
+function rgbToHex(rgb) {
+ const makeHex = c => Math.round(c).toString(16).padStart(2, '0');
+ return `#${makeHex(rgb.r)}${makeHex(rgb.g)}${makeHex(rgb.b)}`;
+}
+function hsvToRgb(h, s, v) {
+ var r, g, b, i, f, p, q, t;
+ if (arguments.length === 1) {
+ s = h.s, v = h.v, h = h.h;
+ }
+ h /= 360;
+ s /= 100;
+ v /= 100;
+ i = floor(h * 6);
+ f = h * 6 - i;
+ p = v * (1 - s);
+ q = v * (1 - f * s);
+ t = v * (1 - (1 - f) * s);
+ switch (i % 6) {
+ case 0: r = v, g = t, b = p; break;
+ case 1: r = q, g = v, b = p; break;
+ case 2: r = p, g = v, b = t; break;
+ case 3: r = p, g = q, b = v; break;
+ case 4: r = t, g = p, b = v; break;
+ case 5: r = v, g = p, b = q; break;
+ }
+ return {
+ r: round(r * 255),
+ g: round(g * 255),
+ b: round(b * 255)
+ };
+}
+function rgbToHsv(r, g, b) {
+ if (arguments.length === 1) {
+ g = r.g, b = r.b, r = r.r;
+ }
+ var max = Math.max(r, g, b), min = Math.min(r, g, b),
+ d = max - min,
+ h,
+ s = (max === 0 ? 0 : d / max),
+ v = max / 255;
+ switch (max) {
+ case min: h = 0; break;
+ case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break;
+ case g: h = (b - r) + d * 2; h /= 6 * d; break;
+ case b: h = (r - g) + d * 4; h /= 6 * d; break;
+ }
+ return {
+ h: h * 360,
+ s: s * 100,
+ v: v * 100
+ };
+}
+const hexToHsv = (hex) => rgbToHsv(hexToRgb(hex));
+function hsvToHex(h, s, v) {
+ if (arguments.length === 1) {
+ s = h.s, v = h.v, h = h.h;
+ }
+ return rgbToHex(hsvToRgb(h, s, v));
+}
+
+function hslToRgb(h, s, l) {
+ h /= 360;
+ s /= 100;
+ l /= 100;
+ let r, g, b;
+ if (s === 0) {
+ r = g = b = l; // achromatic
+ } else {
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ r = hueToRgb(p, q, h + 1/3);
+ g = hueToRgb(p, q, h);
+ b = hueToRgb(p, q, h - 1/3);
+ }
+ return {r: round(r * 255), g: round(g * 255), b: round(b * 255)};
+}
+
+function hueToRgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1/6) return p + (q - p) * 6 * t;
+ if (t < 1/2) return q;
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+}
+
+function channelToLinear(c) {
+ c /= 255;
+ return c <= 0.03928
+ ? c / 12.92
+ : ((c + 0.055) / 1.055) ** 2.4;
+}
+
+function relativeLuminance(hex) {
+ const { r, g, b } = hexToRgb(hex);
+ const R = channelToLinear(r);
+ const G = channelToLinear(g);
+ const B = channelToLinear(b);
+ return 0.2126 * R + 0.7152 * G + 0.0722 * B;
+}
+
+function contrastRatio(hex1, hex2) {
+ const L1 = relativeLuminance(hex1);
+ const L2 = relativeLuminance(hex2);
+ const light = Math.max(L1, L2);
+ const dark = Math.min(L1, L2);
+ return (light + 0.05) / (dark + 0.05);
+}
+
+
+
+function distanceBetweenHex(hex1, hex2) {
+ // convert Hex to RGB
+ const rgb1 = hexToRgb(hex1);
+ const rgb2 = hexToRgb(hex2);
+
+ // convert RGB to XYZ
+ const xyz1 = rgbToXyz(rgb1.r, rgb1.g, rgb1.b);
+ const xyz2 = rgbToXyz(rgb2.r, rgb2.g, rgb2.b);
+
+ // convert XYZ to Lab
+ const lab1 = xyzToLab(xyz1.x, xyz1.y, xyz1.z);
+ const lab2 = xyzToLab(xyz2.x, xyz2.y, xyz2.z);
+
+ // calculate Delta E 2000
+ return deltaE2000(lab1, lab2);
+}
+
+function rgbToXyz(r, g, b) {
+ r /= 255;
+ g /= 255;
+ b /= 255;
+
+ r = (r > 0.04045) ? ((r + 0.055) / 1.055) ** 2.4 : r / 12.92;
+ g = (g > 0.04045) ? ((g + 0.055) / 1.055) ** 2.4 : g / 12.92;
+ b = (b > 0.04045) ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92;
+
+ r *= 100;
+ g *= 100;
+ b *= 100;
+
+ // D65 white point reference primaries
+ const x = r * 0.4124 + g * 0.3576 + b * 0.1805;
+ const y = r * 0.2126 + g * 0.7152 + b * 0.0722;
+ const z = r * 0.0193 + g * 0.1192 + b * 0.9505;
+
+ return { x, y, z };
+}
+
+// converts XYZ values to CIELAB color space.
+function xyzToLab(x, y, z) {
+ // D65 white point values
+ const refX = 95.047;
+ const refY = 100.000;
+ const refZ = 108.883;
+
+ x /= refX;
+ y /= refY;
+ z /= refZ;
+
+ x = (x > 0.008856) ? x ** (1/3) : (7.787 * x) + 16/116;
+ y = (y > 0.008856) ? y ** (1/3) : (7.787 * y) + 16/116;
+ z = (z > 0.008856) ? z ** (1/3) : (7.787 * z) + 16/116;
+
+ const L = (116 * y) - 16;
+ const a = 500 * (x - y);
+ const b = 200 * (y - z);
+
+ return { L, a, b };
+}
+
+
+// calculates the Delta E 2000 difference between two Lab colors.
+// based on the implementation notes from Sharma et al..
+function deltaE2000(lab1, lab2) {
+ const kL = 1.0, kC = 1.0, kH = 1.0; // parametric factors often set to 1.0
+ const deg2rad = Math.PI / 180;
+ const rad2deg = 180 / Math.PI;
+
+ // extract Lab values
+ const L1 = lab1.L, a1 = lab1.a, b1 = lab1.b;
+ const L2 = lab2.L, a2 = lab2.a, b2 = lab2.b;
+
+ // calculate C*ab values (Chroma)
+ const C1 = Math.sqrt(a1*a1 + b1*b1);
+ const C2 = Math.sqrt(a2*a2 + b2*b2);
+ const CBar = (C1 + C2) / 2.0;
+
+ // calculate G (chroma correction factor)
+ const CBarPow7 = CBar ** 7;
+ const G = 0.5 * (1 - Math.sqrt(CBarPow7 / (CBarPow7 + 6103515625.0))); // 6103515625 = 25^7
+
+ // calculate a' and C' (lightness corrected a* and new chroma)
+ const a1Prime = a1 * (1 + G);
+ const a2Prime = a2 * (1 + G);
+ const C1Prime = Math.sqrt(a1Prime*a1Prime + b1*b1);
+ const C2Prime = Math.sqrt(a2Prime*a2Prime + b2*b2);
+ const CBarPrime = (C1Prime + C2Prime) / 2.0;
+ const DeltaCPrime = C2Prime - C1Prime;
+
+ // calculate h' (hue angle)
+ const h1Prime = (Math.atan2(b1, a1Prime) * rad2deg);
+ const h2Prime = (Math.atan2(b2, a2Prime) * rad2deg);
+
+ // normalize hue angles to 0-360 range
+ const normalizedH1 = h1Prime >= 0 ? h1Prime : (h1Prime + 360);
+ const normalizedH2 = h2Prime >= 0 ? h2Prime : (h2Prime + 360);
+
+ // calculate Delta h' (hue difference) and Delta H' (weighted hue difference)
+ let DeltaHPrime;
+ if (C1Prime * C2Prime === 0) {
+ DeltaHPrime = 0; // if one chroma is zero, hue difference is meaningless.
+ } else if (abs(normalizedH1 - normalizedH2) <= 180) {
+ DeltaHPrime = normalizedH2 - normalizedH1;
+ } else if ((normalizedH2 - normalizedH1) > 180) {
+ DeltaHPrime = (normalizedH2 - normalizedH1) - 360;
+ } else { // (h2 - h1) < -180
+ DeltaHPrime = (normalizedH2 - normalizedH1) + 360;
+ }
+
+ // convert Delta H' to a metric difference (ΔH')
+ const DeltaSmallHPrime = 2 * Math.sqrt(C1Prime * C2Prime) * Math.sin(DeltaHPrime * deg2rad / 2.0);
+
+ // calculate Delta L'
+ const DeltaLPrime = L2 - L1;
+
+ // calculate Average H' (HBarPrime)
+ let HBarPrime;
+ if (C1Prime * C2Prime === 0) {
+ HBarPrime = normalizedH1 + normalizedH2; // use sum as average if one is indeterminate
+ } else if (abs(normalizedH1 - normalizedH2) > 180) {
+ if ((normalizedH1 + normalizedH2) < 360) {
+ HBarPrime = (normalizedH1 + normalizedH2 + 360) / 2.0;
+ } else {
+ HBarPrime = (normalizedH1 + normalizedH2 - 360) / 2.0;
+ }
+ } else {
+ HBarPrime = (normalizedH1 + normalizedH2) / 2.0;
+ }
+
+ // calculate T (hue weighting function)
+ const T = 1.0 - 0.17 * Math.cos((HBarPrime - 30.0) * deg2rad)
+ + 0.24 * Math.cos((2.0 * HBarPrime) * deg2rad)
+ + 0.32 * Math.cos((3.0 * HBarPrime + 6.0) * deg2rad)
+ - 0.20 * Math.cos((4.0 * HBarPrime - 63.0) * deg2rad);
+
+ // calculate SL, SC, SH (weighting functions)
+ const SL = 1.0 + ((0.015 * ((HBarPrime - 27.5) ** 2) / (20.0 + ((HBarPrime - 27.5) ** 2))));
+ const SC = 1.0 + 0.045 * CBarPrime;
+ const SH = 1.0 + 0.015 * CBarPrime * T;
+
+ // calculate RT (rotation term)
+ const CBarPrimePow7 = CBarPrime ** 7;
+ const RT = -2.0 * Math.sin(HBarPrime * deg2rad)
+ * Math.sqrt(CBarPrimePow7 / (CBarPrimePow7 + 6103515625.0));
+
+ // calculate the final Delta E 2000 value
+ const deltaE = Math.sqrt(
+ (DeltaLPrime / (kL * SL)) ** 2 +
+ (DeltaCPrime / (kC * SC)) ** 2 +
+ (DeltaSmallHPrime / (kH * SH)) ** 2 +
+ RT * (DeltaCPrime / (kC * SC)) * (DeltaSmallHPrime / (kH * SH))
+ );
+
+ return deltaE;
+}
+
+(function(Scratch) {
+ 'use strict';
+
+ const spectrumIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1NS42NTIwNCIgaGVpZ2h0PSI1NS42NTIwNCIgdmlld0JveD0iMCwwLDU1LjY1MjA0LDU1LjY1MjA0Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgeDE9IjI0Mi4wNjU4NSIgeTE9IjE1Mi4zNjgwMSIgeDI9IjIzNy45MzQxOSIgeTI9IjIwNy42MzE5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMzMzNhZmYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMzNhZmYiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IHgxPSIyMTIuMzY4MDIiIHkxPSIxNzcuOTM0MTciIHgyPSIyNjcuNjMxOTkiIHkyPSIxODIuMDY1ODMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0iY29sb3ItMiI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzZmZjMzIiBzdG9wLW9wYWNpdHk9IjAuNzAxOTYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNmZmMzMiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMTIuMTc0LC0xNTIuMTczOTgpIj48ZyBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMTIuMjkwOTEsMTgwYzAsLTE1LjMwMzMyIDEyLjQwNTc5LC0yNy43MDkxMSAyNy43MDkxMSwtMjcuNzA5MTFjMTUuMzAzMzIsMCAyNy43MDkxMSwxMi40MDU3OSAyNy43MDkxMSwyNy43MDkxMWMwLDE1LjMwMzMyIC0xMi40MDU3OSwyNy43MDkxMSAtMjcuNzA5MTEsMjcuNzA5MTFjLTE1LjMwMzMyLDAgLTI3LjcwOTExLC0xMi40MDU3OSAtMjcuNzA5MTEsLTI3LjcwOTExeiIgZmlsbD0iI2ZmMzMzMyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTIuMzY4MDMsMTc3LjkzNDE3YzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgyLC0yNS41NjYxNmMxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODJjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgyLDI1LjU2NjE2Yy0xNS4yNjA3MywtMS4xNDA5MyAtMjYuNzA3MDksLTE0LjQzNzEgLTI1LjU2NjE2LC0yOS42OTc4MnoiIGZpbGw9InVybCgjY29sb3ItMSkiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSJOYU4iLz48cGF0aCBkPSJNMjM3LjkzNDE5LDIwNy42MzJjLTE1LjI2MDcyLC0xLjE0MDkzIC0yNi43MDcwOSwtMTQuNDM3MSAtMjUuNTY2MTYsLTI5LjY5NzgzYzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgzLC0yNS41NjYxN2MxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODNjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgzLDI1LjU2NjE3eiIgZmlsbD0idXJsKCNjb2xvci0yKSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTMuNDI0LDE4MGMwLC0xNC42Nzc1MyAxMS44OTg0OSwtMjYuNTc2MDIgMjYuNTc2MDIsLTI2LjU3NjAyYzE0LjY3NzUzLDAgMjYuNTc2MDIsMTEuODk4NDkgMjYuNTc2MDIsMjYuNTc2MDJjMCwxNC42Nzc1MyAtMTEuODk4NDksMjYuNTc2MDIgLTI2LjU3NjAyLDI2LjU3NjAyYy0xNC42Nzc1MywwIC0yNi41NzYwMiwtMTEuODk4NDkgLTI2LjU3NjAyLC0yNi41NzYwMnoiIGZpbGw9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIwLjQ0MzE0IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi41Ii8+PC9nPjwvZz48L3N2Zz4=";
+ const blockIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNi41ODk4NSIgaGVpZ2h0PSIxNi41ODk4NSIgdmlld0JveD0iMCwwLDE2LjU4OTg1LDE2LjU4OTg1Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMxLjcwNTA4LC0xNzEuNzA1MDYpIj48ZyBzdHJva2Utd2lkdGg9IjAuNjI1IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMzIuMDQ1LDE4M2MwLC0yLjQ4NTI4IDIuMDE0NzIsLTQuNSA0LjUsLTQuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjV6IiBmaWxsPSIjNGM5N2ZmIiBzdHJva2U9IiMzMzczY2MiLz48cGF0aCBkPSJNMjQwLjA0NSwxNzIuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmY2NjgwIiBzdHJva2U9IiNmZjMzNTUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmZiZjAwIiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMxLjcwNTA5LDE4OC4yOTQ5MnYtMTYuNTg5ODVoMTYuNTg5ODV2MTYuNTg5ODV6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsLW9wYWNpdHk9IjAuNTAxOTYiIGZpbGw9IiNmZmJmMDAiIHN0cm9rZS1vcGFjaXR5PSIwLjUwMTk2IiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMyLjA0NSwxODNjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjNGQ5N2ZmIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjMzM3M2NjIi8+PHBhdGggZD0iTTI0MC4wNDUsMTcyLjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41YzAsLTIuNDg1MjggMi4wMTQ3MiwtNC41IDQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjZmY2NjgwIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjZmYzMzU1Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6OC4yOTQ5MTUwMDAwMDAwMDM6OC4yOTQ5MzUwMDAwMDAwMS0tPg==";
+ class Colors {
+ getInfo() {
+ return {
+ id: 'lxColors',
+ name: Scratch.translate('Colors'),
+ color1: '#f94c97',
+ menuIconURI: blockIcon,
+ blocks: [
+ {
+ opcode: 'newColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('new color [COL]'),
+ arguments: {
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ }
+ }
+ },
+ {
+ opcode: 'newColorRGB',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('from RGB [R] [G] [B]'),
+ arguments: {
+ R: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '164'
+ },
+ G: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '94'
+ },
+ B: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '255'
+ }
+ }
+ },
+ {
+ opcode: 'newColorHSV',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('from HSV [H] [S] [V]'),
+ arguments: {
+ H: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '266'
+ },
+ S: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '63'
+ },
+ V: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '100'
+ }
+ }
+ },
+ {
+ opcode: 'newColorHSL',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('from HSL [H] [S] [L]'),
+ arguments: {
+ H: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '266'
+ },
+ S: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '100'
+ },
+ L: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '68'
+ }
+ }
+ },
+ {
+ opcode: 'newColorDecimal',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ default: 'from decimal [DEC]',
+ description: "From decimal - as in the base system"
+ }),
+ arguments: {
+ DEC: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '10772223'
+ }
+ }
+ },
+ '---',
+ {
+ opcode: 'randomColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('random color'),
+ disableMonitor: true
+ },
+ '---',
+ {
+ opcode: 'additiveBlend',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[COL1] + [COL2]',
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ {
+ opcode: 'subtractiveBlend',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[COL1] - [COL2]',
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ {
+ opcode: 'multiplicativeBlend',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[COL1] * [COL2]',
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ {
+ opcode: 'divisingBlend',
+ blockType: Scratch.BlockType.REPORTER,
+ text: '[COL1] / [COL2]',
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ '---',
+ {
+ opcode: 'differenceBlend',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('difference of [COL1] - [COL2]'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ {
+ opcode: 'screenBlend',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('screen [COL1] * [COL2]'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ {
+ opcode: 'overlayBlend',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('overlay [COL1] * [COL2]'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ '---',
+ {
+ opcode: 'invertColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('invert [COL]'),
+ arguments: {
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ }
+ },
+ {
+ opcode: 'contrastColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ default: 'contrast [COL] by [NUM]',
+ description: "Contrast - as a verb, comparing to highlight differences"
+ }),
+ arguments: {
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ NUM: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '0.5'
+ },
+ }
+ },
+ {
+ opcode: 'grayscaleColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('grayscale [COL]'),
+ arguments: {
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ }
+ }
+ },
+ {
+ opcode: 'percentWhite',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('[NUM] % white'),
+ arguments: {
+ NUM: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '75'
+ }
+ }
+ },
+ '---',
+ {
+ opcode: 'distanceBetweenColors',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('distance between [COL1] and [COL2]'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ {
+ opcode: 'contrastRatioOfColors',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('contrast ratio of [COL1] and [COL2]'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ }
+ }
+ },
+ {
+ opcode: 'nearEqualColors',
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: Scratch.translate('[COL1] ≈ [COL2] threshold [THR]'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ },
+ THR: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '25'
+ }
+ }
+ },
+ {
+ opcode: 'colorFollowsWCAG',
+ blockType: Scratch.BlockType.BOOLEAN,
+ text: Scratch.translate('does [COL1] and [COL2] follow [AAA] for [TXT] text'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ },
+ AAA: {
+ type: Scratch.ArgumentType.STRING,
+ menu: 'WCAG_MENU'
+ },
+ TXT: {
+ type: Scratch.ArgumentType.STRING,
+ menu: 'TEXTWCAG_SIZE_MENU'
+ }
+ },
+ hideFromPalette: true
+ },
+ '---',
+ {
+ opcode: 'interpolateColors',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('interpolate [COL1] to [COL2] ratio [RATIO] using [SPACE]'),
+ arguments: {
+ COL1: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ COL2: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#eb57ab'
+ },
+ RATIO: {
+ type: Scratch.ArgumentType.NUMBER,
+ defaultValue: '0.5'
+ },
+ SPACE: {
+ type: Scratch.ArgumentType.STRING,
+ menu: 'SPACE_MENU'
+ }
+ }
+ },
+ '---',
+ {
+ opcode: 'getChannelFromColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('get [CHN] of [COL]'),
+ arguments: {
+ CHN: {
+ type: Scratch.ArgumentType.STRING,
+ menu: 'CHANNEL_MENU'
+ },
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ }
+ }
+ },
+ {
+ opcode: 'setChannelOfColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('set [CHN] of [COL] to [SET]'),
+ arguments: {
+ CHN: {
+ type: Scratch.ArgumentType.STRING,
+ menu: 'CHANNEL_MENU'
+ },
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ SET: {
+ type: Scratch.ArgumentType.NUMBER,
+ }
+ }
+ },
+ {
+ opcode: 'changeChannelOfColor',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate('change [CHN] of [COL] by [SET]'),
+ arguments: {
+ CHN: {
+ type: Scratch.ArgumentType.STRING,
+ menu: 'CHANNEL_MENU'
+ },
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ },
+ SET: {
+ type: Scratch.ArgumentType.NUMBER,
+ }
+ }
+ },
+ '---',
+ {
+ opcode: 'colorToDecimal',
+ blockType: Scratch.BlockType.REPORTER,
+ text: Scratch.translate({
+ default: '[COL] to decimal',
+ description: "To decimal - as in the base system"
+ }),
+ arguments: {
+ COL: {
+ type: Scratch.ArgumentType.COLOR,
+ defaultValue: '#a45eff'
+ }
+ }
+ },
+ ],
+ menus: {
+ CHANNEL_MENU: {
+ acceptReporters: true,
+ items: [
+ { text: Scratch.translate("red"), value: "red" },
+ { text: Scratch.translate("green"), value: "green" },
+ { text: Scratch.translate("blue"), value: "blue" },
+ { text: Scratch.translate("hue"), value: "hue" },
+ { text: Scratch.translate("saturation"), value: "saturation" },
+ { text: Scratch.translate("value"), value: "value" },
+ ],
+ },
+ SPACE_MENU: {
+ acceptReporters: true,
+ items: [
+ { text: Scratch.translate("RGB"), value: "RGB" },
+ { text: Scratch.translate("HSV"), value: "HSV" },
+ ],
+ },
+ WCAG_MENU: {
+ acceptReporters: true,
+ items: ['A', 'AA', 'AAA'],
+ },
+ TEXTWCAG_SIZE_MENU: {
+ acceptReporters: true,
+ items: [
+ { text: Scratch.translate("normal"), value: "normal" },
+ { text: Scratch.translate("large"), value: "large" },
+ ],
+ },
+ }
+ };
+ }
+ newColor(args) {
+ return args.COL;
+ }
+ newColorRGB(args) {
+ return '#' + toFixHex(args.R) + toFixHex(args.G) + toFixHex(args.B);
+ }
+ newColorHSV(args) {
+ return hsvToHex(args.H, args.S, args.V);
+ }
+ newColorHSL(args) {
+ let convRGB = hslToRgb(args.H, args.S, args.L);
+ return '#' + toFixHex(convRGB.r) + toFixHex(convRGB.g) + toFixHex(convRGB.b);
+ }
+ newColorDecimal(args) {
+ return '#' + toHex(args.DEC);
+ }
+ randomColor() {
+ return '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, '0');
+ }
+ additiveBlend(args) {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ const add = (c1, c2) => clamp(c1 + c2, 0, 255);
+ return rgbToHex({
+ r: add(a.r, b.r),
+ g: add(a.g, b.g),
+ b: add(a.b, b.b)
+ });
+ }
+ subtractiveBlend(args) {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ const sub = (c1, c2) => clamp(c1 - c2, 0, 255);
+ return rgbToHex({
+ r: sub(a.r, b.r),
+ g: sub(a.g, b.g),
+ b: sub(a.b, b.b)
+ });
+ }
+ multiplicativeBlend(args) {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ const mul = (c1, c2) => clamp((c1 * c2) / 255, 0, 255);
+ return rgbToHex({
+ r: mul(a.r, b.r),
+ g: mul(a.g, b.g),
+ b: mul(a.b, b.b)
+ });
+ }
+ divisingBlend(args) {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ const div = (c1, c2) => clamp((c1 / Math.max(c2, 1)) * 255, 0, 255);
+ return rgbToHex({
+ r: div(a.r, b.r),
+ g: div(a.g, b.g),
+ b: div(a.b, b.b)
+ });
+ }
+ differenceBlend(args) {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ const sub = (c1, c2) => clamp(abs(c1 - c2), 0, 255);
+ return rgbToHex({
+ r: sub(a.r, b.r),
+ g: sub(a.g, b.g),
+ b: sub(a.b, b.b)
+ });
+ }
+ screenBlend(args) {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ const scr = (c1, c2) => clamp(c1 + c2 - (c1 * c2) / 255, 0, 255);
+ return rgbToHex({
+ r: scr(a.r, b.r),
+ g: scr(a.g, b.g),
+ b: scr(a.b, b.b)
+ });
+ }
+ overlayBlend(args) {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ const ove = (c1, c2) => clamp(c1 < 128 ? (2 * c1 * c2) / 255 : 255 - (2 * (255 - c1) * (255 - c2)) / 255, 0, 255);
+ return rgbToHex({
+ r: ove(a.r, b.r),
+ g: ove(a.g, b.g),
+ b: ove(a.b, b.b)
+ });
+ }
+ invertColor(args) {
+ let col = hexToRgb(args.COL);
+ const inv = c => 255-c;
+ return rgbToHex({
+ r: inv(col.r),
+ g: inv(col.g),
+ b: inv(col.b),
+ });
+ }
+ contrastColor(args) {
+ let col = hexToRgb(args.COL);
+ const cnt = c => ((c - 128) * (1 - args.NUM)) + 128;
+ return rgbToHex({
+ r: cnt(col.r),
+ g: cnt(col.g),
+ b: cnt(col.b),
+ });
+ }
+ grayscaleColor(args) {
+ let rgb = hexToRgb(args.COL);
+ let gray = Math.round(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b);
+ return '#' + toFixHex(gray) + toFixHex(gray) + toFixHex(gray);
+ }
+ percentWhite(args) {
+ let white = round(args.NUM * 2.55)
+ return '#' + toFixHex(white) + toFixHex(white) + toFixHex(white);
+ }
+ distanceBetweenColors(args) {
+ return distanceBetweenHex(args.COL1, args.COL2);
+ }
+ nearEqualColors(args) {
+ return distanceBetweenHex(args.COL1, args.COL2) <= args.THR;
+ }
+ contrastRatioOfColors(args) {
+ return round(contrastRatio(args.COL1, args.COL2) * 100) / 100;
+ }
+ colorFollowsWCAG(args) {
+ let ratio = round(contrastRatio(args.COL1, args.COL2) * 100) / 100;
+ let req = 0;
+ let level = args.AAA;
+ let size = args.TXT;
+ if (level === 'AA') {
+ req = (size === 'large') ? 3.0 : 4.5;
+ }
+ if (level === 'AAA') {
+ req = (size === 'large') ? 4.5 : 7.0;
+ }
+ return ratio >= req;
+ }
+ interpolateColors(args) {
+ if (args.SPACE == 'RGB') {
+ let a = hexToRgb(args.COL1);
+ let b = hexToRgb(args.COL2);
+ let t = args.RATIO;
+ return rgbToHex({
+ r: lerp(a.r, b.r, t),
+ g: lerp(a.g, b.g, t),
+ b: lerp(a.b, b.b, t)
+ });
+ } else {
+ return interpolateHexColorsHsv(args.COL1, args.COL2, args.RATIO)
+ }
+ }
+ getChannelFromColor(args) {
+ if (['red', 'green', 'blue'].includes(args.CHN)) {
+ let channel = ['red', 'green', 'blue'].indexOf(args.CHN);
+ let letter = ['red', 'green', 'blue'][channel][0];
+ return hexToRgb(args.COL)[letter];
+ } else if (['hue', 'saturation', 'value'].includes(args.CHN)) {
+ let hsv = hexToHsv(args.COL);
+ switch (args.CHN) {
+ case 'hue': return round(hsv.h);
+ case 'saturation': return round(hsv.s);
+ case 'value': return round(hsv.v);
+ }
+ }
+ }
+ setChannelOfColor(args) {
+ if (['red', 'green', 'blue'].includes(args.CHN)) {
+ let rgb = hexToRgb(args.COL);
+ switch (args.CHN) {
+ case 'red': rgb.r = args.SET; break;
+ case 'green': rgb.g = args.SET; break;
+ case 'blue': rgb.b = args.SET; break;
+ }
+ return '#' + toFixHex(rgb.r) + toFixHex(rgb.g) + toFixHex(rgb.b);
+ } else if (['hue', 'saturation', 'value'].includes(args.CHN)) {
+ let hsv = hexToHsv(args.COL);
+ switch (args.CHN) {
+ case 'hue': hsv.h = args.SET; break;
+ case 'saturation': hsv.s = args.SET; break;
+ case 'value': hsv.v = args.SET; break;
+ }
+ return hsvToHex(hsv.h, hsv.s, hsv.v);
+ }
+ }
+ changeChannelOfColor(args) {
+ if (['red', 'green', 'blue'].includes(args.CHN)) {
+ let rgb = hexToRgb(args.COL);
+ switch (args.CHN) {
+ case 'red': rgb.r = clamp(rgb.r + args.SET, 0, 255); break;
+ case 'green': rgb.g = clamp(rgb.g + args.SET, 0, 255); break;
+ case 'blue': rgb.b = clamp(rgb.b + args.SET, 0, 255); break;
+ }
+ return '#' + toFixHex(rgb.r) + toFixHex(rgb.g) + toFixHex(rgb.b);
+ } else if (['hue', 'saturation', 'value'].includes(args.CHN)) {
+ let hsv = hexToHsv(args.COL);
+ switch (args.CHN) {
+ case 'hue': hsv.h = (hsv.h + args.SET) % 360; break;
+ case 'saturation': hsv.s = clamp(hsv.s + args.SET, 0, 100); break;
+ case 'value': hsv.v = clamp(hsv.v + args.SET, 0, 100); break;
+ }
+ return hsvToHex(hsv.h, hsv.s, hsv.v);
+ }
+ }
+ colorToDecimal(args) {
+ return toDec(args.COL.slice(1));
+ }
+ }
+ Scratch.extensions.register(new Colors());
+})(Scratch);
From ab498c14af82837703acc1bdff1b47d5d7be7685 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Wed, 17 Dec 2025 21:32:04 -0600
Subject: [PATCH 02/12] Add files via upload
---
images/colors.svg | 1 +
1 file changed, 1 insertion(+)
create mode 100644 images/colors.svg
diff --git a/images/colors.svg b/images/colors.svg
new file mode 100644
index 0000000000..0e1b22bc91
--- /dev/null
+++ b/images/colors.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
From 103e8555deb2a20492fe071e523fe7b8edf90b85 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Wed, 17 Dec 2025 21:33:06 -0600
Subject: [PATCH 03/12] Delete images/colors.svg
---
images/colors.svg | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 images/colors.svg
diff --git a/images/colors.svg b/images/colors.svg
deleted file mode 100644
index 0e1b22bc91..0000000000
--- a/images/colors.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
From 1d17f8e8de7713c4195b63f595157aae02475842 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Wed, 17 Dec 2025 21:33:37 -0600
Subject: [PATCH 04/12] Create colors.svg
---
images/LincolnX/colors.svg | 1 +
1 file changed, 1 insertion(+)
create mode 100644 images/LincolnX/colors.svg
diff --git a/images/LincolnX/colors.svg b/images/LincolnX/colors.svg
new file mode 100644
index 0000000000..66b6046557
--- /dev/null
+++ b/images/LincolnX/colors.svg
@@ -0,0 +1 @@
+
From 1f02b50bd0bbb6b2bde219cd35cb929699887e46 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Wed, 17 Dec 2025 21:35:45 -0600
Subject: [PATCH 05/12] Add LincolnX/colors extension to extensions.json
---
extensions/extensions.json | 1 +
1 file changed, 1 insertion(+)
diff --git a/extensions/extensions.json b/extensions/extensions.json
index 442f300d59..e27bdf8722 100644
--- a/extensions/extensions.json
+++ b/extensions/extensions.json
@@ -21,6 +21,7 @@
"iframe",
"Clay/htmlEncode",
"Xeltalliv/clippingblending",
+ "LincolnX/colors",
"clipboard",
"obviousAlexC/penPlus",
"penplus",
From 1eb80ebddb4a71be9ab58cd5238551e5ffafbe7f Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Thu, 18 Dec 2025 10:09:26 -0600
Subject: [PATCH 06/12] Update colors.js
---
extensions/LincolnX/colors.js | 36 ++++++++++++-----------------------
1 file changed, 12 insertions(+), 24 deletions(-)
diff --git a/extensions/LincolnX/colors.js b/extensions/LincolnX/colors.js
index 52715f5379..e08c7ef3db 100644
--- a/extensions/LincolnX/colors.js
+++ b/extensions/LincolnX/colors.js
@@ -13,10 +13,10 @@ const clamp = (n, mi, ma) => Math.min(Math.max(n, mi), ma);
const fixHex = (hex) => limitHex(hex, 0, 255).padStart(2, '0');
const toFixHex = (dec) => fixHex(dec.toString(16));
const lerp = (a, b, t) => a + (b - a) * t;
-function interpolateHexColorsHsv(color1, color2, factor) {
- const hsv1 = hexToHsv(color1);
- const hsv2 = hexToHsv(color2);
- factor = Math.min(1, Math.max(0, factor));
+function interpolateHexColorsHsv(hex1, hex2, t) {
+ const hsv1 = hexToHsv(hex1);
+ const hsv2 = hexToHsv(hex2);
+ t = clamp(t, 0, 1)
// Handle hue interpolation for the shortest path around the color wheel
let h1 = hsv1.h;
let h2 = hsv2.h;
@@ -27,23 +27,11 @@ function interpolateHexColorsHsv(color1, color2, factor) {
h2 += 360;
}
- const h = h1 + factor * (h2 - h1);
- const s = hsv1.s + factor * (hsv2.s - hsv1.s);
- const v = hsv1.v + factor * (hsv2.v - hsv1.v);
-
- const interpolatedRgb = hsvToRgb({ h, s, v });
- return rgbToHex(interpolatedRgb);
-}
-function overlayHex(hex1, hex2) {
- let a = toDec(hex1) / 255;
- let b = toDec(hex2) / 255;
- let overlay;
- if (a < 0.5) {
- overlay = 2 * a * b;
- } else {
- overlay = 1 - (2 * (1 - a) * (1 - b));
- }
- return toFixHex(overlay * 255)
+ const h = h1 + t * (h2 - h1);
+ const s = hsv1.s + t * (hsv2.s - hsv1.s);
+ const v = hsv1.v + t * (hsv2.v - hsv1.v);
+
+ return hsvToHex({ h, s, v });
}
function hexToRgb(hex) {
@@ -171,7 +159,7 @@ function contrastRatio(hex1, hex2) {
-function distanceBetweenHex(hex1, hex2) {
+function distanceBetweenHexColorsDeltaE2000(hex1, hex2) {
// convert Hex to RGB
const rgb1 = hexToRgb(hex1);
const rgb2 = hexToRgb(hex2);
@@ -904,10 +892,10 @@ function deltaE2000(lab1, lab2) {
return '#' + toFixHex(white) + toFixHex(white) + toFixHex(white);
}
distanceBetweenColors(args) {
- return distanceBetweenHex(args.COL1, args.COL2);
+ return distanceBetweenHexColorsDeltaE2000(args.COL1, args.COL2);
}
nearEqualColors(args) {
- return distanceBetweenHex(args.COL1, args.COL2) <= args.THR;
+ return distanceBetweenHexColorsDeltaE2000(args.COL1, args.COL2) <= args.THR;
}
contrastRatioOfColors(args) {
return round(contrastRatio(args.COL1, args.COL2) * 100) / 100;
From 758e596c8e1cb8865fe50da5005df1dd3a383d01 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Fri, 26 Dec 2025 20:43:28 -0600
Subject: [PATCH 07/12] Update colors.js
---
extensions/LincolnX/colors.js | 178 +++++++++++++++++-----------------
1 file changed, 89 insertions(+), 89 deletions(-)
diff --git a/extensions/LincolnX/colors.js b/extensions/LincolnX/colors.js
index e08c7ef3db..6b42ebbe94 100644
--- a/extensions/LincolnX/colors.js
+++ b/extensions/LincolnX/colors.js
@@ -4,16 +4,19 @@
// By: LincolnX
// License: MPL-2.0
-const { abs, round, floor, sqrt } = Math;
-
-const toDec = (hex) => parseInt(hex, 16);
-const toHex = (dec) => dec.toString(16);
-const limitHex = (hex, mi, ma) => toHex(Math.min(Math.max(toDec(hex), mi), ma));
-const clamp = (n, mi, ma) => Math.min(Math.max(n, mi), ma);
-const fixHex = (hex) => limitHex(hex, 0, 255).padStart(2, '0');
-const toFixHex = (dec) => fixHex(dec.toString(16));
-const lerp = (a, b, t) => a + (b - a) * t;
-function interpolateHexColorsHsv(hex1, hex2, t) {
+(function(Scratch) {
+ 'use strict';
+
+ const { abs, round, floor, sqrt } = Math;
+
+ const toDec = (hex) => parseInt(hex, 16);
+ const toHex = (dec) => dec.toString(16);
+ const limitHex = (hex, mi, ma) => toHex(Math.min(Math.max(toDec(hex), mi), ma));
+ const clamp = (n, mi, ma) => Math.min(Math.max(n, mi), ma);
+ const fixHex = (hex) => limitHex(hex, 0, 255).padStart(2, '0');
+ const toFixHex = (dec) => fixHex(dec.toString(16));
+ const lerp = (a, b, t) => a + (b - a) * t;
+ function interpolateHexColorsHsv(hex1, hex2, t) {
const hsv1 = hexToHsv(hex1);
const hsv2 = hexToHsv(hex2);
t = clamp(t, 0, 1)
@@ -32,9 +35,9 @@ function interpolateHexColorsHsv(hex1, hex2, t) {
const v = hsv1.v + t * (hsv2.v - hsv1.v);
return hsvToHex({ h, s, v });
-}
+ }
-function hexToRgb(hex) {
+ function hexToRgb(hex) {
let r = 0, g = 0, b = 0;
// Handle 3-digit shorthand
if (hex.length === 4) {
@@ -47,12 +50,12 @@ function hexToRgb(hex) {
b = parseInt(hex.substring(5, 7), 16);
}
return { r, g, b };
-}
-function rgbToHex(rgb) {
+ }
+ function rgbToHex(rgb) {
const makeHex = c => Math.round(c).toString(16).padStart(2, '0');
return `#${makeHex(rgb.r)}${makeHex(rgb.g)}${makeHex(rgb.b)}`;
-}
-function hsvToRgb(h, s, v) {
+ }
+ function hsvToRgb(h, s, v) {
var r, g, b, i, f, p, q, t;
if (arguments.length === 1) {
s = h.s, v = h.v, h = h.h;
@@ -78,8 +81,8 @@ function hsvToRgb(h, s, v) {
g: round(g * 255),
b: round(b * 255)
};
-}
-function rgbToHsv(r, g, b) {
+ }
+ function rgbToHsv(r, g, b) {
if (arguments.length === 1) {
g = r.g, b = r.b, r = r.r;
}
@@ -99,67 +102,67 @@ function rgbToHsv(r, g, b) {
s: s * 100,
v: v * 100
};
-}
-const hexToHsv = (hex) => rgbToHsv(hexToRgb(hex));
-function hsvToHex(h, s, v) {
- if (arguments.length === 1) {
+ }
+ const hexToHsv = (hex) => rgbToHsv(hexToRgb(hex));
+ function hsvToHex(h, s, v) {
+ if (arguments.length === 1) {
s = h.s, v = h.v, h = h.h;
}
- return rgbToHex(hsvToRgb(h, s, v));
-}
-
-function hslToRgb(h, s, l) {
- h /= 360;
- s /= 100;
- l /= 100;
- let r, g, b;
- if (s === 0) {
- r = g = b = l; // achromatic
- } else {
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- const p = 2 * l - q;
- r = hueToRgb(p, q, h + 1/3);
- g = hueToRgb(p, q, h);
- b = hueToRgb(p, q, h - 1/3);
+ return rgbToHex(hsvToRgb(h, s, v));
+ }
+
+ function hslToRgb(h, s, l) {
+ h /= 360;
+ s /= 100;
+ l /= 100;
+ let r, g, b;
+ if (s === 0) {
+ r = g = b = l; // achromatic
+ } else {
+ const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ const p = 2 * l - q;
+ r = hueToRgb(p, q, h + 1/3);
+ g = hueToRgb(p, q, h);
+ b = hueToRgb(p, q, h - 1/3);
+ }
+ return {r: round(r * 255), g: round(g * 255), b: round(b * 255)};
+ }
+
+ function hueToRgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1/6) return p + (q - p) * 6 * t;
+ if (t < 1/2) return q;
+ if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ return p;
+ }
+
+ function channelToLinear(c) {
+ c /= 255;
+ return c <= 0.03928
+ ? c / 12.92
+ : ((c + 0.055) / 1.055) ** 2.4;
+ }
+
+ function relativeLuminance(hex) {
+ const { r, g, b } = hexToRgb(hex);
+ const R = channelToLinear(r);
+ const G = channelToLinear(g);
+ const B = channelToLinear(b);
+ return 0.2126 * R + 0.7152 * G + 0.0722 * B;
}
- return {r: round(r * 255), g: round(g * 255), b: round(b * 255)};
-}
-
-function hueToRgb(p, q, t) {
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
-}
-
-function channelToLinear(c) {
- c /= 255;
- return c <= 0.03928
- ? c / 12.92
- : ((c + 0.055) / 1.055) ** 2.4;
-}
-
-function relativeLuminance(hex) {
- const { r, g, b } = hexToRgb(hex);
- const R = channelToLinear(r);
- const G = channelToLinear(g);
- const B = channelToLinear(b);
- return 0.2126 * R + 0.7152 * G + 0.0722 * B;
-}
-
-function contrastRatio(hex1, hex2) {
- const L1 = relativeLuminance(hex1);
- const L2 = relativeLuminance(hex2);
- const light = Math.max(L1, L2);
- const dark = Math.min(L1, L2);
- return (light + 0.05) / (dark + 0.05);
-}
-
-
-
-function distanceBetweenHexColorsDeltaE2000(hex1, hex2) {
+
+ function contrastRatio(hex1, hex2) {
+ const L1 = relativeLuminance(hex1);
+ const L2 = relativeLuminance(hex2);
+ const light = Math.max(L1, L2);
+ const dark = Math.min(L1, L2);
+ return (light + 0.05) / (dark + 0.05);
+ }
+
+
+
+ function distanceBetweenHexColorsDeltaE2000(hex1, hex2) {
// convert Hex to RGB
const rgb1 = hexToRgb(hex1);
const rgb2 = hexToRgb(hex2);
@@ -174,9 +177,9 @@ function distanceBetweenHexColorsDeltaE2000(hex1, hex2) {
// calculate Delta E 2000
return deltaE2000(lab1, lab2);
-}
+ }
-function rgbToXyz(r, g, b) {
+ function rgbToXyz(r, g, b) {
r /= 255;
g /= 255;
b /= 255;
@@ -195,10 +198,10 @@ function rgbToXyz(r, g, b) {
const z = r * 0.0193 + g * 0.1192 + b * 0.9505;
return { x, y, z };
-}
+ }
-// converts XYZ values to CIELAB color space.
-function xyzToLab(x, y, z) {
+ // converts XYZ values to CIELAB color space.
+ function xyzToLab(x, y, z) {
// D65 white point values
const refX = 95.047;
const refY = 100.000;
@@ -217,12 +220,12 @@ function xyzToLab(x, y, z) {
const b = 200 * (y - z);
return { L, a, b };
-}
+ }
-// calculates the Delta E 2000 difference between two Lab colors.
-// based on the implementation notes from Sharma et al..
-function deltaE2000(lab1, lab2) {
+ // calculates the Delta E 2000 difference between two Lab colors.
+ // based on the implementation notes from Sharma et al..
+ function deltaE2000(lab1, lab2) {
const kL = 1.0, kC = 1.0, kH = 1.0; // parametric factors often set to 1.0
const deg2rad = Math.PI / 180;
const rad2deg = 180 / Math.PI;
@@ -313,10 +316,7 @@ function deltaE2000(lab1, lab2) {
);
return deltaE;
-}
-
-(function(Scratch) {
- 'use strict';
+ }
const spectrumIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1NS42NTIwNCIgaGVpZ2h0PSI1NS42NTIwNCIgdmlld0JveD0iMCwwLDU1LjY1MjA0LDU1LjY1MjA0Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgeDE9IjI0Mi4wNjU4NSIgeTE9IjE1Mi4zNjgwMSIgeDI9IjIzNy45MzQxOSIgeTI9IjIwNy42MzE5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMzMzNhZmYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMzNhZmYiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IHgxPSIyMTIuMzY4MDIiIHkxPSIxNzcuOTM0MTciIHgyPSIyNjcuNjMxOTkiIHkyPSIxODIuMDY1ODMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0iY29sb3ItMiI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzZmZjMzIiBzdG9wLW9wYWNpdHk9IjAuNzAxOTYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNmZmMzMiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMTIuMTc0LC0xNTIuMTczOTgpIj48ZyBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMTIuMjkwOTEsMTgwYzAsLTE1LjMwMzMyIDEyLjQwNTc5LC0yNy43MDkxMSAyNy43MDkxMSwtMjcuNzA5MTFjMTUuMzAzMzIsMCAyNy43MDkxMSwxMi40MDU3OSAyNy43MDkxMSwyNy43MDkxMWMwLDE1LjMwMzMyIC0xMi40MDU3OSwyNy43MDkxMSAtMjcuNzA5MTEsMjcuNzA5MTFjLTE1LjMwMzMyLDAgLTI3LjcwOTExLC0xMi40MDU3OSAtMjcuNzA5MTEsLTI3LjcwOTExeiIgZmlsbD0iI2ZmMzMzMyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTIuMzY4MDMsMTc3LjkzNDE3YzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgyLC0yNS41NjYxNmMxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODJjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgyLDI1LjU2NjE2Yy0xNS4yNjA3MywtMS4xNDA5MyAtMjYuNzA3MDksLTE0LjQzNzEgLTI1LjU2NjE2LC0yOS42OTc4MnoiIGZpbGw9InVybCgjY29sb3ItMSkiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSJOYU4iLz48cGF0aCBkPSJNMjM3LjkzNDE5LDIwNy42MzJjLTE1LjI2MDcyLC0xLjE0MDkzIC0yNi43MDcwOSwtMTQuNDM3MSAtMjUuNTY2MTYsLTI5LjY5NzgzYzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgzLC0yNS41NjYxN2MxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODNjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgzLDI1LjU2NjE3eiIgZmlsbD0idXJsKCNjb2xvci0yKSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTMuNDI0LDE4MGMwLC0xNC42Nzc1MyAxMS44OTg0OSwtMjYuNTc2MDIgMjYuNTc2MDIsLTI2LjU3NjAyYzE0LjY3NzUzLDAgMjYuNTc2MDIsMTEuODk4NDkgMjYuNTc2MDIsMjYuNTc2MDJjMCwxNC42Nzc1MyAtMTEuODk4NDksMjYuNTc2MDIgLTI2LjU3NjAyLDI2LjU3NjAyYy0xNC42Nzc1MywwIC0yNi41NzYwMiwtMTEuODk4NDkgLTI2LjU3NjAyLC0yNi41NzYwMnoiIGZpbGw9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIwLjQ0MzE0IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi41Ii8+PC9nPjwvZz48L3N2Zz4=";
const blockIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNi41ODk4NSIgaGVpZ2h0PSIxNi41ODk4NSIgdmlld0JveD0iMCwwLDE2LjU4OTg1LDE2LjU4OTg1Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMxLjcwNTA4LC0xNzEuNzA1MDYpIj48ZyBzdHJva2Utd2lkdGg9IjAuNjI1IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMzIuMDQ1LDE4M2MwLC0yLjQ4NTI4IDIuMDE0NzIsLTQuNSA0LjUsLTQuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjV6IiBmaWxsPSIjNGM5N2ZmIiBzdHJva2U9IiMzMzczY2MiLz48cGF0aCBkPSJNMjQwLjA0NSwxNzIuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmY2NjgwIiBzdHJva2U9IiNmZjMzNTUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmZiZjAwIiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMxLjcwNTA5LDE4OC4yOTQ5MnYtMTYuNTg5ODVoMTYuNTg5ODV2MTYuNTg5ODV6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsLW9wYWNpdHk9IjAuNTAxOTYiIGZpbGw9IiNmZmJmMDAiIHN0cm9rZS1vcGFjaXR5PSIwLjUwMTk2IiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMyLjA0NSwxODNjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjNGQ5N2ZmIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjMzM3M2NjIi8+PHBhdGggZD0iTTI0MC4wNDUsMTcyLjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41YzAsLTIuNDg1MjggMi4wMTQ3MiwtNC41IDQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjZmY2NjgwIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjZmYzMzU1Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6OC4yOTQ5MTUwMDAwMDAwMDM6OC4yOTQ5MzUwMDAwMDAwMS0tPg==";
From b85424d7da52a18d31641e9b49bb50482f6a18fd Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Fri, 26 Dec 2025 21:29:48 -0600
Subject: [PATCH 08/12] Update colors.js
---
extensions/LincolnX/colors.js | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/extensions/LincolnX/colors.js b/extensions/LincolnX/colors.js
index 6b42ebbe94..3f9f178f76 100644
--- a/extensions/LincolnX/colors.js
+++ b/extensions/LincolnX/colors.js
@@ -235,19 +235,19 @@
const L2 = lab2.L, a2 = lab2.a, b2 = lab2.b;
// calculate C*ab values (Chroma)
- const C1 = Math.sqrt(a1*a1 + b1*b1);
- const C2 = Math.sqrt(a2*a2 + b2*b2);
+ const C1 = sqrt(a1*a1 + b1*b1);
+ const C2 = sqrt(a2*a2 + b2*b2);
const CBar = (C1 + C2) / 2.0;
// calculate G (chroma correction factor)
const CBarPow7 = CBar ** 7;
- const G = 0.5 * (1 - Math.sqrt(CBarPow7 / (CBarPow7 + 6103515625.0))); // 6103515625 = 25^7
+ const G = 0.5 * (1 - sqrt(CBarPow7 / (CBarPow7 + 6103515625.0))); // 6103515625 = 25^7
// calculate a' and C' (lightness corrected a* and new chroma)
const a1Prime = a1 * (1 + G);
const a2Prime = a2 * (1 + G);
- const C1Prime = Math.sqrt(a1Prime*a1Prime + b1*b1);
- const C2Prime = Math.sqrt(a2Prime*a2Prime + b2*b2);
+ const C1Prime = sqrt(a1Prime*a1Prime + b1*b1);
+ const C2Prime = sqrt(a2Prime*a2Prime + b2*b2);
const CBarPrime = (C1Prime + C2Prime) / 2.0;
const DeltaCPrime = C2Prime - C1Prime;
@@ -272,7 +272,7 @@
}
// convert Delta H' to a metric difference (ΔH')
- const DeltaSmallHPrime = 2 * Math.sqrt(C1Prime * C2Prime) * Math.sin(DeltaHPrime * deg2rad / 2.0);
+ const DeltaSmallHPrime = 2 * sqrt(C1Prime * C2Prime) * Math.sin(DeltaHPrime * deg2rad / 2.0);
// calculate Delta L'
const DeltaLPrime = L2 - L1;
@@ -305,10 +305,10 @@
// calculate RT (rotation term)
const CBarPrimePow7 = CBarPrime ** 7;
const RT = -2.0 * Math.sin(HBarPrime * deg2rad)
- * Math.sqrt(CBarPrimePow7 / (CBarPrimePow7 + 6103515625.0));
+ * sqrt(CBarPrimePow7 / (CBarPrimePow7 + 6103515625.0));
// calculate the final Delta E 2000 value
- const deltaE = Math.sqrt(
+ const deltaE =sqrt(
(DeltaLPrime / (kL * SL)) ** 2 +
(DeltaCPrime / (kC * SC)) ** 2 +
(DeltaSmallHPrime / (kH * SH)) ** 2 +
@@ -318,7 +318,7 @@
return deltaE;
}
- const spectrumIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1NS42NTIwNCIgaGVpZ2h0PSI1NS42NTIwNCIgdmlld0JveD0iMCwwLDU1LjY1MjA0LDU1LjY1MjA0Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgeDE9IjI0Mi4wNjU4NSIgeTE9IjE1Mi4zNjgwMSIgeDI9IjIzNy45MzQxOSIgeTI9IjIwNy42MzE5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMzMzNhZmYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMzNhZmYiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IHgxPSIyMTIuMzY4MDIiIHkxPSIxNzcuOTM0MTciIHgyPSIyNjcuNjMxOTkiIHkyPSIxODIuMDY1ODMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0iY29sb3ItMiI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzZmZjMzIiBzdG9wLW9wYWNpdHk9IjAuNzAxOTYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNmZmMzMiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMTIuMTc0LC0xNTIuMTczOTgpIj48ZyBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMTIuMjkwOTEsMTgwYzAsLTE1LjMwMzMyIDEyLjQwNTc5LC0yNy43MDkxMSAyNy43MDkxMSwtMjcuNzA5MTFjMTUuMzAzMzIsMCAyNy43MDkxMSwxMi40MDU3OSAyNy43MDkxMSwyNy43MDkxMWMwLDE1LjMwMzMyIC0xMi40MDU3OSwyNy43MDkxMSAtMjcuNzA5MTEsMjcuNzA5MTFjLTE1LjMwMzMyLDAgLTI3LjcwOTExLC0xMi40MDU3OSAtMjcuNzA5MTEsLTI3LjcwOTExeiIgZmlsbD0iI2ZmMzMzMyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTIuMzY4MDMsMTc3LjkzNDE3YzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgyLC0yNS41NjYxNmMxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODJjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgyLDI1LjU2NjE2Yy0xNS4yNjA3MywtMS4xNDA5MyAtMjYuNzA3MDksLTE0LjQzNzEgLTI1LjU2NjE2LC0yOS42OTc4MnoiIGZpbGw9InVybCgjY29sb3ItMSkiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSJOYU4iLz48cGF0aCBkPSJNMjM3LjkzNDE5LDIwNy42MzJjLTE1LjI2MDcyLC0xLjE0MDkzIC0yNi43MDcwOSwtMTQuNDM3MSAtMjUuNTY2MTYsLTI5LjY5NzgzYzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgzLC0yNS41NjYxN2MxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODNjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgzLDI1LjU2NjE3eiIgZmlsbD0idXJsKCNjb2xvci0yKSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTMuNDI0LDE4MGMwLC0xNC42Nzc1MyAxMS44OTg0OSwtMjYuNTc2MDIgMjYuNTc2MDIsLTI2LjU3NjAyYzE0LjY3NzUzLDAgMjYuNTc2MDIsMTEuODk4NDkgMjYuNTc2MDIsMjYuNTc2MDJjMCwxNC42Nzc1MyAtMTEuODk4NDksMjYuNTc2MDIgLTI2LjU3NjAyLDI2LjU3NjAyYy0xNC42Nzc1MywwIC0yNi41NzYwMiwtMTEuODk4NDkgLTI2LjU3NjAyLC0yNi41NzYwMnoiIGZpbGw9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIwLjQ0MzE0IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi41Ii8+PC9nPjwvZz48L3N2Zz4=";
+ const _spectrumIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1NS42NTIwNCIgaGVpZ2h0PSI1NS42NTIwNCIgdmlld0JveD0iMCwwLDU1LjY1MjA0LDU1LjY1MjA0Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgeDE9IjI0Mi4wNjU4NSIgeTE9IjE1Mi4zNjgwMSIgeDI9IjIzNy45MzQxOSIgeTI9IjIwNy42MzE5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMzMzNhZmYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMzNhZmYiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IHgxPSIyMTIuMzY4MDIiIHkxPSIxNzcuOTM0MTciIHgyPSIyNjcuNjMxOTkiIHkyPSIxODIuMDY1ODMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0iY29sb3ItMiI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzZmZjMzIiBzdG9wLW9wYWNpdHk9IjAuNzAxOTYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNmZmMzMiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMTIuMTc0LC0xNTIuMTczOTgpIj48ZyBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMTIuMjkwOTEsMTgwYzAsLTE1LjMwMzMyIDEyLjQwNTc5LC0yNy43MDkxMSAyNy43MDkxMSwtMjcuNzA5MTFjMTUuMzAzMzIsMCAyNy43MDkxMSwxMi40MDU3OSAyNy43MDkxMSwyNy43MDkxMWMwLDE1LjMwMzMyIC0xMi40MDU3OSwyNy43MDkxMSAtMjcuNzA5MTEsMjcuNzA5MTFjLTE1LjMwMzMyLDAgLTI3LjcwOTExLC0xMi40MDU3OSAtMjcuNzA5MTEsLTI3LjcwOTExeiIgZmlsbD0iI2ZmMzMzMyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTIuMzY4MDMsMTc3LjkzNDE3YzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgyLC0yNS41NjYxNmMxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODJjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgyLDI1LjU2NjE2Yy0xNS4yNjA3MywtMS4xNDA5MyAtMjYuNzA3MDksLTE0LjQzNzEgLTI1LjU2NjE2LC0yOS42OTc4MnoiIGZpbGw9InVybCgjY29sb3ItMSkiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSJOYU4iLz48cGF0aCBkPSJNMjM3LjkzNDE5LDIwNy42MzJjLTE1LjI2MDcyLC0xLjE0MDkzIC0yNi43MDcwOSwtMTQuNDM3MSAtMjUuNTY2MTYsLTI5LjY5NzgzYzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgzLC0yNS41NjYxN2MxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODNjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgzLDI1LjU2NjE3eiIgZmlsbD0idXJsKCNjb2xvci0yKSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTMuNDI0LDE4MGMwLC0xNC42Nzc1MyAxMS44OTg0OSwtMjYuNTc2MDIgMjYuNTc2MDIsLTI2LjU3NjAyYzE0LjY3NzUzLDAgMjYuNTc2MDIsMTEuODk4NDkgMjYuNTc2MDIsMjYuNTc2MDJjMCwxNC42Nzc1MyAtMTEuODk4NDksMjYuNTc2MDIgLTI2LjU3NjAyLDI2LjU3NjAyYy0xNC42Nzc1MywwIC0yNi41NzYwMiwtMTEuODk4NDkgLTI2LjU3NjAyLC0yNi41NzYwMnoiIGZpbGw9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIwLjQ0MzE0IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi41Ii8+PC9nPjwvZz48L3N2Zz4=";
const blockIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNi41ODk4NSIgaGVpZ2h0PSIxNi41ODk4NSIgdmlld0JveD0iMCwwLDE2LjU4OTg1LDE2LjU4OTg1Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMxLjcwNTA4LC0xNzEuNzA1MDYpIj48ZyBzdHJva2Utd2lkdGg9IjAuNjI1IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMzIuMDQ1LDE4M2MwLC0yLjQ4NTI4IDIuMDE0NzIsLTQuNSA0LjUsLTQuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjV6IiBmaWxsPSIjNGM5N2ZmIiBzdHJva2U9IiMzMzczY2MiLz48cGF0aCBkPSJNMjQwLjA0NSwxNzIuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmY2NjgwIiBzdHJva2U9IiNmZjMzNTUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmZiZjAwIiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMxLjcwNTA5LDE4OC4yOTQ5MnYtMTYuNTg5ODVoMTYuNTg5ODV2MTYuNTg5ODV6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsLW9wYWNpdHk9IjAuNTAxOTYiIGZpbGw9IiNmZmJmMDAiIHN0cm9rZS1vcGFjaXR5PSIwLjUwMTk2IiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMyLjA0NSwxODNjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjNGQ5N2ZmIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjMzM3M2NjIi8+PHBhdGggZD0iTTI0MC4wNDUsMTcyLjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41YzAsLTIuNDg1MjggMi4wMTQ3MiwtNC41IDQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjZmY2NjgwIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjZmYzMzU1Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6OC4yOTQ5MTUwMDAwMDAwMDM6OC4yOTQ5MzUwMDAwMDAwMS0tPg==";
class Colors {
getInfo() {
@@ -421,7 +421,7 @@
{
opcode: 'additiveBlend',
blockType: Scratch.BlockType.REPORTER,
- text: '[COL1] + [COL2]',
+ text: Scratch.translate('[COL1] + [COL2]'),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
@@ -436,7 +436,7 @@
{
opcode: 'subtractiveBlend',
blockType: Scratch.BlockType.REPORTER,
- text: '[COL1] - [COL2]',
+ text: Scratch.translate('[COL1] - [COL2]'),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
@@ -451,7 +451,7 @@
{
opcode: 'multiplicativeBlend',
blockType: Scratch.BlockType.REPORTER,
- text: '[COL1] * [COL2]',
+ text: Scratch.translate('[COL1] * [COL2]'),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
@@ -466,7 +466,7 @@
{
opcode: 'divisingBlend',
blockType: Scratch.BlockType.REPORTER,
- text: '[COL1] / [COL2]',
+ text: Scratch.translate('[COL1] / [COL2]'),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
From 780bd79a81579e8053b68974fe9e1fbf0dff6bc2 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Fri, 26 Dec 2025 21:58:10 -0600
Subject: [PATCH 09/12] Update colors.svg
---
images/LincolnX/colors.svg | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/images/LincolnX/colors.svg b/images/LincolnX/colors.svg
index 66b6046557..5ddb8641e9 100644
--- a/images/LincolnX/colors.svg
+++ b/images/LincolnX/colors.svg
@@ -1 +1 @@
-
+
From 7f2a68dad68b23de1862f62e74005f8c5ffdd11f Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Fri, 26 Dec 2025 21:58:49 -0600
Subject: [PATCH 10/12] Update colors.svg
---
images/LincolnX/colors.svg | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/images/LincolnX/colors.svg b/images/LincolnX/colors.svg
index 5ddb8641e9..31de52cc8a 100644
--- a/images/LincolnX/colors.svg
+++ b/images/LincolnX/colors.svg
@@ -1 +1 @@
-
+
From ed406902efda85922c71fbc5af2f3abc2bfea951 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Fri, 26 Dec 2025 21:59:22 -0600
Subject: [PATCH 11/12] Update colors.svg
---
images/LincolnX/colors.svg | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/images/LincolnX/colors.svg b/images/LincolnX/colors.svg
index 31de52cc8a..5258c4da87 100644
--- a/images/LincolnX/colors.svg
+++ b/images/LincolnX/colors.svg
@@ -1 +1 @@
-
+
From ebd68f3f6b086991fcd84f6d6679b6e93f0b6eb9 Mon Sep 17 00:00:00 2001
From: LincolnX <118082287+LincolnXGames@users.noreply.github.com>
Date: Fri, 26 Dec 2025 22:29:02 -0600
Subject: [PATCH 12/12] prettify colors.js
---
extensions/LincolnX/colors.js | 724 +++++++++++++++++++---------------
1 file changed, 405 insertions(+), 319 deletions(-)
diff --git a/extensions/LincolnX/colors.js b/extensions/LincolnX/colors.js
index 3f9f178f76..c5b5600fe6 100644
--- a/extensions/LincolnX/colors.js
+++ b/extensions/LincolnX/colors.js
@@ -4,61 +4,64 @@
// By: LincolnX
// License: MPL-2.0
-(function(Scratch) {
- 'use strict';
+(function (Scratch) {
+ "use strict";
const { abs, round, floor, sqrt } = Math;
const toDec = (hex) => parseInt(hex, 16);
const toHex = (dec) => dec.toString(16);
- const limitHex = (hex, mi, ma) => toHex(Math.min(Math.max(toDec(hex), mi), ma));
+ const limitHex = (hex, mi, ma) =>
+ toHex(Math.min(Math.max(toDec(hex), mi), ma));
const clamp = (n, mi, ma) => Math.min(Math.max(n, mi), ma);
- const fixHex = (hex) => limitHex(hex, 0, 255).padStart(2, '0');
+ const fixHex = (hex) => limitHex(hex, 0, 255).padStart(2, "0");
const toFixHex = (dec) => fixHex(dec.toString(16));
const lerp = (a, b, t) => a + (b - a) * t;
function interpolateHexColorsHsv(hex1, hex2, t) {
const hsv1 = hexToHsv(hex1);
const hsv2 = hexToHsv(hex2);
- t = clamp(t, 0, 1)
+ t = clamp(t, 0, 1);
// Handle hue interpolation for the shortest path around the color wheel
let h1 = hsv1.h;
let h2 = hsv2.h;
let hueDiff = h2 - h1;
if (hueDiff > 180) {
- h1 += 360;
+ h1 += 360;
} else if (hueDiff < -180) {
- h2 += 360;
+ h2 += 360;
}
const h = h1 + t * (h2 - h1);
const s = hsv1.s + t * (hsv2.s - hsv1.s);
const v = hsv1.v + t * (hsv2.v - hsv1.v);
-
+
return hsvToHex({ h, s, v });
}
function hexToRgb(hex) {
- let r = 0, g = 0, b = 0;
+ let r = 0,
+ g = 0,
+ b = 0;
// Handle 3-digit shorthand
if (hex.length === 4) {
- r = parseInt(hex[1] + hex[1], 16);
- g = parseInt(hex[2] + hex[2], 16);
- b = parseInt(hex[3] + hex[3], 16);
+ r = parseInt(hex[1] + hex[1], 16);
+ g = parseInt(hex[2] + hex[2], 16);
+ b = parseInt(hex[3] + hex[3], 16);
} else if (hex.length === 7) {
- r = parseInt(hex.substring(1, 3), 16);
- g = parseInt(hex.substring(3, 5), 16);
- b = parseInt(hex.substring(5, 7), 16);
+ r = parseInt(hex.substring(1, 3), 16);
+ g = parseInt(hex.substring(3, 5), 16);
+ b = parseInt(hex.substring(5, 7), 16);
}
return { r, g, b };
}
function rgbToHex(rgb) {
- const makeHex = c => Math.round(c).toString(16).padStart(2, '0');
+ const makeHex = (c) => Math.round(c).toString(16).padStart(2, "0");
return `#${makeHex(rgb.r)}${makeHex(rgb.g)}${makeHex(rgb.b)}`;
}
function hsvToRgb(h, s, v) {
var r, g, b, i, f, p, q, t;
if (arguments.length === 1) {
- s = h.s, v = h.v, h = h.h;
+ ((s = h.s), (v = h.v), (h = h.h));
}
h /= 360;
s /= 100;
@@ -69,44 +72,68 @@
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch (i % 6) {
- case 0: r = v, g = t, b = p; break;
- case 1: r = q, g = v, b = p; break;
- case 2: r = p, g = v, b = t; break;
- case 3: r = p, g = q, b = v; break;
- case 4: r = t, g = p, b = v; break;
- case 5: r = v, g = p, b = q; break;
+ case 0:
+ ((r = v), (g = t), (b = p));
+ break;
+ case 1:
+ ((r = q), (g = v), (b = p));
+ break;
+ case 2:
+ ((r = p), (g = v), (b = t));
+ break;
+ case 3:
+ ((r = p), (g = q), (b = v));
+ break;
+ case 4:
+ ((r = t), (g = p), (b = v));
+ break;
+ case 5:
+ ((r = v), (g = p), (b = q));
+ break;
}
return {
- r: round(r * 255),
- g: round(g * 255),
- b: round(b * 255)
+ r: round(r * 255),
+ g: round(g * 255),
+ b: round(b * 255),
};
}
function rgbToHsv(r, g, b) {
if (arguments.length === 1) {
- g = r.g, b = r.b, r = r.r;
+ ((g = r.g), (b = r.b), (r = r.r));
}
- var max = Math.max(r, g, b), min = Math.min(r, g, b),
- d = max - min,
- h,
- s = (max === 0 ? 0 : d / max),
- v = max / 255;
+ var max = Math.max(r, g, b),
+ min = Math.min(r, g, b),
+ d = max - min,
+ h,
+ s = max === 0 ? 0 : d / max,
+ v = max / 255;
switch (max) {
- case min: h = 0; break;
- case r: h = (g - b) + d * (g < b ? 6: 0); h /= 6 * d; break;
- case g: h = (b - r) + d * 2; h /= 6 * d; break;
- case b: h = (r - g) + d * 4; h /= 6 * d; break;
+ case min:
+ h = 0;
+ break;
+ case r:
+ h = g - b + d * (g < b ? 6 : 0);
+ h /= 6 * d;
+ break;
+ case g:
+ h = b - r + d * 2;
+ h /= 6 * d;
+ break;
+ case b:
+ h = r - g + d * 4;
+ h /= 6 * d;
+ break;
}
return {
- h: h * 360,
- s: s * 100,
- v: v * 100
+ h: h * 360,
+ s: s * 100,
+ v: v * 100,
};
}
const hexToHsv = (hex) => rgbToHsv(hexToRgb(hex));
function hsvToHex(h, s, v) {
if (arguments.length === 1) {
- s = h.s, v = h.v, h = h.h;
+ ((s = h.s), (v = h.v), (h = h.h));
}
return rgbToHex(hsvToRgb(h, s, v));
}
@@ -121,27 +148,25 @@
} else {
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
- r = hueToRgb(p, q, h + 1/3);
+ r = hueToRgb(p, q, h + 1 / 3);
g = hueToRgb(p, q, h);
- b = hueToRgb(p, q, h - 1/3);
+ b = hueToRgb(p, q, h - 1 / 3);
}
- return {r: round(r * 255), g: round(g * 255), b: round(b * 255)};
+ return { r: round(r * 255), g: round(g * 255), b: round(b * 255) };
}
function hueToRgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
- if (t < 1/6) return p + (q - p) * 6 * t;
- if (t < 1/2) return q;
- if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
function channelToLinear(c) {
c /= 255;
- return c <= 0.03928
- ? c / 12.92
- : ((c + 0.055) / 1.055) ** 2.4;
+ return c <= 0.03928 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
}
function relativeLuminance(hex) {
@@ -160,8 +185,6 @@
return (light + 0.05) / (dark + 0.05);
}
-
-
function distanceBetweenHexColorsDeltaE2000(hex1, hex2) {
// convert Hex to RGB
const rgb1 = hexToRgb(hex1);
@@ -184,9 +207,9 @@
g /= 255;
b /= 255;
- r = (r > 0.04045) ? ((r + 0.055) / 1.055) ** 2.4 : r / 12.92;
- g = (g > 0.04045) ? ((g + 0.055) / 1.055) ** 2.4 : g / 12.92;
- b = (b > 0.04045) ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92;
+ r = r > 0.04045 ? ((r + 0.055) / 1.055) ** 2.4 : r / 12.92;
+ g = g > 0.04045 ? ((g + 0.055) / 1.055) ** 2.4 : g / 12.92;
+ b = b > 0.04045 ? ((b + 0.055) / 1.055) ** 2.4 : b / 12.92;
r *= 100;
g *= 100;
@@ -204,39 +227,44 @@
function xyzToLab(x, y, z) {
// D65 white point values
const refX = 95.047;
- const refY = 100.000;
+ const refY = 100.0;
const refZ = 108.883;
x /= refX;
y /= refY;
z /= refZ;
- x = (x > 0.008856) ? x ** (1/3) : (7.787 * x) + 16/116;
- y = (y > 0.008856) ? y ** (1/3) : (7.787 * y) + 16/116;
- z = (z > 0.008856) ? z ** (1/3) : (7.787 * z) + 16/116;
+ x = x > 0.008856 ? x ** (1 / 3) : 7.787 * x + 16 / 116;
+ y = y > 0.008856 ? y ** (1 / 3) : 7.787 * y + 16 / 116;
+ z = z > 0.008856 ? z ** (1 / 3) : 7.787 * z + 16 / 116;
- const L = (116 * y) - 16;
+ const L = 116 * y - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return { L, a, b };
}
-
// calculates the Delta E 2000 difference between two Lab colors.
// based on the implementation notes from Sharma et al..
function deltaE2000(lab1, lab2) {
- const kL = 1.0, kC = 1.0, kH = 1.0; // parametric factors often set to 1.0
+ const kL = 1.0,
+ kC = 1.0,
+ kH = 1.0; // parametric factors often set to 1.0
const deg2rad = Math.PI / 180;
const rad2deg = 180 / Math.PI;
// extract Lab values
- const L1 = lab1.L, a1 = lab1.a, b1 = lab1.b;
- const L2 = lab2.L, a2 = lab2.a, b2 = lab2.b;
+ const L1 = lab1.L,
+ a1 = lab1.a,
+ b1 = lab1.b;
+ const L2 = lab2.L,
+ a2 = lab2.a,
+ b2 = lab2.b;
// calculate C*ab values (Chroma)
- const C1 = sqrt(a1*a1 + b1*b1);
- const C2 = sqrt(a2*a2 + b2*b2);
+ const C1 = sqrt(a1 * a1 + b1 * b1);
+ const C2 = sqrt(a2 * a2 + b2 * b2);
const CBar = (C1 + C2) / 2.0;
// calculate G (chroma correction factor)
@@ -246,33 +274,35 @@
// calculate a' and C' (lightness corrected a* and new chroma)
const a1Prime = a1 * (1 + G);
const a2Prime = a2 * (1 + G);
- const C1Prime = sqrt(a1Prime*a1Prime + b1*b1);
- const C2Prime = sqrt(a2Prime*a2Prime + b2*b2);
+ const C1Prime = sqrt(a1Prime * a1Prime + b1 * b1);
+ const C2Prime = sqrt(a2Prime * a2Prime + b2 * b2);
const CBarPrime = (C1Prime + C2Prime) / 2.0;
const DeltaCPrime = C2Prime - C1Prime;
// calculate h' (hue angle)
- const h1Prime = (Math.atan2(b1, a1Prime) * rad2deg);
- const h2Prime = (Math.atan2(b2, a2Prime) * rad2deg);
+ const h1Prime = Math.atan2(b1, a1Prime) * rad2deg;
+ const h2Prime = Math.atan2(b2, a2Prime) * rad2deg;
// normalize hue angles to 0-360 range
- const normalizedH1 = h1Prime >= 0 ? h1Prime : (h1Prime + 360);
- const normalizedH2 = h2Prime >= 0 ? h2Prime : (h2Prime + 360);
+ const normalizedH1 = h1Prime >= 0 ? h1Prime : h1Prime + 360;
+ const normalizedH2 = h2Prime >= 0 ? h2Prime : h2Prime + 360;
// calculate Delta h' (hue difference) and Delta H' (weighted hue difference)
let DeltaHPrime;
if (C1Prime * C2Prime === 0) {
- DeltaHPrime = 0; // if one chroma is zero, hue difference is meaningless.
+ DeltaHPrime = 0; // if one chroma is zero, hue difference is meaningless.
} else if (abs(normalizedH1 - normalizedH2) <= 180) {
- DeltaHPrime = normalizedH2 - normalizedH1;
- } else if ((normalizedH2 - normalizedH1) > 180) {
- DeltaHPrime = (normalizedH2 - normalizedH1) - 360;
- } else { // (h2 - h1) < -180
- DeltaHPrime = (normalizedH2 - normalizedH1) + 360;
+ DeltaHPrime = normalizedH2 - normalizedH1;
+ } else if (normalizedH2 - normalizedH1 > 180) {
+ DeltaHPrime = normalizedH2 - normalizedH1 - 360;
+ } else {
+ // (h2 - h1) < -180
+ DeltaHPrime = normalizedH2 - normalizedH1 + 360;
}
-
+
// convert Delta H' to a metric difference (ΔH')
- const DeltaSmallHPrime = 2 * sqrt(C1Prime * C2Prime) * Math.sin(DeltaHPrime * deg2rad / 2.0);
+ const DeltaSmallHPrime =
+ 2 * sqrt(C1Prime * C2Prime) * Math.sin((DeltaHPrime * deg2rad) / 2.0);
// calculate Delta L'
const DeltaLPrime = L2 - L1;
@@ -280,36 +310,42 @@
// calculate Average H' (HBarPrime)
let HBarPrime;
if (C1Prime * C2Prime === 0) {
- HBarPrime = normalizedH1 + normalizedH2; // use sum as average if one is indeterminate
+ HBarPrime = normalizedH1 + normalizedH2; // use sum as average if one is indeterminate
} else if (abs(normalizedH1 - normalizedH2) > 180) {
- if ((normalizedH1 + normalizedH2) < 360) {
- HBarPrime = (normalizedH1 + normalizedH2 + 360) / 2.0;
- } else {
- HBarPrime = (normalizedH1 + normalizedH2 - 360) / 2.0;
- }
+ if (normalizedH1 + normalizedH2 < 360) {
+ HBarPrime = (normalizedH1 + normalizedH2 + 360) / 2.0;
+ } else {
+ HBarPrime = (normalizedH1 + normalizedH2 - 360) / 2.0;
+ }
} else {
- HBarPrime = (normalizedH1 + normalizedH2) / 2.0;
+ HBarPrime = (normalizedH1 + normalizedH2) / 2.0;
}
// calculate T (hue weighting function)
- const T = 1.0 - 0.17 * Math.cos((HBarPrime - 30.0) * deg2rad)
- + 0.24 * Math.cos((2.0 * HBarPrime) * deg2rad)
- + 0.32 * Math.cos((3.0 * HBarPrime + 6.0) * deg2rad)
- - 0.20 * Math.cos((4.0 * HBarPrime - 63.0) * deg2rad);
+ const T =
+ 1.0 -
+ 0.17 * Math.cos((HBarPrime - 30.0) * deg2rad) +
+ 0.24 * Math.cos(2.0 * HBarPrime * deg2rad) +
+ 0.32 * Math.cos((3.0 * HBarPrime + 6.0) * deg2rad) -
+ 0.2 * Math.cos((4.0 * HBarPrime - 63.0) * deg2rad);
// calculate SL, SC, SH (weighting functions)
- const SL = 1.0 + ((0.015 * ((HBarPrime - 27.5) ** 2) / (20.0 + ((HBarPrime - 27.5) ** 2))));
+ const SL =
+ 1.0 +
+ (0.015 * (HBarPrime - 27.5) ** 2) / (20.0 + (HBarPrime - 27.5) ** 2);
const SC = 1.0 + 0.045 * CBarPrime;
const SH = 1.0 + 0.015 * CBarPrime * T;
// calculate RT (rotation term)
const CBarPrimePow7 = CBarPrime ** 7;
- const RT = -2.0 * Math.sin(HBarPrime * deg2rad)
- * sqrt(CBarPrimePow7 / (CBarPrimePow7 + 6103515625.0));
+ const RT =
+ -2.0 *
+ Math.sin(HBarPrime * deg2rad) *
+ sqrt(CBarPrimePow7 / (CBarPrimePow7 + 6103515625.0));
// calculate the final Delta E 2000 value
- const deltaE =sqrt(
- (DeltaLPrime / (kL * SL)) ** 2 +
+ const deltaE = sqrt(
+ (DeltaLPrime / (kL * SL)) ** 2 +
(DeltaCPrime / (kC * SC)) ** 2 +
(DeltaSmallHPrime / (kH * SH)) ** 2 +
RT * (DeltaCPrime / (kC * SC)) * (DeltaSmallHPrime / (kH * SH))
@@ -317,429 +353,436 @@
return deltaE;
}
-
- const _spectrumIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1NS42NTIwNCIgaGVpZ2h0PSI1NS42NTIwNCIgdmlld0JveD0iMCwwLDU1LjY1MjA0LDU1LjY1MjA0Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgeDE9IjI0Mi4wNjU4NSIgeTE9IjE1Mi4zNjgwMSIgeDI9IjIzNy45MzQxOSIgeTI9IjIwNy42MzE5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMzMzNhZmYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMzNhZmYiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IHgxPSIyMTIuMzY4MDIiIHkxPSIxNzcuOTM0MTciIHgyPSIyNjcuNjMxOTkiIHkyPSIxODIuMDY1ODMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0iY29sb3ItMiI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzZmZjMzIiBzdG9wLW9wYWNpdHk9IjAuNzAxOTYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNmZmMzMiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMTIuMTc0LC0xNTIuMTczOTgpIj48ZyBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMTIuMjkwOTEsMTgwYzAsLTE1LjMwMzMyIDEyLjQwNTc5LC0yNy43MDkxMSAyNy43MDkxMSwtMjcuNzA5MTFjMTUuMzAzMzIsMCAyNy43MDkxMSwxMi40MDU3OSAyNy43MDkxMSwyNy43MDkxMWMwLDE1LjMwMzMyIC0xMi40MDU3OSwyNy43MDkxMSAtMjcuNzA5MTEsMjcuNzA5MTFjLTE1LjMwMzMyLDAgLTI3LjcwOTExLC0xMi40MDU3OSAtMjcuNzA5MTEsLTI3LjcwOTExeiIgZmlsbD0iI2ZmMzMzMyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTIuMzY4MDMsMTc3LjkzNDE3YzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgyLC0yNS41NjYxNmMxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODJjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgyLDI1LjU2NjE2Yy0xNS4yNjA3MywtMS4xNDA5MyAtMjYuNzA3MDksLTE0LjQzNzEgLTI1LjU2NjE2LC0yOS42OTc4MnoiIGZpbGw9InVybCgjY29sb3ItMSkiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSJOYU4iLz48cGF0aCBkPSJNMjM3LjkzNDE5LDIwNy42MzJjLTE1LjI2MDcyLC0xLjE0MDkzIC0yNi43MDcwOSwtMTQuNDM3MSAtMjUuNTY2MTYsLTI5LjY5NzgzYzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgzLC0yNS41NjYxN2MxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODNjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgzLDI1LjU2NjE3eiIgZmlsbD0idXJsKCNjb2xvci0yKSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTMuNDI0LDE4MGMwLC0xNC42Nzc1MyAxMS44OTg0OSwtMjYuNTc2MDIgMjYuNTc2MDIsLTI2LjU3NjAyYzE0LjY3NzUzLDAgMjYuNTc2MDIsMTEuODk4NDkgMjYuNTc2MDIsMjYuNTc2MDJjMCwxNC42Nzc1MyAtMTEuODk4NDksMjYuNTc2MDIgLTI2LjU3NjAyLDI2LjU3NjAyYy0xNC42Nzc1MywwIC0yNi41NzYwMiwtMTEuODk4NDkgLTI2LjU3NjAyLC0yNi41NzYwMnoiIGZpbGw9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIwLjQ0MzE0IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi41Ii8+PC9nPjwvZz48L3N2Zz4=";
- const blockIcon = "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNi41ODk4NSIgaGVpZ2h0PSIxNi41ODk4NSIgdmlld0JveD0iMCwwLDE2LjU4OTg1LDE2LjU4OTg1Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMxLjcwNTA4LC0xNzEuNzA1MDYpIj48ZyBzdHJva2Utd2lkdGg9IjAuNjI1IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMzIuMDQ1LDE4M2MwLC0yLjQ4NTI4IDIuMDE0NzIsLTQuNSA0LjUsLTQuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjV6IiBmaWxsPSIjNGM5N2ZmIiBzdHJva2U9IiMzMzczY2MiLz48cGF0aCBkPSJNMjQwLjA0NSwxNzIuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmY2NjgwIiBzdHJva2U9IiNmZjMzNTUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmZiZjAwIiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMxLjcwNTA5LDE4OC4yOTQ5MnYtMTYuNTg5ODVoMTYuNTg5ODV2MTYuNTg5ODV6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsLW9wYWNpdHk9IjAuNTAxOTYiIGZpbGw9IiNmZmJmMDAiIHN0cm9rZS1vcGFjaXR5PSIwLjUwMTk2IiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMyLjA0NSwxODNjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjNGQ5N2ZmIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjMzM3M2NjIi8+PHBhdGggZD0iTTI0MC4wNDUsMTcyLjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41YzAsLTIuNDg1MjggMi4wMTQ3MiwtNC41IDQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjZmY2NjgwIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjZmYzMzU1Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6OC4yOTQ5MTUwMDAwMDAwMDM6OC4yOTQ5MzUwMDAwMDAwMS0tPg==";
+
+ const _spectrumIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSI1NS42NTIwNCIgaGVpZ2h0PSI1NS42NTIwNCIgdmlld0JveD0iMCwwLDU1LjY1MjA0LDU1LjY1MjA0Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgeDE9IjI0Mi4wNjU4NSIgeTE9IjE1Mi4zNjgwMSIgeDI9IjIzNy45MzQxOSIgeTI9IjIwNy42MzE5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIGlkPSJjb2xvci0xIj48c3RvcCBvZmZzZXQ9IjAiIHN0b3AtY29sb3I9IiMzMzNhZmYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzMzNhZmYiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IHgxPSIyMTIuMzY4MDIiIHkxPSIxNzcuOTM0MTciIHgyPSIyNjcuNjMxOTkiIHkyPSIxODIuMDY1ODMiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiBpZD0iY29sb3ItMiI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjMzZmZjMzIiBzdG9wLW9wYWNpdHk9IjAuNzAxOTYiLz48c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMzNmZmMzMiIHN0b3Atb3BhY2l0eT0iMCIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMTIuMTc0LC0xNTIuMTczOTgpIj48ZyBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMTIuMjkwOTEsMTgwYzAsLTE1LjMwMzMyIDEyLjQwNTc5LC0yNy43MDkxMSAyNy43MDkxMSwtMjcuNzA5MTFjMTUuMzAzMzIsMCAyNy43MDkxMSwxMi40MDU3OSAyNy43MDkxMSwyNy43MDkxMWMwLDE1LjMwMzMyIC0xMi40MDU3OSwyNy43MDkxMSAtMjcuNzA5MTEsMjcuNzA5MTFjLTE1LjMwMzMyLDAgLTI3LjcwOTExLC0xMi40MDU3OSAtMjcuNzA5MTEsLTI3LjcwOTExeiIgZmlsbD0iI2ZmMzMzMyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTIuMzY4MDMsMTc3LjkzNDE3YzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgyLC0yNS41NjYxNmMxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODJjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgyLDI1LjU2NjE2Yy0xNS4yNjA3MywtMS4xNDA5MyAtMjYuNzA3MDksLTE0LjQzNzEgLTI1LjU2NjE2LC0yOS42OTc4MnoiIGZpbGw9InVybCgjY29sb3ItMSkiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSJOYU4iLz48cGF0aCBkPSJNMjM3LjkzNDE5LDIwNy42MzJjLTE1LjI2MDcyLC0xLjE0MDkzIC0yNi43MDcwOSwtMTQuNDM3MSAtMjUuNTY2MTYsLTI5LjY5NzgzYzEuMTQwOTMsLTE1LjI2MDczIDE0LjQzNzEsLTI2LjcwNzA5IDI5LjY5NzgzLC0yNS41NjYxN2MxNS4yNjA3MywxLjE0MDkzIDI2LjcwNzA5LDE0LjQzNzEgMjUuNTY2MTYsMjkuNjk3ODNjLTEuMTQwOTMsMTUuMjYwNzMgLTE0LjQzNzEsMjYuNzA3MDkgLTI5LjY5NzgzLDI1LjU2NjE3eiIgZmlsbD0idXJsKCNjb2xvci0yKSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9Ik5hTiIvPjxwYXRoIGQ9Ik0yMTMuNDI0LDE4MGMwLC0xNC42Nzc1MyAxMS44OTg0OSwtMjYuNTc2MDIgMjYuNTc2MDIsLTI2LjU3NjAyYzE0LjY3NzUzLDAgMjYuNTc2MDIsMTEuODk4NDkgMjYuNTc2MDIsMjYuNTc2MDJjMCwxNC42Nzc1MyAtMTEuODk4NDksMjYuNTc2MDIgLTI2LjU3NjAyLDI2LjU3NjAyYy0xNC42Nzc1MywwIC0yNi41NzYwMiwtMTEuODk4NDkgLTI2LjU3NjAyLC0yNi41NzYwMnoiIGZpbGw9Im5vbmUiIHN0cm9rZS1vcGFjaXR5PSIwLjQ0MzE0IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMi41Ii8+PC9nPjwvZz48L3N2Zz4=";
+ const blockIcon =
+ "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIxNi41ODk4NSIgaGVpZ2h0PSIxNi41ODk4NSIgdmlld0JveD0iMCwwLDE2LjU4OTg1LDE2LjU4OTg1Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMjMxLjcwNTA4LC0xNzEuNzA1MDYpIj48ZyBzdHJva2Utd2lkdGg9IjAuNjI1IiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiPjxwYXRoIGQ9Ik0yMzIuMDQ1LDE4M2MwLC0yLjQ4NTI4IDIuMDE0NzIsLTQuNSA0LjUsLTQuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjV6IiBmaWxsPSIjNGM5N2ZmIiBzdHJva2U9IiMzMzczY2MiLz48cGF0aCBkPSJNMjQwLjA0NSwxNzIuNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmY2NjgwIiBzdHJva2U9IiNmZjMzNTUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsPSIjZmZiZjAwIiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMxLjcwNTA5LDE4OC4yOTQ5MnYtMTYuNTg5ODVoMTYuNTg5ODV2MTYuNTg5ODV6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMjQzLjQ1NSwxNzguNWMyLjQ4NTI4LDAgNC41LDIuMDE0NzIgNC41LDQuNWMwLDIuNDg1MjggLTIuMDE0NzIsNC41IC00LjUsNC41Yy0yLjQ4NTI4LDAgLTQuNSwtMi4wMTQ3MiAtNC41LC00LjVjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjV6IiBmaWxsLW9wYWNpdHk9IjAuNTAxOTYiIGZpbGw9IiNmZmJmMDAiIHN0cm9rZS1vcGFjaXR5PSIwLjUwMTk2IiBzdHJva2U9IiNjYzk5MDAiLz48cGF0aCBkPSJNMjMyLjA0NSwxODNjMCwtMi40ODUyOCAyLjAxNDcyLC00LjUgNC41LC00LjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjNGQ5N2ZmIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjMzM3M2NjIi8+PHBhdGggZD0iTTI0MC4wNDUsMTcyLjVjMi40ODUyOCwwIDQuNSwyLjAxNDcyIDQuNSw0LjVjMCwyLjQ4NTI4IC0yLjAxNDcyLDQuNSAtNC41LDQuNWMtMi40ODUyOCwwIC00LjUsLTIuMDE0NzIgLTQuNSwtNC41YzAsLTIuNDg1MjggMi4wMTQ3MiwtNC41IDQuNSwtNC41eiIgZmlsbC1vcGFjaXR5PSIwLjUwMTk2IiBmaWxsPSIjZmY2NjgwIiBzdHJva2Utb3BhY2l0eT0iMC41MDE5NiIgc3Ryb2tlPSIjZmYzMzU1Ii8+PC9nPjwvZz48L3N2Zz48IS0tcm90YXRpb25DZW50ZXI6OC4yOTQ5MTUwMDAwMDAwMDM6OC4yOTQ5MzUwMDAwMDAwMS0tPg==";
class Colors {
getInfo() {
return {
- id: 'lxColors',
- name: Scratch.translate('Colors'),
- color1: '#f94c97',
+ id: "lxColors",
+ name: Scratch.translate("Colors"),
+ color1: "#f94c97",
menuIconURI: blockIcon,
blocks: [
{
- opcode: 'newColor',
+ opcode: "newColor",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('new color [COL]'),
+ text: Scratch.translate("new color [COL]"),
arguments: {
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
- }
- }
+ defaultValue: "#a45eff",
+ },
+ },
},
{
- opcode: 'newColorRGB',
+ opcode: "newColorRGB",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('from RGB [R] [G] [B]'),
+ text: Scratch.translate("from RGB [R] [G] [B]"),
arguments: {
R: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '164'
+ defaultValue: "164",
},
G: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '94'
+ defaultValue: "94",
},
B: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '255'
- }
- }
+ defaultValue: "255",
+ },
+ },
},
{
- opcode: 'newColorHSV',
+ opcode: "newColorHSV",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('from HSV [H] [S] [V]'),
+ text: Scratch.translate("from HSV [H] [S] [V]"),
arguments: {
H: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '266'
+ defaultValue: "266",
},
S: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '63'
+ defaultValue: "63",
},
V: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
- }
- }
+ defaultValue: "100",
+ },
+ },
},
{
- opcode: 'newColorHSL',
+ opcode: "newColorHSL",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('from HSL [H] [S] [L]'),
+ text: Scratch.translate("from HSL [H] [S] [L]"),
arguments: {
H: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '266'
+ defaultValue: "266",
},
S: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '100'
+ defaultValue: "100",
},
L: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '68'
- }
- }
+ defaultValue: "68",
+ },
+ },
},
{
- opcode: 'newColorDecimal',
+ opcode: "newColorDecimal",
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate({
- default: 'from decimal [DEC]',
- description: "From decimal - as in the base system"
+ default: "from decimal [DEC]",
+ description: "From decimal - as in the base system",
}),
arguments: {
DEC: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '10772223'
- }
- }
+ defaultValue: "10772223",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'randomColor',
+ opcode: "randomColor",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('random color'),
- disableMonitor: true
+ text: Scratch.translate("random color"),
+ disableMonitor: true,
},
- '---',
+ "---",
{
- opcode: 'additiveBlend',
+ opcode: "additiveBlend",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('[COL1] + [COL2]'),
+ text: Scratch.translate("[COL1] + [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
{
- opcode: 'subtractiveBlend',
+ opcode: "subtractiveBlend",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('[COL1] - [COL2]'),
+ text: Scratch.translate("[COL1] - [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
{
- opcode: 'multiplicativeBlend',
+ opcode: "multiplicativeBlend",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('[COL1] * [COL2]'),
+ text: Scratch.translate("[COL1] * [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
{
- opcode: 'divisingBlend',
+ opcode: "divisingBlend",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('[COL1] / [COL2]'),
+ text: Scratch.translate("[COL1] / [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'differenceBlend',
+ opcode: "differenceBlend",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('difference of [COL1] - [COL2]'),
+ text: Scratch.translate("difference of [COL1] - [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
{
- opcode: 'screenBlend',
+ opcode: "screenBlend",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('screen [COL1] * [COL2]'),
+ text: Scratch.translate("screen [COL1] * [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
{
- opcode: 'overlayBlend',
+ opcode: "overlayBlend",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('overlay [COL1] * [COL2]'),
+ text: Scratch.translate("overlay [COL1] * [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'invertColor',
+ opcode: "invertColor",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('invert [COL]'),
+ text: Scratch.translate("invert [COL]"),
arguments: {
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
- }
+ },
},
{
- opcode: 'contrastColor',
+ opcode: "contrastColor",
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate({
- default: 'contrast [COL] by [NUM]',
- description: "Contrast - as a verb, comparing to highlight differences"
+ default: "contrast [COL] by [NUM]",
+ description:
+ "Contrast - as a verb, comparing to highlight differences",
}),
arguments: {
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
NUM: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0.5'
+ defaultValue: "0.5",
},
- }
+ },
},
{
- opcode: 'grayscaleColor',
+ opcode: "grayscaleColor",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('grayscale [COL]'),
+ text: Scratch.translate("grayscale [COL]"),
arguments: {
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
- }
- }
+ defaultValue: "#a45eff",
+ },
+ },
},
{
- opcode: 'percentWhite',
+ opcode: "percentWhite",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('[NUM] % white'),
+ text: Scratch.translate("[NUM] % white"),
arguments: {
NUM: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '75'
- }
- }
+ defaultValue: "75",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'distanceBetweenColors',
+ opcode: "distanceBetweenColors",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('distance between [COL1] and [COL2]'),
+ text: Scratch.translate("distance between [COL1] and [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
{
- opcode: 'contrastRatioOfColors',
+ opcode: "contrastRatioOfColors",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('contrast ratio of [COL1] and [COL2]'),
+ text: Scratch.translate("contrast ratio of [COL1] and [COL2]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
- }
- }
+ defaultValue: "#eb57ab",
+ },
+ },
},
{
- opcode: 'nearEqualColors',
+ opcode: "nearEqualColors",
blockType: Scratch.BlockType.BOOLEAN,
- text: Scratch.translate('[COL1] ≈ [COL2] threshold [THR]'),
+ text: Scratch.translate("[COL1] ≈ [COL2] threshold [THR]"),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
+ defaultValue: "#eb57ab",
},
THR: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '25'
- }
- }
+ defaultValue: "25",
+ },
+ },
},
{
- opcode: 'colorFollowsWCAG',
+ opcode: "colorFollowsWCAG",
blockType: Scratch.BlockType.BOOLEAN,
- text: Scratch.translate('does [COL1] and [COL2] follow [AAA] for [TXT] text'),
+ text: Scratch.translate(
+ "does [COL1] and [COL2] follow [AAA] for [TXT] text"
+ ),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
+ defaultValue: "#eb57ab",
},
AAA: {
type: Scratch.ArgumentType.STRING,
- menu: 'WCAG_MENU'
+ menu: "WCAG_MENU",
},
TXT: {
type: Scratch.ArgumentType.STRING,
- menu: 'TEXTWCAG_SIZE_MENU'
- }
+ menu: "TEXTWCAG_SIZE_MENU",
+ },
},
- hideFromPalette: true
+ hideFromPalette: true,
},
- '---',
+ "---",
{
- opcode: 'interpolateColors',
+ opcode: "interpolateColors",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('interpolate [COL1] to [COL2] ratio [RATIO] using [SPACE]'),
+ text: Scratch.translate(
+ "interpolate [COL1] to [COL2] ratio [RATIO] using [SPACE]"
+ ),
arguments: {
COL1: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
COL2: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#eb57ab'
+ defaultValue: "#eb57ab",
},
RATIO: {
type: Scratch.ArgumentType.NUMBER,
- defaultValue: '0.5'
+ defaultValue: "0.5",
},
SPACE: {
type: Scratch.ArgumentType.STRING,
- menu: 'SPACE_MENU'
- }
- }
+ menu: "SPACE_MENU",
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'getChannelFromColor',
+ opcode: "getChannelFromColor",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('get [CHN] of [COL]'),
+ text: Scratch.translate("get [CHN] of [COL]"),
arguments: {
CHN: {
type: Scratch.ArgumentType.STRING,
- menu: 'CHANNEL_MENU'
+ menu: "CHANNEL_MENU",
},
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
- }
- }
+ defaultValue: "#a45eff",
+ },
+ },
},
{
- opcode: 'setChannelOfColor',
+ opcode: "setChannelOfColor",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('set [CHN] of [COL] to [SET]'),
+ text: Scratch.translate("set [CHN] of [COL] to [SET]"),
arguments: {
CHN: {
type: Scratch.ArgumentType.STRING,
- menu: 'CHANNEL_MENU'
+ menu: "CHANNEL_MENU",
},
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
SET: {
type: Scratch.ArgumentType.NUMBER,
- }
- }
+ },
+ },
},
{
- opcode: 'changeChannelOfColor',
+ opcode: "changeChannelOfColor",
blockType: Scratch.BlockType.REPORTER,
- text: Scratch.translate('change [CHN] of [COL] by [SET]'),
+ text: Scratch.translate("change [CHN] of [COL] by [SET]"),
arguments: {
CHN: {
type: Scratch.ArgumentType.STRING,
- menu: 'CHANNEL_MENU'
+ menu: "CHANNEL_MENU",
},
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
+ defaultValue: "#a45eff",
},
SET: {
type: Scratch.ArgumentType.NUMBER,
- }
- }
+ },
+ },
},
- '---',
+ "---",
{
- opcode: 'colorToDecimal',
+ opcode: "colorToDecimal",
blockType: Scratch.BlockType.REPORTER,
text: Scratch.translate({
- default: '[COL] to decimal',
- description: "To decimal - as in the base system"
+ default: "[COL] to decimal",
+ description: "To decimal - as in the base system",
}),
arguments: {
COL: {
type: Scratch.ArgumentType.COLOR,
- defaultValue: '#a45eff'
- }
- }
+ defaultValue: "#a45eff",
+ },
+ },
},
],
menus: {
@@ -763,7 +806,7 @@
},
WCAG_MENU: {
acceptReporters: true,
- items: ['A', 'AA', 'AAA'],
+ items: ["A", "AA", "AAA"],
},
TEXTWCAG_SIZE_MENU: {
acceptReporters: true,
@@ -772,27 +815,34 @@
{ text: Scratch.translate("large"), value: "large" },
],
},
- }
+ },
};
}
newColor(args) {
return args.COL;
}
newColorRGB(args) {
- return '#' + toFixHex(args.R) + toFixHex(args.G) + toFixHex(args.B);
+ return "#" + toFixHex(args.R) + toFixHex(args.G) + toFixHex(args.B);
}
newColorHSV(args) {
return hsvToHex(args.H, args.S, args.V);
}
newColorHSL(args) {
let convRGB = hslToRgb(args.H, args.S, args.L);
- return '#' + toFixHex(convRGB.r) + toFixHex(convRGB.g) + toFixHex(convRGB.b);
+ return (
+ "#" + toFixHex(convRGB.r) + toFixHex(convRGB.g) + toFixHex(convRGB.b)
+ );
}
newColorDecimal(args) {
- return '#' + toHex(args.DEC);
+ return "#" + toHex(args.DEC);
}
randomColor() {
- return '#' + Math.floor(Math.random() * 0xFFFFFF).toString(16).padStart(6, '0');
+ return (
+ "#" +
+ Math.floor(Math.random() * 0xffffff)
+ .toString(16)
+ .padStart(6, "0")
+ );
}
additiveBlend(args) {
let a = hexToRgb(args.COL1);
@@ -801,7 +851,7 @@
return rgbToHex({
r: add(a.r, b.r),
g: add(a.g, b.g),
- b: add(a.b, b.b)
+ b: add(a.b, b.b),
});
}
subtractiveBlend(args) {
@@ -811,7 +861,7 @@
return rgbToHex({
r: sub(a.r, b.r),
g: sub(a.g, b.g),
- b: sub(a.b, b.b)
+ b: sub(a.b, b.b),
});
}
multiplicativeBlend(args) {
@@ -821,7 +871,7 @@
return rgbToHex({
r: mul(a.r, b.r),
g: mul(a.g, b.g),
- b: mul(a.b, b.b)
+ b: mul(a.b, b.b),
});
}
divisingBlend(args) {
@@ -831,7 +881,7 @@
return rgbToHex({
r: div(a.r, b.r),
g: div(a.g, b.g),
- b: div(a.b, b.b)
+ b: div(a.b, b.b),
});
}
differenceBlend(args) {
@@ -841,7 +891,7 @@
return rgbToHex({
r: sub(a.r, b.r),
g: sub(a.g, b.g),
- b: sub(a.b, b.b)
+ b: sub(a.b, b.b),
});
}
screenBlend(args) {
@@ -851,22 +901,29 @@
return rgbToHex({
r: scr(a.r, b.r),
g: scr(a.g, b.g),
- b: scr(a.b, b.b)
+ b: scr(a.b, b.b),
});
}
overlayBlend(args) {
let a = hexToRgb(args.COL1);
let b = hexToRgb(args.COL2);
- const ove = (c1, c2) => clamp(c1 < 128 ? (2 * c1 * c2) / 255 : 255 - (2 * (255 - c1) * (255 - c2)) / 255, 0, 255);
+ const ove = (c1, c2) =>
+ clamp(
+ c1 < 128
+ ? (2 * c1 * c2) / 255
+ : 255 - (2 * (255 - c1) * (255 - c2)) / 255,
+ 0,
+ 255
+ );
return rgbToHex({
r: ove(a.r, b.r),
g: ove(a.g, b.g),
- b: ove(a.b, b.b)
+ b: ove(a.b, b.b),
});
}
invertColor(args) {
let col = hexToRgb(args.COL);
- const inv = c => 255-c;
+ const inv = (c) => 255 - c;
return rgbToHex({
r: inv(col.r),
g: inv(col.g),
@@ -875,27 +932,29 @@
}
contrastColor(args) {
let col = hexToRgb(args.COL);
- const cnt = c => ((c - 128) * (1 - args.NUM)) + 128;
+ const cnt = (c) => (c - 128) * (1 - args.NUM) + 128;
return rgbToHex({
r: cnt(col.r),
g: cnt(col.g),
b: cnt(col.b),
});
}
- grayscaleColor(args) {
- let rgb = hexToRgb(args.COL);
+ grayscaleColor(args) {
+ let rgb = hexToRgb(args.COL);
let gray = Math.round(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b);
- return '#' + toFixHex(gray) + toFixHex(gray) + toFixHex(gray);
+ return "#" + toFixHex(gray) + toFixHex(gray) + toFixHex(gray);
}
percentWhite(args) {
- let white = round(args.NUM * 2.55)
- return '#' + toFixHex(white) + toFixHex(white) + toFixHex(white);
+ let white = round(args.NUM * 2.55);
+ return "#" + toFixHex(white) + toFixHex(white) + toFixHex(white);
}
distanceBetweenColors(args) {
return distanceBetweenHexColorsDeltaE2000(args.COL1, args.COL2);
}
nearEqualColors(args) {
- return distanceBetweenHexColorsDeltaE2000(args.COL1, args.COL2) <= args.THR;
+ return (
+ distanceBetweenHexColorsDeltaE2000(args.COL1, args.COL2) <= args.THR
+ );
}
contrastRatioOfColors(args) {
return round(contrastRatio(args.COL1, args.COL2) * 100) / 100;
@@ -905,76 +964,103 @@
let req = 0;
let level = args.AAA;
let size = args.TXT;
- if (level === 'AA') {
- req = (size === 'large') ? 3.0 : 4.5;
+ if (level === "AA") {
+ req = size === "large" ? 3.0 : 4.5;
}
- if (level === 'AAA') {
- req = (size === 'large') ? 4.5 : 7.0;
+ if (level === "AAA") {
+ req = size === "large" ? 4.5 : 7.0;
}
return ratio >= req;
}
interpolateColors(args) {
- if (args.SPACE == 'RGB') {
+ if (args.SPACE == "RGB") {
let a = hexToRgb(args.COL1);
let b = hexToRgb(args.COL2);
let t = args.RATIO;
return rgbToHex({
r: lerp(a.r, b.r, t),
g: lerp(a.g, b.g, t),
- b: lerp(a.b, b.b, t)
+ b: lerp(a.b, b.b, t),
});
} else {
- return interpolateHexColorsHsv(args.COL1, args.COL2, args.RATIO)
+ return interpolateHexColorsHsv(args.COL1, args.COL2, args.RATIO);
}
}
getChannelFromColor(args) {
- if (['red', 'green', 'blue'].includes(args.CHN)) {
- let channel = ['red', 'green', 'blue'].indexOf(args.CHN);
- let letter = ['red', 'green', 'blue'][channel][0];
+ if (["red", "green", "blue"].includes(args.CHN)) {
+ let channel = ["red", "green", "blue"].indexOf(args.CHN);
+ let letter = ["red", "green", "blue"][channel][0];
return hexToRgb(args.COL)[letter];
- } else if (['hue', 'saturation', 'value'].includes(args.CHN)) {
+ } else if (["hue", "saturation", "value"].includes(args.CHN)) {
let hsv = hexToHsv(args.COL);
switch (args.CHN) {
- case 'hue': return round(hsv.h);
- case 'saturation': return round(hsv.s);
- case 'value': return round(hsv.v);
+ case "hue":
+ return round(hsv.h);
+ case "saturation":
+ return round(hsv.s);
+ case "value":
+ return round(hsv.v);
}
}
}
setChannelOfColor(args) {
- if (['red', 'green', 'blue'].includes(args.CHN)) {
+ if (["red", "green", "blue"].includes(args.CHN)) {
let rgb = hexToRgb(args.COL);
switch (args.CHN) {
- case 'red': rgb.r = args.SET; break;
- case 'green': rgb.g = args.SET; break;
- case 'blue': rgb.b = args.SET; break;
+ case "red":
+ rgb.r = args.SET;
+ break;
+ case "green":
+ rgb.g = args.SET;
+ break;
+ case "blue":
+ rgb.b = args.SET;
+ break;
}
- return '#' + toFixHex(rgb.r) + toFixHex(rgb.g) + toFixHex(rgb.b);
- } else if (['hue', 'saturation', 'value'].includes(args.CHN)) {
+ return "#" + toFixHex(rgb.r) + toFixHex(rgb.g) + toFixHex(rgb.b);
+ } else if (["hue", "saturation", "value"].includes(args.CHN)) {
let hsv = hexToHsv(args.COL);
switch (args.CHN) {
- case 'hue': hsv.h = args.SET; break;
- case 'saturation': hsv.s = args.SET; break;
- case 'value': hsv.v = args.SET; break;
+ case "hue":
+ hsv.h = args.SET;
+ break;
+ case "saturation":
+ hsv.s = args.SET;
+ break;
+ case "value":
+ hsv.v = args.SET;
+ break;
}
return hsvToHex(hsv.h, hsv.s, hsv.v);
}
}
changeChannelOfColor(args) {
- if (['red', 'green', 'blue'].includes(args.CHN)) {
+ if (["red", "green", "blue"].includes(args.CHN)) {
let rgb = hexToRgb(args.COL);
switch (args.CHN) {
- case 'red': rgb.r = clamp(rgb.r + args.SET, 0, 255); break;
- case 'green': rgb.g = clamp(rgb.g + args.SET, 0, 255); break;
- case 'blue': rgb.b = clamp(rgb.b + args.SET, 0, 255); break;
+ case "red":
+ rgb.r = clamp(rgb.r + args.SET, 0, 255);
+ break;
+ case "green":
+ rgb.g = clamp(rgb.g + args.SET, 0, 255);
+ break;
+ case "blue":
+ rgb.b = clamp(rgb.b + args.SET, 0, 255);
+ break;
}
- return '#' + toFixHex(rgb.r) + toFixHex(rgb.g) + toFixHex(rgb.b);
- } else if (['hue', 'saturation', 'value'].includes(args.CHN)) {
+ return "#" + toFixHex(rgb.r) + toFixHex(rgb.g) + toFixHex(rgb.b);
+ } else if (["hue", "saturation", "value"].includes(args.CHN)) {
let hsv = hexToHsv(args.COL);
switch (args.CHN) {
- case 'hue': hsv.h = (hsv.h + args.SET) % 360; break;
- case 'saturation': hsv.s = clamp(hsv.s + args.SET, 0, 100); break;
- case 'value': hsv.v = clamp(hsv.v + args.SET, 0, 100); break;
+ case "hue":
+ hsv.h = (hsv.h + args.SET) % 360;
+ break;
+ case "saturation":
+ hsv.s = clamp(hsv.s + args.SET, 0, 100);
+ break;
+ case "value":
+ hsv.v = clamp(hsv.v + args.SET, 0, 100);
+ break;
}
return hsvToHex(hsv.h, hsv.s, hsv.v);
}