@mkbabb/value.js 0.5.1 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +35 -53
  3. package/dist/easing.d.ts +49 -0
  4. package/dist/index.d.ts +22 -10
  5. package/dist/math.d.ts +17 -1
  6. package/dist/parsing/animation-shorthand.d.ts +19 -0
  7. package/dist/parsing/color.d.ts +10 -3
  8. package/dist/parsing/extract.d.ts +48 -0
  9. package/dist/parsing/index.d.ts +5 -20
  10. package/dist/parsing/serialize.d.ts +24 -0
  11. package/dist/parsing/stylesheet.d.ts +44 -0
  12. package/dist/parsing/units.d.ts +24 -6
  13. package/dist/parsing/utils.d.ts +19 -0
  14. package/dist/postcss-CRluLK2m.js +6400 -0
  15. package/dist/quantize/cluster.d.ts +45 -0
  16. package/dist/quantize/index.d.ts +14 -0
  17. package/dist/quantize/types.d.ts +39 -0
  18. package/dist/standalone-Ck3UyY5I.js +3458 -0
  19. package/dist/units/color/constants.d.ts +21 -0
  20. package/dist/units/color/contrast.d.ts +35 -0
  21. package/dist/units/color/conversions/cylindrical.d.ts +13 -0
  22. package/dist/units/color/conversions/direct.d.ts +41 -0
  23. package/dist/units/color/conversions/hex.d.ts +3 -0
  24. package/dist/units/color/conversions/index.d.ts +17 -0
  25. package/dist/units/color/conversions/kelvin.d.ts +5 -0
  26. package/dist/units/color/conversions/lab.d.ts +8 -0
  27. package/dist/units/color/conversions/oklab.d.ts +11 -0
  28. package/dist/units/color/conversions/transfer.d.ts +22 -0
  29. package/dist/units/color/conversions/xyz-extended.d.ts +13 -0
  30. package/dist/units/color/dispatch.d.ts +25 -0
  31. package/dist/units/color/gamut.d.ts +6 -0
  32. package/dist/units/color/index.d.ts +221 -116
  33. package/dist/units/color/mix.d.ts +16 -0
  34. package/dist/units/color/normalize.d.ts +3 -3
  35. package/dist/units/index.d.ts +71 -1
  36. package/dist/units/interpolate.d.ts +70 -0
  37. package/dist/units/normalize.d.ts +57 -7
  38. package/dist/units/utils.d.ts +1 -1
  39. package/dist/utils.d.ts +3 -2
  40. package/dist/value.js +4323 -4392
  41. package/package.json +52 -28
  42. package/dist/units/color/utils.d.ts +0 -77
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Michael Babb
3
+ Copyright (c) 2026 Mike Babb
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,15 +1,16 @@
1
- # value.js ![image](demo/color-picker/cube.png)
1
+ # `value.js` <img src="demo/color-picker/favicon.svg" style="vertical-align: middle" width="48">
2
2
 
3
- CSS value parsing, color theory, and unit conversion. Typed values with units—`deg`, `px`, `rem`, `oklch()`—the core CSS value vocabulary.
3
+ CSS value parsing, color theory, and unit conversion. Typed values with units—`deg`, `px`, `rem`, `oklch()`—the CSS value vocabulary.
4
4
 
