@mkbabb/value.js 0.10.0 → 0.11.1

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/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
@@ -33,15 +33,6 @@ import {
33
33
  } from "@mkbabb/value.js";
34
34
  ```
35
35
 
36
- ## Upgrading
37
-
38
- If you are upgrading across a major-feature bump, see [`CHANGELOG.md`](CHANGELOG.md) for the full per-version breaking-change list. The two migrations to be aware of:
39
-
40
- - **v0.7.0** — the 51 individual `<from>2<to>` color-conversion functions were removed from the barrel. Replace `import { rgb2hsl } from "@mkbabb/value.js"` with the unified `color2(rgb, "rgb", "hsl")`.
41
- - **v0.8.0** — `lerpLegacy` was removed; `lerp` takes canonical `(a, b, t)` ordering. The published codemod `scripts/migrate-keyframes-js-lerp.mjs` rewrites the legacy call shape automatically; manual migration is a straightforward argument swap (first argument moves to last).
42
-
43
- v0.9.0 carries no breaking changes.
44
-
45
36
  ## Build
46
37
 
47
38
  ```bash
@@ -132,3 +123,12 @@ CSS `matrix()` and `matrix3d()` decomposition per the CSSOM View and CSS Transfo
132
123
  - [`@mkbabb/parse-that`](https://github.com/mkbabb/parse-that) — Parser combinators powering the CSS value grammar.
133
124
 
134
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).
131
+
132
+ ## License
133
+
134
+ [MIT](./LICENSE) © 2026 Mike Babb.
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export type { InterpolatedVar } from './units';
3
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
7
  export type { NormalizeValueUnitsOptions } from './units/normalize';
8
8
  export { lerpValue, lerpComputedValue, lerpColorValue, lerpNumericValue, prepareInterpVar, } from './units/interpolate';
9
9
  export { Color, RGBColor, HSLColor, HSVColor, HWBColor, LABColor, LCHColor, OKLABColor, OKLCHColor, XYZColor, KelvinColor, LinearSRGBColor, DisplayP3Color, AdobeRGBColor, ProPhotoRGBColor, Rec2020Color, } from './units/color';
