@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,259 @@
|
|
|
1
|
+
import { executeWithSchema } from "@tests/helpers/schema-test-utils";
|
|
2
|
+
import Color from "colorjs.io";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
|
|
5
|
+
describe("APCA Contrast Function", () => {
|
|
6
|
+
describe("Schema Definition", () => {
|
|
7
|
+
it("should have correct schema structure", async () => {
|
|
8
|
+
const { getBundledSchema } = await import("@tests/helpers/schema-test-utils");
|
|
9
|
+
const schema = await getBundledSchema("apca_contrast", "function");
|
|
10
|
+
|
|
11
|
+
expect(schema.name).toBe("APCA Contrast");
|
|
12
|
+
expect(schema.type).toBe("function");
|
|
13
|
+
expect((schema as any).keyword).toBe("apca_contrast");
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("Basic Contrast Calculations", () => {
|
|
18
|
+
it("should return high positive contrast for black text on white background", async () => {
|
|
19
|
+
// Black text on white background = dark on light = POSITIVE Lc
|
|
20
|
+
const result = await executeWithSchema(
|
|
21
|
+
"apca_contrast",
|
|
22
|
+
"function",
|
|
23
|
+
`
|
|
24
|
+
variable text: Color.SRGB;
|
|
25
|
+
text.r = 0; text.g = 0; text.b = 0;
|
|
26
|
+
variable bg: Color.SRGB;
|
|
27
|
+
bg.r = 1; bg.g = 1; bg.b = 1;
|
|
28
|
+
apca_contrast(text, bg)
|
|
29
|
+
`,
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(typeof result.value).toBe("number");
|
|
33
|
+
expect(result.value).toBeGreaterThan(100); // Should be around +106
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should return high negative contrast for white text on black background", async () => {
|
|
37
|
+
// White text on black background = light on dark = NEGATIVE Lc
|
|
38
|
+
const result = await executeWithSchema(
|
|
39
|
+
"apca_contrast",
|
|
40
|
+
"function",
|
|
41
|
+
`
|
|
42
|
+
variable text: Color.SRGB;
|
|
43
|
+
text.r = 1; text.g = 1; text.b = 1;
|
|
44
|
+
variable bg: Color.SRGB;
|
|
45
|
+
bg.r = 0; bg.g = 0; bg.b = 0;
|
|
46
|
+
apca_contrast(text, bg)
|
|
47
|
+
`,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(typeof result.value).toBe("number");
|
|
51
|
+
expect(result.value).toBeLessThan(-100); // Should be around -108
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("should return zero contrast for identical colors", async () => {
|
|
55
|
+
const result = await executeWithSchema(
|
|
56
|
+
"apca_contrast",
|
|
57
|
+
"function",
|
|
58
|
+
`
|
|
59
|
+
variable text: Color.SRGB;
|
|
60
|
+
text.r = 0.5; text.g = 0.5; text.b = 0.5;
|
|
61
|
+
variable bg: Color.SRGB;
|
|
62
|
+
bg.r = 0.5; bg.g = 0.5; bg.b = 0.5;
|
|
63
|
+
apca_contrast(text, bg)
|
|
64
|
+
`,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(result.value).toBeCloseTo(0, 1);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe("Polarity (BoW vs WoB)", () => {
|
|
72
|
+
it("should return positive for dark text on light background", async () => {
|
|
73
|
+
// Dark on light = POSITIVE (per APCA spec)
|
|
74
|
+
const result = await executeWithSchema(
|
|
75
|
+
"apca_contrast",
|
|
76
|
+
"function",
|
|
77
|
+
`
|
|
78
|
+
variable text: Color.SRGB;
|
|
79
|
+
text.r = 0.2; text.g = 0.2; text.b = 0.2;
|
|
80
|
+
variable bg: Color.SRGB;
|
|
81
|
+
bg.r = 0.9; bg.g = 0.9; bg.b = 0.9;
|
|
82
|
+
apca_contrast(text, bg)
|
|
83
|
+
`,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
expect(result.value).toBeGreaterThan(0);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("should return negative for light text on dark background", async () => {
|
|
90
|
+
// Light on dark = NEGATIVE (per APCA spec)
|
|
91
|
+
const result = await executeWithSchema(
|
|
92
|
+
"apca_contrast",
|
|
93
|
+
"function",
|
|
94
|
+
`
|
|
95
|
+
variable text: Color.SRGB;
|
|
96
|
+
text.r = 0.9; text.g = 0.9; text.b = 0.9;
|
|
97
|
+
variable bg: Color.SRGB;
|
|
98
|
+
bg.r = 0.2; bg.g = 0.2; bg.b = 0.2;
|
|
99
|
+
apca_contrast(text, bg)
|
|
100
|
+
`,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
expect(result.value).toBeLessThan(0);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
describe("Color.js Parity", () => {
|
|
108
|
+
// Note: In Color.js, colorObj.contrast(other, "APCA") treats:
|
|
109
|
+
// - colorObj as BACKGROUND
|
|
110
|
+
// - other as FOREGROUND (text)
|
|
111
|
+
// Our apca_contrast(text, bg) uses opposite order:
|
|
112
|
+
// - text as FOREGROUND
|
|
113
|
+
// - bg as BACKGROUND
|
|
114
|
+
// So apca_contrast(A, B) should match B.contrast(A, "APCA")
|
|
115
|
+
|
|
116
|
+
it("should match Color.js APCA for black text on white background", async () => {
|
|
117
|
+
const result = await executeWithSchema(
|
|
118
|
+
"apca_contrast",
|
|
119
|
+
"function",
|
|
120
|
+
`
|
|
121
|
+
variable text: Color.SRGB;
|
|
122
|
+
text.r = 0; text.g = 0; text.b = 0;
|
|
123
|
+
variable bg: Color.SRGB;
|
|
124
|
+
bg.r = 1; bg.g = 1; bg.b = 1;
|
|
125
|
+
apca_contrast(text, bg)
|
|
126
|
+
`,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// apca_contrast(black, white) = black text on white bg
|
|
130
|
+
// In Color.js: white.contrast(black, "APCA") = black fg on white bg
|
|
131
|
+
const white = new Color("srgb", [1, 1, 1]);
|
|
132
|
+
const black = new Color("srgb", [0, 0, 0]);
|
|
133
|
+
const colorJsApca = white.contrast(black, "APCA");
|
|
134
|
+
|
|
135
|
+
expect(result.value).toBeCloseTo(colorJsApca, 0);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("should match Color.js APCA for white text on black background", async () => {
|
|
139
|
+
const result = await executeWithSchema(
|
|
140
|
+
"apca_contrast",
|
|
141
|
+
"function",
|
|
142
|
+
`
|
|
143
|
+
variable text: Color.SRGB;
|
|
144
|
+
text.r = 1; text.g = 1; text.b = 1;
|
|
145
|
+
variable bg: Color.SRGB;
|
|
146
|
+
bg.r = 0; bg.g = 0; bg.b = 0;
|
|
147
|
+
apca_contrast(text, bg)
|
|
148
|
+
`,
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// apca_contrast(white, black) = white text on black bg
|
|
152
|
+
// In Color.js: black.contrast(white, "APCA") = white fg on black bg
|
|
153
|
+
const white = new Color("srgb", [1, 1, 1]);
|
|
154
|
+
const black = new Color("srgb", [0, 0, 0]);
|
|
155
|
+
const colorJsApca = black.contrast(white, "APCA");
|
|
156
|
+
|
|
157
|
+
expect(result.value).toBeCloseTo(colorJsApca, 0);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("should match Color.js APCA for blue text on light gray", async () => {
|
|
161
|
+
const result = await executeWithSchema(
|
|
162
|
+
"apca_contrast",
|
|
163
|
+
"function",
|
|
164
|
+
`
|
|
165
|
+
variable text: Color.SRGB;
|
|
166
|
+
text.r = 0.2; text.g = 0.4; text.b = 0.8;
|
|
167
|
+
variable bg: Color.SRGB;
|
|
168
|
+
bg.r = 0.95; bg.g = 0.95; bg.b = 0.95;
|
|
169
|
+
apca_contrast(text, bg)
|
|
170
|
+
`,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
// apca_contrast(blue, lightGray) = blue text on lightGray bg
|
|
174
|
+
// In Color.js: lightGray.contrast(blue, "APCA")
|
|
175
|
+
const blue = new Color("srgb", [0.2, 0.4, 0.8]);
|
|
176
|
+
const lightGray = new Color("srgb", [0.95, 0.95, 0.95]);
|
|
177
|
+
const colorJsApca = lightGray.contrast(blue, "APCA");
|
|
178
|
+
|
|
179
|
+
expect(result.value).toBeCloseTo(colorJsApca, 0);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("should match Color.js APCA for mid-contrast scenario", async () => {
|
|
183
|
+
const result = await executeWithSchema(
|
|
184
|
+
"apca_contrast",
|
|
185
|
+
"function",
|
|
186
|
+
`
|
|
187
|
+
variable text: Color.SRGB;
|
|
188
|
+
text.r = 0.4; text.g = 0.4; text.b = 0.4;
|
|
189
|
+
variable bg: Color.SRGB;
|
|
190
|
+
bg.r = 0.8; bg.g = 0.8; bg.b = 0.8;
|
|
191
|
+
apca_contrast(text, bg)
|
|
192
|
+
`,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const darkGray = new Color("srgb", [0.4, 0.4, 0.4]);
|
|
196
|
+
const lightGray = new Color("srgb", [0.8, 0.8, 0.8]);
|
|
197
|
+
// darkGray text on lightGray bg → lightGray.contrast(darkGray)
|
|
198
|
+
const colorJsApca = lightGray.contrast(darkGray, "APCA");
|
|
199
|
+
|
|
200
|
+
expect(result.value).toBeCloseTo(colorJsApca, 0);
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("WCAG 3.0 Threshold Guidelines", () => {
|
|
205
|
+
it("should meet body text threshold (|Lc| >= 60) for dark gray on white", async () => {
|
|
206
|
+
const result = await executeWithSchema(
|
|
207
|
+
"apca_contrast",
|
|
208
|
+
"function",
|
|
209
|
+
`
|
|
210
|
+
variable text: Color.SRGB;
|
|
211
|
+
text.r = 0.3; text.g = 0.3; text.b = 0.3;
|
|
212
|
+
variable bg: Color.SRGB;
|
|
213
|
+
bg.r = 1; bg.g = 1; bg.b = 1;
|
|
214
|
+
apca_contrast(text, bg)
|
|
215
|
+
`,
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
// Dark gray on white should be positive and meet body text requirements
|
|
219
|
+
expect(result.value).toBeGreaterThan(60);
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
describe("Edge Cases", () => {
|
|
224
|
+
it("should handle very dark colors (soft black clamp)", async () => {
|
|
225
|
+
const result = await executeWithSchema(
|
|
226
|
+
"apca_contrast",
|
|
227
|
+
"function",
|
|
228
|
+
`
|
|
229
|
+
variable text: Color.SRGB;
|
|
230
|
+
text.r = 0.01; text.g = 0.01; text.b = 0.01;
|
|
231
|
+
variable bg: Color.SRGB;
|
|
232
|
+
bg.r = 0.02; bg.g = 0.02; bg.b = 0.02;
|
|
233
|
+
apca_contrast(text, bg)
|
|
234
|
+
`,
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Very dark colors should still produce a valid result
|
|
238
|
+
expect(typeof result.value).toBe("number");
|
|
239
|
+
expect(Number.isFinite(result.value)).toBe(true);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it("should work with chromatic colors", async () => {
|
|
243
|
+
const result = await executeWithSchema(
|
|
244
|
+
"apca_contrast",
|
|
245
|
+
"function",
|
|
246
|
+
`
|
|
247
|
+
variable text: Color.SRGB;
|
|
248
|
+
text.r = 0.8; text.g = 0.2; text.b = 0.2;
|
|
249
|
+
variable bg: Color.SRGB;
|
|
250
|
+
bg.r = 0.2; bg.g = 0.6; bg.b = 0.2;
|
|
251
|
+
apca_contrast(text, bg)
|
|
252
|
+
`,
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
expect(typeof result.value).toBe("number");
|
|
256
|
+
expect(Number.isFinite(result.value)).toBe(true);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// are_similar: Check if two colors are perceptually similar
|
|
2
|
+
// Uses Delta E OK (OKLab Euclidean distance) for perceptual comparison
|
|
3
|
+
// Default threshold 0.02 approximates "just noticeable difference"
|
|
4
|
+
|
|
5
|
+
variable input: List = {input};
|
|
6
|
+
variable color1: Color.OKLab = input.get(0).to.oklab();
|
|
7
|
+
variable color2: Color.OKLab = input.get(1).to.oklab();
|
|
8
|
+
|
|
9
|
+
// Default threshold for JND (just noticeable difference)
|
|
10
|
+
variable threshold: Number = 0.02;
|
|
11
|
+
if (input.length() > 2) [
|
|
12
|
+
threshold = input.get(2);
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
// Calculate Delta E OK (Euclidean distance in OKLab)
|
|
16
|
+
variable delta_l: Number = color1.l - color2.l;
|
|
17
|
+
variable delta_a: Number = color1.a - color2.a;
|
|
18
|
+
variable delta_b: Number = color1.b - color2.b;
|
|
19
|
+
|
|
20
|
+
variable delta_e: Number = sqrt(delta_l * delta_l + delta_a * delta_a + delta_b * delta_b);
|
|
21
|
+
|
|
22
|
+
// Return true if within threshold
|
|
23
|
+
return delta_e <= threshold;
|
|
24
|
+
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "are_similar",
|
|
3
|
+
"type": "function",
|
|
4
|
+
"description": "Determines if two colors are perceptually similar using Delta E OK (Euclidean distance in OKLab space). Returns true if the colors are within the specified threshold. Default threshold of 0.02 corresponds to a 'just noticeable difference' (JND). Based on Björn Ottosson's OKLab color space which provides excellent perceptual uniformity.",
|
|
5
|
+
"keyword": "are_similar",
|
|
6
|
+
"requirements": ["/api/v1/core/srgb-color/0/", "/api/v1/core/oklab-color/0/"],
|
|
7
|
+
"schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"input": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": [
|
|
13
|
+
{
|
|
14
|
+
"description": "First color",
|
|
15
|
+
"type": "color"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "Second color",
|
|
19
|
+
"type": "color"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"description": "Delta E threshold (default 0.02 ≈ JND)",
|
|
23
|
+
"type": "number"
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"minItems": 2,
|
|
27
|
+
"maxItems": 3
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"required": ["input"]
|
|
31
|
+
},
|
|
32
|
+
"returns": {
|
|
33
|
+
"type": "boolean",
|
|
34
|
+
"description": "True if colors are perceptually similar (within threshold)"
|
|
35
|
+
},
|
|
36
|
+
"script": {
|
|
37
|
+
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
38
|
+
"script": "./are-similar.tokenscript"
|
|
39
|
+
},
|
|
40
|
+
"examples": [
|
|
41
|
+
{
|
|
42
|
+
"description": "Nearly identical colors",
|
|
43
|
+
"input": ["#ff0000", "#ff0001"],
|
|
44
|
+
"output": true
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"description": "Obviously different colors",
|
|
48
|
+
"input": ["#ff0000", "#00ff00"],
|
|
49
|
+
"output": false
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"description": "Custom threshold for stricter comparison",
|
|
53
|
+
"input": ["#808080", "#818181", 0.01],
|
|
54
|
+
"output": true
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the are_similar function
|
|
3
|
+
* Checks if two colors are perceptually similar
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { executeWithSchema, getBundledSchema } from "@tests/helpers/schema-test-utils";
|
|
7
|
+
import { describe, expect, it } from "vitest";
|
|
8
|
+
import type { FunctionSpecification } from "@/bundler/types";
|
|
9
|
+
|
|
10
|
+
describe("are_similar function", () => {
|
|
11
|
+
describe("Schema Definition", () => {
|
|
12
|
+
it("should have correct schema structure", async () => {
|
|
13
|
+
const schema = (await getBundledSchema("are_similar", "function")) as FunctionSpecification;
|
|
14
|
+
|
|
15
|
+
expect(schema.name).toBe("are_similar");
|
|
16
|
+
expect(schema.type).toBe("function");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("Function Execution", () => {
|
|
21
|
+
it("should return true for identical colors", async () => {
|
|
22
|
+
const result = await executeWithSchema(
|
|
23
|
+
"are_similar",
|
|
24
|
+
"function",
|
|
25
|
+
`
|
|
26
|
+
variable c1: Color.SRGB;
|
|
27
|
+
c1.r = 0.5; c1.g = 0.5; c1.b = 0.5;
|
|
28
|
+
variable c2: Color.SRGB;
|
|
29
|
+
c2.r = 0.5; c2.g = 0.5; c2.b = 0.5;
|
|
30
|
+
are_similar(c1, c2)
|
|
31
|
+
`,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect(result).toBeDefined();
|
|
35
|
+
const similar = (result as any).value ?? result;
|
|
36
|
+
expect(similar).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("should return false for obviously different colors", async () => {
|
|
40
|
+
const result = await executeWithSchema(
|
|
41
|
+
"are_similar",
|
|
42
|
+
"function",
|
|
43
|
+
`
|
|
44
|
+
variable red: Color.SRGB;
|
|
45
|
+
red.r = 1; red.g = 0; red.b = 0;
|
|
46
|
+
variable green: Color.SRGB;
|
|
47
|
+
green.r = 0; green.g = 1; green.b = 0;
|
|
48
|
+
are_similar(red, green)
|
|
49
|
+
`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
expect(result).toBeDefined();
|
|
53
|
+
const similar = (result as any).value ?? result;
|
|
54
|
+
expect(similar).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// auto_text_color: Select black or white text for optimal contrast
|
|
2
|
+
// Reference: WCAG 2.1 Relative Luminance
|
|
3
|
+
// Reference: https://www.w3.org/TR/WCAG21/#dfn-relative-luminance
|
|
4
|
+
//
|
|
5
|
+
// Returns black (#000) or white (#fff) depending on which provides
|
|
6
|
+
// better contrast against the input background color.
|
|
7
|
+
//
|
|
8
|
+
// Default threshold: 0.179 (geometric mean of WCAG luminance range)
|
|
9
|
+
// - sqrt(1.05 * 0.05) - 0.05 ≈ 0.179
|
|
10
|
+
// - Above threshold: light background → black text
|
|
11
|
+
// - Below threshold: dark background → white text
|
|
12
|
+
|
|
13
|
+
variable input: List = {input};
|
|
14
|
+
|
|
15
|
+
// Default threshold based on WCAG luminance midpoint
|
|
16
|
+
variable threshold: Number = 0.179;
|
|
17
|
+
if (input.length() > 1) [
|
|
18
|
+
threshold = input.get(1);
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
// Convert to XYZ-D65 to get relative luminance (Y component)
|
|
22
|
+
variable xyz: Color.XYZD65 = input.get(0).to.xyzd65();
|
|
23
|
+
variable luminance: Number = xyz.y;
|
|
24
|
+
|
|
25
|
+
// If luminance is above threshold, background is "light" -> use black text
|
|
26
|
+
// If luminance is below threshold, background is "dark" -> use white text
|
|
27
|
+
variable result: Color.SRGB;
|
|
28
|
+
|
|
29
|
+
if (luminance > threshold) [
|
|
30
|
+
// Light background: return black
|
|
31
|
+
result.r = 0;
|
|
32
|
+
result.g = 0;
|
|
33
|
+
result.b = 0;
|
|
34
|
+
] else [
|
|
35
|
+
// Dark background: return white
|
|
36
|
+
result.r = 1;
|
|
37
|
+
result.g = 1;
|
|
38
|
+
result.b = 1;
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
return result;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auto_text_color",
|
|
3
|
+
"type": "function",
|
|
4
|
+
"description": "Returns black or white text color for optimal contrast against a background. Uses WCAG relative luminance to determine if the background is light or dark, then returns the opposite for maximum readability. Essential for generating accessible text colors in design systems.",
|
|
5
|
+
"keyword": "auto_text_color",
|
|
6
|
+
"requirements": ["/api/v1/core/srgb-color/0/", "/api/v1/core/xyz-d65-color/0/"],
|
|
7
|
+
"schema": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"properties": {
|
|
10
|
+
"input": {
|
|
11
|
+
"type": "array",
|
|
12
|
+
"items": [
|
|
13
|
+
{
|
|
14
|
+
"description": "Background color to test",
|
|
15
|
+
"type": "color"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "Optional threshold (0-1, default 0.179 per WCAG). Higher values bias toward white text.",
|
|
19
|
+
"type": "number"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"minItems": 1,
|
|
23
|
+
"maxItems": 2
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"required": ["input"]
|
|
27
|
+
},
|
|
28
|
+
"returns": {
|
|
29
|
+
"type": "color",
|
|
30
|
+
"description": "Either black (#000000) or white (#ffffff) for optimal contrast"
|
|
31
|
+
},
|
|
32
|
+
"script": {
|
|
33
|
+
"type": "https://schema.tokenscript.dev.gcp.tokens.studio/api/v1/core/tokenscript/0/",
|
|
34
|
+
"script": "./auto-text-color.tokenscript"
|
|
35
|
+
},
|
|
36
|
+
"examples": [
|
|
37
|
+
{
|
|
38
|
+
"description": "Dark background returns white text",
|
|
39
|
+
"input": ["#1a1a1a"],
|
|
40
|
+
"output": "#ffffff"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"description": "Light background returns black text",
|
|
44
|
+
"input": ["#f0f0f0"],
|
|
45
|
+
"output": "#000000"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"description": "Mid-tone with custom threshold",
|
|
49
|
+
"input": ["#808080", 0.5],
|
|
50
|
+
"output": "#ffffff"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the auto_text_color function
|
|
3
|
+
* Returns black or white for optimal contrast against a background
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { executeWithSchema, getBundledSchema } from "@tests/helpers/schema-test-utils";
|
|
7
|
+
import { describe, expect, it } from "vitest";
|
|
8
|
+
import type { FunctionSpecification } from "@/bundler/types";
|
|
9
|
+
|
|
10
|
+
describe("auto_text_color function", () => {
|
|
11
|
+
describe("Schema Definition", () => {
|
|
12
|
+
it("should have correct schema structure", async () => {
|
|
13
|
+
const schema = (await getBundledSchema(
|
|
14
|
+
"auto_text_color",
|
|
15
|
+
"function",
|
|
16
|
+
)) as FunctionSpecification;
|
|
17
|
+
|
|
18
|
+
expect(schema.name).toBe("auto_text_color");
|
|
19
|
+
expect(schema.type).toBe("function");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe("Function Execution", () => {
|
|
24
|
+
it("should return white text for dark backgrounds", async () => {
|
|
25
|
+
const result = await executeWithSchema(
|
|
26
|
+
"auto_text_color",
|
|
27
|
+
"function",
|
|
28
|
+
`
|
|
29
|
+
variable bg: Color.SRGB;
|
|
30
|
+
bg.r = 0.1; bg.g = 0.1; bg.b = 0.1;
|
|
31
|
+
auto_text_color(bg)
|
|
32
|
+
`,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
expect(result).toBeDefined();
|
|
36
|
+
const r = (result as any).value?.r?.value ?? (result as any).value?.r;
|
|
37
|
+
expect(r).toBeCloseTo(1, 1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should return black text for light backgrounds", async () => {
|
|
41
|
+
const result = await executeWithSchema(
|
|
42
|
+
"auto_text_color",
|
|
43
|
+
"function",
|
|
44
|
+
`
|
|
45
|
+
variable bg: Color.SRGB;
|
|
46
|
+
bg.r = 0.94; bg.g = 0.94; bg.b = 0.94;
|
|
47
|
+
auto_text_color(bg)
|
|
48
|
+
`,
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
expect(result).toBeDefined();
|
|
52
|
+
const r = (result as any).value?.r?.value ?? (result as any).value?.r;
|
|
53
|
+
expect(r).toBeCloseTo(0, 1);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("should return black text for pure white", async () => {
|
|
57
|
+
const result = await executeWithSchema(
|
|
58
|
+
"auto_text_color",
|
|
59
|
+
"function",
|
|
60
|
+
`
|
|
61
|
+
variable bg: Color.SRGB;
|
|
62
|
+
bg.r = 1; bg.g = 1; bg.b = 1;
|
|
63
|
+
auto_text_color(bg)
|
|
64
|
+
`,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
expect(result).toBeDefined();
|
|
68
|
+
const r = (result as any).value?.r?.value ?? (result as any).value?.r;
|
|
69
|
+
expect(r).toBeCloseTo(0, 1);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should return white text for pure black", async () => {
|
|
73
|
+
const result = await executeWithSchema(
|
|
74
|
+
"auto_text_color",
|
|
75
|
+
"function",
|
|
76
|
+
`
|
|
77
|
+
variable bg: Color.SRGB;
|
|
78
|
+
bg.r = 0; bg.g = 0; bg.b = 0;
|
|
79
|
+
auto_text_color(bg)
|
|
80
|
+
`,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(result).toBeDefined();
|
|
84
|
+
const r = (result as any).value?.r?.value ?? (result as any).value?.r;
|
|
85
|
+
expect(r).toBeCloseTo(1, 1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("should return black text for bright yellow (high luminance)", async () => {
|
|
89
|
+
const result = await executeWithSchema(
|
|
90
|
+
"auto_text_color",
|
|
91
|
+
"function",
|
|
92
|
+
`
|
|
93
|
+
variable bg: Color.SRGB;
|
|
94
|
+
bg.r = 1; bg.g = 1; bg.b = 0;
|
|
95
|
+
auto_text_color(bg)
|
|
96
|
+
`,
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
expect(result).toBeDefined();
|
|
100
|
+
const r = (result as any).value?.r?.value ?? (result as any).value?.r;
|
|
101
|
+
// Yellow has high luminance -> black text
|
|
102
|
+
expect(r).toBeCloseTo(0, 1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it("should return white text for dark blue", async () => {
|
|
106
|
+
const result = await executeWithSchema(
|
|
107
|
+
"auto_text_color",
|
|
108
|
+
"function",
|
|
109
|
+
`
|
|
110
|
+
variable bg: Color.SRGB;
|
|
111
|
+
bg.r = 0; bg.g = 0.4; bg.b = 0.8;
|
|
112
|
+
auto_text_color(bg)
|
|
113
|
+
`,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(result).toBeDefined();
|
|
117
|
+
const r = (result as any).value?.r?.value ?? (result as any).value?.r;
|
|
118
|
+
// Blue has low luminance -> white text
|
|
119
|
+
expect(r).toBeCloseTo(1, 1);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Select the most contrasting color from a list of candidates
|
|
2
|
+
// Uses WCAG 2.1 contrast ratio based on relative luminance
|
|
3
|
+
//
|
|
4
|
+
// Algorithm:
|
|
5
|
+
// 1. Calculate relative luminance: L = 0.2126*R + 0.7152*G + 0.0722*B (linear RGB)
|
|
6
|
+
// 2. Contrast ratio: (L_lighter + 0.05) / (L_darker + 0.05)
|
|
7
|
+
// 3. Return candidate with highest contrast ratio
|
|
8
|
+
//
|
|
9
|
+
// Default candidates: black (#000) and white (#fff)
|
|
10
|
+
|
|
11
|
+
variable input: List = {input};
|
|
12
|
+
variable bg: Color.LinearSRGB = input.get(0).to.linearsrgb();
|
|
13
|
+
|
|
14
|
+
// Calculate background luminance
|
|
15
|
+
variable bg_lum: Number = 0.2126 * bg.r + 0.7152 * bg.g + 0.0722 * bg.b;
|
|
16
|
+
|
|
17
|
+
// Get candidates list, default to black and white
|
|
18
|
+
variable candidates: List;
|
|
19
|
+
if (input.length() > 1) [
|
|
20
|
+
candidates = input.get(1);
|
|
21
|
+
] else [
|
|
22
|
+
// Default: black and white
|
|
23
|
+
variable black: Color.SRGB;
|
|
24
|
+
black.r = 0; black.g = 0; black.b = 0;
|
|
25
|
+
variable white: Color.SRGB;
|
|
26
|
+
white.r = 1; white.g = 1; white.b = 1;
|
|
27
|
+
candidates = black, white;
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
// Find candidate with highest contrast
|
|
31
|
+
variable best_color: Color.SRGB = candidates.get(0).to.srgb();
|
|
32
|
+
variable best_contrast: Number = 0;
|
|
33
|
+
variable candidate: Color.LinearSRGB;
|
|
34
|
+
variable cand_lum: Number = 0;
|
|
35
|
+
variable lighter: Number = 0;
|
|
36
|
+
variable darker: Number = 0;
|
|
37
|
+
variable contrast: Number = 0;
|
|
38
|
+
|
|
39
|
+
variable i: Number = 0;
|
|
40
|
+
while (i < candidates.length()) [
|
|
41
|
+
candidate = candidates.get(i).to.linearsrgb();
|
|
42
|
+
|
|
43
|
+
// Calculate candidate luminance
|
|
44
|
+
cand_lum = 0.2126 * candidate.r + 0.7152 * candidate.g + 0.0722 * candidate.b;
|
|
45
|
+
|
|
46
|
+
// Calculate contrast ratio (lighter / darker)
|
|
47
|
+
lighter = bg_lum;
|
|
48
|
+
darker = cand_lum;
|
|
49
|
+
if (cand_lum > bg_lum) [
|
|
50
|
+
lighter = cand_lum;
|
|
51
|
+
darker = bg_lum;
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
contrast = (lighter + 0.05) / (darker + 0.05);
|
|
55
|
+
|
|
56
|
+
// Track best
|
|
57
|
+
if (contrast > best_contrast) [
|
|
58
|
+
best_contrast = contrast;
|
|
59
|
+
best_color = candidates.get(i).to.srgb();
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
i = i + 1;
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
return best_color;
|
|
66
|
+
|