5
- [demo](https://color.babb.dev)
5
+ [demo](https://color.babb.dev) & [app guide](docs/colors/app.md)
6
6
 
7
7
  ## Features
8
8
 
9
9
  - Parse any CSS value: lengths, angles, times, colors, `calc()`, `var()`, gradients, transforms
10
+ - CSS Color Level 4 support: `color()`, `color-mix()`, relative color syntax
10
11
  - **15 color spaces**: RGB, HSL, HSV, HWB, Lab, LCh, OKLab, OKLCh, XYZ, Kelvin, sRGB-linear, Display P3, Adobe RGB, ProPhoto RGB, Rec. 2020
11
12
  - Color space conversion via **XYZ hub** with analytical gamut mapping (Ottosson's algorithm)
12
- - CSS Color Level 4 support: `color()`, `color-mix()`, relative color syntax
13
+ - **Color quantization**: OKLab-native palette extraction (MMCQ + k-means++) with chroma-weighted clustering and JND deduplication
13
14
  - CSS math functions: `calc()`, `min()`, `max()`, `clamp()`, trig, exponential
14
15
  - 30+ easing functions: cubic-bezier, stepped, linear(), bounce, sine, expo
15
16
  - 2D/3D matrix decomposition with quaternion slerp interpolation
@@ -38,8 +39,8 @@ import {
38
39
  npm run build # library → dist/value.js + value.cjs + value.d.ts
39
40
  npm run gh-pages # demo → dist/
40
41
  npm run dev # dev server (Vite default port)
41
- npm test # vitest (1372 tests)
42
- npm run test:e2e # playwright (desktop + mobile)
42
+ npm test # vitest unit suite
43
+ npm run test:e2e # playwright smoke suite (5 projects)
43
44
  ```
44
45
 
45
46
  ## Structure
@@ -66,77 +67,53 @@ src/
66
67
  │ ├── index.ts # Color<T> base + space classes
67
68
  │ ├── constants.ts # ranges, matrices, white points, named colors
68
69
  │ ├── matrix.ts # Vec3/Mat3 (row-major, replaces gl-matrix)
69
- │ ├── utils.ts # conversions via XYZ, mixColors, gamutMap
70
+ │ ├── conversions/ # focused per-family converters (hex, kelvin, lab, …)
71
+ │ ├── dispatch.ts # color2(), DIRECT_PATHS, gamutMap dispatch
70
72
  │ ├── normalize.ts # color normalization to [0,1], space conversion
71
73
  │ ├── gamut.ts # Ottosson analytical sRGB gamut mapping
72
74
  │ └── colorFilter.ts # CSS filter solver (SPSA)
75
+ ├── quantize/ # image color quantization
76
+ │ ├── index.ts # quantizePixels, dominantColor (public API)
77
+ │ ├── cluster.ts # MMCQ median cut, k-means++, JND dedup
78
+ │ └── types.ts # QuantizeOptions, QuantizedColor
73
79
  └── transform/
74
80
  └── decompose.ts # 2D/3D matrix decomposition, quaternion slerp
75
-
76
- test/ # vitest unit tests (24 files)
77
- e2e/ # playwright E2E (14 specs)
78
- demo/ # Vue 3.5 color picker (reka-ui, Tailwind)
79
- api/ # Hono + MongoDB palette API (Docker)
80
- docs/ # color-theory.md, gamut-mapping.md
81
- assets/docs/ # 10 color space reference pages
82
81
  ```
83
82
 
84
83
  ## Color Spaces
85
84
 
86
85
  All conversions route through the **XYZ D65** hub, enabling any-to-any conversion. Perceptual spaces (OKLab, Lab) use D50 natively with Bradford chromatic adaptation where needed.
87
86
 
88
- Each color space is documented in [`assets/docs/`](assets/docs/)historical context, component ranges, conversion functions, and practical applications.
87
+ Each color space is documented in [`assets/docs/`](assets/docs/), therein with historical context, component ranges, conversion functions, and practical applications.
89
88
 
90
89
  ### Gamut Mapping
91
90
 
92
91
  Out-of-gamut colors are mapped using Björn Ottosson's analytical sRGB algorithm: a polynomial initial guess refined by a single Halley's method step (cubic convergence). Significantly faster than CSS Color 4's iterative binary search. Hue is preserved exactly; an adaptive `L0` formula blends between chroma reduction and mid-gray anchoring.
93
92
 
94
- See [`docs/gamut-mapping.md`](docs/gamut-mapping.md) for the full treatment.
95
-
96
- ## Easing
97
-
98
- 30+ timing functions covering the CSS `<easing-function>` grammar plus bounce and back. `CSSCubicBezier` solves via Newton-Raphson with bisection fallback; `cssLinear()` implements CSS Easing Level 2 piecewise-linear with gap filling per spec; stepped easings support all four jump terms.
99
-
100
- ## Transforms
101
-
102
- CSS `matrix()` and `matrix3d()` decomposition per the CSSOM View and CSS Transforms specs. 3D uses Gram-Schmidt orthogonalization + quaternion extraction. `slerp` for rotation interpolation. `interpolateDecomposed()` for full transform blending.
103
-
104
- ## Palette API
93
+ See [`docs/colors/gamut-mapping.md`](docs/colors/gamut-mapping.md) for the full treatment.
105
94
 
106
- The [demo](https://color.babb.dev) is backed by a palette API for saving, sharing, and voting on color palettes. Users register via `POST /sessions`, which mints a UUID token and a four-word slug—no accounts required. Palettes are slug-addressed, votable (atomic toggle), and sortable by popularity or recency. A color name registry lets users propose names for CSS colors; admins approve or reject through a moderation queue.
95
+ ### Color Quantization
107
96
 
108
- Hono + MongoDB, Dockerized. See [`api/README.md`](api/README.md) for endpoints, schema, and deployment.
97
+ `quantizePixels()` extracts a perceptual palette from raw image data. The pipeline operates natively in OKLab—MMCQ pre-clustering, k-means++ with chroma-weighted distance, JND deduplication.
109
98
 
110
- | Feature | Mechanism |
111
- | ----------------- | ------------------------------------------------------------------------------------------------------------ |
112
- | **Sessions** | `POST /sessions` → UUID token + user slug; stored with hashed IP; 30-day TTL |
113
- | **Palettes** | CRUD by slug; 1–50 color stops with CSS string + optional name + position |
114
- | **Voting** | `POST /palettes/:slug/vote` — idempotent toggle; unique composite index on `{userSlug, paletteSlug}` |
115
- | **Color names** | `POST /colors/propose` → admin approval queue → `GET /colors/approved` feeds the demo's custom name registry |
116
- | **Rate limiting** | 60 reads/min, 10 writes/min per IP (in-memory, rightmost X-Forwarded-For) |
99
+ ```ts
100
+ import { quantizePixels, dominantColor } from "@mkbabb/value.js";
117
101
 
118
- ### Palette System Flow (Frontend + API)
102
+ const palette = quantizePixels(pixels, width, height, { k: 6 });
103
+ const dominant = dominantColor(pixels, width, height);
104
+ ```
119
105
 
120
- 1. **Save locally** (`PaletteForm` / `usePaletteStore`): stores palettes in browser storage (`color-palettes`) for instant local recall.
121
- 2. **Publish** (`PaletteDialog`): ensures a session (`POST /sessions`), then publishes via `POST /palettes` with `X-Session-Token`.
122
- 3. **Browse + vote** (`Browse` tab): fetches `GET /palettes` and toggles votes with `POST /palettes/:slug/vote` (server maintains atomic vote counts).
123
- 4. **Suggest color names** (`ColorInput` propose form): ensures session, then submits `POST /colors/propose` for moderation.
124
- 5. **Admin moderation** (`AdminPanel`): token login (`Authorization: Bearer ...`), queue from `GET /admin/queue`, actions for approve/reject + feature/delete.
106
+ See [`docs/colors/quantization.md`](docs/colors/quantization.md) for the full pipeline.
125
107
 
126
- ### Validation
108
+ ## Easing
127
109
 
128
- - **API smoke (live backend)**: session creation, publish/list/get, vote toggle, rename, propose, admin queue, approve, feature, delete.
129
- - **Playwright live integration**: [`e2e/palette-api-live.spec.ts`](e2e/palette-api-live.spec.ts) validates save/publish/vote/propose/admin end-to-end against a real API.
110
+ 30+ timing functions covering the CSS `<easing-function>` grammar plus bounce and back. `CSSCubicBezier` solves via Newton-Raphson with bisection fallback; `cssLinear()` implements CSS Easing Level 2 piecewise-linear with gap filling per spec; stepped easings support all four jump terms.
130
111
 
131
- ```bash
132
- # 1) Start API (example)
133
- MONGODB_URI=mongodb://127.0.0.1:27019/palette-e2e ADMIN_TOKEN=test-admin-token PORT=3100 npm --prefix api run dev
112
+ ## Transforms
134
113
 
135
- # 2) Run live frontend + backend integration
136
- PALETTE_API_E2E=1 VITE_API_URL=http://127.0.0.1:3100 npx playwright test e2e/palette-api-live.spec.ts --project=desktop
137
- ```
114
+ CSS `matrix()` and `matrix3d()` decomposition per the CSSOM View and CSS Transforms specs. 3D uses Gram-Schmidt orthogonalization + quaternion extraction. `slerp` for rotation interpolation. `interpolateDecomposed()` for full transform blending.
138
115
 
139
- ## Sources, acknowledgements, &c.
116
+ ## Sources, acknowledgements, & c.
140
117
 
141
118
  - Ottosson, B. (2020). [A perceptual color space for image processing](https://bottosson.github.io/posts/oklab/). — OKLab: the perceptual color space used for `color-mix()` and gamut mapping.
142
119
  - Ottosson, B. (2021). [sRGB gamut clipping](https://bottosson.github.io/posts/gamutclipping/). — Analytical gamut mapping algorithm (cubic boundary + Halley's method).
@@ -145,8 +122,13 @@ PALETTE_API_E2E=1 VITE_API_URL=http://127.0.0.1:3100 npx playwright test e2e/pal
145
122
  - Lindbloom, B. [XYZ to Correlated Color Temperature](http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_T.html). — CCT conversion reference.
146
123
  - [`@mkbabb/parse-that`](https://github.com/mkbabb/parse-that) — Parser combinators powering the CSS value grammar.
147
124
 
148
- See [`docs/color-theory.md`](docs/color-theory.md) for the full bibliography.
125
+ See [`docs/colors/theory.md`](docs/colors/theory.md) for the full bibliography.
126
+
127
+ ## Contributing
128
+
129
+ See [CONTRIBUTING.md](./CONTRIBUTING.md). The README shape follows the perimeter-level
130
+ [canonical README shape](https://github.com/mkbabb/glass-ui/blob/master/docs/precepts/canonical-readme-shape.md).
149
131
 
150
132
  ## License
151
133
 
152
- GPL-3.0-only
134
+ [MIT](./LICENSE) © 2026 Mike Babb.
package/dist/easing.d.ts CHANGED
@@ -1,3 +1,10 @@
1
+ /**
2
+ * A timing function (a.k.a. easing function): maps progress
3
+ * `t ∈ [0, 1]` to an eased output (typically `∈ [0, 1]`, but easings
4
+ * such as `bounceInEase` overshoot). Canonical home for the type
5
+ * (keyframes.js parallel-declares the same shape — keep parity here).
6
+ */
7
+ export type TimingFunction = (t: number) => number;
1
8
  export declare function linear(t: number): number;
2
9
  /**
3
10
  * CSS Easing Level 2 `linear()` — piecewise-linear timing function.
@@ -24,6 +31,11 @@ export declare function easeInCubic(t: number): number;
24
31
  export declare function easeOutCubic(t: number): number;
25
32
  export declare function easeInOutCubic(t: number): number;
26
33
  export declare function smoothStep3(t: number): number;
34
+ /**
35
+ * Solve X(t) = x for t using Newton-Raphson with bisection fallback.
36
+ * X(t) = 3(1-t)^2*t*x1 + 3(1-t)*t^2*x2 + t^3
37
+ */
38
+ export declare function solveCubicBezierX(x: number, x1: number, x2: number, epsilon?: number): number;
27
39
  export declare const CSSCubicBezier: (x1: number, y1: number, x2: number, y2: number) => (x: number) => number;
28
40
  export declare function easeInBounce(t: number): number;
29
41
  export declare function bounceInEase(t: number): number;
@@ -44,15 +56,52 @@ export declare const jumpTerms: readonly ["jump-start", "jump-end", "jump-none",
44
56
  export declare function steppedEase(steps: number, jumpTerm?: (typeof jumpTerms)[number]): (t: number) => number;
45
57
  export declare function stepStart(): (t: number) => number;
46
58
  export declare function stepEnd(): (t: number) => number;
59
+ /**
60
+ * Canonical cubic-bezier control-point tables for every CSS timing
61
+ * function expressible as a bezier curve. The CSS spec only defines the
62
+ * four "standard" keywords (ease, ease-in, ease-out, ease-in-out), but
63
+ * the sine/quad/cubic/expo/circ/back families used throughout the
64
+ * animation ecosystem have well-known bezier approximations — these are
65
+ * the values popularised by Robert Penner and shipped by libraries like
66
+ * GSAP, anime.js, and the Material Design curves.
67
+ *
68
+ * Keeping all of these in value.js means every consumer (keyframes.js,
69
+ * bbnf-buddy, future editors) can ask "is this name a bezier curve?"
70
+ * and "what are its control points?" from one source of truth, rather
71
+ * than hand-rolling parallel tables.
72
+ */
47
73
  export declare const bezierPresets: {
74
+ readonly linear: readonly [0, 0, 1, 1];
48
75
  readonly ease: readonly [0.25, 0.1, 0.25, 1];
49
76
  readonly "ease-in": readonly [0.42, 0, 1, 1];
50
77
  readonly "ease-out": readonly [0, 0, 0.58, 1];
51
78
  readonly "ease-in-out": readonly [0.42, 0, 0.58, 1];
79
+ readonly "ease-in-sine": readonly [0.47, 0, 0.745, 0.715];
80
+ readonly "ease-out-sine": readonly [0.39, 0.575, 0.565, 1];
81
+ readonly "ease-in-out-sine": readonly [0.445, 0.05, 0.55, 0.95];
82
+ readonly "ease-in-quad": readonly [0.55, 0.085, 0.68, 0.53];
83
+ readonly "ease-out-quad": readonly [0.25, 0.46, 0.45, 0.94];
84
+ readonly "ease-in-out-quad": readonly [0.455, 0.03, 0.515, 0.955];
85
+ readonly "ease-in-cubic": readonly [0.55, 0.055, 0.675, 0.19];
86
+ readonly "ease-out-cubic": readonly [0.215, 0.61, 0.355, 1];
87
+ readonly "ease-in-out-cubic": readonly [0.645, 0.045, 0.355, 1];
88
+ readonly "ease-in-expo": readonly [0.95, 0.05, 0.795, 0.035];
89
+ readonly "ease-out-expo": readonly [0.19, 1, 0.22, 1];
90
+ readonly "ease-in-out-expo": readonly [1, 0, 0, 1];
91
+ readonly "ease-in-circ": readonly [0.6, 0.04, 0.98, 0.335];
92
+ readonly "ease-out-circ": readonly [0.075, 0.82, 0.165, 1];
93
+ readonly "ease-in-out-circ": readonly [0.785, 0.135, 0.15, 0.86];
52
94
  readonly "ease-in-back": readonly [0.6, -0.28, 0.735, 0.045];
53
95
  readonly "ease-out-back": readonly [0.175, 0.885, 0.32, 1.275];
54
96
  readonly "ease-in-out-back": readonly [0.68, -0.55, 0.265, 1.55];
55
97
  };
98
+ /**
99
+ * Short human descriptions for every timing-function name value.js knows
100
+ * about — bezier presets, analytic functions, and step variants. Editors
101
+ * and demos can surface these as tooltip copy without hand-maintaining a
102
+ * parallel map.
103
+ */
104
+ export declare const timingFunctionDescriptions: Record<string, string>;
56
105
  export declare const timingFunctions: {
57
106
  readonly linear: typeof linear;
58
107
  readonly easeInQuad: typeof easeInQuad;
package/dist/index.d.ts CHANGED
@@ -1,29 +1,41 @@
1
1
  export { ValueUnit, FunctionValue, ValueArray } from './units';
2
2
  export type { InterpolatedVar } from './units';
3
- export { ABSOLUTE_LENGTH_UNITS, RELATIVE_LENGTH_UNITS, LENGTH_UNITS, TIME_UNITS, ANGLE_UNITS, PERCENTAGE_UNITS, FREQUENCY_UNITS, RESOLUTION_UNITS, FLEX_UNITS, COMPUTED_UNITS, STRING_UNITS, COLOR_UNITS, UNITS, BLACKLISTED_COALESCE_UNITS, STYLE_NAMES, } from './units/constants';
3
+ export { ABSOLUTE_LENGTH_UNITS, RELATIVE_LENGTH_UNITS, LENGTH_UNITS, TIME_UNITS, ANGLE_UNITS, PERCENTAGE_UNITS, FREQUENCY_UNITS, RESOLUTION_UNITS, FLEX_UNITS, COMPUTED_UNITS, UNITS, STYLE_NAMES, } from './units/constants';
4
4
  export type { MatrixValues } from './units/constants';
5
5
  export { isColorUnit, flattenObject, unflattenObject, unflattenObjectToString, isCSSStyleName, unpackMatrixValues, convertAbsoluteUnitToPixels, convertToPixels, convertToMs, convertToDegrees, convertToHz, convertToDPI, convert2, } from './units/utils';
6
- export { getComputedValue, normalizeNumericUnits, normalizeValueUnits, } from './units/normalize';
6
+ export { getComputedValue, normalizeNumericUnits, normalizeValueUnits, bumpLayoutEpoch, getLayoutEpoch, } from './units/normalize';
7
+ export type { NormalizeValueUnitsOptions } from './units/normalize';
8
+ export { lerpValue, lerpComputedValue, lerpColorValue, lerpNumericValue, prepareInterpVar, } from './units/interpolate';
7
9
  export { Color, RGBColor, HSLColor, HSVColor, HWBColor, LABColor, LCHColor, OKLABColor, OKLCHColor, XYZColor, KelvinColor, LinearSRGBColor, DisplayP3Color, AdobeRGBColor, ProPhotoRGBColor, Rec2020Color, } from './units/color';
8
10
  export type { ColorSpaceMap } from './units/color';
9
11
  export { RGBA_MAX, ALPHA_RANGE, RGB_RANGE, UNIT_RANGE, HUE_RANGE, COLOR_SPACE_RANGES, ALPHA_DENORM_UNIT, COLOR_SPACE_DENORM_UNITS, COLOR_SPACE_NAMES, WHITE_POINT_D65, WHITE_POINT_D50, WHITE_POINT_D65_D50, WHITE_POINT_D50_D65, WHITE_POINTS, XYZ_TO_LMS_MATRIX, LMS_TO_XYZ_MATRIX, LMS_TO_OKLAB_MATRIX, OKLAB_TO_LMS_MATRIX, LMS_TO_LINEAR_SRGB, LINEAR_SRGB_TO_LMS, OKLAB_TO_LMS_COEFF, GAMUT_SECTOR_COEFFICIENTS, COLOR_NAMES, } from './units/color/constants';
10
12
  export type { ColorSpace, WhitePoint } from './units/color/constants';
11
13
  export { transformMat3, transposeMat3, multiplyMat3, invertMat3, } from './units/color/matrix';
12
14
  export type { Vec3, Mat3 } from './units/color/matrix';
13
- export { getFormattedColorSpaceRange, hex2rgb, rgb2hex, kelvin2rgb, rgb2kelvin, hsv2hsl, hsl2hsv, hwb2hsl, hsl2hwb, rgb2hsl, hsl2rgb, xyz2lab, lab2xyz, srgbToLinear, linearToSrgb, rgb2xyz, xyz2rgb, lch2lab, lab2lch, oklab2xyz, xyz2oklab, oklab2lab, lab2oklab, oklab2oklch, oklch2oklab, oklch2lab, lab2oklch, hsl2xyz, xyz2hsl, hsv2xyz, xyz2hsv, hwb2xyz, xyz2hwb, lch2xyz, xyz2lch, oklch2xyz, xyz2oklch, kelvin2xyz, xyz2kelvin, adobeRgbToLinear, linearToAdobeRgb, proPhotoToLinear, linearToProPhoto, rec2020ToLinear, linearToRec2020, linearSrgb2xyz, xyz2linearSrgb, displayP32xyz, xyz2displayP3, adobeRgb2xyz, xyz2adobeRgb, proPhoto2xyz, xyz2proPhoto, rec20202xyz, xyz2rec2020, color2, gamutMap, interpolateHue, mixColors, CYLINDRICAL_HUE_COMPONENT, } from './units/color/utils';
14
- export type { HueInterpolationMethod } from './units/color/utils';
15
+ export { getFormattedColorSpaceRange, color2, gamutMap, interpolateHue, mixColors, CYLINDRICAL_HUE_COMPONENT, } from './units/color/dispatch';
16
+ export type { HueInterpolationMethod } from './units/color/dispatch';
17
+ export { computeSafeAccent, safeAccentColor, needsContrastAdjustment, getOklchLightness, } from './units/color/contrast';
18
+ export { mixColorsN } from './units/color/mix';
15
19
  export { normalizeColorUnitComponent, normalizeColor, normalizeColorUnit, colorUnit2, normalizeColorUnits, } from './units/color/normalize';
16
- export { DELTA_E_OK_JND, deltaEOK, oklabToLinearSRGB, isInSRGBGamut, computeMaxSaturation, findCusp, findGamutIntersection, gamutMapOKLab, srgbToOKLab, gamutMapSRGB, } from './units/color/gamut';
17
- export { clamp, scale, lerp, logerp, deCasteljau, cubicBezier, interpBezier, cubicBezierToSVG, cubicBezierToString, } from './math';
20
+ export { DELTA_E_OK_JND, deltaEOK, oklabToLinearSRGB, isInSRGBGamut, computeMaxSaturation, findCusp, findGamutIntersection, gamutMapOKLab, srgbToOKLab, gamutMapSRGB, rawOklabToOklch, rawOklchToOklab, oklabToRgb255, } from './units/color/gamut';
21
+ export { clamp, scale, lerp, lerpArray, logerp, deCasteljau, cubicBezier, interpBezier, cubicBezierToSVG, cubicBezierToString, } from './math';
18
22
  export { FRAME_RATE, isObject, clone, arrayEquals, sleep, waitUntil, debounce, createHash, memoize, hyphenToCamelCase, camelCaseToHyphen, seekPreviousValue, requestAnimationFrame, cancelAnimationFrame, } from './utils';
19
23
  export type { MemoizeOptions } from './utils';
20
24
  export { rgb2ColorFilter, cssFiltersToString } from './units/color/colorFilter';
21
- export { linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, smoothStep3, CSSCubicBezier, easeInBounce, bounceInEase, bounceInEaseHalf, bounceOutEase, bounceOutEaseHalf, bounceInOutEase, easeInSine, easeOutSine, easeInOutSine, easeInCirc, easeOutCirc, easeInOutCirc, easeInExpo, easeOutExpo, easeInOutExpo, jumpTerms, steppedEase, stepStart, stepEnd, cssLinear, bezierPresets, timingFunctions, } from './easing';
22
- export type { LinearStop } from './easing';
25
+ export { linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, smoothStep3, CSSCubicBezier, solveCubicBezierX, easeInBounce, bounceInEase, bounceInEaseHalf, bounceOutEase, bounceOutEaseHalf, bounceInOutEase, easeInSine, easeOutSine, easeInOutSine, easeInCirc, easeOutCirc, easeInOutCirc, easeInExpo, easeOutExpo, easeInOutExpo, jumpTerms, steppedEase, stepStart, stepEnd, cssLinear, bezierPresets, timingFunctions, timingFunctionDescriptions, } from './easing';
26
+ export type { LinearStop, TimingFunction } from './easing';
23
27
  export { CSS_WIDE_KEYWORDS, CSSString, CSSFunction, CSSJSON, CSSValues, parseCSSValue, parseCSSPercent, parseCSSTime, } from './parsing';
24
- export { CSSValueUnit, parseCSSValueUnit } from './parsing/units';
28
+ export { parseCSSStylesheet } from './parsing/stylesheet';
29
+ export type { Stylesheet, StylesheetItem, KeyframeRule, KeyframeSelector, Declaration, PropertyDescriptor, } from './parsing/stylesheet';
30
+ export { extractKeyframes, extractProperties, extractStyleRules, extractAnimationOptions, } from './parsing/extract';
31
+ export type { CSSAnimationOptions } from './parsing/extract';
32
+ export { parseAnimationShorthand, reverseAnimationShorthand, } from './parsing/animation-shorthand';
33
+ export { serializeStylesheet, serializeStylesheetItem, serializeDeclaration, serializeKeyframeSelector, formatCSS, stylesheetToString, } from './parsing/serialize';
34
+ export { CSSValueUnit, parseCSSValueUnit, reverseCSSTime, reverseCSSIterationCount, } from './parsing/units';
25
35
  export { evaluateMathFunction } from './parsing/math';
26
- export { CSSColor, parseCSSColor } from './parsing/color';
36
+ export { CSSColor, parseCSSColor, registerColorNames, clearCustomColorNames, getCustomColorNames, } from './parsing/color';
27
37
  export { istring, identifier, none, integer, number, succeed, fail, tryParse, parseResult, } from './parsing/utils';
38
+ export { quantizePixels, dominantColor } from './quantize';
39
+ export type { QuantizeOptions, QuantizedColor } from './quantize';
28
40
  export { decomposeMatrix2D, decomposeMatrix3D, recomposeMatrix3D, slerp, interpolateDecomposed, } from './transform/decompose';
29
41
  export type { DecomposedMatrix2D, DecomposedMatrix3D, Vec4, Mat4, } from './transform/decompose';
package/dist/math.d.ts CHANGED
@@ -1,6 +1,22 @@
1
1
  export declare function clamp(value: number, min: number, max: number): number;
2
2
  export declare function scale(value: number, fromMin: number, fromMax: number, toMin?: number, toMax?: number): number;
3
- export declare function lerp(t: number, start: number, end: number): number;
3
+ export declare function lerp(start: number, end: number, t: number): number;
4
+ /**
5
+ * SoA (struct-of-arrays) bulk lerp (Wave D2). Interpolates `K` numeric channels
6
+ * in one flat loop over contiguous `Float64Array`s, writing into a caller-owned
7
+ * `out` buffer — eliminating the AoS pointer-chase and the per-channel closure
8
+ * dispatch of K independent `{value}` carriers.
9
+ *
10
+ * This is the carrier primitive a numeric-animation SoA substrate adopts; it is
11
+ * pixel-identical to K independent `lerp()` calls. MEASURE-FIRST (the charter's
12
+ * land bar): it is SLOWER at K=1 and BITES from K≥2 — measured on this machine
13
+ * (bench/numeric-soa.mjs) at 1.56× (K=2) → 4.25× (K=64), so callers should use
14
+ * it only for multi-channel (K≥2) frames. D1 monomorphization is NOT shipped (a
15
+ * measured non-win, r-interpolation-carrier).
16
+ *
17
+ * `start`, `stop`, `out` must share the same length; only `out` is written.
18
+ */
19
+ export declare function lerpArray(start: Float64Array, stop: Float64Array, t: number, out: Float64Array): Float64Array;
4
20
  export declare function logerp(t: number, start: number, end: number): number;
5
21
  export declare function deCasteljau(t: number, points: number[]): number;
6
22
  export declare function cubicBezier(t: number, x1: number, y1: number, x2: number, y2: number): number[];
@@ -0,0 +1,19 @@
1
+ import { CSSAnimationOptions } from './extract';
2
+ /**
3
+ * Parse the value of an `animation` shorthand declaration into one
4
+ * options object per comma-segment.
5
+ *
6
+ * Per CSS Animations spec, `animation: 1s ease-in, 500ms linear`
7
+ * declares two animations. This function returns both; callers that
8
+ * only support a single animation should take `[0]`.
9
+ */
10
+ export declare const parseAnimationShorthand: import('../utils').Memoized<(input: string) => CSSAnimationOptions[]>;
11
+ /**
12
+ * Reverse: emit a single `animation` shorthand string from an options
13
+ * object. Always emits in canonical order:
14
+ * `<duration> <timing-function> <delay> <iteration-count>
15
+ * <direction> <fill-mode> <composition> <name>`.
16
+ *
17
+ * Fields that are unset are omitted entirely (no `0s` placeholders).
18
+ */
19
+ export declare const reverseAnimationShorthand: (opts: CSSAnimationOptions) => string;
@@ -13,8 +13,8 @@ type ComponentExpr = {
13
13
  type: "none";
14
14
  };
15
15
  export declare const CSSColor: {
16
- Value: Parser<ValueUnit<any, string | undefined>>;
17
- colorValue: Parser<ValueUnit<any, string | undefined>>;
16
+ Value: Parser<ValueUnit<any, string>>;
17
+ colorValue: Parser<ValueUnit<any, string>>;
18
18
  componentExpr: Parser<ComponentExpr>;
19
19
  sep: Parser<string>;
20
20
  alphaSep: Parser<string>;
@@ -23,5 +23,12 @@ export declare const CSSColor: {
23
23
  export declare function registerColorNames(names: Record<string, string>): void;
24
24
  export declare function clearCustomColorNames(): void;
25
25
  export declare function getCustomColorNames(): ReadonlyMap<string, string>;
26
- export declare function parseCSSColor(input: string): ValueUnit;
26
+ /**
27
+ * Parse a CSS color string into a `ValueUnit<Color>`. Memoised — the returned
28
+ * ValueUnit is shared across callers, so callers MUST NOT mutate it. Clone
29
+ * before mutating if a per-call instance is needed.
30
+ *
31
+ * The cache is invalidated by `registerColorNames` and `clearCustomColorNames`.
32
+ */
33
+ export declare const parseCSSColor: import('../utils').Memoized<(input: string) => ValueUnit>;
27
34
  export {};
@@ -0,0 +1,48 @@
1
+ import { Declaration, KeyframeRule, PropertyDescriptor, Stylesheet } from './stylesheet';
2
+ /**
3
+ * Animation options as expressed in CSS — a CSS-spec subset shared by
4
+ * `animation` shorthand and the individual `animation-*` longhand
5
+ * properties. Renderer-specific options (WAAPI, color space, hue
6
+ * method) are not represented here; consumers extend this type.
7
+ */
8
+ export type CSSAnimationOptions = {
9
+ name?: string;
10
+ duration?: number;
11
+ delay?: number;
12
+ iterationCount?: number;
13
+ direction?: "normal" | "reverse" | "alternate" | "alternate-reverse";
14
+ fillMode?: "none" | "forwards" | "backwards" | "both";
15
+ timingFunction?: string;
16
+ composition?: "replace" | "add" | "accumulate";
17
+ };
18
+ /**
19
+ * Group every `@keyframes` block in the stylesheet by name. Unnamed
20
+ * blocks are keyed by the empty string. When two `@keyframes` rules
21
+ * share a name, their rule lists are concatenated (CSS cascade order).
22
+ */
23
+ export declare const extractKeyframes: (s: Stylesheet) => Map<string, KeyframeRule[]>;
24
+ /**
25
+ * Index every `@property` registration by its custom property name.
26
+ * Later registrations override earlier ones.
27
+ */
28
+ export declare const extractProperties: (s: Stylesheet) => Map<string, PropertyDescriptor>;
29
+ /** Return every top-level qualified rule (`.foo { ... }`). */
30
+ export declare const extractStyleRules: (s: Stylesheet) => {
31
+ selectors: string[];
32
+ declarations: Declaration[];
33
+ }[];
34
+ /**
35
+ * Walk the stylesheet's top-level style rules and merge every
36
+ * recognised `animation-*` longhand into a single options object.
37
+ *
38
+ * Shorthand `animation: ...` is handled by `parseAnimationShorthand`
39
+ * (a separate entry) — we don't re-tokenise it here. If a style rule
40
+ * has both shorthand and longhand declarations, the consumer is
41
+ * expected to call `parseAnimationShorthand` itself and merge.
42
+ *
43
+ * Longhand declarations later in the stylesheet override earlier ones
44
+ * (CSS cascade). Cross-rule merging is intentional — most stylesheets
45
+ * keep all `animation-*` for one animation in a single rule, but the
46
+ * extractor stays robust either way.
47
+ */
48
+ export declare const extractAnimationOptions: (s: Stylesheet) => CSSAnimationOptions;
@@ -1,32 +1,17 @@
1
1
  import { Parser } from '@mkbabb/parse-that';
2
2
  import { FunctionValue, ValueArray, ValueUnit } from '../units';
3
3
  export declare const CSS_WIDE_KEYWORDS: readonly ["inherit", "initial", "unset", "revert", "revert-layer"];
4
- export declare const CSSString: Parser<ValueUnit<string, string | undefined>>;
4
+ export declare const CSSString: Parser<ValueUnit<string, string>>;
5
5
  export declare const CSSFunction: {
6
6
  Function: Parser<any>;
7
7
  Value: Parser<any>;
8
8
  FunctionArgs: Parser<ValueArray<any>>;
9
9
  };
10
- export declare const CSSJSON: Parser<ValueUnit<any, string>>;
10
+ export declare const CSSJSON: Parser<ValueUnit<any, "json">>;
11
11
  export declare const CSSValues: {
12
12
  Value: Parser<any>;
13
13
  Values: Parser<any[]>;
14
14
  };
15
- export declare const parseCSSValue: ((input: string) => ValueUnit | FunctionValue) & {
16
- cache: Map<string, {
17
- value: ValueUnit<any, string | undefined> | FunctionValue<any, string>;
18
- timestamp: number;
19
- }>;
20
- };
21
- export declare const parseCSSPercent: ((input: string | number) => number) & {
22
- cache: Map<string, {
23
- value: number;
24
- timestamp: number;
25
- }>;
26
- };
27
- export declare const parseCSSTime: ((input: string) => number) & {
28
- cache: Map<string, {
29
- value: number;
30
- timestamp: number;
31
- }>;
32
- };
15
+ export declare const parseCSSValue: import('../utils').Memoized<(input: string) => ValueUnit | FunctionValue>;
16
+ export declare const parseCSSPercent: import('../utils').Memoized<(input: string | number) => number>;
17
+ export declare const parseCSSTime: import('../utils').Memoized<(input: string) => number>;
@@ -0,0 +1,24 @@
1
+ import { Declaration, KeyframeSelector, Stylesheet, StylesheetItem } from './stylesheet';
2
+ export declare const serializeKeyframeSelector: (s: KeyframeSelector) => string;
3
+ export declare const serializeDeclaration: (d: Declaration) => string;
4
+ export declare const serializeStylesheetItem: (item: StylesheetItem) => string;
5
+ /**
6
+ * Emit a `Stylesheet` AST as a CSS string. Items are separated by a
7
+ * single blank line. The output is structurally equivalent to the
8
+ * input (modulo whitespace) when round-tripped through
9
+ * `parseCSSStylesheet`.
10
+ */
11
+ export declare const serializeStylesheet: (s: Stylesheet) => string;
12
+ /**
13
+ * Format a CSS string via Prettier's PostCSS parser. Prettier and its
14
+ * PostCSS plugin are loaded lazily via dynamic `import()` — calling
15
+ * code that never invokes `formatCSS` doesn't pay the bundle cost.
16
+ *
17
+ * Use this for human-readable output (editors, file dumps). Programs
18
+ * that read the AST directly should not need it.
19
+ */
20
+ export declare function formatCSS(css: string, printWidth?: number): Promise<string>;
21
+ /**
22
+ * Convenience: serialise + format in one call.
23
+ */
24
+ export declare function stylesheetToString(s: Stylesheet, printWidth?: number): Promise<string>;
@@ -0,0 +1,44 @@
1
+ import { ValueArray } from '../units';
2
+ export type Declaration = {
3
+ name: string;
4
+ value: ValueArray;
5
+ important: boolean;
6
+ };
7
+ export type KeyframeSelector = {
8
+ kind: "percent";
9
+ value: number;
10
+ } | {
11
+ kind: "named";
12
+ name: "entry" | "exit" | "cover" | "contain";
13
+ };
14
+ export type KeyframeRule = {
15
+ selectors: KeyframeSelector[];
16
+ declarations: Declaration[];
17
+ timingFunction?: string;
18
+ composition?: "replace" | "add" | "accumulate";
19
+ };
20
+ export type PropertyDescriptor = {
21
+ syntax?: string;
22
+ inherits?: boolean;
23
+ initialValue?: ValueArray;
24
+ };
25
+ export type StylesheetItem = {
26
+ kind: "keyframes";
27
+ name?: string;
28
+ rules: KeyframeRule[];
29
+ } | {
30
+ kind: "property";
31
+ name: string;
32
+ descriptor: PropertyDescriptor;
33
+ } | {
34
+ kind: "style";
35
+ selectors: string[];
36
+ declarations: Declaration[];
37
+ } | {
38
+ kind: "unknown";
39
+ atName: string;
40
+ prelude: string;
41
+ body: string | null;
42
+ };
43
+ export type Stylesheet = StylesheetItem[];
44
+ export declare const parseCSSStylesheet: import('../utils').Memoized<(input: string) => Stylesheet>;
@@ -6,14 +6,32 @@ export declare const CSSValueUnit: {
6
6
  Length: Parser<ValueUnit<number, string>>;
7
7
  Angle: Parser<ValueUnit<number, string>>;
8
8
  Time: Parser<ValueUnit<number, string>>;
9
- TimePercentage: Parser<ValueUnit<any, string | undefined>>;
9
+ TimePercentage: Parser<ValueUnit<any, string>>;
10
10
  Frequency: Parser<ValueUnit<number, string>>;
11
11
  Resolution: Parser<ValueUnit<number, string>>;
12
12
  Flex: Parser<ValueUnit<number, string>>;
13
- Percentage: Parser<ValueUnit<any, string | undefined>>;
14
- Color: Parser<ValueUnit<any, string | undefined>>;
15
- Slash: Parser<ValueUnit<string, string>>;
16
- Value: Parser<ValueUnit<any, string | undefined>>;
13
+ Percentage: Parser<ValueUnit<any, string>>;
14
+ Color: Parser<ValueUnit<any, string>>;
15
+ Slash: Parser<ValueUnit<string, "string">>;
16
+ Value: Parser<ValueUnit<any, string>>;
17
17
  sep: Parser<string>;
18
18
  };
19
- export declare function parseCSSValueUnit(input: string): ValueUnit;
19
+ /**
20
+ * Parse a CSS dimension/value string into a `ValueUnit`. Memoised — the
21
+ * returned `ValueUnit` is shared across callers, so callers MUST NOT mutate it.
22
+ * Mirrors the memo contract of the sibling `parseCSSValue`/`parseCSSColor`.
23
+ */
24
+ export declare const parseCSSValueUnit: import('../utils').Memoized<(input: string) => ValueUnit>;
25
+ /**
26
+ * Format a millisecond duration as a CSS time string. Emits `<n>s`
27
+ * for durations ≥ 5 s (where seconds become more readable than
28
+ * milliseconds); otherwise `<n>ms`. Threshold matches the historical
29
+ * keyframes.js convention so round-trips don't drift.
30
+ */
31
+ export declare function reverseCSSTime(time: number): string;
32
+ /**
33
+ * Format an iteration count for the `animation-iteration-count`
34
+ * property. `Infinity` becomes the keyword `infinite`; finite values
35
+ * render as their decimal representation.
36
+ */
37
+ export declare function reverseCSSIterationCount(count: number): string;
@@ -1,6 +1,20 @@
1
1
  import { Parser } from '@mkbabb/parse-that';
2
2
  /** Case-insensitive string match. Returns the matched portion of the input. */
3
3
  export declare const istring: (str: string) => Parser<string>;
4
+ /**
5
+ * Maximal-munch unit classifier. Consumes the longest identifier-shaped token
6
+ * and succeeds ONLY when the whole token (case-insensitively) is a member of
7
+ * `units`; otherwise the parser fails.
8
+ *
9
+ * This replaces `any(...units.map(istring))`, which was order-dependent and —
10
+ * because `istring` compiles a non-anchored RegExp re-flagged sticky `y` by
11
+ * parse-that — matched a unit as a *prefix* of the continuation: `100vhx`
12
+ * tokenized `vh` and silently dropped the trailing `x`, and a unit that was a
13
+ * prefix of a later-declared one would shadow it. Maximal-munch over the full
14
+ * token removes both hazards (vj-parser-aug §2.2). The canonical declared
15
+ * spelling is returned so output is byte-identical for every valid unit.
16
+ */
17
+ export declare const unitParser: (units: readonly string[]) => Parser<string>;
4
18
  export declare const identifier: Parser<string>;
5
19
  export declare const none: Parser<string>;
6
20
  export declare const integer: Parser<number>;
@@ -12,6 +26,11 @@ export declare function fail(message: string): Parser<never>;
12
26
  /**
13
27
  * Try to parse; return the result or throw on failure.
14
28
  * Equivalent to Parsimmon's `.tryParse()`.
29
+ *
30
+ * The thrown error includes a 16-char context window (8 before / 8 after
31
+ * the failure offset) so callers — particularly the demo's color-picker
32
+ * error toasts — can pinpoint where the parse derailed. (E.W1 Lane D /
33
+ * E-AUDIT-5 §9 item 11.)
15
34
  */
16
35
  export declare function tryParse<T>(parser: Parser<T>, input: string): T;
17
36
  /**