color-bits 1.1.1 → 1.2.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.
Files changed (83) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/README.md +29 -13
  3. package/benchmarks/compare-oklab.ts +44 -0
  4. package/benchmarks/compare-srgb.ts +59 -0
  5. package/benchmarks/compare.ts +46 -0
  6. package/benchmarks/parse.ts +41 -0
  7. package/build/conversion/channels.d.ts +61 -0
  8. package/build/conversion/channels.js +353 -0
  9. package/build/conversion/channels.js.map +1 -0
  10. package/build/{convert.d.ts → conversion/color-spaces.d.ts} +25 -23
  11. package/build/conversion/color-spaces.js +263 -0
  12. package/build/conversion/color-spaces.js.map +1 -0
  13. package/build/core/bits.d.ts +28 -0
  14. package/build/{core.js → core/bits.js} +25 -26
  15. package/build/core/bits.js.map +1 -0
  16. package/build/core/bytes.d.ts +5 -0
  17. package/build/core/bytes.js +12 -0
  18. package/build/core/bytes.js.map +1 -0
  19. package/build/css.d.ts +1 -0
  20. package/build/css.js +18 -0
  21. package/build/css.js.map +1 -0
  22. package/build/formatting/index.d.ts +27 -0
  23. package/build/{format.js → formatting/index.js} +13 -67
  24. package/build/formatting/index.js.map +1 -0
  25. package/build/index.d.ts +6 -4
  26. package/build/index.js +6 -4
  27. package/build/index.js.map +1 -1
  28. package/build/namedColors.d.ts +1 -0
  29. package/build/namedColors.js +18 -0
  30. package/build/namedColors.js.map +1 -0
  31. package/build/operations/adjust.d.ts +19 -0
  32. package/build/operations/adjust.js +40 -0
  33. package/build/operations/adjust.js.map +1 -0
  34. package/build/operations/blend.d.ts +9 -0
  35. package/build/operations/blend.js +19 -0
  36. package/build/operations/blend.js.map +1 -0
  37. package/build/operations/color-mix.d.ts +20 -0
  38. package/build/operations/color-mix.js +144 -0
  39. package/build/operations/color-mix.js.map +1 -0
  40. package/build/operations/luminance.d.ts +7 -0
  41. package/build/operations/luminance.js +20 -0
  42. package/build/operations/luminance.js.map +1 -0
  43. package/build/parsing/calc.d.ts +8 -0
  44. package/build/parsing/calc.js +205 -0
  45. package/build/parsing/calc.js.map +1 -0
  46. package/build/parsing/color-mix.d.ts +8 -0
  47. package/build/parsing/color-mix.js +70 -0
  48. package/build/parsing/color-mix.js.map +1 -0
  49. package/build/parsing/css.d.ts +23 -0
  50. package/build/parsing/css.js +115 -0
  51. package/build/parsing/css.js.map +1 -0
  52. package/build/parsing/fast.d.ts +19 -0
  53. package/build/parsing/fast.js +184 -0
  54. package/build/parsing/fast.js.map +1 -0
  55. package/build/parsing/named-colors.d.ts +12 -0
  56. package/build/parsing/named-colors.js +68 -0
  57. package/build/parsing/named-colors.js.map +1 -0
  58. package/build/parsing/relative.d.ts +9 -0
  59. package/build/parsing/relative.js +158 -0
  60. package/build/parsing/relative.js.map +1 -0
  61. package/build/parsing/tokenizer.d.ts +16 -0
  62. package/build/parsing/tokenizer.js +78 -0
  63. package/build/parsing/tokenizer.js.map +1 -0
  64. package/build/parsing/values.d.ts +41 -0
  65. package/build/parsing/values.js +139 -0
  66. package/build/parsing/values.js.map +1 -0
  67. package/package.json +15 -3
  68. package/benchmarks/index.ts +0 -35
  69. package/build/bit.d.ts +0 -3
  70. package/build/bit.js +0 -29
  71. package/build/bit.js.map +0 -1
  72. package/build/convert.js +0 -347
  73. package/build/convert.js.map +0 -1
  74. package/build/core.d.ts +0 -27
  75. package/build/core.js.map +0 -1
  76. package/build/format.d.ts +0 -27
  77. package/build/format.js.map +0 -1
  78. package/build/functions.d.ts +0 -33
  79. package/build/functions.js +0 -95
  80. package/build/functions.js.map +0 -1
  81. package/build/parse.d.ts +0 -17
  82. package/build/parse.js +0 -392
  83. package/build/parse.js.map +0 -1
