@utilix-tech/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/dist/api-8aZtWhSj.d.cts +308 -0
- package/dist/api-8aZtWhSj.d.ts +308 -0
- package/dist/chunk-2CJSLYWI.js +520 -0
- package/dist/chunk-3BAHSW4C.js +1385 -0
- package/dist/chunk-6YPV2AB5.js +1246 -0
- package/dist/chunk-BPVAB4P2.js +1126 -0
- package/dist/chunk-FL53T24A.js +257 -0
- package/dist/chunk-L6KCTHUW.js +1086 -0
- package/dist/chunk-MLKGABMK.js +7 -0
- package/dist/chunk-ROTPLW7T.js +265 -0
- package/dist/chunk-TSAGO3XP.js +1253 -0
- package/dist/chunk-UC656APA.js +1080 -0
- package/dist/chunk-W4UBLYFU.js +1219 -0
- package/dist/chunk-XST6X3HT.js +896 -0
- package/dist/chunk-XXYZLLHI.js +2227 -0
- package/dist/chunk-ZKL2VX2G.js +2215 -0
- package/dist/chunk-ZPQZEIXP.js +685 -0
- package/dist/code-QNrdLIR3.d.cts +327 -0
- package/dist/code-QNrdLIR3.d.ts +327 -0
- package/dist/color-tPwZCr9H.d.cts +229 -0
- package/dist/color-tPwZCr9H.d.ts +229 -0
- package/dist/css-Cf7AMGM-.d.cts +318 -0
- package/dist/css-Cf7AMGM-.d.ts +318 -0
- package/dist/data-rMjWNiOJ.d.cts +215 -0
- package/dist/data-rMjWNiOJ.d.ts +215 -0
- package/dist/encoding-7gmq-vkV.d.cts +108 -0
- package/dist/encoding-7gmq-vkV.d.ts +108 -0
- package/dist/generators-BGtRGpJZ.d.cts +118 -0
- package/dist/generators-BGtRGpJZ.d.ts +118 -0
- package/dist/hashing-CnetQFD_.d.cts +76 -0
- package/dist/hashing-CnetQFD_.d.ts +76 -0
- package/dist/index.cjs +15466 -0
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +20 -0
- package/dist/json-BjSoIS1h.d.cts +198 -0
- package/dist/json-BjSoIS1h.d.ts +198 -0
- package/dist/misc-DcVBManm.d.cts +201 -0
- package/dist/misc-DcVBManm.d.ts +201 -0
- package/dist/network-CNtmrDeN.d.cts +170 -0
- package/dist/network-CNtmrDeN.d.ts +170 -0
- package/dist/text-DqAjPtQ0.d.cts +319 -0
- package/dist/text-DqAjPtQ0.d.ts +319 -0
- package/dist/time-DbT8fjaF.d.cts +145 -0
- package/dist/time-DbT8fjaF.d.ts +145 -0
- package/dist/tools/api.cjs +1216 -0
- package/dist/tools/api.d.cts +1 -0
- package/dist/tools/api.d.ts +1 -0
- package/dist/tools/api.js +2 -0
- package/dist/tools/code.cjs +2212 -0
- package/dist/tools/code.d.cts +1 -0
- package/dist/tools/code.d.ts +1 -0
- package/dist/tools/code.js +2 -0
- package/dist/tools/color.cjs +1122 -0
- package/dist/tools/color.d.cts +1 -0
- package/dist/tools/color.d.ts +1 -0
- package/dist/tools/color.js +2 -0
- package/dist/tools/css.cjs +1242 -0
- package/dist/tools/css.d.cts +1 -0
- package/dist/tools/css.d.ts +1 -0
- package/dist/tools/css.js +2 -0
- package/dist/tools/data.cjs +1097 -0
- package/dist/tools/data.d.cts +1 -0
- package/dist/tools/data.d.ts +1 -0
- package/dist/tools/data.js +2 -0
- package/dist/tools/encoding.cjs +261 -0
- package/dist/tools/encoding.d.cts +1 -0
- package/dist/tools/encoding.d.ts +1 -0
- package/dist/tools/encoding.js +2 -0
- package/dist/tools/generators.cjs +521 -0
- package/dist/tools/generators.d.cts +1 -0
- package/dist/tools/generators.d.ts +1 -0
- package/dist/tools/generators.js +2 -0
- package/dist/tools/hashing.cjs +259 -0
- package/dist/tools/hashing.d.cts +1 -0
- package/dist/tools/hashing.d.ts +1 -0
- package/dist/tools/hashing.js +2 -0
- package/dist/tools/json.cjs +1270 -0
- package/dist/tools/json.d.cts +1 -0
- package/dist/tools/json.d.ts +1 -0
- package/dist/tools/json.js +2 -0
- package/dist/tools/misc.cjs +1082 -0
- package/dist/tools/misc.d.cts +1 -0
- package/dist/tools/misc.d.ts +1 -0
- package/dist/tools/misc.js +2 -0
- package/dist/tools/network.cjs +1381 -0
- package/dist/tools/network.d.cts +1 -0
- package/dist/tools/network.d.ts +1 -0
- package/dist/tools/network.js +2 -0
- package/dist/tools/text.cjs +2224 -0
- package/dist/tools/text.d.cts +1 -0
- package/dist/tools/text.d.ts +1 -0
- package/dist/tools/text.js +2 -0
- package/dist/tools/time.cjs +681 -0
- package/dist/tools/time.d.cts +1 -0
- package/dist/tools/time.d.ts +1 -0
- package/dist/tools/time.js +2 -0
- package/dist/tools/units.cjs +892 -0
- package/dist/tools/units.d.cts +1 -0
- package/dist/tools/units.d.ts +1 -0
- package/dist/tools/units.js +2 -0
- package/dist/units-6lwDYBvX.d.cts +302 -0
- package/dist/units-6lwDYBvX.d.ts +302 -0
- package/package.json +148 -0
|
@@ -0,0 +1,1122 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/tools/color.ts
|
|
4
|
+
var NAMED_COLORS_CONVERTER = {
|
|
5
|
+
red: "#FF0000",
|
|
6
|
+
green: "#008000",
|
|
7
|
+
blue: "#0000FF",
|
|
8
|
+
white: "#FFFFFF",
|
|
9
|
+
black: "#000000",
|
|
10
|
+
yellow: "#FFFF00",
|
|
11
|
+
orange: "#FFA500",
|
|
12
|
+
purple: "#800080",
|
|
13
|
+
pink: "#FFC0CB",
|
|
14
|
+
cyan: "#00FFFF",
|
|
15
|
+
magenta: "#FF00FF",
|
|
16
|
+
brown: "#A52A2A",
|
|
17
|
+
gray: "#808080",
|
|
18
|
+
grey: "#808080",
|
|
19
|
+
lime: "#00FF00",
|
|
20
|
+
indigo: "#4B0082",
|
|
21
|
+
violet: "#EE82EE",
|
|
22
|
+
silver: "#C0C0C0",
|
|
23
|
+
gold: "#FFD700",
|
|
24
|
+
navy: "#000080"
|
|
25
|
+
};
|
|
26
|
+
function hexToRgb(hex) {
|
|
27
|
+
const clean = hex.replace("#", "");
|
|
28
|
+
let r, g, b;
|
|
29
|
+
if (clean.length === 3) {
|
|
30
|
+
r = parseInt(clean[0] + clean[0], 16);
|
|
31
|
+
g = parseInt(clean[1] + clean[1], 16);
|
|
32
|
+
b = parseInt(clean[2] + clean[2], 16);
|
|
33
|
+
} else if (clean.length === 6) {
|
|
34
|
+
r = parseInt(clean.slice(0, 2), 16);
|
|
35
|
+
g = parseInt(clean.slice(2, 4), 16);
|
|
36
|
+
b = parseInt(clean.slice(4, 6), 16);
|
|
37
|
+
} else {
|
|
38
|
+
throw new Error(`Invalid hex: ${hex}`);
|
|
39
|
+
}
|
|
40
|
+
return { r, g, b };
|
|
41
|
+
}
|
|
42
|
+
function rgbToHex(rgb) {
|
|
43
|
+
const toHex = (n) => Math.round(Math.min(255, Math.max(0, n))).toString(16).padStart(2, "0").toUpperCase();
|
|
44
|
+
return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
|
|
45
|
+
}
|
|
46
|
+
function rgbToHsl(rgb) {
|
|
47
|
+
const r = rgb.r / 255;
|
|
48
|
+
const g = rgb.g / 255;
|
|
49
|
+
const b = rgb.b / 255;
|
|
50
|
+
const max = Math.max(r, g, b);
|
|
51
|
+
const min = Math.min(r, g, b);
|
|
52
|
+
const delta = max - min;
|
|
53
|
+
let h = 0;
|
|
54
|
+
let s = 0;
|
|
55
|
+
const l = (max + min) / 2;
|
|
56
|
+
if (delta !== 0) {
|
|
57
|
+
s = delta / (1 - Math.abs(2 * l - 1));
|
|
58
|
+
if (max === r) {
|
|
59
|
+
h = (g - b) / delta % 6;
|
|
60
|
+
} else if (max === g) {
|
|
61
|
+
h = (b - r) / delta + 2;
|
|
62
|
+
} else {
|
|
63
|
+
h = (r - g) / delta + 4;
|
|
64
|
+
}
|
|
65
|
+
h = h * 60;
|
|
66
|
+
if (h < 0) h += 360;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
h: Math.round(h),
|
|
70
|
+
s: Math.round(s * 100),
|
|
71
|
+
l: Math.round(l * 100)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function hslToRgb(hsl) {
|
|
75
|
+
const h = hsl.h;
|
|
76
|
+
const s = hsl.s / 100;
|
|
77
|
+
const l = hsl.l / 100;
|
|
78
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
79
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
80
|
+
const m = l - c / 2;
|
|
81
|
+
let r = 0, g = 0, b = 0;
|
|
82
|
+
if (h < 60) {
|
|
83
|
+
r = c;
|
|
84
|
+
g = x;
|
|
85
|
+
b = 0;
|
|
86
|
+
} else if (h < 120) {
|
|
87
|
+
r = x;
|
|
88
|
+
g = c;
|
|
89
|
+
b = 0;
|
|
90
|
+
} else if (h < 180) {
|
|
91
|
+
r = 0;
|
|
92
|
+
g = c;
|
|
93
|
+
b = x;
|
|
94
|
+
} else if (h < 240) {
|
|
95
|
+
r = 0;
|
|
96
|
+
g = x;
|
|
97
|
+
b = c;
|
|
98
|
+
} else if (h < 300) {
|
|
99
|
+
r = x;
|
|
100
|
+
g = 0;
|
|
101
|
+
b = c;
|
|
102
|
+
} else {
|
|
103
|
+
r = c;
|
|
104
|
+
g = 0;
|
|
105
|
+
b = x;
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
r: Math.round((r + m) * 255),
|
|
109
|
+
g: Math.round((g + m) * 255),
|
|
110
|
+
b: Math.round((b + m) * 255)
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function rgbToHsv(rgb) {
|
|
114
|
+
const r = rgb.r / 255;
|
|
115
|
+
const g = rgb.g / 255;
|
|
116
|
+
const b = rgb.b / 255;
|
|
117
|
+
const max = Math.max(r, g, b);
|
|
118
|
+
const min = Math.min(r, g, b);
|
|
119
|
+
const delta = max - min;
|
|
120
|
+
let h = 0;
|
|
121
|
+
const s = max === 0 ? 0 : delta / max;
|
|
122
|
+
const v = max;
|
|
123
|
+
if (delta !== 0) {
|
|
124
|
+
if (max === r) {
|
|
125
|
+
h = (g - b) / delta % 6;
|
|
126
|
+
} else if (max === g) {
|
|
127
|
+
h = (b - r) / delta + 2;
|
|
128
|
+
} else {
|
|
129
|
+
h = (r - g) / delta + 4;
|
|
130
|
+
}
|
|
131
|
+
h = h * 60;
|
|
132
|
+
if (h < 0) h += 360;
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
h: Math.round(h),
|
|
136
|
+
s: Math.round(s * 100),
|
|
137
|
+
v: Math.round(v * 100)
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function hsvToRgb(hsv) {
|
|
141
|
+
const h = hsv.h;
|
|
142
|
+
const s = hsv.s / 100;
|
|
143
|
+
const v = hsv.v / 100;
|
|
144
|
+
const c = v * s;
|
|
145
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
146
|
+
const m = v - c;
|
|
147
|
+
let r = 0, g = 0, b = 0;
|
|
148
|
+
if (h < 60) {
|
|
149
|
+
r = c;
|
|
150
|
+
g = x;
|
|
151
|
+
b = 0;
|
|
152
|
+
} else if (h < 120) {
|
|
153
|
+
r = x;
|
|
154
|
+
g = c;
|
|
155
|
+
b = 0;
|
|
156
|
+
} else if (h < 180) {
|
|
157
|
+
r = 0;
|
|
158
|
+
g = c;
|
|
159
|
+
b = x;
|
|
160
|
+
} else if (h < 240) {
|
|
161
|
+
r = 0;
|
|
162
|
+
g = x;
|
|
163
|
+
b = c;
|
|
164
|
+
} else if (h < 300) {
|
|
165
|
+
r = x;
|
|
166
|
+
g = 0;
|
|
167
|
+
b = c;
|
|
168
|
+
} else {
|
|
169
|
+
r = c;
|
|
170
|
+
g = 0;
|
|
171
|
+
b = x;
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
r: Math.round((r + m) * 255),
|
|
175
|
+
g: Math.round((g + m) * 255),
|
|
176
|
+
b: Math.round((b + m) * 255)
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function buildConverterResult(rgb) {
|
|
180
|
+
const hex = rgbToHex(rgb);
|
|
181
|
+
const hsl = rgbToHsl(rgb);
|
|
182
|
+
const hsv = rgbToHsv(rgb);
|
|
183
|
+
return {
|
|
184
|
+
hex,
|
|
185
|
+
rgb,
|
|
186
|
+
hsl,
|
|
187
|
+
hsv,
|
|
188
|
+
css: {
|
|
189
|
+
hex: hex.toLowerCase(),
|
|
190
|
+
rgb: `rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,
|
|
191
|
+
hsl: `hsl(${hsl.h}, ${hsl.s}%, ${hsl.l}%)`
|
|
192
|
+
},
|
|
193
|
+
valid: true
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function converterErrorResult(error) {
|
|
197
|
+
return {
|
|
198
|
+
hex: "",
|
|
199
|
+
rgb: { r: 0, g: 0, b: 0 },
|
|
200
|
+
hsl: { h: 0, s: 0, l: 0 },
|
|
201
|
+
hsv: { h: 0, s: 0, v: 0 },
|
|
202
|
+
css: { hex: "", rgb: "", hsl: "" },
|
|
203
|
+
valid: false,
|
|
204
|
+
error
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
function parseColor(input) {
|
|
208
|
+
if (!input || !input.trim()) {
|
|
209
|
+
return converterErrorResult("Empty input");
|
|
210
|
+
}
|
|
211
|
+
const trimmed = input.trim().toLowerCase();
|
|
212
|
+
const namedHex = NAMED_COLORS_CONVERTER[trimmed];
|
|
213
|
+
if (namedHex) {
|
|
214
|
+
try {
|
|
215
|
+
const rgb = hexToRgb(namedHex);
|
|
216
|
+
return buildConverterResult(rgb);
|
|
217
|
+
} catch {
|
|
218
|
+
return converterErrorResult(`Failed to parse named color: ${trimmed}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const hexMatch = trimmed.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/);
|
|
222
|
+
if (hexMatch) {
|
|
223
|
+
try {
|
|
224
|
+
const rgb = hexToRgb(trimmed);
|
|
225
|
+
return buildConverterResult(rgb);
|
|
226
|
+
} catch {
|
|
227
|
+
return converterErrorResult(`Invalid hex color: ${input}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
const rgbMatch = trimmed.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/);
|
|
231
|
+
if (rgbMatch) {
|
|
232
|
+
const r = parseInt(rgbMatch[1]);
|
|
233
|
+
const g = parseInt(rgbMatch[2]);
|
|
234
|
+
const b = parseInt(rgbMatch[3]);
|
|
235
|
+
if (r > 255 || g > 255 || b > 255) {
|
|
236
|
+
return converterErrorResult("RGB values must be 0\u2013255");
|
|
237
|
+
}
|
|
238
|
+
return buildConverterResult({ r, g, b });
|
|
239
|
+
}
|
|
240
|
+
const hslMatch = trimmed.match(/^hsla?\(\s*(\d+)\s*,\s*([\d.]+)%?\s*,\s*([\d.]+)%?/);
|
|
241
|
+
if (hslMatch) {
|
|
242
|
+
const h = parseInt(hslMatch[1]);
|
|
243
|
+
const s = parseFloat(hslMatch[2]);
|
|
244
|
+
const l = parseFloat(hslMatch[3]);
|
|
245
|
+
if (h > 360 || s > 100 || l > 100) {
|
|
246
|
+
return converterErrorResult("HSL values out of range");
|
|
247
|
+
}
|
|
248
|
+
const rgb = hslToRgb({ h, s, l });
|
|
249
|
+
return buildConverterResult(rgb);
|
|
250
|
+
}
|
|
251
|
+
return converterErrorResult(`Unrecognized color format: ${input}`);
|
|
252
|
+
}
|
|
253
|
+
var PALETTE_TYPES = [
|
|
254
|
+
"complementary",
|
|
255
|
+
"analogous",
|
|
256
|
+
"triadic",
|
|
257
|
+
"tetradic",
|
|
258
|
+
"split-complementary",
|
|
259
|
+
"monochromatic",
|
|
260
|
+
"shades"
|
|
261
|
+
];
|
|
262
|
+
function paletteWrapHue(h) {
|
|
263
|
+
return (h % 360 + 360) % 360;
|
|
264
|
+
}
|
|
265
|
+
function paletteClamp(value, min, max) {
|
|
266
|
+
return Math.min(max, Math.max(min, value));
|
|
267
|
+
}
|
|
268
|
+
function hexToHsl(hex) {
|
|
269
|
+
let cleaned = hex.trim();
|
|
270
|
+
if (!/^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(cleaned)) {
|
|
271
|
+
throw new Error("Invalid hex color");
|
|
272
|
+
}
|
|
273
|
+
if (cleaned.length === 4) {
|
|
274
|
+
cleaned = "#" + cleaned[1] + cleaned[1] + cleaned[2] + cleaned[2] + cleaned[3] + cleaned[3];
|
|
275
|
+
}
|
|
276
|
+
const r = parseInt(cleaned.slice(1, 3), 16) / 255;
|
|
277
|
+
const g = parseInt(cleaned.slice(3, 5), 16) / 255;
|
|
278
|
+
const b = parseInt(cleaned.slice(5, 7), 16) / 255;
|
|
279
|
+
const max = Math.max(r, g, b);
|
|
280
|
+
const min = Math.min(r, g, b);
|
|
281
|
+
const delta = max - min;
|
|
282
|
+
let h = 0;
|
|
283
|
+
let s = 0;
|
|
284
|
+
const l = (max + min) / 2;
|
|
285
|
+
if (delta !== 0) {
|
|
286
|
+
s = delta / (1 - Math.abs(2 * l - 1));
|
|
287
|
+
if (max === r) {
|
|
288
|
+
h = ((g - b) / delta + (g < b ? 6 : 0)) * 60;
|
|
289
|
+
} else if (max === g) {
|
|
290
|
+
h = ((b - r) / delta + 2) * 60;
|
|
291
|
+
} else {
|
|
292
|
+
h = ((r - g) / delta + 4) * 60;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
h: Math.round(h * 10) / 10,
|
|
297
|
+
s: Math.round(s * 1e3) / 10,
|
|
298
|
+
l: Math.round(l * 1e3) / 10
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function hslToHex(h, s, l) {
|
|
302
|
+
const sn = s / 100;
|
|
303
|
+
const ln = l / 100;
|
|
304
|
+
const c = (1 - Math.abs(2 * ln - 1)) * sn;
|
|
305
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
306
|
+
const m = ln - c / 2;
|
|
307
|
+
let r = 0, g = 0, b = 0;
|
|
308
|
+
if (h >= 0 && h < 60) {
|
|
309
|
+
r = c;
|
|
310
|
+
g = x;
|
|
311
|
+
b = 0;
|
|
312
|
+
} else if (h >= 60 && h < 120) {
|
|
313
|
+
r = x;
|
|
314
|
+
g = c;
|
|
315
|
+
b = 0;
|
|
316
|
+
} else if (h >= 120 && h < 180) {
|
|
317
|
+
r = 0;
|
|
318
|
+
g = c;
|
|
319
|
+
b = x;
|
|
320
|
+
} else if (h >= 180 && h < 240) {
|
|
321
|
+
r = 0;
|
|
322
|
+
g = x;
|
|
323
|
+
b = c;
|
|
324
|
+
} else if (h >= 240 && h < 300) {
|
|
325
|
+
r = x;
|
|
326
|
+
g = 0;
|
|
327
|
+
b = c;
|
|
328
|
+
} else {
|
|
329
|
+
r = c;
|
|
330
|
+
g = 0;
|
|
331
|
+
b = x;
|
|
332
|
+
}
|
|
333
|
+
const clampHex = (v) => Math.max(0, Math.min(255, Math.round(v))).toString(16).padStart(2, "0");
|
|
334
|
+
return `#${clampHex((r + m) * 255)}${clampHex((g + m) * 255)}${clampHex((b + m) * 255)}`;
|
|
335
|
+
}
|
|
336
|
+
function paletteMakeColor(hsl, label) {
|
|
337
|
+
return {
|
|
338
|
+
hex: hslToHex(hsl.h, hsl.s, hsl.l),
|
|
339
|
+
hsl,
|
|
340
|
+
label
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function paletteIsValidHex(hex) {
|
|
344
|
+
return /^#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})$/.test(hex.trim());
|
|
345
|
+
}
|
|
346
|
+
function generatePalette(hex, type) {
|
|
347
|
+
if (!paletteIsValidHex(hex)) {
|
|
348
|
+
return { error: "Invalid hex color" };
|
|
349
|
+
}
|
|
350
|
+
const base = hexToHsl(hex);
|
|
351
|
+
const { h, s, l } = base;
|
|
352
|
+
let colors;
|
|
353
|
+
switch (type) {
|
|
354
|
+
case "complementary": {
|
|
355
|
+
colors = [
|
|
356
|
+
paletteMakeColor(base, "Base"),
|
|
357
|
+
paletteMakeColor({ h: paletteWrapHue(h + 180), s, l }, "Complement")
|
|
358
|
+
];
|
|
359
|
+
break;
|
|
360
|
+
}
|
|
361
|
+
case "analogous": {
|
|
362
|
+
colors = [
|
|
363
|
+
paletteMakeColor({ h: paletteWrapHue(h - 30), s, l }, "Analogous -30\xB0"),
|
|
364
|
+
paletteMakeColor(base, "Base"),
|
|
365
|
+
paletteMakeColor({ h: paletteWrapHue(h + 30), s, l }, "Analogous +30\xB0")
|
|
366
|
+
];
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
case "triadic": {
|
|
370
|
+
colors = [
|
|
371
|
+
paletteMakeColor(base, "Base"),
|
|
372
|
+
paletteMakeColor({ h: paletteWrapHue(h + 120), s, l }, "Triadic 1"),
|
|
373
|
+
paletteMakeColor({ h: paletteWrapHue(h + 240), s, l }, "Triadic 2")
|
|
374
|
+
];
|
|
375
|
+
break;
|
|
376
|
+
}
|
|
377
|
+
case "tetradic": {
|
|
378
|
+
colors = [
|
|
379
|
+
paletteMakeColor(base, "Base"),
|
|
380
|
+
paletteMakeColor({ h: paletteWrapHue(h + 90), s, l }, "Tetradic 1"),
|
|
381
|
+
paletteMakeColor({ h: paletteWrapHue(h + 180), s, l }, "Tetradic 2"),
|
|
382
|
+
paletteMakeColor({ h: paletteWrapHue(h + 270), s, l }, "Tetradic 3")
|
|
383
|
+
];
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
case "split-complementary": {
|
|
387
|
+
colors = [
|
|
388
|
+
paletteMakeColor(base, "Base"),
|
|
389
|
+
paletteMakeColor({ h: paletteWrapHue(h + 150), s, l }, "Split Complement 1"),
|
|
390
|
+
paletteMakeColor({ h: paletteWrapHue(h + 210), s, l }, "Split Complement 2")
|
|
391
|
+
];
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
case "monochromatic": {
|
|
395
|
+
const lightnessSteps = [
|
|
396
|
+
{ offset: -30, label: "Lighter 2" },
|
|
397
|
+
{ offset: -15, label: "Lighter 1" },
|
|
398
|
+
{ offset: 0, label: "Base" },
|
|
399
|
+
{ offset: 15, label: "Darker 1" },
|
|
400
|
+
{ offset: 30, label: "Darker 2" }
|
|
401
|
+
];
|
|
402
|
+
colors = lightnessSteps.map(
|
|
403
|
+
({ offset, label }) => paletteMakeColor({ h, s, l: paletteClamp(l + offset, 0, 100) }, label)
|
|
404
|
+
);
|
|
405
|
+
break;
|
|
406
|
+
}
|
|
407
|
+
case "shades": {
|
|
408
|
+
colors = Array.from({ length: 8 }, (_, i) => {
|
|
409
|
+
const lightness = Math.round(10 + i * 80 / 7);
|
|
410
|
+
return paletteMakeColor({ h, s, l: lightness }, `Shade ${i + 1}`);
|
|
411
|
+
});
|
|
412
|
+
break;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
return {
|
|
416
|
+
type,
|
|
417
|
+
baseHex: hex.trim(),
|
|
418
|
+
colors
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
function contrastParseColor(color) {
|
|
422
|
+
const trimmed = color.trim();
|
|
423
|
+
const shortHexMatch = trimmed.match(
|
|
424
|
+
/^#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])$/
|
|
425
|
+
);
|
|
426
|
+
if (shortHexMatch) {
|
|
427
|
+
return {
|
|
428
|
+
r: parseInt(shortHexMatch[1] + shortHexMatch[1], 16),
|
|
429
|
+
g: parseInt(shortHexMatch[2] + shortHexMatch[2], 16),
|
|
430
|
+
b: parseInt(shortHexMatch[3] + shortHexMatch[3], 16)
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
const fullHexMatch = trimmed.match(
|
|
434
|
+
/^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
|
|
435
|
+
);
|
|
436
|
+
if (fullHexMatch) {
|
|
437
|
+
return {
|
|
438
|
+
r: parseInt(fullHexMatch[1], 16),
|
|
439
|
+
g: parseInt(fullHexMatch[2], 16),
|
|
440
|
+
b: parseInt(fullHexMatch[3], 16)
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
const rgbMatch = trimmed.match(
|
|
444
|
+
/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/
|
|
445
|
+
);
|
|
446
|
+
if (rgbMatch) {
|
|
447
|
+
const r = parseInt(rgbMatch[1], 10);
|
|
448
|
+
const g = parseInt(rgbMatch[2], 10);
|
|
449
|
+
const b = parseInt(rgbMatch[3], 10);
|
|
450
|
+
if (r > 255 || g > 255 || b > 255) return null;
|
|
451
|
+
return { r, g, b };
|
|
452
|
+
}
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
function lineariseChannel(c255) {
|
|
456
|
+
const sRGB = c255 / 255;
|
|
457
|
+
if (sRGB <= 0.04045) {
|
|
458
|
+
return sRGB / 12.92;
|
|
459
|
+
}
|
|
460
|
+
return Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
|
461
|
+
}
|
|
462
|
+
function hexToLinearRgb(hex) {
|
|
463
|
+
const parsed = contrastParseColor(hex);
|
|
464
|
+
if (!parsed) {
|
|
465
|
+
throw new Error("Invalid color format");
|
|
466
|
+
}
|
|
467
|
+
return {
|
|
468
|
+
r: lineariseChannel(parsed.r),
|
|
469
|
+
g: lineariseChannel(parsed.g),
|
|
470
|
+
b: lineariseChannel(parsed.b)
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
function relativeLuminance(r, g, b) {
|
|
474
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
475
|
+
}
|
|
476
|
+
function checkContrast(color1, color2) {
|
|
477
|
+
const parsed1 = contrastParseColor(color1);
|
|
478
|
+
if (!parsed1) return { error: "Invalid color format" };
|
|
479
|
+
const parsed2 = contrastParseColor(color2);
|
|
480
|
+
if (!parsed2) return { error: "Invalid color format" };
|
|
481
|
+
const linear1 = {
|
|
482
|
+
r: lineariseChannel(parsed1.r),
|
|
483
|
+
g: lineariseChannel(parsed1.g),
|
|
484
|
+
b: lineariseChannel(parsed1.b)
|
|
485
|
+
};
|
|
486
|
+
const linear2 = {
|
|
487
|
+
r: lineariseChannel(parsed2.r),
|
|
488
|
+
g: lineariseChannel(parsed2.g),
|
|
489
|
+
b: lineariseChannel(parsed2.b)
|
|
490
|
+
};
|
|
491
|
+
const lum1 = relativeLuminance(linear1.r, linear1.g, linear1.b);
|
|
492
|
+
const lum2 = relativeLuminance(linear2.r, linear2.g, linear2.b);
|
|
493
|
+
const L1 = Math.max(lum1, lum2);
|
|
494
|
+
const L2 = Math.min(lum1, lum2);
|
|
495
|
+
const ratio = (L1 + 0.05) / (L2 + 0.05);
|
|
496
|
+
const aa = {
|
|
497
|
+
normal: ratio >= 4.5,
|
|
498
|
+
large: ratio >= 3,
|
|
499
|
+
ui: ratio >= 3
|
|
500
|
+
};
|
|
501
|
+
const aaa = {
|
|
502
|
+
normal: ratio >= 7,
|
|
503
|
+
large: ratio >= 4.5
|
|
504
|
+
};
|
|
505
|
+
let level;
|
|
506
|
+
if (aaa.normal) {
|
|
507
|
+
level = "AAA";
|
|
508
|
+
} else if (aa.normal) {
|
|
509
|
+
level = "AA";
|
|
510
|
+
} else if (aa.large) {
|
|
511
|
+
level = "AA Large";
|
|
512
|
+
} else {
|
|
513
|
+
level = "Fail";
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
ratio,
|
|
517
|
+
ratioString: ratio.toFixed(2) + ":1",
|
|
518
|
+
aa,
|
|
519
|
+
aaa,
|
|
520
|
+
level
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
var NAMED_COLORS = {
|
|
524
|
+
black: "#000000",
|
|
525
|
+
white: "#ffffff",
|
|
526
|
+
red: "#ff0000",
|
|
527
|
+
green: "#008000",
|
|
528
|
+
blue: "#0000ff",
|
|
529
|
+
yellow: "#ffff00",
|
|
530
|
+
cyan: "#00ffff",
|
|
531
|
+
magenta: "#ff00ff",
|
|
532
|
+
orange: "#ffa500",
|
|
533
|
+
purple: "#800080",
|
|
534
|
+
pink: "#ffc0cb",
|
|
535
|
+
brown: "#a52a2a",
|
|
536
|
+
gray: "#808080",
|
|
537
|
+
grey: "#808080",
|
|
538
|
+
lime: "#00ff00",
|
|
539
|
+
navy: "#000080",
|
|
540
|
+
teal: "#008080",
|
|
541
|
+
silver: "#c0c0c0",
|
|
542
|
+
gold: "#ffd700",
|
|
543
|
+
coral: "#ff7f50"
|
|
544
|
+
};
|
|
545
|
+
function hexToRgbPicker(hex) {
|
|
546
|
+
const clean = hex.replace("#", "");
|
|
547
|
+
if (clean.length === 3) {
|
|
548
|
+
const r = parseInt(clean[0] + clean[0], 16);
|
|
549
|
+
const g = parseInt(clean[1] + clean[1], 16);
|
|
550
|
+
const b = parseInt(clean[2] + clean[2], 16);
|
|
551
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
|
|
552
|
+
return { r, g, b };
|
|
553
|
+
}
|
|
554
|
+
if (clean.length === 6) {
|
|
555
|
+
const r = parseInt(clean.slice(0, 2), 16);
|
|
556
|
+
const g = parseInt(clean.slice(2, 4), 16);
|
|
557
|
+
const b = parseInt(clean.slice(4, 6), 16);
|
|
558
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) return null;
|
|
559
|
+
return { r, g, b };
|
|
560
|
+
}
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
function rgbToHexPicker(rgb) {
|
|
564
|
+
const toHex = (n) => Math.round(Math.max(0, Math.min(255, n))).toString(16).padStart(2, "0");
|
|
565
|
+
return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
|
|
566
|
+
}
|
|
567
|
+
function rgbToHslPicker(rgb) {
|
|
568
|
+
const r = rgb.r / 255;
|
|
569
|
+
const g = rgb.g / 255;
|
|
570
|
+
const b = rgb.b / 255;
|
|
571
|
+
const max = Math.max(r, g, b);
|
|
572
|
+
const min = Math.min(r, g, b);
|
|
573
|
+
const l = (max + min) / 2;
|
|
574
|
+
if (max === min) return { h: 0, s: 0, l: Math.round(l * 100) };
|
|
575
|
+
const d = max - min;
|
|
576
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
577
|
+
let h;
|
|
578
|
+
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
579
|
+
else if (max === g) h = ((b - r) / d + 2) / 6;
|
|
580
|
+
else h = ((r - g) / d + 4) / 6;
|
|
581
|
+
return {
|
|
582
|
+
h: Math.round(h * 360),
|
|
583
|
+
s: Math.round(s * 100),
|
|
584
|
+
l: Math.round(l * 100)
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function hslToRgbPicker(hsl) {
|
|
588
|
+
const h = hsl.h / 360;
|
|
589
|
+
const s = hsl.s / 100;
|
|
590
|
+
const l = hsl.l / 100;
|
|
591
|
+
if (s === 0) {
|
|
592
|
+
const v = Math.round(l * 255);
|
|
593
|
+
return { r: v, g: v, b: v };
|
|
594
|
+
}
|
|
595
|
+
const hue2rgb = (p2, q2, t) => {
|
|
596
|
+
let tt = t;
|
|
597
|
+
if (tt < 0) tt += 1;
|
|
598
|
+
if (tt > 1) tt -= 1;
|
|
599
|
+
if (tt < 1 / 6) return p2 + (q2 - p2) * 6 * tt;
|
|
600
|
+
if (tt < 1 / 2) return q2;
|
|
601
|
+
if (tt < 2 / 3) return p2 + (q2 - p2) * (2 / 3 - tt) * 6;
|
|
602
|
+
return p2;
|
|
603
|
+
};
|
|
604
|
+
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
605
|
+
const p = 2 * l - q;
|
|
606
|
+
return {
|
|
607
|
+
r: Math.round(hue2rgb(p, q, h + 1 / 3) * 255),
|
|
608
|
+
g: Math.round(hue2rgb(p, q, h) * 255),
|
|
609
|
+
b: Math.round(hue2rgb(p, q, h - 1 / 3) * 255)
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
function rgbToHsvPicker(rgb) {
|
|
613
|
+
const r = rgb.r / 255;
|
|
614
|
+
const g = rgb.g / 255;
|
|
615
|
+
const b = rgb.b / 255;
|
|
616
|
+
const max = Math.max(r, g, b);
|
|
617
|
+
const min = Math.min(r, g, b);
|
|
618
|
+
const d = max - min;
|
|
619
|
+
const v = max;
|
|
620
|
+
const s = max === 0 ? 0 : d / max;
|
|
621
|
+
let h = 0;
|
|
622
|
+
if (max !== min) {
|
|
623
|
+
if (max === r) h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
624
|
+
else if (max === g) h = ((b - r) / d + 2) / 6;
|
|
625
|
+
else h = ((r - g) / d + 4) / 6;
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
h: Math.round(h * 360),
|
|
629
|
+
s: Math.round(s * 100),
|
|
630
|
+
v: Math.round(v * 100)
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function hsvToRgbPicker(hsv) {
|
|
634
|
+
const h = hsv.h / 360;
|
|
635
|
+
const s = hsv.s / 100;
|
|
636
|
+
const v = hsv.v / 100;
|
|
637
|
+
if (s === 0) {
|
|
638
|
+
const val = Math.round(v * 255);
|
|
639
|
+
return { r: val, g: val, b: val };
|
|
640
|
+
}
|
|
641
|
+
const i = Math.floor(h * 6);
|
|
642
|
+
const f = h * 6 - i;
|
|
643
|
+
const p = v * (1 - s);
|
|
644
|
+
const q = v * (1 - f * s);
|
|
645
|
+
const t = v * (1 - (1 - f) * s);
|
|
646
|
+
let r, g, b;
|
|
647
|
+
switch (i % 6) {
|
|
648
|
+
case 0:
|
|
649
|
+
r = v;
|
|
650
|
+
g = t;
|
|
651
|
+
b = p;
|
|
652
|
+
break;
|
|
653
|
+
case 1:
|
|
654
|
+
r = q;
|
|
655
|
+
g = v;
|
|
656
|
+
b = p;
|
|
657
|
+
break;
|
|
658
|
+
case 2:
|
|
659
|
+
r = p;
|
|
660
|
+
g = v;
|
|
661
|
+
b = t;
|
|
662
|
+
break;
|
|
663
|
+
case 3:
|
|
664
|
+
r = p;
|
|
665
|
+
g = q;
|
|
666
|
+
b = v;
|
|
667
|
+
break;
|
|
668
|
+
case 4:
|
|
669
|
+
r = t;
|
|
670
|
+
g = p;
|
|
671
|
+
b = v;
|
|
672
|
+
break;
|
|
673
|
+
default:
|
|
674
|
+
r = v;
|
|
675
|
+
g = p;
|
|
676
|
+
b = q;
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
r: Math.round(r * 255),
|
|
680
|
+
g: Math.round(g * 255),
|
|
681
|
+
b: Math.round(b * 255)
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function rgbToCmyk(rgb) {
|
|
685
|
+
const r = rgb.r / 255;
|
|
686
|
+
const g = rgb.g / 255;
|
|
687
|
+
const b = rgb.b / 255;
|
|
688
|
+
const k = 1 - Math.max(r, g, b);
|
|
689
|
+
if (k === 1) return { c: 0, m: 0, y: 0, k: 100 };
|
|
690
|
+
const c = (1 - r - k) / (1 - k);
|
|
691
|
+
const m = (1 - g - k) / (1 - k);
|
|
692
|
+
const y = (1 - b - k) / (1 - k);
|
|
693
|
+
return {
|
|
694
|
+
c: Math.round(c * 100),
|
|
695
|
+
m: Math.round(m * 100),
|
|
696
|
+
y: Math.round(y * 100),
|
|
697
|
+
k: Math.round(k * 100)
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function pickerFindNearestNamedColor(rgb) {
|
|
701
|
+
let nearest;
|
|
702
|
+
let minDist = Infinity;
|
|
703
|
+
for (const [name, hex] of Object.entries(NAMED_COLORS)) {
|
|
704
|
+
if (name === "grey") continue;
|
|
705
|
+
const c = hexToRgbPicker(hex);
|
|
706
|
+
if (!c) continue;
|
|
707
|
+
const dist = Math.sqrt(
|
|
708
|
+
Math.pow(rgb.r - c.r, 2) + Math.pow(rgb.g - c.g, 2) + Math.pow(rgb.b - c.b, 2)
|
|
709
|
+
);
|
|
710
|
+
if (dist < minDist) {
|
|
711
|
+
minDist = dist;
|
|
712
|
+
nearest = name;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (minDist < 30) return nearest;
|
|
716
|
+
return void 0;
|
|
717
|
+
}
|
|
718
|
+
function pickerBuildColorResult(rgb) {
|
|
719
|
+
const hex = rgbToHexPicker(rgb);
|
|
720
|
+
const hsl = rgbToHslPicker(rgb);
|
|
721
|
+
const hsv = rgbToHsvPicker(rgb);
|
|
722
|
+
const cmyk = rgbToCmyk(rgb);
|
|
723
|
+
const name = pickerFindNearestNamedColor(rgb);
|
|
724
|
+
return { hex, rgb, hsl, hsv, cmyk, name };
|
|
725
|
+
}
|
|
726
|
+
function parseColorPicker(input) {
|
|
727
|
+
const trimmed = input.trim().toLowerCase();
|
|
728
|
+
if (NAMED_COLORS[trimmed]) {
|
|
729
|
+
const rgb = hexToRgbPicker(NAMED_COLORS[trimmed]);
|
|
730
|
+
if (!rgb) return { error: `Invalid named color: ${trimmed}` };
|
|
731
|
+
return pickerBuildColorResult(rgb);
|
|
732
|
+
}
|
|
733
|
+
if (trimmed.startsWith("#")) {
|
|
734
|
+
const rgb = hexToRgbPicker(trimmed);
|
|
735
|
+
if (!rgb) return { error: `Invalid hex color: ${input}` };
|
|
736
|
+
return pickerBuildColorResult(rgb);
|
|
737
|
+
}
|
|
738
|
+
const rgbMatch = trimmed.match(/^rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/);
|
|
739
|
+
if (rgbMatch) {
|
|
740
|
+
const r = parseInt(rgbMatch[1]);
|
|
741
|
+
const g = parseInt(rgbMatch[2]);
|
|
742
|
+
const b = parseInt(rgbMatch[3]);
|
|
743
|
+
if (r > 255 || g > 255 || b > 255)
|
|
744
|
+
return { error: "RGB values must be between 0 and 255" };
|
|
745
|
+
return pickerBuildColorResult({ r, g, b });
|
|
746
|
+
}
|
|
747
|
+
const hslMatch = trimmed.match(
|
|
748
|
+
/^hsl\(\s*(\d+)\s*,\s*(\d+)%?\s*,\s*(\d+)%?\s*\)$/
|
|
749
|
+
);
|
|
750
|
+
if (hslMatch) {
|
|
751
|
+
const h = parseInt(hslMatch[1]);
|
|
752
|
+
const s = parseInt(hslMatch[2]);
|
|
753
|
+
const l = parseInt(hslMatch[3]);
|
|
754
|
+
if (h > 360 || s > 100 || l > 100)
|
|
755
|
+
return { error: "HSL values out of range" };
|
|
756
|
+
const rgb = hslToRgbPicker({ h, s, l });
|
|
757
|
+
return pickerBuildColorResult(rgb);
|
|
758
|
+
}
|
|
759
|
+
return { error: `Unable to parse color: ${input}` };
|
|
760
|
+
}
|
|
761
|
+
function shadesClamp(n, min, max) {
|
|
762
|
+
return Math.min(Math.max(n, min), max);
|
|
763
|
+
}
|
|
764
|
+
function shadesNormalizeHex(hex) {
|
|
765
|
+
let h = hex.trim().replace(/^#/, "");
|
|
766
|
+
if (h.length === 3) {
|
|
767
|
+
h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
|
|
768
|
+
}
|
|
769
|
+
return h;
|
|
770
|
+
}
|
|
771
|
+
function hexToRgbShades(hex) {
|
|
772
|
+
const h = shadesNormalizeHex(hex);
|
|
773
|
+
return {
|
|
774
|
+
r: parseInt(h.slice(0, 2), 16),
|
|
775
|
+
g: parseInt(h.slice(2, 4), 16),
|
|
776
|
+
b: parseInt(h.slice(4, 6), 16)
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
function rgbToHexShades(r, g, b) {
|
|
780
|
+
const toHex = (n) => Math.round(shadesClamp(n, 0, 255)).toString(16).padStart(2, "0");
|
|
781
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
782
|
+
}
|
|
783
|
+
function hexToHslShades(hex) {
|
|
784
|
+
const { r, g, b } = hexToRgbShades(hex);
|
|
785
|
+
const rn = r / 255;
|
|
786
|
+
const gn = g / 255;
|
|
787
|
+
const bn = b / 255;
|
|
788
|
+
const max = Math.max(rn, gn, bn);
|
|
789
|
+
const min = Math.min(rn, gn, bn);
|
|
790
|
+
const delta = max - min;
|
|
791
|
+
let h = 0;
|
|
792
|
+
let s = 0;
|
|
793
|
+
const l = (max + min) / 2;
|
|
794
|
+
if (delta !== 0) {
|
|
795
|
+
s = delta / (1 - Math.abs(2 * l - 1));
|
|
796
|
+
if (max === rn) {
|
|
797
|
+
h = (gn - bn) / delta % 6;
|
|
798
|
+
} else if (max === gn) {
|
|
799
|
+
h = (bn - rn) / delta + 2;
|
|
800
|
+
} else {
|
|
801
|
+
h = (rn - gn) / delta + 4;
|
|
802
|
+
}
|
|
803
|
+
h = h * 60;
|
|
804
|
+
if (h < 0) h += 360;
|
|
805
|
+
}
|
|
806
|
+
return {
|
|
807
|
+
h: Math.round(h),
|
|
808
|
+
s: Math.round(s * 100),
|
|
809
|
+
l: Math.round(l * 100)
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
function hslToHexShades(h, s, l) {
|
|
813
|
+
const sn = s / 100;
|
|
814
|
+
const ln = l / 100;
|
|
815
|
+
const c = (1 - Math.abs(2 * ln - 1)) * sn;
|
|
816
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
817
|
+
const m = ln - c / 2;
|
|
818
|
+
let r = 0, g = 0, b = 0;
|
|
819
|
+
if (h < 60) {
|
|
820
|
+
r = c;
|
|
821
|
+
g = x;
|
|
822
|
+
b = 0;
|
|
823
|
+
} else if (h < 120) {
|
|
824
|
+
r = x;
|
|
825
|
+
g = c;
|
|
826
|
+
b = 0;
|
|
827
|
+
} else if (h < 180) {
|
|
828
|
+
r = 0;
|
|
829
|
+
g = c;
|
|
830
|
+
b = x;
|
|
831
|
+
} else if (h < 240) {
|
|
832
|
+
r = 0;
|
|
833
|
+
g = x;
|
|
834
|
+
b = c;
|
|
835
|
+
} else if (h < 300) {
|
|
836
|
+
r = x;
|
|
837
|
+
g = 0;
|
|
838
|
+
b = c;
|
|
839
|
+
} else {
|
|
840
|
+
r = c;
|
|
841
|
+
g = 0;
|
|
842
|
+
b = x;
|
|
843
|
+
}
|
|
844
|
+
return rgbToHexShades((r + m) * 255, (g + m) * 255, (b + m) * 255);
|
|
845
|
+
}
|
|
846
|
+
function generateShades(hex, steps = 5) {
|
|
847
|
+
const { h, s, l } = hexToHslShades(hex);
|
|
848
|
+
const shades = [];
|
|
849
|
+
const step = l / (steps + 1);
|
|
850
|
+
for (let i = 1; i <= steps; i++) {
|
|
851
|
+
const newL = Math.max(0, l - step * i);
|
|
852
|
+
shades.push(hslToHexShades(h, s, newL));
|
|
853
|
+
}
|
|
854
|
+
return shades;
|
|
855
|
+
}
|
|
856
|
+
function generateTints(hex, steps = 5) {
|
|
857
|
+
const { h, s, l } = hexToHslShades(hex);
|
|
858
|
+
const tints = [];
|
|
859
|
+
const step = (100 - l) / (steps + 1);
|
|
860
|
+
for (let i = 1; i <= steps; i++) {
|
|
861
|
+
const newL = Math.min(100, l + step * i);
|
|
862
|
+
tints.push(hslToHexShades(h, s, newL));
|
|
863
|
+
}
|
|
864
|
+
return tints;
|
|
865
|
+
}
|
|
866
|
+
function generateScale(hex, steps = 5) {
|
|
867
|
+
return {
|
|
868
|
+
tints: generateTints(hex, steps),
|
|
869
|
+
base: hex.startsWith("#") ? hex : `#${hex}`,
|
|
870
|
+
shades: generateShades(hex, steps)
|
|
871
|
+
};
|
|
872
|
+
}
|
|
873
|
+
function getContrastColor(hex) {
|
|
874
|
+
const { r, g, b } = hexToRgbShades(hex);
|
|
875
|
+
const toLinear = (c) => {
|
|
876
|
+
const sRGB = c / 255;
|
|
877
|
+
return sRGB <= 0.03928 ? sRGB / 12.92 : Math.pow((sRGB + 0.055) / 1.055, 2.4);
|
|
878
|
+
};
|
|
879
|
+
const luminance = 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
880
|
+
return luminance < 0.179 ? "#ffffff" : "#000000";
|
|
881
|
+
}
|
|
882
|
+
function colorName(hex) {
|
|
883
|
+
const { h, s, l } = hexToHslShades(hex);
|
|
884
|
+
if (l < 15) return "gray";
|
|
885
|
+
if (l > 90) return "gray";
|
|
886
|
+
if (s < 15) return "gray";
|
|
887
|
+
if (h >= 20 && h <= 40 && s < 60 && l < 40) return "brown";
|
|
888
|
+
if (h < 15 || h >= 345) return "red";
|
|
889
|
+
if (h < 25) return "orange";
|
|
890
|
+
if (h < 45) return "orange";
|
|
891
|
+
if (h < 65) return "yellow";
|
|
892
|
+
if (h < 155) return "green";
|
|
893
|
+
if (h < 185) return "cyan";
|
|
894
|
+
if (h < 200) return "teal";
|
|
895
|
+
if (h < 255) return "blue";
|
|
896
|
+
if (h < 290) return "purple";
|
|
897
|
+
if (h < 315) return "pink";
|
|
898
|
+
if (h < 345) return "pink";
|
|
899
|
+
return "red";
|
|
900
|
+
}
|
|
901
|
+
function hexToRgbBlend(hex) {
|
|
902
|
+
const cleaned = hex.startsWith("#") ? hex.slice(1) : hex;
|
|
903
|
+
let r, g, b;
|
|
904
|
+
if (cleaned.length === 3) {
|
|
905
|
+
r = parseInt(cleaned[0] + cleaned[0], 16);
|
|
906
|
+
g = parseInt(cleaned[1] + cleaned[1], 16);
|
|
907
|
+
b = parseInt(cleaned[2] + cleaned[2], 16);
|
|
908
|
+
} else if (cleaned.length === 6) {
|
|
909
|
+
r = parseInt(cleaned.slice(0, 2), 16);
|
|
910
|
+
g = parseInt(cleaned.slice(2, 4), 16);
|
|
911
|
+
b = parseInt(cleaned.slice(4, 6), 16);
|
|
912
|
+
} else {
|
|
913
|
+
return { error: `Invalid hex color: ${hex}` };
|
|
914
|
+
}
|
|
915
|
+
if (isNaN(r) || isNaN(g) || isNaN(b)) {
|
|
916
|
+
return { error: `Invalid hex color: ${hex}` };
|
|
917
|
+
}
|
|
918
|
+
if (!/^[0-9a-fA-F]+$/.test(cleaned)) {
|
|
919
|
+
return { error: `Invalid hex color: ${hex}` };
|
|
920
|
+
}
|
|
921
|
+
return { r, g, b };
|
|
922
|
+
}
|
|
923
|
+
function rgbToHexBlend(r, g, b) {
|
|
924
|
+
const clamp = (v) => Math.max(0, Math.min(255, Math.round(v)));
|
|
925
|
+
return "#" + [clamp(r), clamp(g), clamp(b)].map((v) => v.toString(16).padStart(2, "0")).join("");
|
|
926
|
+
}
|
|
927
|
+
function hexToHslBlend(hex) {
|
|
928
|
+
const rgb = hexToRgbBlend(hex);
|
|
929
|
+
if (typeof rgb === "object" && "error" in rgb) return rgb;
|
|
930
|
+
const r = rgb.r / 255;
|
|
931
|
+
const g = rgb.g / 255;
|
|
932
|
+
const b = rgb.b / 255;
|
|
933
|
+
const max = Math.max(r, g, b);
|
|
934
|
+
const min = Math.min(r, g, b);
|
|
935
|
+
const delta = max - min;
|
|
936
|
+
let h = 0;
|
|
937
|
+
let s = 0;
|
|
938
|
+
const l = (max + min) / 2;
|
|
939
|
+
if (delta !== 0) {
|
|
940
|
+
s = delta / (1 - Math.abs(2 * l - 1));
|
|
941
|
+
if (max === r) {
|
|
942
|
+
h = (g - b) / delta % 6;
|
|
943
|
+
} else if (max === g) {
|
|
944
|
+
h = (b - r) / delta + 2;
|
|
945
|
+
} else {
|
|
946
|
+
h = (r - g) / delta + 4;
|
|
947
|
+
}
|
|
948
|
+
h = h * 60;
|
|
949
|
+
if (h < 0) h += 360;
|
|
950
|
+
}
|
|
951
|
+
return {
|
|
952
|
+
h: Math.round(h * 100) / 100,
|
|
953
|
+
s: Math.round(s * 100 * 100) / 100,
|
|
954
|
+
l: Math.round(l * 100 * 100) / 100
|
|
955
|
+
};
|
|
956
|
+
}
|
|
957
|
+
function hslToHexBlend(h, s, l) {
|
|
958
|
+
const sNorm = s / 100;
|
|
959
|
+
const lNorm = l / 100;
|
|
960
|
+
const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
|
|
961
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
962
|
+
const m = lNorm - c / 2;
|
|
963
|
+
let r = 0, g = 0, b = 0;
|
|
964
|
+
if (h >= 0 && h < 60) {
|
|
965
|
+
r = c;
|
|
966
|
+
g = x;
|
|
967
|
+
b = 0;
|
|
968
|
+
} else if (h >= 60 && h < 120) {
|
|
969
|
+
r = x;
|
|
970
|
+
g = c;
|
|
971
|
+
b = 0;
|
|
972
|
+
} else if (h >= 120 && h < 180) {
|
|
973
|
+
r = 0;
|
|
974
|
+
g = c;
|
|
975
|
+
b = x;
|
|
976
|
+
} else if (h >= 180 && h < 240) {
|
|
977
|
+
r = 0;
|
|
978
|
+
g = x;
|
|
979
|
+
b = c;
|
|
980
|
+
} else if (h >= 240 && h < 300) {
|
|
981
|
+
r = x;
|
|
982
|
+
g = 0;
|
|
983
|
+
b = c;
|
|
984
|
+
} else {
|
|
985
|
+
r = c;
|
|
986
|
+
g = 0;
|
|
987
|
+
b = x;
|
|
988
|
+
}
|
|
989
|
+
return rgbToHexBlend(
|
|
990
|
+
Math.round((r + m) * 255),
|
|
991
|
+
Math.round((g + m) * 255),
|
|
992
|
+
Math.round((b + m) * 255)
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
function blendColors(color1, color2, steps = 5, mode = "rgb") {
|
|
996
|
+
const rgb1Result = hexToRgbBlend(color1);
|
|
997
|
+
if (typeof rgb1Result === "object" && "error" in rgb1Result) return rgb1Result;
|
|
998
|
+
const rgb2Result = hexToRgbBlend(color2);
|
|
999
|
+
if (typeof rgb2Result === "object" && "error" in rgb2Result) return rgb2Result;
|
|
1000
|
+
if (steps < 1) return { error: "steps must be at least 1" };
|
|
1001
|
+
if (steps === 1)
|
|
1002
|
+
return [color1.toLowerCase().startsWith("#") ? color1 : "#" + color1];
|
|
1003
|
+
const result = [];
|
|
1004
|
+
if (mode === "rgb") {
|
|
1005
|
+
const c1 = rgb1Result;
|
|
1006
|
+
const c2 = rgb2Result;
|
|
1007
|
+
for (let i = 0; i < steps; i++) {
|
|
1008
|
+
const t = i / (steps - 1);
|
|
1009
|
+
result.push(
|
|
1010
|
+
rgbToHexBlend(
|
|
1011
|
+
c1.r + (c2.r - c1.r) * t,
|
|
1012
|
+
c1.g + (c2.g - c1.g) * t,
|
|
1013
|
+
c1.b + (c2.b - c1.b) * t
|
|
1014
|
+
)
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
} else {
|
|
1018
|
+
const hsl1Result = hexToHslBlend(color1);
|
|
1019
|
+
if (typeof hsl1Result === "object" && "error" in hsl1Result)
|
|
1020
|
+
return hsl1Result;
|
|
1021
|
+
const hsl2Result = hexToHslBlend(color2);
|
|
1022
|
+
if (typeof hsl2Result === "object" && "error" in hsl2Result)
|
|
1023
|
+
return hsl2Result;
|
|
1024
|
+
const c1 = hsl1Result;
|
|
1025
|
+
const c2 = hsl2Result;
|
|
1026
|
+
let dh = c2.h - c1.h;
|
|
1027
|
+
if (dh > 180) dh -= 360;
|
|
1028
|
+
if (dh < -180) dh += 360;
|
|
1029
|
+
for (let i = 0; i < steps; i++) {
|
|
1030
|
+
const t = i / (steps - 1);
|
|
1031
|
+
let h = c1.h + dh * t;
|
|
1032
|
+
if (h < 0) h += 360;
|
|
1033
|
+
if (h >= 360) h -= 360;
|
|
1034
|
+
result.push(
|
|
1035
|
+
hslToHexBlend(
|
|
1036
|
+
h,
|
|
1037
|
+
c1.s + (c2.s - c1.s) * t,
|
|
1038
|
+
c1.l + (c2.l - c1.l) * t
|
|
1039
|
+
)
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
return result;
|
|
1044
|
+
}
|
|
1045
|
+
function mixColors(color1, color2, ratio = 0.5) {
|
|
1046
|
+
const rgb1Result = hexToRgbBlend(color1);
|
|
1047
|
+
if (typeof rgb1Result === "object" && "error" in rgb1Result) return rgb1Result;
|
|
1048
|
+
const rgb2Result = hexToRgbBlend(color2);
|
|
1049
|
+
if (typeof rgb2Result === "object" && "error" in rgb2Result) return rgb2Result;
|
|
1050
|
+
const c1 = rgb1Result;
|
|
1051
|
+
const c2 = rgb2Result;
|
|
1052
|
+
return rgbToHexBlend(
|
|
1053
|
+
c1.r + (c2.r - c1.r) * ratio,
|
|
1054
|
+
c1.g + (c2.g - c1.g) * ratio,
|
|
1055
|
+
c1.b + (c2.b - c1.b) * ratio
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
function complementaryColor(hex) {
|
|
1059
|
+
const hslResult = hexToHslBlend(hex);
|
|
1060
|
+
if (typeof hslResult === "object" && "error" in hslResult) return hslResult;
|
|
1061
|
+
const { h, s, l } = hslResult;
|
|
1062
|
+
const newH = (h + 180) % 360;
|
|
1063
|
+
return hslToHexBlend(newH, s, l);
|
|
1064
|
+
}
|
|
1065
|
+
function analogousColors(hex, angle = 30) {
|
|
1066
|
+
const hslResult = hexToHslBlend(hex);
|
|
1067
|
+
if (typeof hslResult === "object" && "error" in hslResult) return hslResult;
|
|
1068
|
+
const { h, s, l } = hslResult;
|
|
1069
|
+
const h1 = (h + angle + 360) % 360;
|
|
1070
|
+
const h2 = (h - angle + 360) % 360;
|
|
1071
|
+
return [hslToHexBlend(h1, s, l), hslToHexBlend(h2, s, l)];
|
|
1072
|
+
}
|
|
1073
|
+
function triadicColors(hex) {
|
|
1074
|
+
const hslResult = hexToHslBlend(hex);
|
|
1075
|
+
if (typeof hslResult === "object" && "error" in hslResult) return hslResult;
|
|
1076
|
+
const { h, s, l } = hslResult;
|
|
1077
|
+
const h1 = (h + 120) % 360;
|
|
1078
|
+
const h2 = (h + 240) % 360;
|
|
1079
|
+
return [hslToHexBlend(h1, s, l), hslToHexBlend(h2, s, l)];
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
exports.NAMED_COLORS = NAMED_COLORS;
|
|
1083
|
+
exports.PALETTE_TYPES = PALETTE_TYPES;
|
|
1084
|
+
exports.analogousColors = analogousColors;
|
|
1085
|
+
exports.blendColors = blendColors;
|
|
1086
|
+
exports.checkContrast = checkContrast;
|
|
1087
|
+
exports.colorName = colorName;
|
|
1088
|
+
exports.complementaryColor = complementaryColor;
|
|
1089
|
+
exports.generatePalette = generatePalette;
|
|
1090
|
+
exports.generateScale = generateScale;
|
|
1091
|
+
exports.generateShades = generateShades;
|
|
1092
|
+
exports.generateTints = generateTints;
|
|
1093
|
+
exports.getContrastColor = getContrastColor;
|
|
1094
|
+
exports.hexToHsl = hexToHsl;
|
|
1095
|
+
exports.hexToHslBlend = hexToHslBlend;
|
|
1096
|
+
exports.hexToHslShades = hexToHslShades;
|
|
1097
|
+
exports.hexToLinearRgb = hexToLinearRgb;
|
|
1098
|
+
exports.hexToRgb = hexToRgb;
|
|
1099
|
+
exports.hexToRgbBlend = hexToRgbBlend;
|
|
1100
|
+
exports.hexToRgbPicker = hexToRgbPicker;
|
|
1101
|
+
exports.hexToRgbShades = hexToRgbShades;
|
|
1102
|
+
exports.hslToHex = hslToHex;
|
|
1103
|
+
exports.hslToHexBlend = hslToHexBlend;
|
|
1104
|
+
exports.hslToHexShades = hslToHexShades;
|
|
1105
|
+
exports.hslToRgb = hslToRgb;
|
|
1106
|
+
exports.hslToRgbPicker = hslToRgbPicker;
|
|
1107
|
+
exports.hsvToRgb = hsvToRgb;
|
|
1108
|
+
exports.hsvToRgbPicker = hsvToRgbPicker;
|
|
1109
|
+
exports.mixColors = mixColors;
|
|
1110
|
+
exports.parseColor = parseColor;
|
|
1111
|
+
exports.parseColorPicker = parseColorPicker;
|
|
1112
|
+
exports.relativeLuminance = relativeLuminance;
|
|
1113
|
+
exports.rgbToCmyk = rgbToCmyk;
|
|
1114
|
+
exports.rgbToHex = rgbToHex;
|
|
1115
|
+
exports.rgbToHexBlend = rgbToHexBlend;
|
|
1116
|
+
exports.rgbToHexPicker = rgbToHexPicker;
|
|
1117
|
+
exports.rgbToHexShades = rgbToHexShades;
|
|
1118
|
+
exports.rgbToHsl = rgbToHsl;
|
|
1119
|
+
exports.rgbToHslPicker = rgbToHslPicker;
|
|
1120
|
+
exports.rgbToHsv = rgbToHsv;
|
|
1121
|
+
exports.rgbToHsvPicker = rgbToHsvPicker;
|
|
1122
|
+
exports.triadicColors = triadicColors;
|