@@ -12,12 +12,13 @@ export { RGBA_MAX, ALPHA_RANGE, RGB_RANGE, UNIT_RANGE, HUE_RANGE, COLOR_SPACE_RA
12
12
  export type { ColorSpace, WhitePoint } from './units/color/constants';
13
13
  export { transformMat3, transposeMat3, multiplyMat3, invertMat3, } from './units/color/matrix';
14
14
  export type { Vec3, Mat3 } from './units/color/matrix';
15
- export { getFormattedColorSpaceRange, color2, gamutMap, interpolateHue, mixColors, CYLINDRICAL_HUE_COMPONENT, computeSafeAccent, safeAccentColor, needsContrastAdjustment, getOklchLightness, } from './units/color/dispatch';
15
+ export { getFormattedColorSpaceRange, color2, gamutMap, interpolateHue, mixColors, CYLINDRICAL_HUE_COMPONENT, } from './units/color/dispatch';
16
16
  export type { HueInterpolationMethod } from './units/color/dispatch';
17
+ export { computeSafeAccent, safeAccentColor, needsContrastAdjustment, getOklchLightness, } from './units/color/contrast';
17
18
  export { mixColorsN } from './units/color/mix';
18
19
  export { normalizeColorUnitComponent, normalizeColor, normalizeColorUnit, colorUnit2, normalizeColorUnits, } from './units/color/normalize';
19
20
  export { DELTA_E_OK_JND, deltaEOK, oklabToLinearSRGB, isInSRGBGamut, computeMaxSaturation, findCusp, findGamutIntersection, gamutMapOKLab, srgbToOKLab, gamutMapSRGB, rawOklabToOklch, rawOklchToOklab, oklabToRgb255, } from './units/color/gamut';
20
- export { clamp, scale, lerp, logerp, deCasteljau, cubicBezier, interpBezier, cubicBezierToSVG, cubicBezierToString, } from './math';
21
+ export { clamp, scale, lerp, lerpArray, logerp, deCasteljau, cubicBezier, interpBezier, cubicBezierToSVG, cubicBezierToString, } from './math';
21
22
  export { FRAME_RATE, isObject, clone, arrayEquals, sleep, waitUntil, debounce, createHash, memoize, hyphenToCamelCase, camelCaseToHyphen, seekPreviousValue, requestAnimationFrame, cancelAnimationFrame, } from './utils';
22
23
  export type { MemoizeOptions } from './utils';
23
24
  export { rgb2ColorFilter, cssFiltersToString } from './units/color/colorFilter';
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
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[];
@@ -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>;
@@ -3,7 +3,6 @@ import { ColorSpace } from './constants';
3
3
  import { hex2rgb, rgb2hex } from './conversions/hex';
4
4
  export { hex2rgb, rgb2hex };
5
5
  export { deltaEOK, isInSRGBGamut, DELTA_E_OK_JND } from './gamut';
6
- export { computeSafeAccent, safeAccentColor, needsContrastAdjustment, getOklchLightness, } from './contrast';
7
6
  export declare const getFormattedColorSpaceRange: <T extends ColorSpace>(colorSpace: T) => ColorSpaceMap<{
8
7
  min: string;
9
8
  max: string;
@@ -271,5 +271,6 @@ export type ColorSpaceMap<T> = {
271
271
  "prophoto-rgb": ProPhotoRGBColor<T>;
272
272
  rec2020: Rec2020Color<T>;
273
273
  };
274
- export { getFormattedColorSpaceRange, color2, gamutMap, interpolateHue, mixColors, CYLINDRICAL_HUE_COMPONENT, computeSafeAccent, safeAccentColor, needsContrastAdjustment, getOklchLightness, hex2rgb, rgb2hex, } from './dispatch';
274
+ export { getFormattedColorSpaceRange, color2, gamutMap, interpolateHue, mixColors, CYLINDRICAL_HUE_COMPONENT, hex2rgb, rgb2hex, } from './dispatch';
275
275
  export type { HueInterpolationMethod } from './dispatch';
276
+ export { computeSafeAccent, safeAccentColor, needsContrastAdjustment, getOklchLightness, } from './contrast';
@@ -1,6 +1,7 @@
1
1
  import { UNITS } from './constants';
2
2
  import { ColorSpace } from './color/constants';
3
3
  import { HueInterpolationMethod } from './color/dispatch';
4
+ import { ColorChannelPlan } from './interpolate';
4
5
  export { registerColorNames, clearCustomColorNames, getCustomColorNames } from '../parsing/color';
5
6
  export declare class ValueUnit<T = any, U extends string = Exclude<(typeof UNITS)[number], undefined> | string> {
6
7
  value: T;
@@ -86,4 +87,35 @@ export type InterpolatedVar<T> = {
86
87
  * runtime dispatch in `lerpValue` will resolve at call time.
87
88
  */
88
89
  _lerp?: (t: number, iv: InterpolatedVar<any>) => ValueUnit<any>;
90
+ /**
91
+ * Frozen color-channel plan set by `prepareInterpVar` for color ivs (B3).
92
+ * Drives the closure-free per-frame loop in `lerpColorValue`; absent for
93
+ * non-color ivs and externally constructed colors (which take the fallback
94
+ * walk). Typed loosely here to avoid a cycle with `interpolate.ts`.
95
+ */
96
+ _colorPlan?: ColorChannelPlan;
97
+ /**
98
+ * Resolved computed-endpoint cache set by `lerpComputedValue` for computed
99
+ * ivs (C1, tranche-F Wave C). A computed leaf (`var`/`calc`) re-resolves
100
+ * BOTH endpoints against the live box every frame — but the resolved pair
101
+ * is invariant while the layout epoch is stable, so the first frame after a
102
+ * (re)resolve stamps `(startN, stopN, unit, target, epoch)` here and every
103
+ * later frame collapses to a bare `lerp(startN, stopN, t)`. Invalidated
104
+ * when the target changes or the layout epoch advances (resize). Absent for
105
+ * non-computed ivs. Typed loosely to avoid a cycle with `interpolate.ts`.
106
+ */
107
+ _computedCache?: ComputedEndpointCache;
108
+ };
109
+ /**
110
+ * The resolved-endpoint cache stamped on a computed `InterpolatedVar` (C1).
111
+ * `target` and `epoch` are the invalidation keys: a steady frame whose live
112
+ * target and layout epoch both match the stamp serves `startN`/`stopN`/`unit`
113
+ * directly; any mismatch re-resolves both endpoints and re-stamps.
114
+ */
115
+ export type ComputedEndpointCache = {
116
+ startN: number;
117
+ stopN: number;
118
+ unit: string | undefined;
119
+ target: HTMLElement;
120
+ epoch: number;
89
121
  };
@@ -6,9 +6,18 @@ import { ValueUnit, InterpolatedVar } from './index';
6
6
  * element's live computed style and lerping the resulting numeric
7
7
  * values.
8
8
  *
9
+ * C1 (tranche-F Wave C) — the endpoint cache. The resolved
10
+ * `(startN, stopN, unit)` pair is invariant while the layout epoch is
11
+ * stable, so it is cached on the iv (`_computedCache`) the first frame
12
+ * and every later steady frame collapses to a bare
13
+ * `lerp(startN, stopN, t)` — no `getComputedValue` memo call, no
14
+ * `value.toString()`, no forced reflow. The cache busts when the live
15
+ * target changes or the layout epoch advances (resize → `bumpLayoutEpoch`).
16
+ * Pixel-identical while the epoch is stable.
17
+ *
9
18
  * Mutates and returns the `value` field of the InterpolatedVar.
10
19
  */
11
- export declare function lerpComputedValue(t: number, { start, stop, value }: InterpolatedVar<any>): ValueUnit<any>;
20
+ export declare function lerpComputedValue(t: number, iv: InterpolatedVar<any>): ValueUnit<any>;
12
21
  /**
13
22
  * A `Color` at interpolation time: its channels may be raw `number`s (numeric
14
23
  * pipeline) or `ValueUnit<number>` wrappers (parser-produced colors). Both
@@ -16,6 +25,20 @@ export declare function lerpComputedValue(t: number, { start, stop, value }: Int
16
25
  * branch dispatches between them.
17
26
  */
18
27
  type InterpColor = Color<ValueUnit<number> | number>;
28
+ /**
29
+ * Frozen per-frame color-channel plan (B3). Built once at `prepareInterpVar`;
30
+ * drives `lerpColorValue`'s closure-free hot loop. Parallel arrays (a light SoA)
31
+ * indexed by channel position: the unwrapped start/stop numbers, the hue
32
+ * channel index (or -1), the destination ValueUnit ref per channel (or null
33
+ * when the destination slot is a raw number written via `setChannel`).
34
+ */
35
+ export type ColorChannelPlan = {
36
+ keys: readonly string[];
37
+ startN: Float64Array;
38
+ stopN: Float64Array;
39
+ hueIndex: number;
40
+ dstVU: (ValueUnit<number> | null)[];
41
+ };
19
42
  /**
20
43
  * Interpolate a colour `ValueUnit` (`unit === "color"`). Walks each
21
44
  * channel of the parsed `Color` instance and lerps independently. The
@@ -2,6 +2,16 @@ import { ValueUnit, InterpolatedVar } from '.';
2
2
  import { ColorSpace } from './color/constants';
3
3
  import { HueInterpolationMethod } from './color/dispatch';
4
4
  import { isColorUnit } from './utils';
5
+ /** The current layout-epoch generation. Bumped on resize. */
6
+ export declare const getLayoutEpoch: () => number;
7
+ /**
8
+ * Invalidate every layout-epoch-stamped cache (the C1 endpoint cache and the
9
+ * `getComputedValue` memo) by advancing the generation counter. Call on any
10
+ * event that changes a computed-unit resolution — a viewport `resize`, a
11
+ * container `ResizeObserver` callback, a writing-mode flip, etc. Cheap (one
12
+ * integer increment + one memo clear); the next computed frame re-resolves.
13
+ */
14
+ export declare const bumpLayoutEpoch: () => number;
5
15
  /**
6
16
  * Resolve a computed CSS value (`var()`, `calc()`, or other
7
17
  * deferred-evaluation unit) against a target element by writing it