@@ -0,0 +1,19 @@
1
+ import type { ColorBits } from '../core/bits';
2
+ /**
3
+ * Modifies color alpha channel.
4
+ * @param color - ColorBits
5
+ * @param value - Value in the range [0, 1]
6
+ */
7
+ export declare function alpha(color: ColorBits, value: number): ColorBits;
8
+ /**
9
+ * Darkens a color.
10
+ * @param color - ColorBits
11
+ * @param coefficient - Multiplier in the range [0, 1]
12
+ */
13
+ export declare function darken(color: ColorBits, coefficient: number): ColorBits;
14
+ /**
15
+ * Lighten a color.
16
+ * @param color - ColorBits
17
+ * @param coefficient - Multiplier in the range [0, 1]
18
+ */
19
+ export declare function lighten(color: ColorBits, coefficient: number): ColorBits;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.alpha = alpha;
4
+ exports.darken = darken;
5
+ exports.lighten = lighten;
6
+ const bits_1 = require("../core/bits");
7
+ /**
8
+ * Modifies color alpha channel.
9
+ * @param color - ColorBits
10
+ * @param value - Value in the range [0, 1]
11
+ */
12
+ function alpha(color, value) {
13
+ return (0, bits_1.setAlpha)(color, Math.round(value * 255));
14
+ }
15
+ /**
16
+ * Darkens a color.
17
+ * @param color - ColorBits
18
+ * @param coefficient - Multiplier in the range [0, 1]
19
+ */
20
+ function darken(color, coefficient) {
21
+ const r = (0, bits_1.getRed)(color);
22
+ const g = (0, bits_1.getGreen)(color);
23
+ const b = (0, bits_1.getBlue)(color);
24
+ const a = (0, bits_1.getAlpha)(color);
25
+ const factor = 1 - coefficient;
26
+ return (0, bits_1.newColor)(r * factor, g * factor, b * factor, a);
27
+ }
28
+ /**
29
+ * Lighten a color.
30
+ * @param color - ColorBits
31
+ * @param coefficient - Multiplier in the range [0, 1]
32
+ */
33
+ function lighten(color, coefficient) {
34
+ const r = (0, bits_1.getRed)(color);
35
+ const g = (0, bits_1.getGreen)(color);
36
+ const b = (0, bits_1.getBlue)(color);
37
+ const a = (0, bits_1.getAlpha)(color);
38
+ return (0, bits_1.newColor)(r + (255 - r) * coefficient, g + (255 - g) * coefficient, b + (255 - b) * coefficient, a);
39
+ }
40
+ //# sourceMappingURL=adjust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adjust.js","sourceRoot":"","sources":["../../src/operations/adjust.ts"],"names":[],"mappings":";;AAQA,sBAEC;AAOD,wBAcC;AAOD,0BAYC;AAjDD,uCAAsF;AAEtF;;;;GAIG;AACH,SAAgB,KAAK,CAAC,KAAgB,EAAE,KAAa;IACnD,OAAO,IAAA,eAAQ,EAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAA;AACjD,CAAC;AAED;;;;GAIG;AACH,SAAgB,MAAM,CAAC,KAAgB,EAAE,WAAmB;IAC1D,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,KAAK,CAAC,CAAA;IACvB,MAAM,CAAC,GAAG,IAAA,eAAQ,EAAC,KAAK,CAAC,CAAA;IACzB,MAAM,CAAC,GAAG,IAAA,cAAO,EAAC,KAAK,CAAC,CAAA;IACxB,MAAM,CAAC,GAAG,IAAA,eAAQ,EAAC,KAAK,CAAC,CAAA;IAEzB,MAAM,MAAM,GAAG,CAAC,GAAG,WAAW,CAAA;IAE9B,OAAO,IAAA,eAAQ,EACb,CAAC,GAAG,MAAM,EACV,CAAC,GAAG,MAAM,EACV,CAAC,GAAG,MAAM,EACV,CAAC,CACF,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,SAAgB,OAAO,CAAC,KAAgB,EAAE,WAAmB;IAC3D,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,KAAK,CAAC,CAAA;IACvB,MAAM,CAAC,GAAG,IAAA,eAAQ,EAAC,KAAK,CAAC,CAAA;IACzB,MAAM,CAAC,GAAG,IAAA,cAAO,EAAC,KAAK,CAAC,CAAA;IACxB,MAAM,CAAC,GAAG,IAAA,eAAQ,EAAC,KAAK,CAAC,CAAA;IAEzB,OAAO,IAAA,eAAQ,EACb,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,WAAW,EAC3B,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,WAAW,EAC3B,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,WAAW,EAC3B,CAAC,CACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ColorBits } from '../core/bits';
2
+ /**
3
+ * Blend (aka mix) two colors together.
4
+ * @param background The background color
5
+ * @param overlay The overlay color affected by `opacity`
6
+ * @param opacity Opacity (alpha) for `overlay`
7
+ * @param [gamma=1.0] Gamma correction coefficient. `1.0` to match browser behavior, `2.2` for gamma-corrected blending.
8
+ */
9
+ export declare function blend(background: ColorBits, overlay: ColorBits, opacity: number, gamma?: number): ColorBits;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.blend = blend;
4
+ const bits_1 = require("../core/bits");
5
+ /**
6
+ * Blend (aka mix) two colors together.
7
+ * @param background The background color
8
+ * @param overlay The overlay color affected by `opacity`
9
+ * @param opacity Opacity (alpha) for `overlay`
10
+ * @param [gamma=1.0] Gamma correction coefficient. `1.0` to match browser behavior, `2.2` for gamma-corrected blending.
11
+ */
12
+ function blend(background, overlay, opacity, gamma = 1.0) {
13
+ const blendChannel = (b, o) => Math.round((b ** (1 / gamma) * (1 - opacity) + o ** (1 / gamma) * opacity) ** gamma);
14
+ const r = blendChannel((0, bits_1.getRed)(background), (0, bits_1.getRed)(overlay));
15
+ const g = blendChannel((0, bits_1.getGreen)(background), (0, bits_1.getGreen)(overlay));
16
+ const b = blendChannel((0, bits_1.getBlue)(background), (0, bits_1.getBlue)(overlay));
17
+ return (0, bits_1.newColor)(r, g, b, 255);
18
+ }
19
+ //# sourceMappingURL=blend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blend.js","sourceRoot":"","sources":["../../src/operations/blend.ts"],"names":[],"mappings":";;AAUA,sBASC;AAlBD,uCAAkE;AAElE;;;;;;GAMG;AACH,SAAgB,KAAK,CAAC,UAAqB,EAAE,OAAkB,EAAE,OAAe,EAAE,KAAK,GAAG,GAAG;IAC3F,MAAM,YAAY,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE,CAC5C,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,KAAK,CAAC,CAAA;IAEtF,MAAM,CAAC,GAAG,YAAY,CAAC,IAAA,aAAM,EAAC,UAAU,CAAC,EAAE,IAAA,aAAM,EAAC,OAAO,CAAC,CAAC,CAAA;IAC3D,MAAM,CAAC,GAAG,YAAY,CAAC,IAAA,eAAQ,EAAC,UAAU,CAAC,EAAE,IAAA,eAAQ,EAAC,OAAO,CAAC,CAAC,CAAA;IAC/D,MAAM,CAAC,GAAG,YAAY,CAAC,IAAA,cAAO,EAAC,UAAU,CAAC,EAAE,IAAA,cAAO,EAAC,OAAO,CAAC,CAAC,CAAA;IAE7D,OAAO,IAAA,eAAQ,EAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { ColorBits } from '../core/bits';
2
+ import type { ColorModel } from '../conversion/channels';
3
+ export type HueMethod = 'shorter' | 'longer' | 'increasing' | 'decreasing';
4
+ export interface ColorMixOptions {
5
+ /** interpolation space: a model name ("oklab", "hsl", …) or a ColorModel, e.g. colorSpaceModel('srgb') */
6
+ space: string | ColorModel;
7
+ /** hue interpolation method for polar spaces (default "shorter") */
8
+ hue?: HueMethod;
9
+ /** percentage (0..100) of color1; defaults derived from p2 or 50 */
10
+ p1?: number;
11
+ /** percentage (0..100) of color2; defaults derived from p1 or 50 */
12
+ p2?: number;
13
+ }
14
+ /**
15
+ * Mix two colors in the given color space.
16
+ * @param color1 first color
17
+ * @param color2 second color
18
+ * @param options interpolation space, hue method and percentages
19
+ */
20
+ export declare function colorMix(color1: ColorBits, color2: ColorBits, options: ColorMixOptions): ColorBits;
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ // CSS Color 5 color-mix(). Interpolates two colors in a chosen color space with
3
+ // premultiplied alpha and the four hue-interpolation methods.
4
+ // https://drafts.csswg.org/css-color-5/#color-mix
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.colorMix = colorMix;
7
+ const bits_1 = require("../core/bits");
8
+ const bytes_1 = require("../core/bytes");
9
+ const channels_1 = require("../conversion/channels");
10
+ // Both interpolation endpoints are alive at once, so each gets its own buffer.
11
+ const CHANNELS_1 = new Float64Array(3);
12
+ const CHANNELS_2 = new Float64Array(3);
13
+ function fail(message) {
14
+ throw new Error('color-mix(): ' + message);
15
+ }
16
+ function normalizeHue(h) {
17
+ return ((h % 360) + 360) % 360;
18
+ }
19
+ function adjustHue(h1, h2, method) {
20
+ h1 = normalizeHue(h1);
21
+ h2 = normalizeHue(h2);
22
+ const d = h2 - h1;
23
+ switch (method) {
24
+ case 'shorter':
25
+ if (d > 180)
26
+ h1 += 360;
27
+ else if (d < -180)
28
+ h2 += 360;
29
+ break;
30
+ case 'longer':
31
+ if (d > 0 && d < 180)
32
+ h1 += 360;
33
+ else if (d > -180 && d <= 0)
34
+ h2 += 360;
35
+ break;
36
+ case 'increasing':
37
+ if (h2 < h1)
38
+ h2 += 360;
39
+ break;
40
+ case 'decreasing':
41
+ if (h1 < h2)
42
+ h1 += 360;
43
+ break;
44
+ }
45
+ return [h1, h2];
46
+ }
47
+ /**
48
+ * Mix two colors in the given color space.
49
+ * @param color1 first color
50
+ * @param color2 second color
51
+ * @param options interpolation space, hue method and percentages
52
+ */
53
+ function colorMix(color1, color2, options) {
54
+ const { space } = options;
55
+ const model = typeof space === 'string' ? (0, channels_1.colorModel)(space.toLowerCase()) : space;
56
+ if (model === null) {
57
+ fail(`unsupported color space: "${space}"`);
58
+ }
59
+ const hueIndex = model.hues.indexOf(true);
60
+ if (options.hue !== undefined && hueIndex === -1) {
61
+ fail('hue method requires a polar space');
62
+ }
63
+ const method = options.hue ?? 'shorter';
64
+ // Resolve percentages and the (sub-100%) alpha multiplier.
65
+ // https://drafts.csswg.org/css-color-5/#color-mix-percent-norm
66
+ const o1 = options.p1;
67
+ const o2 = options.p2;
68
+ if ((o1 !== undefined && !(o1 >= 0 && o1 <= 100)) || (o2 !== undefined && !(o2 >= 0 && o2 <= 100))) {
69
+ fail('percentages must be within [0%, 100%]');
70
+ }
71
+ let p1;
72
+ let p2;
73
+ let alphaMultiplier = 1;
74
+ if (o1 === undefined && o2 === undefined) {
75
+ p1 = 50;
76
+ p2 = 50;
77
+ }
78
+ else if (o1 === undefined) {
79
+ p2 = o2;
80
+ p1 = 100 - p2;
81
+ }
82
+ else if (o2 === undefined) {
83
+ p1 = o1;
84
+ p2 = 100 - p1;
85
+ }
86
+ else {
87
+ const sum = o1 + o2;
88
+ if (sum === 0) {
89
+ fail('percentages must not sum to zero');
90
+ }
91
+ if (sum < 100) {
92
+ alphaMultiplier = sum / 100;
93
+ }
94
+ p1 = (o1 / sum) * 100;
95
+ p2 = (o2 / sum) * 100;
96
+ }
97
+ const w1 = p1 / 100;
98
+ const w2 = p2 / 100;
99
+ const r1 = (0, bits_1.getRed)(color1), g1 = (0, bits_1.getGreen)(color1), b1 = (0, bits_1.getBlue)(color1);
100
+ const r2 = (0, bits_1.getRed)(color2), g2 = (0, bits_1.getGreen)(color2), b2 = (0, bits_1.getBlue)(color2);
101
+ const a1 = (0, bits_1.getAlpha)(color1) / 255;
102
+ const a2 = (0, bits_1.getAlpha)(color2) / 255;
103
+ const c1 = model.fromSrgb(r1 / 255, g1 / 255, b1 / 255, CHANNELS_1);
104
+ const c2 = model.fromSrgb(r2 / 255, g2 / 255, b2 / 255, CHANNELS_2);
105
+ const hi = hueIndex;
106
+ if (hi >= 0) {
107
+ // A powerless hue (achromatic color) is treated as missing and takes the
108
+ // other color's hue. https://drafts.csswg.org/css-color-4/#interpolation-missing
109
+ // Achromaticity is detected on the sRGB bytes (exact); the converted
110
+ // channels carry conversion noise (e.g. white -> lch chroma ~0.02).
111
+ const missing1 = r1 === g1 && g1 === b1;
112
+ const missing2 = r2 === g2 && g2 === b2;
113
+ if (missing1 && !missing2) {
114
+ c1[hi] = c2[hi];
115
+ }
116
+ else if (missing2 && !missing1) {
117
+ c2[hi] = c1[hi];
118
+ }
119
+ const [h1, h2] = adjustHue(c1[hi], c2[hi], method);
120
+ c1[hi] = h1;
121
+ c2[hi] = h2;
122
+ }
123
+ // Premultiply every non-hue coordinate by its alpha.
124
+ for (let i = 0; i < 3; i++) {
125
+ if (i !== hi) {
126
+ c1[i] *= a1;
127
+ c2[i] *= a2;
128
+ }
129
+ }
130
+ const mixedAlpha = a1 * w1 + a2 * w2;
131
+ for (let i = 0; i < 3; i++) {
132
+ let v = c1[i] * w1 + c2[i] * w2;
133
+ if (i === hi) {
134
+ v = normalizeHue(v);
135
+ }
136
+ else if (mixedAlpha !== 0) {
137
+ v /= mixedAlpha; // un-premultiply
138
+ }
139
+ c1[i] = v;
140
+ }
141
+ const alphaByte = (0, bytes_1.clampByte)(mixedAlpha * alphaMultiplier * 255);
142
+ return model.toColor(c1[0], c1[1], c1[2], alphaByte);
143
+ }
144
+ //# sourceMappingURL=color-mix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-mix.js","sourceRoot":"","sources":["../../src/operations/color-mix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ColorBits } from '../core/bits';
2
+ /**
3
+ * The relative brightness of any point in a color space, normalized to 0 for
4
+ * darkest black and 1 for lightest white.
5
+ * @returns The relative brightness of the color in the range 0 - 1, with 3 digits precision
6
+ */
7
+ export declare function getLuminance(color: ColorBits): number;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getLuminance = getLuminance;
4
+ const bits_1 = require("../core/bits");
5
+ /**
6
+ * The relative brightness of any point in a color space, normalized to 0 for
7
+ * darkest black and 1 for lightest white.
8
+ * @returns The relative brightness of the color in the range 0 - 1, with 3 digits precision
9
+ */
10
+ function getLuminance(color) {
11
+ const r = (0, bits_1.getRed)(color) / 255;
12
+ const g = (0, bits_1.getGreen)(color) / 255;
13
+ const b = (0, bits_1.getBlue)(color) / 255;
14
+ const apply = (v) => v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
15
+ const r1 = apply(r);
16
+ const g1 = apply(g);
17
+ const b1 = apply(b);
18
+ return Math.round((0.2126 * r1 + 0.7152 * g1 + 0.0722 * b1) * 1000) / 1000;
19
+ }
20
+ //# sourceMappingURL=luminance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"luminance.js","sourceRoot":"","sources":["../../src/operations/luminance.ts"],"names":[],"mappings":";;AAQA,oCAYC;AAnBD,uCAAwD;AAExD;;;;GAIG;AACH,SAAgB,YAAY,CAAC,KAAgB;IAC3C,MAAM,CAAC,GAAG,IAAA,aAAM,EAAC,KAAK,CAAC,GAAG,GAAG,CAAA;IAC7B,MAAM,CAAC,GAAG,IAAA,eAAQ,EAAC,KAAK,CAAC,GAAG,GAAG,CAAA;IAC/B,MAAM,CAAC,GAAG,IAAA,cAAO,EAAC,KAAK,CAAC,GAAG,GAAG,CAAA;IAE9B,MAAM,KAAK,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,GAAG,CAAA;IAEpF,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACnB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IACnB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;IAEnB,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,GAAG,MAAM,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;AAC5E,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Evaluate a calc() expression against channel keyword bindings.
3
+ * @param body the expression, with or without the outer `calc(` `)`, e.g.
4
+ * "calc(l * 0.8)" or "l * 0.8"; nested calc() is supported
5
+ * @param scope channel keyword bindings
6
+ * @param range percentage reference for the destination channel
7
+ */
8
+ export declare function evaluateCalc(body: string, scope: Map<string, number>, range: number): number;
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ // Minimal CSS calc() evaluator for relative-color channel expressions, e.g.
3
+ // `calc(l * 0.8)`, `calc(h + 120)`, `calc((r + g) / 2)`. Supports + - * /, unary
4
+ // +/-, parentheses, nested calc(), numbers, percentages and angle dimensions.
5
+ //
6
+ // Identifiers are channel keywords resolved from `scope` (e.g. r, g, b, l, h,
7
+ // alpha). Percentages resolve against `range` — the destination channel's
8
+ // reference (e.g. 255 for rgb, 1 for oklab lightness) — matching how a bare
9
+ // percentage is interpreted in that channel slot.
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.evaluateCalc = evaluateCalc;
12
+ const ZERO = 48; // '0'
13
+ const NINE = 57; // '9'
14
+ const DOT = 46; // '.'
15
+ const PERCENT = 37; // '%'
16
+ function isDigit(c) {
17
+ return (c >= ZERO && c <= NINE) || c === DOT;
18
+ }
19
+ function isAlpha(c) {
20
+ // a-z, A-Z (identifiers like `alpha`, channel letters, angle units)
21
+ return (c >= 97 && c <= 122) || (c >= 65 && c <= 90);
22
+ }
23
+ const ANGLE_TO_DEG = {
24
+ deg: 1,
25
+ grad: 360 / 400,
26
+ rad: 180 / Math.PI,
27
+ turn: 360,
28
+ };
29
+ /** A complete CSS <number>: the lexer's digit scan can overshoot (e.g. "1.2.3", "2e"). */
30
+ const NUMBER = /^(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i;
31
+ function fail(message, input) {
32
+ throw new Error(`calc(): ${message} in "${input}"`);
33
+ }
34
+ function lex(input, range) {
35
+ const tokens = [];
36
+ let i = 0;
37
+ const n = input.length;
38
+ while (i < n) {
39
+ const c = input.charCodeAt(i);
40
+ if (c === 32 || c === 9 || c === 10 || c === 13) { // whitespace
41
+ i++;
42
+ continue;
43
+ }
44
+ if (c === 40) {
45
+ tokens.push({ t: 'lparen' });
46
+ i++;
47
+ continue;
48
+ }
49
+ if (c === 41) {
50
+ tokens.push({ t: 'rparen' });
51
+ i++;
52
+ continue;
53
+ }
54
+ if (c === 43 || c === 45 || c === 42 || c === 47) { // + - * /
55
+ tokens.push({ t: 'op', v: input[i] });
56
+ i++;
57
+ continue;
58
+ }
59
+ if (isDigit(c)) {
60
+ let j = i + 1;
61
+ while (j < n && isDigit(input.charCodeAt(j)))
62
+ j++;
63
+ // scientific notation (e.g. 1e-3)
64
+ if (j < n && (input.charCodeAt(j) === 101 || input.charCodeAt(j) === 69)) {
65
+ j++;
66
+ if (j < n && (input.charCodeAt(j) === 43 || input.charCodeAt(j) === 45))
67
+ j++;
68
+ while (j < n && isDigit(input.charCodeAt(j)))
69
+ j++;
70
+ }
71
+ const raw = input.slice(i, j);
72
+ if (!NUMBER.test(raw)) {
73
+ fail(`invalid number "${raw}"`, input);
74
+ }
75
+ let value = parseFloat(raw);
76
+ i = j;
77
+ // trailing unit: % or angle
78
+ if (i < n && input.charCodeAt(i) === PERCENT) {
79
+ value = (value / 100) * range;
80
+ i++;
81
+ }
82
+ else {
83
+ let u = i;
84
+ while (u < n && isAlpha(input.charCodeAt(u)))
85
+ u++;
86
+ if (u > i) {
87
+ const unit = input.slice(i, u).toLowerCase();
88
+ const factor = ANGLE_TO_DEG[unit];
89
+ if (factor !== undefined) {
90
+ value *= factor;
91
+ i = u;
92
+ }
93
+ }
94
+ }
95
+ tokens.push({ t: 'num', v: value });
96
+ continue;
97
+ }
98
+ if (isAlpha(c)) {
99
+ let j = i + 1;
100
+ while (j < n && isAlpha(input.charCodeAt(j)))
101
+ j++;
102
+ const id = input.slice(i, j).toLowerCase();
103
+ i = j;
104
+ // `calc(` (outer and nested) reads as a plain parenthesis
105
+ if (id === 'calc' && i < n && input.charCodeAt(i) === 40) {
106
+ continue;
107
+ }
108
+ tokens.push({ t: 'id', v: id });
109
+ continue;
110
+ }
111
+ fail(`unexpected character "${input[i]}"`, input);
112
+ }
113
+ return tokens;
114
+ }
115
+ /**
116
+ * Evaluate a calc() expression against channel keyword bindings.
117
+ * @param body the expression, with or without the outer `calc(` `)`, e.g.
118
+ * "calc(l * 0.8)" or "l * 0.8"; nested calc() is supported
119
+ * @param scope channel keyword bindings
120
+ * @param range percentage reference for the destination channel
121
+ */
122
+ function evaluateCalc(body, scope, range) {
123
+ const tokens = lex(body, range);
124
+ let pos = 0;
125
+ const peek = () => tokens[pos];
126
+ const next = () => tokens[pos++];
127
+ function parseExpr() {
128
+ let value = parseTerm();
129
+ for (;;) {
130
+ const tok = peek();
131
+ if (tok && tok.t === 'op' && (tok.v === '+' || tok.v === '-')) {
132
+ next();
133
+ const rhs = parseTerm();
134
+ value = tok.v === '+' ? value + rhs : value - rhs;
135
+ }
136
+ else {
137
+ return value;
138
+ }
139
+ }
140
+ }
141
+ function parseTerm() {
142
+ let value = parseFactor();
143
+ for (;;) {
144
+ const tok = peek();
145
+ if (tok && tok.t === 'op' && (tok.v === '*' || tok.v === '/')) {
146
+ next();
147
+ const rhs = parseFactor();
148
+ value = tok.v === '*' ? value * rhs : value / rhs;
149
+ }
150
+ else {
151
+ return value;
152
+ }
153
+ }
154
+ }
155
+ function parseFactor() {
156
+ const tok = next();
157
+ if (!tok) {
158
+ fail('unexpected end of expression', body);
159
+ }
160
+ switch (tok.t) {
161
+ case 'num':
162
+ return tok.v;
163
+ case 'id': {
164
+ // constants and channel keywords
165
+ if (tok.v === 'pi')
166
+ return Math.PI;
167
+ if (tok.v === 'e')
168
+ return Math.E;
169
+ if (tok.v === 'infinity')
170
+ return Infinity;
171
+ if (tok.v === 'nan')
172
+ return NaN;
173
+ if (tok.v === 'none')
174
+ return 0;
175
+ const bound = scope.get(tok.v);
176
+ if (bound === undefined) {
177
+ fail(`unknown identifier "${tok.v}"`, body);
178
+ }
179
+ return bound;
180
+ }
181
+ case 'op':
182
+ if (tok.v === '-')
183
+ return -parseFactor();
184
+ if (tok.v === '+')
185
+ return parseFactor();
186
+ fail(`unexpected "${tok.v}"`, body);
187
+ case 'lparen': {
188
+ const value = parseExpr();
189
+ const close = next();
190
+ if (!close || close.t !== 'rparen') {
191
+ fail('expected ")"', body);
192
+ }
193
+ return value;
194
+ }
195
+ default:
196
+ fail('unexpected token', body);
197
+ }
198
+ }
199
+ const result = parseExpr();
200
+ if (pos !== tokens.length) {
201
+ fail('trailing tokens', body);
202
+ }
203
+ return result;
204
+ }
205
+ //# sourceMappingURL=calc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"calc.js","sourceRoot":"","sources":["../../src/parsing/calc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ColorBits } from '../core/bits';
2
+ import type { Tokens } from './tokenizer';
3
+ /**
4
+ * Resolve a color-mix() from its tokenized form.
5
+ * @param tokens tokenized `color-mix(...)`
6
+ * @param parseColor parser for the two color arguments (recursive)
7
+ */
8
+ export declare function resolveColorMix(tokens: Tokens, parseColor: (input: string) => ColorBits): ColorBits;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveColorMix = resolveColorMix;
4
+ const channels_1 = require("../conversion/channels");
5
+ const color_mix_1 = require("../operations/color-mix");
6
+ const PERCENT = 37; // '%'
7
+ function fail(message) {
8
+ throw new Error('color-mix(): ' + message);
9
+ }
10
+ /** Parse one `<color> [<percentage>]` (order-independent) color-mix argument. */
11
+ function parseColorArg(group, parseColor) {
12
+ let colorToken;
13
+ let p;
14
+ for (const token of group) {
15
+ if (token.charCodeAt(token.length - 1) === PERCENT) {
16
+ if (p !== undefined) {
17
+ fail(`unexpected "${token}"`);
18
+ }
19
+ p = parseFloat(token);
20
+ }
21
+ else {
22
+ if (colorToken !== undefined) {
23
+ fail(`unexpected "${token}"`);
24
+ }
25
+ colorToken = token;
26
+ }
27
+ }
28
+ if (colorToken === undefined) {
29
+ fail('missing color');
30
+ }
31
+ return { color: parseColor(colorToken), p };
32
+ }
33
+ /**
34
+ * Resolve a color-mix() from its tokenized form.
35
+ * @param tokens tokenized `color-mix(...)`
36
+ * @param parseColor parser for the two color arguments (recursive)
37
+ */
38
+ function resolveColorMix(tokens, parseColor) {
39
+ const groups = [[]];
40
+ for (const token of tokens.tokens) {
41
+ if (token === ',') {
42
+ groups.push([]);
43
+ }
44
+ else {
45
+ groups[groups.length - 1].push(token);
46
+ }
47
+ }
48
+ if (groups.length !== 3) {
49
+ fail('expected "in <space>, <color>, <color>"');
50
+ }
51
+ const inClause = groups[0].map((token) => token.toLowerCase());
52
+ if (inClause[0] !== 'in' || inClause[1] === undefined) {
53
+ fail('expected "in <space>, <color>, <color>"');
54
+ }
55
+ const space = inClause[1];
56
+ let hue;
57
+ if (inClause.length !== 2) {
58
+ const method = inClause[2];
59
+ if (inClause.length !== 4 || inClause[3] !== 'hue' ||
60
+ (method !== 'shorter' && method !== 'longer' && method !== 'increasing' && method !== 'decreasing')) {
61
+ fail(`invalid interpolation: "${groups[0].join(' ')}"`);
62
+ }
63
+ hue = method;
64
+ }
65
+ const arg1 = parseColorArg(groups[1], parseColor);
66
+ const arg2 = parseColorArg(groups[2], parseColor);
67
+ // CSS also allows predefined color() spaces, which have no named model.
68
+ return (0, color_mix_1.colorMix)(arg1.color, arg2.color, { space: (0, channels_1.colorSpaceModel)(space) ?? space, hue, p1: arg1.p, p2: arg2.p });
69
+ }
70
+ //# sourceMappingURL=color-mix.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-mix.js","sourceRoot":"","sources":["../../src/parsing/color-mix.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { ColorBits } from '../core/bits';
2
+ export { colorMix } from '../operations/color-mix';
3
+ export type { HueMethod, ColorMixOptions } from '../operations/color-mix';
4
+ export { colorModel, colorSpaceModel } from '../conversion/channels';
5
+ export type { ColorModel } from '../conversion/channels';
6
+ export { resolveNamed, namedColors } from './named-colors';
7
+ /**
8
+ * Resolves a color keyword that has no fixed value — `currentColor` and system
9
+ * colors (e.g. `Canvas`, `ButtonText`). Return ColorBits, a CSS color string, or
10
+ * null if unresolved. In the browser this is typically backed by
11
+ * `getComputedStyle`.
12
+ */
13
+ export type ColorResolver = (name: string) => ColorBits | string | null;
14
+ export interface ParseCSSOptions {
15
+ resolve?: ColorResolver;
16
+ }
17
+ /**
18
+ * Parse any CSS Color Module 4/5 color: hex, named colors, rgb()/hsl()/hwb()/
19
+ * lab()/lch()/oklab()/oklch()/color(), their relative `from …` forms, calc()
20
+ * channels, and color-mix(). For `currentColor`/system colors, pass
21
+ * `options.resolve`.
22
+ */
23
+ export declare function parseCSS(input: string, options?: ParseCSSOptions): ColorBits;