@tokens-studio/tokenscript-schemas 0.0.11 → 0.0.13
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/dist/cli/index.cjs +31 -8
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +30 -8
- package/dist/cli/index.js.map +1 -1
- package/package.json +2 -1
- package/src/bundler/bundle-schema.ts +146 -0
- package/src/bundler/index.ts +151 -0
- package/src/bundler/schema-dependency-resolver.ts +299 -0
- package/src/bundler/selective-bundler.test.ts +94 -0
- package/src/bundler/selective-bundler.ts +159 -0
- package/src/bundler/types.ts +93 -0
- package/src/bundler/utils.ts +74 -0
- package/src/cli/commands/bundle.integration.test.ts +153 -0
- package/src/cli/commands/bundle.test.ts +57 -0
- package/src/cli/commands/bundle.ts +237 -0
- package/src/cli/commands/list.ts +109 -0
- package/src/cli/config-schema.ts +36 -0
- package/src/cli/index.ts +50 -0
- package/src/cli/output-generator.ts +63 -0
- package/src/downloader/index.ts +248 -0
- package/src/downloader/types.ts +48 -0
- package/src/index.ts +8 -0
- package/src/schemas/functions/adjust_chroma/adjust-chroma.tokenscript +27 -0
- package/src/schemas/functions/adjust_chroma/schema.json +48 -0
- package/src/schemas/functions/adjust_chroma/unit.test.ts +76 -0
- package/src/schemas/functions/adjust_hue/adjust-hue.tokenscript +32 -0
- package/src/schemas/functions/adjust_hue/schema.json +48 -0
- package/src/schemas/functions/adjust_hue/unit.test.ts +68 -0
- package/src/schemas/functions/adjust_lightness/adjust-lightness.tokenscript +31 -0
- package/src/schemas/functions/adjust_lightness/schema.json +48 -0
- package/src/schemas/functions/adjust_lightness/unit.test.ts +88 -0
- package/src/schemas/functions/adjust_to_contrast/adjust-to-contrast.tokenscript +131 -0
- package/src/schemas/functions/adjust_to_contrast/schema.json +56 -0
- package/src/schemas/functions/adjust_to_contrast/unit.test.ts +81 -0
- package/src/schemas/functions/alpha_blend/alpha-blend.tokenscript +46 -0
- package/src/schemas/functions/alpha_blend/schema.json +28 -0
- package/src/schemas/functions/alpha_blend/unit.test.ts +135 -0
- package/src/schemas/functions/alpha_scale/alpha-scale.tokenscript +38 -0
- package/src/schemas/functions/alpha_scale/schema.json +24 -0
- package/src/schemas/functions/alpha_scale/unit.test.ts +50 -0
- package/src/schemas/functions/analogous/analogous.tokenscript +47 -0
- package/src/schemas/functions/analogous/schema.json +28 -0
- package/src/schemas/functions/analogous/unit.test.ts +64 -0
- package/src/schemas/functions/apca_contrast/apca-contrast.tokenscript +129 -0
- package/src/schemas/functions/apca_contrast/schema.json +24 -0
- package/src/schemas/functions/apca_contrast/unit.test.ts +259 -0
- package/src/schemas/functions/are_similar/are-similar.tokenscript +24 -0
- package/src/schemas/functions/are_similar/schema.json +57 -0
- package/src/schemas/functions/are_similar/unit.test.ts +57 -0
- package/src/schemas/functions/auto_text_color/auto-text-color.tokenscript +41 -0
- package/src/schemas/functions/auto_text_color/schema.json +53 -0
- package/src/schemas/functions/auto_text_color/unit.test.ts +122 -0
- package/src/schemas/functions/best_contrast/best-contrast.tokenscript +66 -0
- package/src/schemas/functions/best_contrast/schema.json +24 -0
- package/src/schemas/functions/best_contrast/unit.test.ts +64 -0
- package/src/schemas/functions/chroma/chroma.tokenscript +12 -0
- package/src/schemas/functions/chroma/schema.json +44 -0
- package/src/schemas/functions/chroma/unit.test.ts +86 -0
- package/src/schemas/functions/clamp_chroma/clamp-chroma.tokenscript +21 -0
- package/src/schemas/functions/clamp_chroma/schema.json +52 -0
- package/src/schemas/functions/clamp_chroma/unit.test.ts +60 -0
- package/src/schemas/functions/clamp_lightness/clamp-lightness.tokenscript +21 -0
- package/src/schemas/functions/clamp_lightness/schema.json +52 -0
- package/src/schemas/functions/clamp_lightness/unit.test.ts +76 -0
- package/src/schemas/functions/clamp_to_gamut/clamp-to-gamut.tokenscript +41 -0
- package/src/schemas/functions/clamp_to_gamut/schema.json +20 -0
- package/src/schemas/functions/clamp_to_gamut/unit.test.ts +167 -0
- package/src/schemas/functions/complement/complement.tokenscript +21 -0
- package/src/schemas/functions/complement/schema.json +20 -0
- package/src/schemas/functions/complement/unit.test.ts +81 -0
- package/src/schemas/functions/contrast_ratio/contrast-ratio.tokenscript +36 -0
- package/src/schemas/functions/contrast_ratio/schema.json +24 -0
- package/src/schemas/functions/contrast_ratio/unit.test.ts +91 -0
- package/src/schemas/functions/cooler/cooler.tokenscript +45 -0
- package/src/schemas/functions/cooler/schema.json +43 -0
- package/src/schemas/functions/cooler/unit.test.ts +69 -0
- package/src/schemas/functions/darken/darken.tokenscript +37 -0
- package/src/schemas/functions/darken/schema.json +24 -0
- package/src/schemas/functions/darken/unit.test.ts +105 -0
- package/src/schemas/functions/delta_e_2000/delta-e-2000.tokenscript +184 -0
- package/src/schemas/functions/delta_e_2000/schema.json +36 -0
- package/src/schemas/functions/delta_e_2000/unit.test.ts +243 -0
- package/src/schemas/functions/delta_e_76/delta-e-76.tokenscript +45 -0
- package/src/schemas/functions/delta_e_76/schema.json +24 -0
- package/src/schemas/functions/delta_e_76/unit.test.ts +123 -0
- package/src/schemas/functions/delta_e_ok/delta-e-ok.tokenscript +43 -0
- package/src/schemas/functions/delta_e_ok/schema.json +24 -0
- package/src/schemas/functions/delta_e_ok/unit.test.ts +235 -0
- package/src/schemas/functions/desaturate/desaturate.tokenscript +32 -0
- package/src/schemas/functions/desaturate/schema.json +24 -0
- package/src/schemas/functions/desaturate/unit.test.ts +54 -0
- package/src/schemas/functions/distributed/distributed.tokenscript +54 -0
- package/src/schemas/functions/distributed/schema.json +32 -0
- package/src/schemas/functions/distributed/unit.test.ts +58 -0
- package/src/schemas/functions/diverging/diverging.tokenscript +58 -0
- package/src/schemas/functions/diverging/schema.json +32 -0
- package/src/schemas/functions/diverging/unit.test.ts +70 -0
- package/src/schemas/functions/grayscale/grayscale.tokenscript +17 -0
- package/src/schemas/functions/grayscale/schema.json +20 -0
- package/src/schemas/functions/grayscale/unit.test.ts +79 -0
- package/src/schemas/functions/harmonize/harmonize.tokenscript +61 -0
- package/src/schemas/functions/harmonize/schema.json +52 -0
- package/src/schemas/functions/harmonize/unit.test.ts +56 -0
- package/src/schemas/functions/hue/hue.tokenscript +12 -0
- package/src/schemas/functions/hue/schema.json +44 -0
- package/src/schemas/functions/hue/unit.test.ts +75 -0
- package/src/schemas/functions/hue_difference/hue-difference.tokenscript +42 -0
- package/src/schemas/functions/hue_difference/schema.json +24 -0
- package/src/schemas/functions/hue_difference/unit.test.ts +125 -0
- package/src/schemas/functions/in_gamut/in-gamut.tokenscript +51 -0
- package/src/schemas/functions/in_gamut/schema.json +24 -0
- package/src/schemas/functions/in_gamut/unit.test.ts +178 -0
- package/src/schemas/functions/interpolate/interpolate.tokenscript +61 -0
- package/src/schemas/functions/interpolate/schema.json +52 -0
- package/src/schemas/functions/interpolate/unit.test.ts +96 -0
- package/src/schemas/functions/invert/invert-initializer.tokenscript +29 -0
- package/src/schemas/functions/invert/schema.json +20 -0
- package/src/schemas/functions/invert/unit.test.ts +216 -0
- package/src/schemas/functions/is_cool/is-cool.tokenscript +41 -0
- package/src/schemas/functions/is_cool/schema.json +20 -0
- package/src/schemas/functions/is_cool/unit.test.ts +189 -0
- package/src/schemas/functions/is_dark/is-dark.tokenscript +16 -0
- package/src/schemas/functions/is_dark/schema.json +24 -0
- package/src/schemas/functions/is_dark/unit.test.ts +87 -0
- package/src/schemas/functions/is_light/is-light.tokenscript +16 -0
- package/src/schemas/functions/is_light/schema.json +24 -0
- package/src/schemas/functions/is_light/unit.test.ts +86 -0
- package/src/schemas/functions/is_neutral/is-neutral.tokenscript +16 -0
- package/src/schemas/functions/is_neutral/schema.json +53 -0
- package/src/schemas/functions/is_neutral/unit.test.ts +85 -0
- package/src/schemas/functions/is_warm/is-warm.tokenscript +62 -0
- package/src/schemas/functions/is_warm/schema.json +20 -0
- package/src/schemas/functions/is_warm/unit.test.ts +161 -0
- package/src/schemas/functions/lighten/lighten.tokenscript +37 -0
- package/src/schemas/functions/lighten/schema.json +24 -0
- package/src/schemas/functions/lighten/unit.test.ts +109 -0
- package/src/schemas/functions/lightness/lightness.tokenscript +12 -0
- package/src/schemas/functions/lightness/schema.json +49 -0
- package/src/schemas/functions/lightness/unit.test.ts +99 -0
- package/src/schemas/functions/luminance/luminance.tokenscript +16 -0
- package/src/schemas/functions/luminance/schema.json +20 -0
- package/src/schemas/functions/luminance/unit.test.ts +105 -0
- package/src/schemas/functions/meets_contrast/meets-contrast.tokenscript +49 -0
- package/src/schemas/functions/meets_contrast/schema.json +28 -0
- package/src/schemas/functions/meets_contrast/unit.test.ts +170 -0
- package/src/schemas/functions/mix/mix.tokenscript +47 -0
- package/src/schemas/functions/mix/schema.json +28 -0
- package/src/schemas/functions/mix/unit.test.ts +95 -0
- package/src/schemas/functions/monochromatic/monochromatic.tokenscript +72 -0
- package/src/schemas/functions/monochromatic/schema.json +24 -0
- package/src/schemas/functions/monochromatic/unit.test.ts +91 -0
- package/src/schemas/functions/muted/muted.tokenscript +25 -0
- package/src/schemas/functions/muted/schema.json +48 -0
- package/src/schemas/functions/muted/unit.test.ts +100 -0
- package/src/schemas/functions/neutral_variant/neutral-variant.tokenscript +23 -0
- package/src/schemas/functions/neutral_variant/schema.json +48 -0
- package/src/schemas/functions/neutral_variant/unit.test.ts +102 -0
- package/src/schemas/functions/relative_luminance/relative-luminance.tokenscript +15 -0
- package/src/schemas/functions/relative_luminance/schema.json +49 -0
- package/src/schemas/functions/relative_luminance/unit.test.ts +104 -0
- package/src/schemas/functions/rotate_hue/rotate-hue.tokenscript +20 -0
- package/src/schemas/functions/rotate_hue/schema.json +24 -0
- package/src/schemas/functions/rotate_hue/unit.test.ts +86 -0
- package/src/schemas/functions/saturate/saturate.tokenscript +33 -0
- package/src/schemas/functions/saturate/schema.json +24 -0
- package/src/schemas/functions/saturate/unit.test.ts +59 -0
- package/src/schemas/functions/scale_chroma/scale-chroma.tokenscript +22 -0
- package/src/schemas/functions/scale_chroma/schema.json +48 -0
- package/src/schemas/functions/scale_chroma/unit.test.ts +79 -0
- package/src/schemas/functions/scale_lightness/scale-lightness.tokenscript +23 -0
- package/src/schemas/functions/scale_lightness/schema.json +48 -0
- package/src/schemas/functions/scale_lightness/unit.test.ts +73 -0
- package/src/schemas/functions/sepia/schema.json +48 -0
- package/src/schemas/functions/sepia/sepia.tokenscript +54 -0
- package/src/schemas/functions/sepia/unit.test.ts +88 -0
- package/src/schemas/functions/set_chroma/schema.json +24 -0
- package/src/schemas/functions/set_chroma/set-chroma.tokenscript +18 -0
- package/src/schemas/functions/set_chroma/unit.test.ts +79 -0
- package/src/schemas/functions/set_hue/schema.json +24 -0
- package/src/schemas/functions/set_hue/set-hue.tokenscript +18 -0
- package/src/schemas/functions/set_hue/unit.test.ts +90 -0
- package/src/schemas/functions/set_lightness/schema.json +24 -0
- package/src/schemas/functions/set_lightness/set-lightness.tokenscript +18 -0
- package/src/schemas/functions/set_lightness/unit.test.ts +80 -0
- package/src/schemas/functions/shade_scale/schema.json +24 -0
- package/src/schemas/functions/shade_scale/shade-scale.tokenscript +61 -0
- package/src/schemas/functions/shade_scale/unit.test.ts +64 -0
- package/src/schemas/functions/split_complement/schema.json +24 -0
- package/src/schemas/functions/split_complement/split-complement.tokenscript +38 -0
- package/src/schemas/functions/split_complement/unit.test.ts +53 -0
- package/src/schemas/functions/steps/schema.json +28 -0
- package/src/schemas/functions/steps/steps.tokenscript +54 -0
- package/src/schemas/functions/steps/unit.test.ts +71 -0
- package/src/schemas/functions/tetradic/schema.json +20 -0
- package/src/schemas/functions/tetradic/tetradic.tokenscript +40 -0
- package/src/schemas/functions/tetradic/unit.test.ts +50 -0
- package/src/schemas/functions/tint_scale/schema.json +32 -0
- package/src/schemas/functions/tint_scale/tint-scale.tokenscript +71 -0
- package/src/schemas/functions/tint_scale/unit.test.ts +64 -0
- package/src/schemas/functions/to_gamut/schema.json +48 -0
- package/src/schemas/functions/to_gamut/to-gamut.tokenscript +96 -0
- package/src/schemas/functions/to_gamut/unit.test.ts +97 -0
- package/src/schemas/functions/triadic/schema.json +20 -0
- package/src/schemas/functions/triadic/triadic.tokenscript +33 -0
- package/src/schemas/functions/triadic/unit.test.ts +64 -0
- package/src/schemas/functions/vibrant/schema.json +48 -0
- package/src/schemas/functions/vibrant/unit.test.ts +55 -0
- package/src/schemas/functions/vibrant/vibrant.tokenscript +29 -0
- package/src/schemas/functions/warmer/schema.json +43 -0
- package/src/schemas/functions/warmer/unit.test.ts +69 -0
- package/src/schemas/functions/warmer/warmer.tokenscript +45 -0
- package/src/schemas/functions/wcag_level/schema.json +48 -0
- package/src/schemas/functions/wcag_level/unit.test.ts +75 -0
- package/src/schemas/functions/wcag_level/wcag-level.tokenscript +50 -0
- package/src/schemas/types/css-color/from-hsl-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-hwb-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-lab-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-lch-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-oklab-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-oklch-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-p3-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-rgb-color.tokenscript +15 -0
- package/src/schemas/types/css-color/from-srgb-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-srgb-linear-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-xyz-d50-color.tokenscript +16 -0
- package/src/schemas/types/css-color/from-xyz-d65-color.tokenscript +16 -0
- package/src/schemas/types/css-color/initializer.tokenscript +13 -0
- package/src/schemas/types/css-color/schema.json +148 -0
- package/src/schemas/types/css-color/unit.test.ts +402 -0
- package/src/schemas/types/hex-color/initializer.tokenscript +3 -0
- package/src/schemas/types/hex-color/schema.json +24 -0
- package/src/schemas/types/hex-color/unit.test.ts +123 -0
- package/src/schemas/types/hsl-color/from-srgb.tokenscript +87 -0
- package/src/schemas/types/hsl-color/initializer.tokenscript +16 -0
- package/src/schemas/types/hsl-color/schema.json +48 -0
- package/src/schemas/types/hsl-color/unit.test.ts +201 -0
- package/src/schemas/types/hsv-color/from-srgb.tokenscript +80 -0
- package/src/schemas/types/hsv-color/initializer.tokenscript +16 -0
- package/src/schemas/types/hsv-color/schema.json +48 -0
- package/src/schemas/types/hsv-color/unit.test.ts +162 -0
- package/src/schemas/types/hwb-color/from-hsv.tokenscript +31 -0
- package/src/schemas/types/hwb-color/initializer.tokenscript +16 -0
- package/src/schemas/types/hwb-color/schema.json +48 -0
- package/src/schemas/types/hwb-color/unit.test.ts +150 -0
- package/src/schemas/types/lab-color/from-xyz-d50.tokenscript +78 -0
- package/src/schemas/types/lab-color/initializer.tokenscript +16 -0
- package/src/schemas/types/lab-color/schema.json +48 -0
- package/src/schemas/types/lab-color/unit.test.ts +263 -0
- package/src/schemas/types/lch-color/from-lab.tokenscript +44 -0
- package/src/schemas/types/lch-color/initializer.tokenscript +16 -0
- package/src/schemas/types/lch-color/schema.json +48 -0
- package/src/schemas/types/lch-color/unit.test.ts +173 -0
- package/src/schemas/types/okhsl-color/from-oklab.tokenscript +410 -0
- package/src/schemas/types/okhsl-color/initializer.tokenscript +24 -0
- package/src/schemas/types/okhsl-color/schema.json +48 -0
- package/src/schemas/types/okhsl-color/unit.test.ts +514 -0
- package/src/schemas/types/okhsv-color/from-oklab.tokenscript +286 -0
- package/src/schemas/types/okhsv-color/initializer.tokenscript +24 -0
- package/src/schemas/types/okhsv-color/schema.json +48 -0
- package/src/schemas/types/okhsv-color/unit.test.ts +499 -0
- package/src/schemas/types/oklab-color/from-okhsl.tokenscript +195 -0
- package/src/schemas/types/oklab-color/from-okhsv.tokenscript +197 -0
- package/src/schemas/types/oklab-color/from-oklch.tokenscript +39 -0
- package/src/schemas/types/oklab-color/from-xyz-d65.tokenscript +43 -0
- package/src/schemas/types/oklab-color/initializer.tokenscript +16 -0
- package/src/schemas/types/oklab-color/schema.json +78 -0
- package/src/schemas/types/oklab-color/unit.test.ts +345 -0
- package/src/schemas/types/oklch-color/from-oklab.tokenscript +45 -0
- package/src/schemas/types/oklch-color/initializer.tokenscript +16 -0
- package/src/schemas/types/oklch-color/schema.json +48 -0
- package/src/schemas/types/oklch-color/unit.test.ts +267 -0
- package/src/schemas/types/p3-color/from-p3-linear.tokenscript +59 -0
- package/src/schemas/types/p3-color/initializer.tokenscript +16 -0
- package/src/schemas/types/p3-color/schema.json +48 -0
- package/src/schemas/types/p3-color/unit.test.ts +119 -0
- package/src/schemas/types/p3-linear-color/from-xyz-d65.tokenscript +47 -0
- package/src/schemas/types/p3-linear-color/initializer.tokenscript +16 -0
- package/src/schemas/types/p3-linear-color/schema.json +48 -0
- package/src/schemas/types/p3-linear-color/unit.test.ts +82 -0
- package/src/schemas/types/rgb-color/from-hex.tokenscript +43 -0
- package/src/schemas/types/rgb-color/initializer.tokenscript +16 -0
- package/src/schemas/types/rgb-color/schema.json +55 -0
- package/src/schemas/types/rgb-color/to-hex.tokenscript +42 -0
- package/src/schemas/types/rgb-color/unit.test.ts +302 -0
- package/src/schemas/types/srgb-color/from-hsl.tokenscript +106 -0
- package/src/schemas/types/srgb-color/from-linear.tokenscript +58 -0
- package/src/schemas/types/srgb-color/from-rgb.tokenscript +20 -0
- package/src/schemas/types/srgb-color/initializer.tokenscript +16 -0
- package/src/schemas/types/srgb-color/schema.json +68 -0
- package/src/schemas/types/srgb-color/unit.test.ts +303 -0
- package/src/schemas/types/srgb-linear-color/from-srgb.tokenscript +55 -0
- package/src/schemas/types/srgb-linear-color/from-xyz-d65.tokenscript +34 -0
- package/src/schemas/types/srgb-linear-color/initializer.tokenscript +13 -0
- package/src/schemas/types/srgb-linear-color/schema.json +58 -0
- package/src/schemas/types/srgb-linear-color/unit.test.ts +291 -0
- package/src/schemas/types/xyz-d50-color/from-xyz-d65.tokenscript +36 -0
- package/src/schemas/types/xyz-d50-color/initializer.tokenscript +16 -0
- package/src/schemas/types/xyz-d50-color/schema.json +48 -0
- package/src/schemas/types/xyz-d50-color/unit.test.ts +240 -0
- package/src/schemas/types/xyz-d65-color/from-linear-p3.tokenscript +47 -0
- package/src/schemas/types/xyz-d65-color/from-linear-srgb.tokenscript +38 -0
- package/src/schemas/types/xyz-d65-color/from-oklab.tokenscript +44 -0
- package/src/schemas/types/xyz-d65-color/initializer.tokenscript +16 -0
- package/src/schemas/types/xyz-d65-color/schema.json +68 -0
- package/src/schemas/types/xyz-d65-color/unit.test.ts +319 -0
- package/src/utils/schema-uri.ts +192 -0
- package/src/utils/type.ts +194 -0
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linear sRGB Color Schema Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the Linear sRGB color space (gamma-decoded)
|
|
5
|
+
* Validates against ColorJS for parity
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { log } from "@tests/helpers/logger";
|
|
9
|
+
import { executeWithSchema, getBundledSchema } from "@tests/helpers/schema-test-utils";
|
|
10
|
+
import Color from "colorjs.io";
|
|
11
|
+
import { describe, expect, it } from "vitest";
|
|
12
|
+
import type { ColorSpecification } from "@/bundler/types";
|
|
13
|
+
|
|
14
|
+
// ColorJS reference tolerance
|
|
15
|
+
const TOLERANCE = 1e-9;
|
|
16
|
+
|
|
17
|
+
describe("Linear sRGB Color Schema", () => {
|
|
18
|
+
describe("Schema Definition", () => {
|
|
19
|
+
it("should have correct schema structure", async () => {
|
|
20
|
+
const schema = (await getBundledSchema("srgb-linear-color")) as ColorSpecification;
|
|
21
|
+
|
|
22
|
+
expect(schema.name).toBe("LinearSRGB");
|
|
23
|
+
expect(schema.type).toBe("color");
|
|
24
|
+
expect(schema.schema).toBeDefined();
|
|
25
|
+
expect(schema.schema?.properties).toHaveProperty("r");
|
|
26
|
+
expect(schema.schema?.properties).toHaveProperty("g");
|
|
27
|
+
expect(schema.schema?.properties).toHaveProperty("b");
|
|
28
|
+
expect(schema.schema?.required).toEqual(["r", "g", "b"]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should have linearsrgb initializer", async () => {
|
|
32
|
+
const schema = (await getBundledSchema("srgb-linear-color")) as ColorSpecification;
|
|
33
|
+
|
|
34
|
+
expect(schema.initializers).toHaveLength(1);
|
|
35
|
+
expect(schema.initializers[0].keyword).toBe("linearsrgb");
|
|
36
|
+
expect(schema.initializers[0].script.script).toContain("Color.LinearSRGB");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should have conversion from sRGB", async () => {
|
|
40
|
+
const schema = (await getBundledSchema("srgb-linear-color")) as ColorSpecification;
|
|
41
|
+
|
|
42
|
+
expect(schema.conversions.length).toBeGreaterThanOrEqual(1);
|
|
43
|
+
|
|
44
|
+
const srgbToLinear = schema.conversions.find((c: { source: string }) =>
|
|
45
|
+
c.source.includes("srgb-color"),
|
|
46
|
+
);
|
|
47
|
+
expect(srgbToLinear).toBeDefined();
|
|
48
|
+
expect(srgbToLinear?.lossless).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("Initialization", () => {
|
|
53
|
+
it("should create linear sRGB color from values", async () => {
|
|
54
|
+
const result = await executeWithSchema(
|
|
55
|
+
"srgb-linear-color",
|
|
56
|
+
"type",
|
|
57
|
+
`
|
|
58
|
+
variable c: Color.LinearSRGB;
|
|
59
|
+
c.r = 0.5;
|
|
60
|
+
c.g = 0.25;
|
|
61
|
+
c.b = 0.1;
|
|
62
|
+
c
|
|
63
|
+
`,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(result?.constructor.name).toBe("ColorSymbol");
|
|
67
|
+
expect((result as any).subType).toBe("LinearSRGB");
|
|
68
|
+
expect((result as any).value.r.value).toBeCloseTo(0.5, 10);
|
|
69
|
+
expect((result as any).value.g.value).toBeCloseTo(0.25, 10);
|
|
70
|
+
expect((result as any).value.b.value).toBeCloseTo(0.1, 10);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("Conversion from sRGB to Linear sRGB", () => {
|
|
75
|
+
it("should convert sRGB red to linear sRGB", async () => {
|
|
76
|
+
const result = await executeWithSchema(
|
|
77
|
+
"srgb-linear-color",
|
|
78
|
+
"type",
|
|
79
|
+
`
|
|
80
|
+
variable srgb: Color.SRGB;
|
|
81
|
+
srgb.r = 1;
|
|
82
|
+
srgb.g = 0;
|
|
83
|
+
srgb.b = 0;
|
|
84
|
+
srgb.to.linearsrgb()
|
|
85
|
+
`,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// ColorJS reference
|
|
89
|
+
const colorJS = new Color("srgb", [1, 0, 0]).to("srgb-linear");
|
|
90
|
+
|
|
91
|
+
expect(result?.constructor.name).toBe("ColorSymbol");
|
|
92
|
+
expect((result as any).subType).toBe("LinearSRGB");
|
|
93
|
+
expect((result as any).value.r.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
94
|
+
expect((result as any).value.g.value).toBeCloseTo(colorJS.coords[1], 9);
|
|
95
|
+
expect((result as any).value.b.value).toBeCloseTo(colorJS.coords[2], 9);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("should convert mid-gray sRGB to linear (tests gamma curve)", async () => {
|
|
99
|
+
// Mid-gray (0.5) is a good test for the gamma curve
|
|
100
|
+
// Linear value should be ~0.214 (not 0.5!)
|
|
101
|
+
const result = await executeWithSchema(
|
|
102
|
+
"srgb-linear-color",
|
|
103
|
+
"type",
|
|
104
|
+
`
|
|
105
|
+
variable srgb: Color.SRGB;
|
|
106
|
+
srgb.r = 0.5;
|
|
107
|
+
srgb.g = 0.5;
|
|
108
|
+
srgb.b = 0.5;
|
|
109
|
+
srgb.to.linearsrgb()
|
|
110
|
+
`,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// ColorJS reference
|
|
114
|
+
const colorJS = new Color("srgb", [0.5, 0.5, 0.5]).to("srgb-linear");
|
|
115
|
+
|
|
116
|
+
log.info(`\n=== MID-GRAY Gamma Conversion ===`);
|
|
117
|
+
log.info(`Input sRGB: { r: 0.5, g: 0.5, b: 0.5 }`);
|
|
118
|
+
log.info(
|
|
119
|
+
`TokenScript: { r: ${(result as any).value.r.value}, g: ${(result as any).value.g.value}, b: ${(result as any).value.b.value} }`,
|
|
120
|
+
);
|
|
121
|
+
log.info(
|
|
122
|
+
`ColorJS: { r: ${colorJS.coords[0]}, g: ${colorJS.coords[1]}, b: ${colorJS.coords[2]} }`,
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
expect((result as any).value.r.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
126
|
+
expect((result as any).value.g.value).toBeCloseTo(colorJS.coords[1], 9);
|
|
127
|
+
expect((result as any).value.b.value).toBeCloseTo(colorJS.coords[2], 9);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should handle linear region (sRGB ≤ 0.04045)", async () => {
|
|
131
|
+
// Test value below threshold - should use linear formula
|
|
132
|
+
const result = await executeWithSchema(
|
|
133
|
+
"srgb-linear-color",
|
|
134
|
+
"type",
|
|
135
|
+
`
|
|
136
|
+
variable srgb: Color.SRGB;
|
|
137
|
+
srgb.r = 0.03;
|
|
138
|
+
srgb.g = 0.03;
|
|
139
|
+
srgb.b = 0.03;
|
|
140
|
+
srgb.to.linearsrgb()
|
|
141
|
+
`,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// ColorJS reference
|
|
145
|
+
const colorJS = new Color("srgb", [0.03, 0.03, 0.03]).to("srgb-linear");
|
|
146
|
+
|
|
147
|
+
// Linear formula: 0.03 / 12.92 ≈ 0.00232
|
|
148
|
+
expect((result as any).value.r.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
149
|
+
expect((result as any).value.g.value).toBeCloseTo(colorJS.coords[1], 9);
|
|
150
|
+
expect((result as any).value.b.value).toBeCloseTo(colorJS.coords[2], 9);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should handle threshold boundary (0.04045)", async () => {
|
|
154
|
+
// Test exactly at threshold
|
|
155
|
+
const result = await executeWithSchema(
|
|
156
|
+
"srgb-linear-color",
|
|
157
|
+
"type",
|
|
158
|
+
`
|
|
159
|
+
variable srgb: Color.SRGB;
|
|
160
|
+
srgb.r = 0.04045;
|
|
161
|
+
srgb.g = 0.04045;
|
|
162
|
+
srgb.b = 0.04045;
|
|
163
|
+
srgb.to.linearsrgb()
|
|
164
|
+
`,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const colorJS = new Color("srgb", [0.04045, 0.04045, 0.04045]).to("srgb-linear");
|
|
168
|
+
|
|
169
|
+
expect((result as any).value.r.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("ColorJS Parity", () => {
|
|
174
|
+
const testCases = [
|
|
175
|
+
{ name: "red", srgb: [1, 0, 0] },
|
|
176
|
+
{ name: "green", srgb: [0, 1, 0] },
|
|
177
|
+
{ name: "blue", srgb: [0, 0, 1] },
|
|
178
|
+
{ name: "black", srgb: [0, 0, 0] },
|
|
179
|
+
{ name: "white", srgb: [1, 1, 1] },
|
|
180
|
+
{ name: "gray-50%", srgb: [0.5, 0.5, 0.5] },
|
|
181
|
+
{ name: "gray-21.8%", srgb: [0.218, 0.218, 0.218] }, // Linear 0.04 approx
|
|
182
|
+
{ name: "coral", srgb: [1, 0.341, 0.2] },
|
|
183
|
+
{ name: "near-black", srgb: [0.01, 0.01, 0.01] },
|
|
184
|
+
{ name: "near-white", srgb: [0.99, 0.99, 0.99] },
|
|
185
|
+
];
|
|
186
|
+
|
|
187
|
+
for (const { name, srgb } of testCases) {
|
|
188
|
+
it(`should match ColorJS for ${name}`, async () => {
|
|
189
|
+
const result = await executeWithSchema(
|
|
190
|
+
"srgb-linear-color",
|
|
191
|
+
"type",
|
|
192
|
+
`
|
|
193
|
+
variable srgb: Color.SRGB;
|
|
194
|
+
srgb.r = ${srgb[0]};
|
|
195
|
+
srgb.g = ${srgb[1]};
|
|
196
|
+
srgb.b = ${srgb[2]};
|
|
197
|
+
srgb.to.linearsrgb()
|
|
198
|
+
`,
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
// ColorJS reference
|
|
202
|
+
const colorJS = new Color("srgb", srgb as [number, number, number]).to("srgb-linear");
|
|
203
|
+
|
|
204
|
+
const tsR = (result as any).value.r.value;
|
|
205
|
+
const tsG = (result as any).value.g.value;
|
|
206
|
+
const tsB = (result as any).value.b.value;
|
|
207
|
+
|
|
208
|
+
const diffR = Math.abs(tsR - colorJS.coords[0]);
|
|
209
|
+
const diffG = Math.abs(tsG - colorJS.coords[1]);
|
|
210
|
+
const diffB = Math.abs(tsB - colorJS.coords[2]);
|
|
211
|
+
const maxDiff = Math.max(diffR, diffG, diffB);
|
|
212
|
+
|
|
213
|
+
log.info(`\n=== ${name.toUpperCase()} ColorJS Parity ===`);
|
|
214
|
+
log.info(`Input sRGB: { r: ${srgb[0]}, g: ${srgb[1]}, b: ${srgb[2]} }`);
|
|
215
|
+
log.info(
|
|
216
|
+
`TokenScript: { r: ${tsR.toFixed(9)}, g: ${tsG.toFixed(9)}, b: ${tsB.toFixed(9)} }`,
|
|
217
|
+
);
|
|
218
|
+
log.info(
|
|
219
|
+
`ColorJS: { r: ${colorJS.coords[0].toFixed(9)}, g: ${colorJS.coords[1].toFixed(9)}, b: ${colorJS.coords[2].toFixed(9)} }`,
|
|
220
|
+
);
|
|
221
|
+
log.info(`Max Diff: ${maxDiff.toExponential(2)}`);
|
|
222
|
+
log.info(`Status: ${maxDiff < TOLERANCE ? "✅ PASS" : "❌ FAIL"}`);
|
|
223
|
+
|
|
224
|
+
expect(maxDiff).toBeLessThan(TOLERANCE);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe("Edge Cases", () => {
|
|
230
|
+
it("should handle pure black (0, 0, 0)", async () => {
|
|
231
|
+
const result = await executeWithSchema(
|
|
232
|
+
"srgb-linear-color",
|
|
233
|
+
"type",
|
|
234
|
+
`
|
|
235
|
+
variable srgb: Color.SRGB;
|
|
236
|
+
srgb.r = 0;
|
|
237
|
+
srgb.g = 0;
|
|
238
|
+
srgb.b = 0;
|
|
239
|
+
srgb.to.linearsrgb()
|
|
240
|
+
`,
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
expect((result as any).value.r.value).toBe(0);
|
|
244
|
+
expect((result as any).value.g.value).toBe(0);
|
|
245
|
+
expect((result as any).value.b.value).toBe(0);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it("should handle pure white (1, 1, 1)", async () => {
|
|
249
|
+
const result = await executeWithSchema(
|
|
250
|
+
"srgb-linear-color",
|
|
251
|
+
"type",
|
|
252
|
+
`
|
|
253
|
+
variable srgb: Color.SRGB;
|
|
254
|
+
srgb.r = 1;
|
|
255
|
+
srgb.g = 1;
|
|
256
|
+
srgb.b = 1;
|
|
257
|
+
srgb.to.linearsrgb()
|
|
258
|
+
`,
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
expect((result as any).value.r.value).toBeCloseTo(1, 9);
|
|
262
|
+
expect((result as any).value.g.value).toBeCloseTo(1, 9);
|
|
263
|
+
expect((result as any).value.b.value).toBeCloseTo(1, 9);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe("Conversion Chain: RGB → sRGB → Linear sRGB", () => {
|
|
268
|
+
it("should convert RGB 255 through to linear sRGB", async () => {
|
|
269
|
+
const result = await executeWithSchema(
|
|
270
|
+
"srgb-linear-color",
|
|
271
|
+
"type",
|
|
272
|
+
`
|
|
273
|
+
variable rgb: Color.Rgb;
|
|
274
|
+
rgb.r = 255;
|
|
275
|
+
rgb.g = 128;
|
|
276
|
+
rgb.b = 64;
|
|
277
|
+
|
|
278
|
+
variable srgb: Color.SRGB = rgb.to.srgb();
|
|
279
|
+
srgb.to.linearsrgb()
|
|
280
|
+
`,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
// ColorJS reference: RGB → sRGB → Linear sRGB
|
|
284
|
+
const colorJS = new Color("srgb", [1, 128 / 255, 64 / 255]).to("srgb-linear");
|
|
285
|
+
|
|
286
|
+
expect((result as any).value.r.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
287
|
+
expect((result as any).value.g.value).toBeCloseTo(colorJS.coords[1], 9);
|
|
288
|
+
expect((result as any).value.b.value).toBeCloseTo(colorJS.coords[2], 9);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// XYZ-D65 to XYZ-D50 Conversion
|
|
2
|
+
// Chromatic adaptation using Bradford transform
|
|
3
|
+
// Reference: https://github.com/color-js/color.js/blob/main/src/adapt.js
|
|
4
|
+
//
|
|
5
|
+
// Bradford CAT matrix for D65 → D50:
|
|
6
|
+
// [1.0479298 0.0229469 -0.0501923]
|
|
7
|
+
// [0.0296278 0.9904344 -0.0170738]
|
|
8
|
+
// [-0.0092430 0.0150552 0.7518743]
|
|
9
|
+
//
|
|
10
|
+
// Input: Color.XYZD65 with x, y, z tristimulus (D65 white)
|
|
11
|
+
// Output: Color.XYZD50 with x, y, z tristimulus (D50 white)
|
|
12
|
+
|
|
13
|
+
// Get input XYZ-D65 values
|
|
14
|
+
variable x65: Number = {input}.x;
|
|
15
|
+
variable y65: Number = {input}.y;
|
|
16
|
+
variable z65: Number = {input}.z;
|
|
17
|
+
|
|
18
|
+
// Bradford chromatic adaptation matrix (ColorJS exact values)
|
|
19
|
+
// Row 1
|
|
20
|
+
variable x50: Number = x65 * 1.0479297925449969 + y65 * 0.022946870601609652 + z65 * -0.05019226628920524;
|
|
21
|
+
// Row 2
|
|
22
|
+
variable y50: Number = x65 * 0.02962780877005599 + y65 * 0.9904344267538799 + z65 * -0.017073799063418826;
|
|
23
|
+
// Row 3
|
|
24
|
+
variable z50: Number = x65 * -0.009243040646204504 + y65 * 0.015055191490298152 + z65 * 0.7518742814281371;
|
|
25
|
+
|
|
26
|
+
// Create output
|
|
27
|
+
variable output: Color.XYZD50;
|
|
28
|
+
output.x = x50;
|
|
29
|
+
output.y = y50;
|
|
30
|
+
output.z = z50;
|
|
31
|
+
|
|
32
|
+
return output;
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// XYZ-D50 Color Initializer
|
|
2
|
+
// Creates an XYZ-D50 color from tristimulus values
|
|
3
|
+
// Input: List of [x, y, z] tristimulus values
|
|
4
|
+
|
|
5
|
+
variable xyz_values: List = {input};
|
|
6
|
+
variable output: Color.XYZD50;
|
|
7
|
+
|
|
8
|
+
output.x = xyz_values.get(0);
|
|
9
|
+
output.y = xyz_values.get(1);
|
|
10
|
+
output.z = xyz_values.get(2);
|
|
11
|
+
|
|
12
|
+
return output;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "XYZD50",
|
|
3
|
+
"type": "color",
|
|
4
|
+
"description": "CIE XYZ color space with D50 white point. Used for Lab and LCH color spaces.",
|
|
5
|
+
"schema": {
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"x": {
|
|
9
|
+
"type": "number",
|
|
10
|
+
"description": "X tristimulus value"
|
|
11
|
+
},
|
|
12
|
+
"y": {
|
|
13
|
+
"type": "number",
|
|
14
|
+
"description": "Y tristimulus value (luminance)"
|
|
15
|
+
},
|
|
16
|
+
"z": {
|
|
17
|
+
"type": "number",
|
|
18
|
+
"description": "Z tristimulus value"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"required": ["x", "y", "z"],
|
|
22
|
+
"order": ["x", "y", "z"],
|
|
23
|
+
"additionalProperties": false
|
|
24
|
+
},
|
|
25
|
+
"initializers": [
|
|
26
|
+
{
|
|
27
|
+
"title": "XYZ-D50 Color Initializer",
|
|
28
|
+
"keyword": "xyzd50",
|
|
29
|
+
"description": "Creates an XYZ-D50 color from tristimulus values",
|
|
30
|
+
"script": {
|
|
31
|
+
"type": "/api/v1/core/tokenscript/0/",
|
|
32
|
+
"script": "./initializer.tokenscript"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"conversions": [
|
|
37
|
+
{
|
|
38
|
+
"source": "/api/v1/core/xyz-d65-color/0/",
|
|
39
|
+
"target": "$self",
|
|
40
|
+
"description": "Converts XYZ-D65 to XYZ-D50 using Bradford chromatic adaptation",
|
|
41
|
+
"lossless": true,
|
|
42
|
+
"script": {
|
|
43
|
+
"type": "/api/v1/core/tokenscript/0/",
|
|
44
|
+
"script": "./from-xyz-d65.tokenscript"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XYZ-D50 Color Schema Tests
|
|
3
|
+
*
|
|
4
|
+
* Tests for the CIE XYZ-D50 color space (D50 white point)
|
|
5
|
+
* Validates against ColorJS for parity
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { log } from "@tests/helpers/logger";
|
|
9
|
+
import { executeWithSchema, getBundledSchema } from "@tests/helpers/schema-test-utils";
|
|
10
|
+
import Color from "colorjs.io";
|
|
11
|
+
import { describe, expect, it } from "vitest";
|
|
12
|
+
import type { ColorSpecification } from "@/bundler/types";
|
|
13
|
+
|
|
14
|
+
// ColorJS reference tolerance
|
|
15
|
+
const TOLERANCE = 1e-9;
|
|
16
|
+
|
|
17
|
+
describe("XYZ-D50 Color Schema", () => {
|
|
18
|
+
describe("Schema Definition", () => {
|
|
19
|
+
it("should have correct schema structure", async () => {
|
|
20
|
+
const schema = (await getBundledSchema("xyz-d50-color")) as ColorSpecification;
|
|
21
|
+
|
|
22
|
+
expect(schema.name).toBe("XYZD50");
|
|
23
|
+
expect(schema.type).toBe("color");
|
|
24
|
+
expect(schema.schema).toBeDefined();
|
|
25
|
+
expect(schema.schema?.properties).toHaveProperty("x");
|
|
26
|
+
expect(schema.schema?.properties).toHaveProperty("y");
|
|
27
|
+
expect(schema.schema?.properties).toHaveProperty("z");
|
|
28
|
+
expect(schema.schema?.required).toEqual(["x", "y", "z"]);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should have xyzd50 initializer", async () => {
|
|
32
|
+
const schema = (await getBundledSchema("xyz-d50-color")) as ColorSpecification;
|
|
33
|
+
|
|
34
|
+
expect(schema.initializers).toHaveLength(1);
|
|
35
|
+
expect(schema.initializers[0].keyword).toBe("xyzd50");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should have conversion from XYZ-D65", async () => {
|
|
39
|
+
const schema = (await getBundledSchema("xyz-d50-color")) as ColorSpecification;
|
|
40
|
+
|
|
41
|
+
expect(schema.conversions).toHaveLength(1);
|
|
42
|
+
|
|
43
|
+
const d65ToD50 = schema.conversions.find((c: { source: string }) =>
|
|
44
|
+
c.source.includes("xyz-d65-color"),
|
|
45
|
+
);
|
|
46
|
+
expect(d65ToD50).toBeDefined();
|
|
47
|
+
expect(d65ToD50?.lossless).toBe(true);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("Initialization", () => {
|
|
52
|
+
it("should create XYZ-D50 color from values", async () => {
|
|
53
|
+
const result = await executeWithSchema(
|
|
54
|
+
"xyz-d50-color",
|
|
55
|
+
"type",
|
|
56
|
+
`
|
|
57
|
+
variable c: Color.XYZD50;
|
|
58
|
+
c.x = 0.5;
|
|
59
|
+
c.y = 0.4;
|
|
60
|
+
c.z = 0.3;
|
|
61
|
+
c
|
|
62
|
+
`,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
expect(result?.constructor.name).toBe("ColorSymbol");
|
|
66
|
+
expect((result as any).subType).toBe("XYZD50");
|
|
67
|
+
expect((result as any).value.x.value).toBeCloseTo(0.5, 10);
|
|
68
|
+
expect((result as any).value.y.value).toBeCloseTo(0.4, 10);
|
|
69
|
+
expect((result as any).value.z.value).toBeCloseTo(0.3, 10);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe("Chromatic Adaptation: XYZ-D65 to XYZ-D50", () => {
|
|
74
|
+
it("should convert D65 white point to D50 white point", async () => {
|
|
75
|
+
// D65 white point
|
|
76
|
+
const result = await executeWithSchema(
|
|
77
|
+
"xyz-d50-color",
|
|
78
|
+
"type",
|
|
79
|
+
`
|
|
80
|
+
variable xyz65: Color.XYZD65;
|
|
81
|
+
xyz65.x = 0.95047;
|
|
82
|
+
xyz65.y = 1.0;
|
|
83
|
+
xyz65.z = 1.08883;
|
|
84
|
+
xyz65.to.xyzd50()
|
|
85
|
+
`,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// ColorJS reference
|
|
89
|
+
const colorJS = new Color("xyz-d65", [0.95047, 1.0, 1.08883]).to("xyz-d50");
|
|
90
|
+
|
|
91
|
+
log.info(`\n=== D65 White → D50 ===`);
|
|
92
|
+
log.info(`Input D65: { x: 0.95047, y: 1.0, z: 1.08883 }`);
|
|
93
|
+
log.info(
|
|
94
|
+
`TokenScript: { x: ${(result as any).value.x.value}, y: ${(result as any).value.y.value}, z: ${(result as any).value.z.value} }`,
|
|
95
|
+
);
|
|
96
|
+
log.info(
|
|
97
|
+
`ColorJS: { x: ${colorJS.coords[0]}, y: ${colorJS.coords[1]}, z: ${colorJS.coords[2]} }`,
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
101
|
+
expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
|
|
102
|
+
expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should convert red XYZ-D65 to XYZ-D50", async () => {
|
|
106
|
+
// Red in XYZ-D65
|
|
107
|
+
const colorJSRed = new Color("srgb", [1, 0, 0]).to("xyz-d65");
|
|
108
|
+
|
|
109
|
+
const result = await executeWithSchema(
|
|
110
|
+
"xyz-d50-color",
|
|
111
|
+
"type",
|
|
112
|
+
`
|
|
113
|
+
variable xyz65: Color.XYZD65;
|
|
114
|
+
xyz65.x = ${colorJSRed.coords[0]};
|
|
115
|
+
xyz65.y = ${colorJSRed.coords[1]};
|
|
116
|
+
xyz65.z = ${colorJSRed.coords[2]};
|
|
117
|
+
xyz65.to.xyzd50()
|
|
118
|
+
`,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
const colorJS = new Color("srgb", [1, 0, 0]).to("xyz-d50");
|
|
122
|
+
|
|
123
|
+
expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
124
|
+
expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
|
|
125
|
+
expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("Full Conversion Chain: sRGB → Linear → XYZ-D65 → XYZ-D50", () => {
|
|
130
|
+
it("should convert sRGB red through to XYZ-D50", async () => {
|
|
131
|
+
const result = await executeWithSchema(
|
|
132
|
+
"xyz-d50-color",
|
|
133
|
+
"type",
|
|
134
|
+
`
|
|
135
|
+
variable srgb: Color.SRGB;
|
|
136
|
+
srgb.r = 1;
|
|
137
|
+
srgb.g = 0;
|
|
138
|
+
srgb.b = 0;
|
|
139
|
+
|
|
140
|
+
variable linear: Color.LinearSRGB = srgb.to.linearsrgb();
|
|
141
|
+
variable xyz65: Color.XYZD65 = linear.to.xyzd65();
|
|
142
|
+
xyz65.to.xyzd50()
|
|
143
|
+
`,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// ColorJS reference
|
|
147
|
+
const colorJS = new Color("srgb", [1, 0, 0]).to("xyz-d50");
|
|
148
|
+
|
|
149
|
+
log.info(`\n=== sRGB RED → XYZ-D50 (full chain) ===`);
|
|
150
|
+
log.info(
|
|
151
|
+
`TokenScript: { x: ${(result as any).value.x.value}, y: ${(result as any).value.y.value}, z: ${(result as any).value.z.value} }`,
|
|
152
|
+
);
|
|
153
|
+
log.info(
|
|
154
|
+
`ColorJS: { x: ${colorJS.coords[0]}, y: ${colorJS.coords[1]}, z: ${colorJS.coords[2]} }`,
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
expect((result as any).value.x.value).toBeCloseTo(colorJS.coords[0], 9);
|
|
158
|
+
expect((result as any).value.y.value).toBeCloseTo(colorJS.coords[1], 9);
|
|
159
|
+
expect((result as any).value.z.value).toBeCloseTo(colorJS.coords[2], 9);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("ColorJS Parity", () => {
|
|
164
|
+
const testCases = [
|
|
165
|
+
{ name: "red", srgb: [1, 0, 0] },
|
|
166
|
+
{ name: "green", srgb: [0, 1, 0] },
|
|
167
|
+
{ name: "blue", srgb: [0, 0, 1] },
|
|
168
|
+
{ name: "white", srgb: [1, 1, 1] },
|
|
169
|
+
{ name: "black", srgb: [0, 0, 0] },
|
|
170
|
+
{ name: "gray-50%", srgb: [0.5, 0.5, 0.5] },
|
|
171
|
+
{ name: "cyan", srgb: [0, 1, 1] },
|
|
172
|
+
{ name: "magenta", srgb: [1, 0, 1] },
|
|
173
|
+
{ name: "yellow", srgb: [1, 1, 0] },
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
for (const { name, srgb } of testCases) {
|
|
177
|
+
it(`should match ColorJS for ${name}`, async () => {
|
|
178
|
+
const result = await executeWithSchema(
|
|
179
|
+
"xyz-d50-color",
|
|
180
|
+
"type",
|
|
181
|
+
`
|
|
182
|
+
variable srgb: Color.SRGB;
|
|
183
|
+
srgb.r = ${srgb[0]};
|
|
184
|
+
srgb.g = ${srgb[1]};
|
|
185
|
+
srgb.b = ${srgb[2]};
|
|
186
|
+
|
|
187
|
+
variable linear: Color.LinearSRGB = srgb.to.linearsrgb();
|
|
188
|
+
variable xyz65: Color.XYZD65 = linear.to.xyzd65();
|
|
189
|
+
xyz65.to.xyzd50()
|
|
190
|
+
`,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// ColorJS reference
|
|
194
|
+
const colorJS = new Color("srgb", srgb as [number, number, number]).to("xyz-d50");
|
|
195
|
+
|
|
196
|
+
const tsX = (result as any).value.x.value;
|
|
197
|
+
const tsY = (result as any).value.y.value;
|
|
198
|
+
const tsZ = (result as any).value.z.value;
|
|
199
|
+
|
|
200
|
+
const diffX = Math.abs(tsX - colorJS.coords[0]);
|
|
201
|
+
const diffY = Math.abs(tsY - colorJS.coords[1]);
|
|
202
|
+
const diffZ = Math.abs(tsZ - colorJS.coords[2]);
|
|
203
|
+
const maxDiff = Math.max(diffX, diffY, diffZ);
|
|
204
|
+
|
|
205
|
+
log.info(`\n=== ${name.toUpperCase()} ColorJS Parity ===`);
|
|
206
|
+
log.info(`Input sRGB: { r: ${srgb[0]}, g: ${srgb[1]}, b: ${srgb[2]} }`);
|
|
207
|
+
log.info(
|
|
208
|
+
`TokenScript: { x: ${tsX.toFixed(9)}, y: ${tsY.toFixed(9)}, z: ${tsZ.toFixed(9)} }`,
|
|
209
|
+
);
|
|
210
|
+
log.info(
|
|
211
|
+
`ColorJS: { x: ${colorJS.coords[0].toFixed(9)}, y: ${colorJS.coords[1].toFixed(9)}, z: ${colorJS.coords[2].toFixed(9)} }`,
|
|
212
|
+
);
|
|
213
|
+
log.info(`Max Diff: ${maxDiff.toExponential(2)}`);
|
|
214
|
+
log.info(`Status: ${maxDiff < TOLERANCE ? "✅ PASS" : "❌ FAIL"}`);
|
|
215
|
+
|
|
216
|
+
expect(maxDiff).toBeLessThan(TOLERANCE);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("Edge Cases", () => {
|
|
222
|
+
it("should handle black (0, 0, 0)", async () => {
|
|
223
|
+
const result = await executeWithSchema(
|
|
224
|
+
"xyz-d50-color",
|
|
225
|
+
"type",
|
|
226
|
+
`
|
|
227
|
+
variable xyz65: Color.XYZD65;
|
|
228
|
+
xyz65.x = 0;
|
|
229
|
+
xyz65.y = 0;
|
|
230
|
+
xyz65.z = 0;
|
|
231
|
+
xyz65.to.xyzd50()
|
|
232
|
+
`,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
expect((result as any).value.x.value).toBe(0);
|
|
236
|
+
expect((result as any).value.y.value).toBe(0);
|
|
237
|
+
expect((result as any).value.z.value).toBe(0);
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Linear P3 to XYZ-D65 Conversion
|
|
2
|
+
// Converts linear Display-P3 RGB to CIE XYZ (D65)
|
|
3
|
+
// Reference: CSS Color Level 4 specification
|
|
4
|
+
//
|
|
5
|
+
// Uses the P3 to XYZ-D65 transformation matrix
|
|
6
|
+
// Matrix values from ColorJS / CSS Color Level 4
|
|
7
|
+
//
|
|
8
|
+
// Input: Color.LinearP3 with linear r, g, b values
|
|
9
|
+
// Output: Color.XYZD65 with x, y, z tristimulus values
|
|
10
|
+
|
|
11
|
+
// Get linear P3 values
|
|
12
|
+
variable r: Number = {input}.r;
|
|
13
|
+
variable g: Number = {input}.g;
|
|
14
|
+
variable b: Number = {input}.b;
|
|
15
|
+
|
|
16
|
+
// Linear P3 to XYZ-D65 matrix
|
|
17
|
+
// Row 1
|
|
18
|
+
variable m00: Number = 0.4865709486482162;
|
|
19
|
+
variable m01: Number = 0.26566769316909306;
|
|
20
|
+
variable m02: Number = 0.1982172852343625;
|
|
21
|
+
|
|
22
|
+
// Row 2
|
|
23
|
+
variable m10: Number = 0.2289745640697488;
|
|
24
|
+
variable m11: Number = 0.6917385218365064;
|
|
25
|
+
variable m12: Number = 0.079286914093745;
|
|
26
|
+
|
|
27
|
+
// Row 3
|
|
28
|
+
variable m20: Number = 0.0;
|
|
29
|
+
variable m21: Number = 0.04511338185890264;
|
|
30
|
+
variable m22: Number = 1.043944368900976;
|
|
31
|
+
|
|
32
|
+
// Matrix multiplication: [x, y, z] = M × [r, g, b]
|
|
33
|
+
variable x: Number = m00 * r + m01 * g + m02 * b;
|
|
34
|
+
variable y: Number = m10 * r + m11 * g + m12 * b;
|
|
35
|
+
variable z: Number = m20 * r + m21 * g + m22 * b;
|
|
36
|
+
|
|
37
|
+
// Create output
|
|
38
|
+
variable output: Color.XYZD65;
|
|
39
|
+
output.x = x;
|
|
40
|
+
output.y = y;
|
|
41
|
+
output.z = z;
|
|
42
|
+
|
|
43
|
+
return output;
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|