colorizr
Version:
Manipulate colors like a boss
1 lines • 172 kB
Source Map (JSON)
{"version":3,"sources":["../src/converters/index.ts","../src/modules/constants.ts","../src/modules/invariant.ts","../src/modules/css-colors.ts","../src/modules/validators.ts","../src/format-hex.ts","../src/modules/utils.ts","../src/modules/alpha.ts","../src/converters/hex2rgb.ts","../src/converters/rgb2hsl.ts","../src/converters/hex2hsl.ts","../src/modules/gamma.ts","../src/converters/rgb2oklab.ts","../src/converters/hex2oklab.ts","../src/converters/oklab2oklch.ts","../src/converters/rgb2oklch.ts","../src/converters/hex2oklch.ts","../src/modules/hue2rgb.ts","../src/converters/hsl2rgb.ts","../src/converters/rgb2hex.ts","../src/converters/hsl2hex.ts","../src/converters/hsl2oklab.ts","../src/converters/hsl2oklch.ts","../src/converters/oklab2rgb.ts","../src/converters/oklab2hex.ts","../src/converters/oklab2hsl.ts","../src/converters/oklch2oklab.ts","../src/converters/oklch2rgb.ts","../src/converters/oklch2hex.ts","../src/converters/oklch2hsl.ts","../src/format-css.ts","../src/extract-color-parts.ts","../src/modules/parsed-color.ts","../src/convertCSS.ts","../src/apca.ts","../src/brightness-difference.ts","../src/chroma.ts","../src/color-difference.ts","../src/luminance.ts","../src/contrast.ts","../src/compare.ts","../src/modules/updater.ts","../src/darken.ts","../src/delta-e.ts","../src/desaturate.ts","../src/grayscale.ts","../src/rotate.ts","../src/invert.ts","../src/lighten.ts","../src/mix.ts","../src/opacify.ts","../src/opacity.ts","../src/readable-color.ts","../src/saturate.ts","../src/modules/linear-rgb.ts","../src/to-gamut.ts","../src/transparentize.ts","../src/colorizr.ts","../src/get-color-type.ts","../src/is-valid-color.ts","../src/name.ts","../src/parse-css.ts","../src/p3.ts","../src/palette.ts","../src/random.ts","../src/scale.ts","../src/scheme.ts"],"sourcesContent":["export { default as hex2hsl } from '~/converters/hex2hsl';\nexport { default as hex2oklab } from '~/converters/hex2oklab';\nexport { default as hex2oklch } from '~/converters/hex2oklch';\nexport { default as hex2rgb } from '~/converters/hex2rgb';\n\nexport { default as hsl2hex } from '~/converters/hsl2hex';\nexport { default as hsl2oklab } from '~/converters/hsl2oklab';\nexport { default as hsl2oklch } from '~/converters/hsl2oklch';\nexport { default as hsl2rgb } from '~/converters/hsl2rgb';\n\nexport { default as oklab2hex } from '~/converters/oklab2hex';\nexport { default as oklab2hsl } from '~/converters/oklab2hsl';\nexport { default as oklab2oklch } from '~/converters/oklab2oklch';\nexport { default as oklab2rgb } from '~/converters/oklab2rgb';\n\nexport { default as oklch2hex } from '~/converters/oklch2hex';\nexport { default as oklch2hsl } from '~/converters/oklch2hsl';\nexport { default as oklch2oklab } from '~/converters/oklch2oklab';\nexport { default as oklch2rgb } from '~/converters/oklch2rgb';\n\nexport { default as rgb2hex } from '~/converters/rgb2hex';\nexport { default as rgb2hsl } from '~/converters/rgb2hsl';\nexport { default as rgb2oklab } from '~/converters/rgb2oklab';\nexport { default as rgb2oklch } from '~/converters/rgb2oklch';\n","import { ColorKeysTuple, ColorModelKey } from '~/types';\n\nexport const COLOR_KEYS: Record<ColorModelKey, ColorKeysTuple> = {\n hsl: ['h', 's', 'l'],\n oklab: ['l', 'a', 'b'],\n oklch: ['l', 'c', 'h'],\n rgb: ['r', 'g', 'b'],\n};\nexport const CLMS_TO_OKLAB: number[][] = [\n [0.210454268309314, 0.7936177747023054, -0.0040720430116193],\n [1.9779985324311684, -2.4285922420485799, 0.450593709617411],\n [0.0259040424655478, 0.7827717124575296, -0.8086757549230774],\n];\nexport const DEG2RAD = Math.PI / 180;\nexport const LMS_TO_LRGB: number[][] = [\n [4.0767416621, -3.3077115913, 0.2309699292],\n [-1.2684380046, 2.6097574011, -0.3413193965],\n [-0.0041960863, -0.7034186147, 1.707614701],\n];\nexport const LRGB_TO_LMS: number[][] = [\n [0.4122214708, 0.5363325363, 0.0514459929],\n [0.2119034982, 0.6806995451, 0.1073969566],\n [0.0883024619, 0.2817188376, 0.6299787005],\n];\nexport const OKLAB_TO_CLMS: number[][] = [\n [1.0, 0.3963377773761749, 0.2158037573099136],\n [1.0, -0.1055613458156586, -0.0638541728258133],\n [1.0, -0.0894841775298119, -1.2914855480194092],\n];\nexport const P3_TO_SRGB: number[][] = [\n [1.2249401762805601, -0.22494017628055996, 0],\n [-0.042056954709688121, 1.0420569547096883, 0],\n [-0.019637554590334422, -0.078636045550631792, 1.0982736001409665],\n];\nexport const P3_TO_XYZ: number[][] = [\n [0.4865709486482162, 0.26566769316909306, 0.1982172852343625],\n [0.22897456406974884, 0.6917385218365064, 0.079286914093745],\n [0.0, 0.04511338185890264, 1.043944368900976],\n];\nexport const SRGB_TO_P3: number[][] = [\n [0.8224270476, 0.1775729524, 0],\n [0.0331008087, 0.9668991913, 0],\n [0.0170720188, 0.0723477973, 0.9105801839],\n];\nexport const XYZ_TO_SRGB: number[][] = [\n [3.2409699419045226, -1.5373831775700939, -0.4986107602930034],\n [-0.9692436362808796, 1.8759675015077202, 0.0415550574071756],\n [0.0556300796969936, -0.2039769588889765, 1.0569715142428786],\n];\nexport const GAMUT_EPSILON = 0.000001;\nexport const PRECISION = 5;\nexport const RAD2DEG = 180 / Math.PI;\n\nexport const MESSAGES = {\n alpha: 'alpha must be a number between 0 and 1',\n alphaAdjustment: 'alpha must be a number between -1 and 1',\n amount: 'amount must be a number between 0 and 100',\n colorRequired: 'color is required',\n degreesRange: 'degrees must be a number between -360 and 360',\n hueArgs: 'point, chroma and h are required',\n hueRange: 'hue must be a number between 0 and 360',\n input: 'input is required',\n inputHex: 'input is required and must be a hex',\n inputNumber: 'input is required and must be a number',\n inputString: 'input is required and must be a string',\n invalid: 'invalid input',\n invalidColor: 'invalid color',\n invalidCSS: 'invalid CSS string',\n invalidHex: 'invalid hex',\n invalidKey: 'invalid key',\n invalidModel: 'invalid model',\n invalidRange: 'color value out of range',\n left: 'left is required and must be a string',\n lightnessRange: 'lightness must be a number between 0 and 1',\n options: 'invalid options',\n paletteSize: 'palette size must be at least 2',\n ratioRange: 'ratio must be a number between 0 and 1',\n right: 'right is required and must be a string',\n threshold: 'threshold must be a number between 0 and 255',\n thresholdNormalized: 'threshold must be a number between 0 and 1',\n} as const;\n\nexport const MONOCHROMATIC_LIGHTNESS_MAX = 80;\n","/**\n * Assert a condition and throw an error if false.\n *\n * @param condition - The condition to assert.\n * @param message - The error message if condition is false.\n * @throws Error with name 'colorizr' if condition is false.\n */\nexport function invariant(condition: boolean, message: string): asserts condition {\n if (condition) {\n return;\n }\n\n if (process.env.NODE_ENV !== 'production' && message === undefined) {\n throw new Error('invariant requires an error message argument');\n }\n\n const error = !message\n ? new Error(\n 'Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.',\n )\n : new Error(message);\n\n error.name = 'colorizr';\n\n throw error;\n}\n","/**\n * CSS named colors\n */\n\nexport type CSSColor = keyof typeof cssColors;\n\nexport const cssColors = {\n aliceblue: '#f0f8ff',\n antiquewhite: '#faebd7',\n aqua: '#00ffff',\n aquamarine: '#7fffd4',\n azure: '#f0ffff',\n beige: '#f5f5dc',\n bisque: '#ffe4c4',\n black: '#000000',\n blanchedalmond: '#ffebcd',\n blue: '#0000ff',\n blueviolet: '#8a2be2',\n brown: '#a52a2a',\n burlywood: '#deb887',\n cadetblue: '#5f9ea0',\n chartreuse: '#7fff00',\n chocolate: '#d2691e',\n coral: '#ff7f50',\n cornflowerblue: '#6495ed',\n cornsilk: '#fff8dc',\n crimson: '#dc143c',\n cyan: '#00ffff',\n darkblue: '#00008b',\n darkcyan: '#008b8b',\n darkgoldenrod: '#b8860b',\n darkgray: '#a9a9a9',\n darkgreen: '#006400',\n darkkhaki: '#bdb76b',\n darkmagenta: '#8b008b',\n darkolivegreen: '#556b2f',\n darkorange: '#ff8c00',\n darkorchid: '#9932cc',\n darkred: '#8b0000',\n darksalmon: '#e9967a',\n darkseagreen: '#8fbc8f',\n darkslateblue: '#483d8b',\n darkslategray: '#2f4f4f',\n darkslategrey: '#2f4f4f',\n darkturquoise: '#00ced1',\n darkviolet: '#9400d3',\n deeppink: '#ff1493',\n deepskyblue: '#00bfff',\n dimgray: '#696969',\n dimgrey: '#696969',\n dodgerblue: '#1e90ff',\n firebrick: '#b22222',\n floralwhite: '#fffaf0',\n forestgreen: '#228b22',\n fuchsia: '#ff00ff',\n gainsboro: '#dcdcdc',\n ghostwhite: '#f8f8ff',\n gold: '#ffd700',\n goldenrod: '#daa520',\n gray: '#808080',\n grey: '#808080',\n green: '#008000',\n greenyellow: '#adff2f',\n honeydew: '#f0fff0',\n hotpink: '#ff69b4',\n indianred: '#cd5c5c',\n indigo: '#4b0082',\n ivory: '#fffff0',\n khaki: '#f0e68c',\n lavender: '#e6e6fa',\n lavenderblush: '#fff0f5',\n lawngreen: '#7cfc00',\n lemonchiffon: '#fffacd',\n lightblue: '#add8e6',\n lightcoral: '#f08080',\n lightcyan: '#e0ffff',\n lightgoldenrodyellow: '#fafad2',\n lightgray: '#d3d3d3',\n lightgreen: '#90ee90',\n lightgrey: '#d3d3d3',\n lightpink: '#ffb6c1',\n lightsalmon: '#ffa07a',\n lightseagreen: '#20b2aa',\n lightskyblue: '#87cefa',\n lightslategray: '#778899',\n lightslategrey: '#778899',\n lightsteelblue: '#b0c4de',\n lightyellow: '#ffffe0',\n lime: '#00ff00',\n limegreen: '#32cd32',\n linen: '#faf0e6',\n magenta: '#ff00ff',\n maroon: '#800000',\n mediumaquamarine: '#66cdaa',\n mediumblue: '#0000cd',\n mediumorchid: '#ba55d3',\n mediumpurple: '#9370db',\n mediumseagreen: '#3cb371',\n mediumslateblue: '#7b68ee',\n mediumspringgreen: '#00fa9a',\n mediumturquoise: '#48d1cc',\n mediumvioletred: '#c71585',\n midnightblue: '#191970',\n mintcream: '#f5fffa',\n mistyrose: '#ffe4e1',\n moccasin: '#ffe4b5',\n navajowhite: '#ffdead',\n navy: '#000080',\n oldlace: '#fdf5e6',\n olive: '#808000',\n olivedrab: '#6b8e23',\n orange: '#ffa500',\n orangered: '#ff4500',\n orchid: '#da70d6',\n palegoldenrod: '#eee8aa',\n palegreen: '#98fb98',\n paleturquoise: '#afeeee',\n palevioletred: '#db7093',\n papayawhip: '#ffefd5',\n peachpuff: '#ffdab9',\n peru: '#cd853f',\n pink: '#ffc0cb',\n plum: '#dda0dd',\n powderblue: '#b0e0e6',\n purple: '#800080',\n rebeccapurple: '#663399',\n red: '#ff0000',\n rosybrown: '#bc8f8f',\n royalblue: '#4169e1',\n saddlebrown: '#8b4513',\n salmon: '#fa8072',\n sandybrown: '#f4a460',\n seagreen: '#2e8b57',\n seashell: '#fff5ee',\n sienna: '#a0522d',\n silver: '#c0c0c0',\n skyblue: '#87ceeb',\n slateblue: '#6a5acd',\n slategray: '#708090',\n slategrey: '#708090',\n snow: '#fffafa',\n springgreen: '#00ff7f',\n steelblue: '#4682b4',\n tan: '#d2b48c',\n teal: '#008080',\n thistle: '#d8bfd8',\n tomato: '#ff6347',\n turquoise: '#40e0d0',\n violet: '#ee82ee',\n wheat: '#f5deb3',\n white: '#ffffff',\n whitesmoke: '#f5f5f5',\n yellow: '#ffff00',\n yellowgreen: '#9acd32',\n};\n","import { COLOR_KEYS } from '~/modules/constants';\nimport { CSSColor, cssColors } from '~/modules/css-colors';\n\nimport { ColorModel, HEX, HSL, LAB, LCH, PlainObject, RGB } from '~/types';\n\nconst hexRegex = /^#(?:[\\da-f]{3,4}|[\\da-f]{6,8})$/i;\n\nexport function hasValidMatches(input: unknown): input is string[] {\n return Array.isArray(input) && input.length === 6;\n}\n\nexport function isHex(input: unknown): input is HEX {\n if (!isString(input)) {\n return false;\n }\n\n return hexRegex.test(input);\n}\n\n/**\n * Check if an object contains HSL values\n * The input must be an object with keys 'h', 's', and 'l'\n * with values between 0 and 360 for hue or 0 and 100 for the others.\n */\nexport function isHSL(input: unknown): input is HSL {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'h') {\n return value >= 0 && value <= 360;\n }\n\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.hsl.includes(key) && value >= 0 && value <= 100;\n })\n );\n}\n\n/**\n * Check if an object contains LAB values\n * The input must be an object with keys 'l', 'a', and 'b' with values between -1 and 1.\n */\nexport function isLAB(input: unknown): input is LAB {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'l') {\n return value >= 0 && value <= 100;\n }\n\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.oklab.includes(key) && value >= -1 && value <= 1;\n })\n );\n}\n\n/**\n * Check if an object contains LAB values\n * The input must be an object with keys 'l', 'c', and 'h' with values between 0 and 360.\n */\nexport function isLCH(input: unknown): input is LCH {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'l') {\n return value >= 0 && value <= 100;\n }\n\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.oklch.includes(key) && value >= 0 && value <= (key === 'h' ? 360 : 1);\n })\n );\n}\n\n/**\n * Check if the input is a CSS named color\n */\nexport function isNamedColor(input: unknown): input is CSSColor {\n return isString(input) && Object.keys(cssColors).includes(input.toLowerCase());\n}\n\n/**\n * Check if the input is a number and not NaN\n */\nexport function isNumber(input: unknown): input is number {\n return typeof input === 'number' && !Number.isNaN(input);\n}\n\n/**\n * Check if the input is a number within a specific range (inclusive)\n */\nexport function isNumberInRange(input: unknown, min: number, max: number): input is number {\n return isNumber(input) && input >= min && input <= max;\n}\n\n/**\n * Check if the input is an object\n */\nexport function isPlainObject(input: unknown): input is PlainObject {\n if (!input) {\n return false;\n }\n\n const { toString } = Object.prototype;\n const prototype = Object.getPrototypeOf(input);\n\n return (\n toString.call(input) === '[object Object]' &&\n (prototype === null || prototype === Object.getPrototypeOf({}))\n );\n}\n\n/**\n * Check if an object contains RGB values.\n * The input must be an object with keys 'r', 'g', and 'b' with values between 0 and 255.\n */\nexport function isRGB(input: unknown): input is RGB {\n if (!isPlainObject(input)) {\n return false;\n }\n\n const entries = Object.entries(input);\n\n return (\n !!entries.length &&\n entries.every(([key, value]) => {\n if (key === 'alpha') {\n return value >= 0 && value <= 1;\n }\n\n return COLOR_KEYS.rgb.includes(key) && value >= 0 && value <= 255;\n })\n );\n}\n\n/**\n * Check if the input is a string\n */\nexport function isString(input: unknown, validate = true): input is string {\n const isValid = typeof input === 'string';\n\n if (validate) {\n return isValid && !!input.trim().length;\n }\n\n return isValid;\n}\n\nexport function isValidColorModel<T extends ColorModel>(input: T): input is T {\n return isHSL(input) || isRGB(input) || isLAB(input) || isLCH(input);\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex, isString } from '~/modules/validators';\n\nimport { HEX } from '~/types';\n\n/**\n * Normalize a hex color string to 6 or 8 characters.\n *\n * @param input - The hex color string (3, 4, 6, or 8 characters).\n * @returns The normalized hex color string.\n */\nexport default function formatHex(input: string): HEX {\n invariant(isString(input), MESSAGES.inputString);\n\n let color = input.startsWith('#') ? input.slice(1) : input;\n\n invariant(isHex(`#${color}`), MESSAGES.inputHex);\n\n if (color.length === 3 || color.length === 4) {\n const values = [...color];\n\n color = '';\n\n values.forEach(d => {\n color += `${d}${d}`;\n });\n }\n\n const hex = `#${color}`;\n\n invariant(isHex(hex), MESSAGES.invalidHex);\n\n return hex;\n}\n","import { COLOR_KEYS, MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHSL, isLAB, isLCH, isNumber, isPlainObject, isRGB } from '~/modules/validators';\n\nimport { ColorModel, ColorModelKey, ConverterParameters, LAB, LCH } from '~/types';\n\n/**\n * Clamp a value between a min and max.\n *\n * @param value - The value to clamp.\n * @param min - The minimum value (default: 0).\n * @param max - The maximum value (default: 100).\n * @returns The clamped value.\n */\nexport function clamp(value: number, min = 0, max = 100): number {\n return Math.min(Math.max(value, min), max);\n}\n\n/**\n * Constrain the degrees between 0 and 360.\n *\n * @param input - The base degrees value.\n * @param amount - The amount to add to the degrees.\n * @returns The constrained degrees value (0-360).\n */\nexport function constrainDegrees(input: number, amount: number): number {\n invariant(isNumber(input), MESSAGES.inputNumber);\n\n return (((input + amount) % 360) + 360) % 360;\n}\n\n/**\n * Normalize OkLab/OkLCH lightness from percentage (0-100) to 0-1 range.\n */\nexport function normalizeOkLightness<T extends { l: number }>(color: T): T {\n if (color.l > 1) {\n return { ...color, l: parseFloat((color.l / 100).toPrecision(15)) };\n }\n\n return color;\n}\n\n/**\n * Parse the input parameters for converters.\n *\n * @param input - The converter parameters (object or tuple).\n * @param model - The target color model.\n * @returns The parsed color model object.\n */\nexport function parseInput<T extends ColorModel>(\n input: ConverterParameters<T>,\n model: ColorModelKey,\n): T {\n const keys = COLOR_KEYS[model];\n const validator = {\n hsl: isHSL,\n oklab: isLAB,\n oklch: isLCH,\n rgb: isRGB,\n };\n\n invariant(isPlainObject(input) || Array.isArray(input), MESSAGES.invalid);\n\n const value = Array.isArray(input)\n ? ({ [keys[0]]: input[0], [keys[1]]: input[1], [keys[2]]: input[2] } as unknown as T)\n : input;\n\n invariant(validator[model](value), `${MESSAGES.invalidColor}: ${model}`);\n\n return value;\n}\n\n/**\n * Restrict the values to a certain number of digits.\n * When precision is undefined, returns input unchanged (no rounding).\n *\n * @param input - The LAB or LCH color model.\n * @param precision - The number of significant digits. Undefined = no rounding.\n * @param forcePrecision - Whether to use decimal places (true) or significant digits (false).\n * @returns The color model with restricted values.\n */\nexport function restrictValues<T extends LAB | LCH>(\n input: T,\n precision?: number,\n forcePrecision = true,\n): T {\n if (precision == null) {\n return input;\n }\n\n const output = new Map(Object.entries(input));\n\n for (const [key, value] of output.entries()) {\n output.set(key, round(value, precision, forcePrecision));\n }\n\n return Object.fromEntries(output) as T;\n}\n\n/**\n * Round decimal numbers.\n *\n * @param input - The number to round.\n * @param precision - The number of digits (default: 2).\n * @param forcePrecision - When true, rounds to N decimal places. When false, rounds to N significant digits.\n * @returns The rounded number.\n */\nexport function round(input: number, precision = 2, forcePrecision = true): number {\n if (!isNumber(input) || input === 0) {\n return 0;\n }\n\n if (forcePrecision) {\n const factor = 10 ** precision;\n\n return Math.round(input * factor) / factor;\n }\n\n // Significant digits mode (matches color.js toPrecision behavior):\n // For |n| >= 1: N significant digits. For |n| < 1: N decimal places.\n const integer = Math.trunc(input);\n let digits = 0;\n\n if (integer) {\n digits = Math.floor(Math.log10(Math.abs(integer))) + 1;\n }\n\n const factor = 10 ** (precision - digits);\n\n return Math.floor(input * factor + 0.5) / factor;\n}\n\n/**\n * Log a warning in development mode.\n *\n * @param message - The warning message to log.\n */\nexport function warn(message: string): void {\n if (process.env.NODE_ENV !== 'production') {\n // eslint-disable-next-line no-console\n console.warn(`[colorizr] ${message}`);\n }\n}\n\n/**\n * Pre-computed step keys for each step count (3-20).\n *\n * Based on Tailwind CSS color scale conventions:\n * - Standard keys: 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950\n * - 500 is typically the \"base\" color\n * - Lower numbers = lighter, higher numbers = darker (in light mode)\n *\n * Keys are symmetrically distributed to maintain visual balance.\n */\nconst STEP_KEYS: Record<number, number[]> = {\n 3: [100, 500, 900],\n 4: [100, 400, 600, 900],\n 5: [100, 300, 500, 700, 900],\n 6: [100, 200, 400, 600, 800, 900],\n 7: [100, 200, 400, 500, 600, 800, 900],\n 8: [100, 200, 300, 500, 600, 700, 800, 900],\n 9: [100, 200, 300, 400, 500, 600, 700, 800, 900],\n 10: [50, 100, 200, 300, 400, 500, 600, 700, 800, 900],\n 11: [50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950],\n 12: [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 900, 950],\n 13: [50, 100, 150, 200, 300, 400, 500, 600, 700, 800, 850, 900, 950],\n 14: [50, 100, 150, 200, 250, 300, 400, 500, 600, 700, 800, 850, 900, 950],\n 15: [50, 100, 150, 200, 250, 300, 400, 500, 600, 700, 750, 800, 850, 900, 950],\n 16: [50, 100, 150, 200, 250, 300, 350, 400, 500, 600, 700, 750, 800, 850, 900, 950],\n 17: [50, 100, 150, 200, 250, 300, 350, 400, 500, 600, 650, 700, 750, 800, 850, 900, 950],\n 18: [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 600, 650, 700, 750, 800, 850, 900, 950],\n 19: [\n 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950,\n ],\n 20: [\n 50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700, 750, 800, 850, 900, 950,\n 1000,\n ],\n};\n\n/**\n * Get the step keys for a given step count.\n *\n * @param steps - The number of steps (clamped to 3-20).\n * @returns The array of step keys.\n */\nexport function getScaleStepKeys(steps: number): number[] {\n const value = clamp(Math.round(steps), 3, 20);\n\n return STEP_KEYS[value];\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { round } from '~/modules/utils';\nimport { isHex, isNumber, isNumberInRange, isValidColorModel } from '~/modules/validators';\n\nimport { ColorModel, ConverterParameters, HEX } from '~/types';\n\n/**\n * Add an alpha value to a color model.\n *\n * @param input - The color model object.\n * @param alpha - A number between 0 and 1 (values > 1 are divided by 100).\n * @returns The color model with alpha applied.\n */\nexport function addAlpha<T extends ColorModel>(input: T, alpha?: number): T {\n invariant(isValidColorModel(input), MESSAGES.invalid);\n\n const value = normalizeAlpha(alpha);\n\n if (value === undefined || value === 1) {\n return input;\n }\n\n return { ...input, alpha: value };\n}\n\n/**\n * Add an alpha value to a hex string.\n *\n * @param input - The hex color string.\n * @param alpha - A number between 0 and 1.\n * @returns The hex color string with alpha.\n */\nexport function addAlphaToHex(input: string, alpha: number): HEX {\n invariant(isHex(input), MESSAGES.inputHex);\n invariant(isNumberInRange(alpha, 0, 1), MESSAGES.alpha);\n\n if (alpha >= 1) {\n return removeAlphaFromHex(input);\n }\n\n return `${removeAlphaFromHex(input)}${convertAlphaToHex(alpha)}` as HEX;\n}\n\n/**\n * Convert an alpha value to a hex value.\n *\n * @param input - A number between 0 and 1 (values > 1 are divided by 100).\n * @returns The two-character hex string.\n */\nexport function convertAlphaToHex(input: number): string {\n invariant(isNumber(input), MESSAGES.inputNumber);\n\n const alpha = normalizeAlpha(input);\n\n return Math.round(alpha * 255)\n .toString(16)\n .padStart(2, '0');\n}\n\n/**\n * Extract alpha from converter input.\n *\n * @param input - The converter parameters (object or tuple).\n * @returns The alpha value or undefined for tuple inputs.\n */\nexport function extractAlpha<T extends ColorModel>(\n input: ConverterParameters<T>,\n): number | undefined {\n if (Array.isArray(input)) {\n return undefined;\n }\n\n return input.alpha;\n}\n\n/**\n * Extract the alpha value from a hex string.\n *\n * @param input - The hex color string.\n * @returns The alpha value (0-1), defaults to 1 if no alpha present.\n */\nexport function extractAlphaFromHex(input: string): number {\n invariant(isHex(input), MESSAGES.inputString);\n\n const alpha = input.substring(7, 9);\n\n if (!alpha) {\n return 1;\n }\n\n return round(parseInt(alpha, 16) / 255);\n}\n\n/**\n * Normalize an alpha value to the 0-1 range.\n * Values > 1 are treated as percentages and divided by 100.\n */\nexport function normalizeAlpha(value: number): number;\nexport function normalizeAlpha(value: number | undefined): number | undefined;\nexport function normalizeAlpha(value: number | undefined): number | undefined {\n if (value === undefined) {\n return undefined;\n }\n\n return Math.min(1, Math.max(0, value > 1 ? value / 100 : value));\n}\n\n/**\n * Remove the alpha value from a hex string.\n *\n * @param input - The hex color string.\n * @returns The hex color string without alpha.\n */\nexport function removeAlphaFromHex(input: string): HEX {\n invariant(isHex(input), MESSAGES.inputHex);\n\n if (input.length === 5) {\n return input.substring(0, 4) as HEX;\n }\n\n return input.substring(0, 7) as HEX;\n}\n","import formatHex from '~/format-hex';\nimport { addAlpha, extractAlphaFromHex } from '~/modules/alpha';\nimport { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport { RGB } from '~/types';\n\n/**\n * Convert HEX to RGB.\n *\n * @param input - The hex color string.\n * @returns The RGB color object.\n */\nexport default function hex2rgb(input: string): RGB {\n invariant(isHex(input), MESSAGES.inputHex);\n\n const hex = formatHex(input).slice(1);\n const alpha = extractAlphaFromHex(input);\n\n return addAlpha(\n {\n r: parseInt(hex.charAt(0) + hex.charAt(1), 16),\n g: parseInt(hex.charAt(2) + hex.charAt(3), 16),\n b: parseInt(hex.charAt(4) + hex.charAt(5), 16),\n },\n alpha,\n );\n}\n","import { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { clamp, parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, RGB } from '~/types';\n\n/**\n * Convert RGB to HSL.\n *\n * @param input - The RGB color object or tuple.\n * @returns The HSL color object.\n */\nexport default function rgb2hsl(input: ConverterParameters<RGB>): HSL {\n const value = parseInput(input, 'rgb');\n const alpha = extractAlpha(input);\n\n const rLimit = clamp(value.r, 0, 255) / 255;\n const gLimit = clamp(value.g, 0, 255) / 255;\n const bLimit = clamp(value.b, 0, 255) / 255;\n\n const min = Math.min(rLimit, gLimit, bLimit);\n const max = Math.max(rLimit, gLimit, bLimit);\n const delta = max - min;\n\n let h = 0;\n let s;\n const l = (max + min) / 2;\n let rate;\n\n switch (max) {\n case rLimit:\n rate = !delta ? 0 : (gLimit - bLimit) / delta;\n h = 60 * rate;\n break;\n case gLimit:\n rate = (bLimit - rLimit) / delta;\n h = 60 * rate + 120;\n break;\n case bLimit:\n rate = (rLimit - gLimit) / delta;\n h = 60 * rate + 240;\n break;\n /* v8 ignore next 2 -- @preserve */\n default:\n break;\n }\n\n if (h < 0) {\n h = 360 + h;\n }\n\n if (min === max) {\n s = 0;\n } else {\n s = l < 0.5 ? delta / (2 * l) : delta / (2 - 2 * l);\n }\n\n return addAlpha(\n {\n h: Math.abs(+(h % 360).toFixed(2)),\n s: +(s * 100).toFixed(2),\n l: +(l * 100).toFixed(2),\n },\n alpha,\n );\n}\n","import hex2rgb from '~/converters/hex2rgb';\nimport rgb2hsl from '~/converters/rgb2hsl';\nimport { addAlpha, extractAlphaFromHex } from '~/modules/alpha';\nimport { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport { HSL } from '~/types';\n\n/**\n * Convert HEX to HSL.\n *\n * @param input - The hex color string.\n * @returns The HSL color object.\n */\nexport default function hex2hsl(input: string): HSL {\n invariant(isHex(input), MESSAGES.inputHex);\n\n const alpha = extractAlphaFromHex(input);\n\n return addAlpha(rgb2hsl(hex2rgb(input)), alpha);\n}\n","export function srgbGammaDecode(input: number): number {\n const abs = Math.abs(input);\n\n if (abs < 0.04045) {\n return input / 12.92;\n }\n\n return (Math.sign(input) || 1) * ((abs + 0.055) / 1.055) ** 2.4;\n}\n\nexport function srgbGammaEncode(input: number): number {\n const abs = Math.abs(input);\n const sign = input < 0 ? -1 : 1;\n\n if (abs > 0.0031308) {\n return sign * (abs ** (1 / 2.4) * 1.055 - 0.055);\n }\n\n return input * 12.92;\n}\n","import { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { CLMS_TO_OKLAB, LRGB_TO_LMS } from '~/modules/constants';\nimport { srgbGammaDecode } from '~/modules/gamma';\nimport { parseInput, restrictValues } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, RGB } from '~/types';\n\nconst { cbrt } = Math;\n\n/**\n * Convert RGB to OkLab.\n *\n * @param input - The RGB color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLab color object.\n */\nexport default function rgb2oklab(input: ConverterParameters<RGB>, precision?: number): LAB {\n const value = parseInput(input, 'rgb');\n const alpha = extractAlpha(input);\n\n const [lr, lg, lb] = [\n srgbGammaDecode(value.r / 255),\n srgbGammaDecode(value.g / 255),\n srgbGammaDecode(value.b / 255),\n ];\n const l = cbrt(LRGB_TO_LMS[0][0] * lr + LRGB_TO_LMS[0][1] * lg + LRGB_TO_LMS[0][2] * lb);\n const m = cbrt(LRGB_TO_LMS[1][0] * lr + LRGB_TO_LMS[1][1] * lg + LRGB_TO_LMS[1][2] * lb);\n const s = cbrt(LRGB_TO_LMS[2][0] * lr + LRGB_TO_LMS[2][1] * lg + LRGB_TO_LMS[2][2] * lb);\n\n const lab = restrictValues(\n {\n l: CLMS_TO_OKLAB[0][0] * l + CLMS_TO_OKLAB[0][1] * m + CLMS_TO_OKLAB[0][2] * s,\n a: CLMS_TO_OKLAB[1][0] * l + CLMS_TO_OKLAB[1][1] * m + CLMS_TO_OKLAB[1][2] * s,\n b: CLMS_TO_OKLAB[2][0] * l + CLMS_TO_OKLAB[2][1] * m + CLMS_TO_OKLAB[2][2] * s,\n },\n precision,\n );\n\n return addAlpha(lab, alpha);\n}\n","import hex2rgb from '~/converters/hex2rgb';\nimport rgb2oklab from '~/converters/rgb2oklab';\nimport { addAlpha, extractAlphaFromHex } from '~/modules/alpha';\nimport { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport { LAB } from '~/types';\n\n/**\n * Convert HEX to OkLab.\n *\n * @param input - The hex color string.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLab color object.\n */\nexport default function hex2oklab(input: string, precision?: number): LAB {\n invariant(isHex(input), MESSAGES.inputHex);\n\n const alpha = extractAlphaFromHex(input);\n\n return addAlpha(rgb2oklab(hex2rgb(input), precision), alpha);\n}\n","import { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { RAD2DEG } from '~/modules/constants';\nimport { parseInput, restrictValues } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, LCH } from '~/types';\n\nconst { atan2, sqrt } = Math;\n\n/**\n * Convert OkLab to OkLCH.\n *\n * @param input - The OkLab color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLCH color object.\n */\nexport default function oklab2oklch(input: ConverterParameters<LAB>, precision?: number): LCH {\n const { l, a, b } = parseInput(input, 'oklab');\n const alpha = extractAlpha(input);\n\n const c = sqrt(a ** 2 + b ** 2);\n let h = (atan2(b, a) * RAD2DEG + 360) % 360;\n\n if (c < 1e-6) {\n h = 0;\n }\n\n return addAlpha(restrictValues({ l, c, h }, precision), alpha);\n}\n","import oklab2oklch from '~/converters/oklab2oklch';\nimport rgb2oklab from '~/converters/rgb2oklab';\nimport { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, LCH, RGB } from '~/types';\n\n/**\n * Convert RGB to OkLCH.\n *\n * @param input - The RGB color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLCH color object.\n */\nexport default function rgb2oklch(input: ConverterParameters<RGB>, precision?: number): LCH {\n const value = parseInput(input, 'rgb');\n const alpha = extractAlpha(input);\n\n return addAlpha(oklab2oklch(rgb2oklab(value), precision), alpha);\n}\n","import hex2rgb from '~/converters/hex2rgb';\nimport rgb2oklch from '~/converters/rgb2oklch';\nimport { addAlpha, extractAlphaFromHex } from '~/modules/alpha';\nimport { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isHex } from '~/modules/validators';\n\nimport { LCH } from '~/types';\n\n/**\n * Convert HEX to OkLCH.\n *\n * @param input - The hex color string.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLCH color object.\n */\nexport default function hex2oklch(input: string, precision?: number): LCH {\n invariant(isHex(input), MESSAGES.inputHex);\n\n const alpha = extractAlphaFromHex(input);\n\n return addAlpha(rgb2oklch(hex2rgb(input), precision), alpha);\n}\n","import { MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { isNumber } from '~/modules/validators';\n\n/**\n * Convert hue to RGB using chroma and median point.\n *\n * @param point - The median point value.\n * @param chroma - The chroma value.\n * @param h - The hue value (0-1).\n * @returns The RGB component value.\n */\nexport default function hue2rgb(point: number, chroma: number, h: number): number {\n invariant(isNumber(point) && isNumber(chroma) && isNumber(h), MESSAGES.hueArgs);\n let hue = h;\n\n if (hue < 0) {\n hue += 1;\n }\n\n if (hue > 1) {\n hue -= 1;\n }\n\n if (hue < 1 / 6) {\n return point + (chroma - point) * 6 * hue;\n }\n\n if (hue < 1 / 2) {\n return chroma;\n }\n\n if (hue < 2 / 3) {\n return point + (chroma - point) * (2 / 3 - hue) * 6;\n }\n\n return point;\n}\n","import { addAlpha, extractAlpha } from '~/modules/alpha';\nimport hue2rgb from '~/modules/hue2rgb';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, RGB } from '~/types';\n\n/**\n * Convert HSL to RGB.\n *\n * @param input - The HSL color object or tuple.\n * @returns The RGB color object.\n */\nexport default function hsl2rgb(input: ConverterParameters<HSL>): RGB {\n const value = parseInput(input, 'hsl');\n const alpha = extractAlpha(input);\n\n const h = value.h / 360;\n const s = value.s / 100;\n const l = value.l / 100;\n\n let r;\n let g;\n let b;\n\n let point;\n let chroma;\n\n if (s < 0.0001) {\n r = l;\n g = l;\n b = l;\n } else {\n chroma = l < 0.5 ? l * (1 + s) : l + s - l * s;\n point = 2 * l - chroma;\n\n r = hue2rgb(point, chroma, h + 1 / 3);\n g = hue2rgb(point, chroma, h);\n b = hue2rgb(point, chroma, h - 1 / 3);\n }\n\n return addAlpha(\n {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(b * 255),\n },\n alpha,\n );\n}\n","import { addAlphaToHex, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HEX, RGB } from '~/types';\n\n/**\n * Convert RGB to HEX.\n *\n * @param input - The RGB color object or tuple.\n * @returns The hex color string.\n */\nexport default function rgb2hex(input: ConverterParameters<RGB>): HEX {\n const rgb = parseInput(input, 'rgb');\n const alpha = extractAlpha(input);\n\n const hex: HEX = `#${[rgb.r, rgb.g, rgb.b]\n .map(d => `0${Math.floor(d).toString(16)}`.slice(-2))\n .join('')}`;\n\n return alpha !== undefined && alpha < 1 ? addAlphaToHex(hex, alpha) : hex;\n}\n","import hsl2rgb from '~/converters/hsl2rgb';\nimport rgb2hex from '~/converters/rgb2hex';\nimport { addAlphaToHex, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HEX, HSL } from '~/types';\n\n/**\n * Convert HSL to HEX.\n *\n * @param input - The HSL color object or tuple.\n * @returns The hex color string.\n */\nexport default function hsl2hex(input: ConverterParameters<HSL>): HEX {\n const value = parseInput(input, 'hsl');\n const alpha = extractAlpha(input);\n\n const hex = rgb2hex(hsl2rgb(value));\n\n return alpha !== undefined && alpha < 1 ? addAlphaToHex(hex, alpha) : hex;\n}\n","import hsl2rgb from '~/converters/hsl2rgb';\nimport rgb2oklab from '~/converters/rgb2oklab';\nimport { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, LAB } from '~/types';\n\n/**\n * Convert HSL to OkLab.\n *\n * @param input - The HSL color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLab color object.\n */\nexport default function hsl2oklab(input: ConverterParameters<HSL>, precision?: number): LAB {\n const value = parseInput(input, 'hsl');\n const alpha = extractAlpha(input);\n\n return addAlpha(rgb2oklab(hsl2rgb(value), precision), alpha);\n}\n","import hsl2rgb from '~/converters/hsl2rgb';\nimport rgb2oklch from '~/converters/rgb2oklch';\nimport { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, LCH } from '~/types';\n\n/**\n * Convert HSL to OkLCH.\n *\n * @param input - The HSL color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLCH color object.\n */\nexport default function hsl2oklch(input: ConverterParameters<HSL>, precision?: number): LCH {\n const value = parseInput(input, 'hsl');\n const alpha = extractAlpha(input);\n\n return addAlpha(rgb2oklch(hsl2rgb(value), precision), alpha);\n}\n","import { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { LMS_TO_LRGB, OKLAB_TO_CLMS } from '~/modules/constants';\nimport { srgbGammaEncode } from '~/modules/gamma';\nimport { clamp, parseInput, round } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, RGB } from '~/types';\n\n/**\n * Convert OkLab to RGB.\n *\n * @param input - The OkLab color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The RGB color object.\n */\nexport default function oklab2rgb(input: ConverterParameters<LAB>, precision = 0): RGB {\n const { l: L, a: A, b: B } = parseInput(input, 'oklab');\n const alpha = extractAlpha(input);\n\n const l = (OKLAB_TO_CLMS[0][0] * L + OKLAB_TO_CLMS[0][1] * A + OKLAB_TO_CLMS[0][2] * B) ** 3;\n const m = (OKLAB_TO_CLMS[1][0] * L + OKLAB_TO_CLMS[1][1] * A + OKLAB_TO_CLMS[1][2] * B) ** 3;\n const s = (OKLAB_TO_CLMS[2][0] * L + OKLAB_TO_CLMS[2][1] * A + OKLAB_TO_CLMS[2][2] * B) ** 3;\n\n const r =\n 255 * srgbGammaEncode(LMS_TO_LRGB[0][0] * l + LMS_TO_LRGB[0][1] * m + LMS_TO_LRGB[0][2] * s);\n const g =\n 255 * srgbGammaEncode(LMS_TO_LRGB[1][0] * l + LMS_TO_LRGB[1][1] * m + LMS_TO_LRGB[1][2] * s);\n const b =\n 255 * srgbGammaEncode(LMS_TO_LRGB[2][0] * l + LMS_TO_LRGB[2][1] * m + LMS_TO_LRGB[2][2] * s);\n\n return addAlpha(\n {\n r: clamp(round(r, precision), 0, 255),\n g: clamp(round(g, precision), 0, 255),\n b: clamp(round(b, precision), 0, 255),\n },\n alpha,\n );\n}\n","import oklab2rgb from '~/converters/oklab2rgb';\nimport rgb2hex from '~/converters/rgb2hex';\nimport { addAlphaToHex, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HEX, LAB } from '~/types';\n\n/**\n * Convert OkLab to HEX.\n *\n * @param input - The OkLab color object or tuple.\n * @returns The hex color string.\n */\nexport default function oklab2hex(input: ConverterParameters<LAB>): HEX {\n const value = parseInput(input, 'oklab');\n const alpha = extractAlpha(input);\n\n const hex = rgb2hex(oklab2rgb(value));\n\n return alpha !== undefined && alpha < 1 ? addAlphaToHex(hex, alpha) : hex;\n}\n","import oklab2rgb from '~/converters/oklab2rgb';\nimport rgb2hsl from '~/converters/rgb2hsl';\nimport { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, LAB } from '~/types';\n\n/**\n * Convert OkLab to HSL.\n *\n * @param input - The OkLab color object or tuple.\n * @returns The HSL color object.\n */\nexport default function oklab2hsl(input: ConverterParameters<LAB>): HSL {\n const value = parseInput(input, 'oklab');\n const alpha = extractAlpha(input);\n\n return addAlpha(rgb2hsl(oklab2rgb(value)), alpha);\n}\n","/* eslint-disable prefer-const */\nimport { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { DEG2RAD } from '~/modules/constants';\nimport { parseInput, restrictValues } from '~/modules/utils';\n\nimport { ConverterParameters, LAB, LCH } from '~/types';\n\nconst { sin, cos } = Math;\n\n/**\n * Convert OkLCH to OkLab.\n *\n * @param input - The OkLCH color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The OkLab color object.\n */\nexport default function oklch2oklab(input: ConverterParameters<LCH>, precision?: number): LAB {\n let { l, c, h } = parseInput(input, 'oklch');\n const alpha = extractAlpha(input);\n\n /* v8 ignore next 3 -- @preserve */\n if (Number.isNaN(h) || h < 0) {\n h = 0;\n }\n\n return addAlpha(\n restrictValues({ l, a: c * cos(h * DEG2RAD), b: c * sin(h * DEG2RAD) }, precision),\n alpha,\n );\n}\n","import oklab2rgb from '~/converters/oklab2rgb';\nimport oklch2oklab from '~/converters/oklch2oklab';\nimport { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, LCH, RGB } from '~/types';\n\n/**\n * Convert OkLCH to RGB.\n *\n * @param input - The OkLCH color object or tuple.\n * @param precision - The number of decimal places for the result.\n * @returns The RGB color object.\n */\nexport default function oklch2rgb(input: ConverterParameters<LCH>, precision = 0): RGB {\n const value = parseInput(input, 'oklch');\n const alpha = extractAlpha(input);\n\n return addAlpha(oklab2rgb(oklch2oklab(value), precision), alpha);\n}\n","import oklch2rgb from '~/converters/oklch2rgb';\nimport rgb2hex from '~/converters/rgb2hex';\nimport { addAlphaToHex, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HEX, LCH } from '~/types';\n\n/**\n * Convert OkLCH to HEX.\n *\n * @param input - The OkLCH color object or tuple.\n * @returns The hex color string.\n */\nexport default function oklch2hex(input: ConverterParameters<LCH>): HEX {\n const value = parseInput(input, 'oklch');\n const alpha = extractAlpha(input);\n\n const hex = rgb2hex(oklch2rgb(value));\n\n return alpha !== undefined && alpha < 1 ? addAlphaToHex(hex, alpha) : hex;\n}\n","import oklch2rgb from '~/converters/oklch2rgb';\nimport rgb2hsl from '~/converters/rgb2hsl';\nimport { addAlpha, extractAlpha } from '~/modules/alpha';\nimport { parseInput } from '~/modules/utils';\n\nimport { ConverterParameters, HSL, LCH } from '~/types';\n\n/**\n * Convert OkLCH to HSL.\n *\n * @param input - The OkLCH color object or tuple.\n * @returns The HSL color object.\n */\nexport default function oklch2hsl(input: ConverterParameters<LCH>): HSL {\n const value = parseInput(input, 'oklch');\n const alpha = extractAlpha(input);\n\n return addAlpha(rgb2hsl(oklch2rgb(value)), alpha);\n}\n","/* eslint-disable sonarjs/no-redundant-assignments */\nimport * as converters from '~/converters';\nimport { convertAlphaToHex, normalizeAlpha, removeAlphaFromHex } from '~/modules/alpha';\nimport { MESSAGES, PRECISION } from '~/modules/constants';\nimport { CSSColor, cssColors } from '~/modules/css-colors';\nimport { invariant } from '~/modules/invariant';\nimport { restrictValues, round } from '~/modules/utils';\nimport {\n isHex,\n isHSL,\n isLAB,\n isLCH,\n isNamedColor,\n isNumber,\n isRGB,\n isString,\n isValidColorModel,\n} from '~/modules/validators';\n\nimport { ColorModel, ColorReturn, ColorType, ColorValue } from '~/types';\n\nexport interface FormatCSSOptions {\n /**\n * The alpha value of the color (0-1).\n */\n alpha?: number;\n /**\n * Output color format.\n */\n format?: ColorType;\n /**\n * The number of digits of the output.\n * @default 5\n */\n precision?: number;\n /**\n * The separator between the values.\n *\n * oklab and oklch always use space as a separator.\n * @default ' '\n */\n separator?: string;\n}\n\nfunction getColorModel<T extends ColorModel | string>(input: T): ColorType {\n if (isHex(input) || isNamedColor(input)) {\n return 'hex';\n }\n\n if (isHSL(input)) {\n return 'hsl';\n }\n\n if (isLAB(input)) {\n return 'oklab';\n }\n\n if (isLCH(input)) {\n return 'oklch';\n }\n\n if (isRGB(input)) {\n return 'rgb';\n }\n\n throw new Error(MESSAGES.invalid);\n}\n\nfunction getColorValue<TInput extends ColorModel | string, TOutput extends ColorType>(\n input: TInput,\n output: TOutput,\n): ColorReturn<TOutput> {\n const value = isNamedColor(input) ? cssColors[input.toLowerCase() as CSSColor] : input;\n const from = getColorModel(value);\n\n if (from === output) {\n return value as ColorReturn<TOutput>;\n }\n\n const converterKey = `${from}2${output}` as keyof typeof converters;\n const converter = (converters as Record<string, (x: any) => any>)[converterKey];\n\n if (!converter) {\n throw new Error(`Converter not found for ${from} to ${output}`);\n }\n\n return converter(value);\n}\n\n/**\n * Format a color model to a CSS color string.\n *\n * @param input - The color model or hex string.\n * @param formatOrOptions - Output format or formatting options object.\n * @returns The formatted CSS color string.\n */\nexport default function formatCSS<T extends ColorValue>(\n input: T,\n formatOrOptions?: ColorType | FormatCSSOptions,\n): string {\n invariant(isHex(input) || isValidColorModel(input), MESSAGES.invalid);\n\n const {\n alpha,\n format,\n precision = PRECISION,\n separator: baseSeparator = ' ',\n } = isString(formatOrOptions, false) ? { format: formatOrOptions } : (formatOrOptions ?? {});\n\n const colorFormat = format || getColorModel(input);\n const normalizedAlpha = isNumber(alpha) ? normalizeAlpha(alpha) : undefined;\n const opacity =\n normalizedAlpha !== undefined && normalizedAlpha !== 1\n ? `${round(normalizedAlpha * 100)}%`\n : null;\n let params = [];\n let separator = baseSeparator;\n\n switch (colorFormat) {\n case 'hsl': {\n const { h, s, l } = getColorValue(input, 'hsl');\n\n params = [h, `${s}%`, `${l}%`];\n break;\n }\n case 'oklab': {\n separator = ' ';\n const { l, a, b } = restrictValues(getColorValue(input, 'oklab'), precision, false);\n\n params = [`${round(l * 100, precision, false)}%`, a, b];\n break;\n }\n case 'oklch': {\n separator = ' ';\n const { l, c, h } = restrictValues(getColorValue(input, 'oklch'), precision, false);\n\n params = [`${round(l * 100, precision, false)}%`, c, c === 0 ? 'none' : h];\n break;\n }\n case 'rgb': {\n const { r, g, b } = getColorValue(input, 'rgb');\n\n params = [r, g, b];\n break;\n }\n default: {\n const hex = removeAlphaFromHex(getColorValue(input, 'hex'));\n\n if (normalizedAlpha !== undefined && normalizedAlpha !== 1) {\n return `${hex}${convertAlphaToHex(normalizedAlpha)}`;\n }\n\n return hex;\n }\n }\n\n return `${colorFormat}(${params.join(separator)}${opacity ? ` / ${opacity}` : ''})`;\n}\n","import hex2rgb from '~/converters/hex2rgb';\nimport { extractAlphaFromHex, normalizeAlpha } from '~/modules/alpha';\nimport { COLOR_KEYS, MESSAGES } from '~/modules/constants';\nimport { invariant } from '~/modules/invariant';\nimport { hasValidMatches, isHex, isString } from '~/modules/validators';\n\nimport { ColorModelKey, PlainObject } from '~/types';\n\n// Regex components for parsing CSS color strings\nconst MODEL = '(rgb|hsl|oklab|oklch)a?';\nconst SEP = '(?:\\\\s*[,/]\\\\s*|\\\\s+)';\n// Accepts: numbers, percentages, 'none', and angle units (deg, grad, rad, turn)\nconst VALUE = '(none|[\\\\d%.-]+(?:deg|grad|rad|turn)?)';\n\nconst colorRegex = new RegExp(\n `${MODEL}\\\\s*\\\\(\\\\s*${VALUE}${SEP}${VALUE}${SEP}${VALUE}(?:${SEP}${VALUE})?\\\\s*\\\\)`,\n 'i',\n);\n\nexport type ExtractColorPartsReturn = {\n alpha?: number;\n model: ColorModelKey;\n} & PlainObject<number>;\n\n/**\n * Convert angle value with units to degrees\n */\nfunction parseAngle(value: string): number {\n const number_ = parseFloat(value);\n let result: number;\n\n if (value.endsWith('grad')) {\n result = number_ * 0.9; // 400grad = 360deg\n } else if (value.endsWith('rad')) {\n result = number_ * (180 / Math.PI); // radians to degrees\n } else if (value.endsWith('turn')) {\n result = number_ * 360; // 1turn = 360deg\n } else {\n //