@oh-my-pi/pi-utils 18.14.1 → 18.14.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.14.2] - 2026-10-31
6
+
7
+ ### Added
8
+
9
+ - Added `oklabDistance`, the perceptual distance between two hex colors ([#17520](https://github.com/can1357/oh-my-pi/pull/17520))
10
+
5
11
  ## [18.14.1] - 2026-10-31
6
12
 
7
13
  ### Added
@@ -99,6 +99,8 @@ export interface OKLCH {
99
99
  * hues, so carrying them between colors preserves the palette's "weight".
100
100
  */
101
101
  export declare function hexToOklch(hex: string): OKLCH;
102
+ /** Perceptual distance between two hex colors: Euclidean OKLab, 0 when identical, 1 from black to white. */
103
+ export declare function oklabDistance(a: string, b: string): number;
102
104
  /**
103
105
  * The sRGB gamut cusp for an OKLCH hue: the lightness/chroma point where the
104
106
  * hue reaches its maximum chroma inside sRGB.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-utils",
3
- "version": "18.14.1",
3
+ "version": "18.14.2",
4
4
  "description": "Shared utilities for pi packages",
5
5
  "keywords": [
6
6
  "cli",
@@ -56,7 +56,7 @@
56
56
  "fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
57
57
  },
58
58
  "dependencies": {
59
- "@oh-my-pi/pi-natives": "18.14.1"
59
+ "@oh-my-pi/pi-natives": "18.14.2"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/bun": "^1.3.14"
package/src/color.ts CHANGED
@@ -311,6 +311,12 @@ function oklabToLinearRgb(L: number, a: number, b: number): { r: number; g: numb
311
311
  };
312
312
  }
313
313
 
314
+ /** Convert a hex color to OKLab. */
315
+ function hexToOklab(hex: string): { L: number; a: number; b: number } {
316
+ const rgb = hexToRgb(hex);
317
+ return linearRgbToOklab(linearizeChannel(rgb.r), linearizeChannel(rgb.g), linearizeChannel(rgb.b));
318
+ }
319
+
314
320
  /**
315
321
  * Convert a hex color to OKLCH (perceptual lightness/chroma/hue).
316
322
  *
@@ -318,14 +324,20 @@ function oklabToLinearRgb(L: number, a: number, b: number): { r: number; g: numb
318
324
  * hues, so carrying them between colors preserves the palette's "weight".
319
325
  */
320
326
  export function hexToOklch(hex: string): OKLCH {
321
- const rgb = hexToRgb(hex);
322
- const lab = linearRgbToOklab(linearizeChannel(rgb.r), linearizeChannel(rgb.g), linearizeChannel(rgb.b));
327
+ const lab = hexToOklab(hex);
323
328
  const c = Math.hypot(lab.a, lab.b);
324
329
  let h = (Math.atan2(lab.b, lab.a) * 180) / Math.PI;
325
330
  if (h < 0) h += 360;
326
331
  return { l: lab.L, c, h };
327
332
  }
328
333
 
334
+ /** Perceptual distance between two hex colors: Euclidean OKLab, 0 when identical, 1 from black to white. */
335
+ export function oklabDistance(a: string, b: string): number {
336
+ const x = hexToOklab(a);
337
+ const y = hexToOklab(b);
338
+ return Math.hypot(x.L - y.L, x.a - y.a, x.b - y.b);
339
+ }
340
+
329
341
  /**
330
342
  * Max OKLab saturation (C/L) that stays inside sRGB for the hue direction
331
343
  * `(a, b)`, via Björn Ottosson's polynomial fit plus one Halley refinement.