@astryxdesign/lab 0.1.6-canary.e8d4c3f → 0.1.6-canary.eb23da7

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ChartDotGLInteractive.d.ts","sourceRoot":"","sources":["../../src/Chart/ChartDotGLInteractive.tsx"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAKf,MAAM,WAAW,0BAA0B;IACzC,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;CAC9E;AA+GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,OAAO,EACP,KAAK,EACL,IAAQ,EACR,OAAa,EACb,aAAa,GACd,EAAE,0BAA0B,sCAyV5B"}
1
+ {"version":3,"file":"ChartDotGLInteractive.d.ts","sourceRoot":"","sources":["../../src/Chart/ChartDotGLInteractive.tsx"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,MAAM,WAAW,0BAA0B;IACzC,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;CAC9E;AAoHD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,OAAO,EACP,KAAK,EACL,IAAQ,EACR,OAAa,EACb,aAAa,GACd,EAAE,0BAA0B,sCAyV5B"}
@@ -13,8 +13,12 @@
13
13
  import { useRef, useEffect, useCallback, useState, useMemo } from 'react';
14
14
  import { useChart } from "./ChartContext.js";
15
15
  import { xPixel } from "./utils.js";
16
- import { hexToGL } from "./webgl.js";
17
16
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
17
+ function hexToGL(hex) {
18
+ const n = parseInt(hex.replace('#', ''), 16);
19
+ return [(n >> 16) / 255, (n >> 8 & 0xff) / 255, (n & 0xff) / 255];
20
+ }
21
+
18
22
  /** Encode a point index as an RGB color (supports up to 16.7M points) */
19
23
  function indexToColor(i) {
20
24
  // Add 1 so index 0 isn't black (which is our clear color)
@@ -1 +1 @@
1
- {"version":3,"file":"getChartColors.d.ts","sourceRoot":"","sources":["../../src/Chart/getChartColors.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0BAA0B,CAAC;AAO3D,kDAAkD;AAClD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,KAAK,GACL,MAAM,GACN,QAAQ,GACR,MAAM,CAAC;AAEX,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;IAC3D,SAAS,EAAE;QACT,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACtC,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC7B,MAAM,CACJ,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,aAAa,EAClB,CAAC,EAAE,MAAM,EACT,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,EAAE,CAAC;KACb,CAAC;IACF,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,qEAAqE;IACrE,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7C;AAED,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAoErD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,aAAa,GACrB,cAAc,CA+EhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,OAAO,GAAG,MAAgB,GAC/B,cAAc,CAsBhB"}
1
+ {"version":3,"file":"getChartColors.d.ts","sourceRoot":"","sources":["../../src/Chart/getChartColors.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0BAA0B,CAAC;AAM3D,kDAAkD;AAClD,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,UAAU,GACV,QAAQ,GACR,MAAM,GACN,QAAQ,GACR,KAAK,GACL,MAAM,GACN,QAAQ,GACR,MAAM,CAAC;AAEX,8EAA8E;AAC9E,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACjC,UAAU,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC,CAAC;IAC3D,SAAS,EAAE;QACT,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACtC,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QAC7B,MAAM,CACJ,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,aAAa,EAClB,CAAC,EAAE,MAAM,EACT,QAAQ,CAAC,EAAE,MAAM,GAChB,MAAM,EAAE,CAAC;KACb,CAAC;IACF,QAAQ,EAAE;QACR,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,qEAAqE;IACrE,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CAC7C;AAED,iEAAiE;AACjE,MAAM,MAAM,aAAa,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;AAgErD;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,aAAa,GACrB,cAAc,CA+EhB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,OAAO,GAAG,MAAgB,GAC/B,cAAc,CAsBhB"}
@@ -9,8 +9,6 @@
9
9
  * the full chart colors API. The useChartColors hook wraps this.
10
10
  */
11
11
 
12
- import { parseHex, parseColor, formatColor } from '@astryxdesign/core/utils';
13
-
14
12
  // =============================================================================
15
13
  // Types
16
14
  // =============================================================================
@@ -41,25 +39,15 @@ function pickFromRamp(stops, n) {
41
39
  length: n
42
40
  }, (_, i) => stops[Math.round(i * 4 / (n - 1))]);
43
41
  }
44
-
45
- /**
46
- * Apply an opacity to a concrete CSS color (hex — with or without the leading
47
- * `#` — `rgb()`/`rgba()`, named). The `opacity` argument always wins over any
48
- * alpha already in the color. Unparseable input (e.g. `var()`) is returned
49
- * unchanged.
50
- */
51
42
  function hexAlpha(hex, opacity) {
52
- // parseColor requires the `#` for hex; fall back to parseHex so bare hex
53
- // strings (`0064E0`) keep working as they did before #3739.
54
- const rgba = parseColor(hex) ?? parseHex(hex);
55
- if (rgba === null) {
43
+ const match = hex.match(/^#?([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/);
44
+ if (!match) {
56
45
  return hex;
57
46
  }
58
- const a = Number.isFinite(opacity) ? Math.max(0, Math.min(1, opacity)) : 1;
59
- return formatColor({
60
- ...rgba,
61
- a
62
- });
47
+ const r = parseInt(match[1], 16);
48
+ const g = parseInt(match[2], 16);
49
+ const b = parseInt(match[3], 16);
50
+ return `rgba(${r},${g},${b},${Math.max(0, Math.min(1, opacity))})`;
63
51
  }
64
52
 
65
53
  // =============================================================================
@@ -1,11 +1,19 @@
1
+ /**
2
+ * @file webgl.ts
3
+ * @output Shared WebGL utilities for chart components
4
+ * @position Internal helpers; consumed by all GL mark components
5
+ *
6
+ * Provides:
7
+ * - Canvas mounting outside SVG (avoids foreignObject DPR blur)
8
+ * - Premultiplied alpha setup (correct compositing over page)
9
+ * - Shader compilation helpers
10
+ * - Smoothstep circle fragment for crisp point sprites
11
+ */
1
12
  /** Compile a WebGL shader, returns null on failure */
2
13
  export declare function compileShader(gl: WebGLRenderingContext, type: number, src: string): WebGLShader | null;
3
14
  /** Link a vertex + fragment shader into a program */
4
15
  export declare function createProgram(gl: WebGLRenderingContext, vertSrc: string, fragSrc: string): WebGLProgram | null;
5
- /**
6
- * Parse a hex color (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`) to [r, g, b]
7
- * floats 0-1. Unparseable input returns a neutral fallback instead of NaN.
8
- */
16
+ /** Parse hex (#RRGGBB) to [r, g, b] floats 0-1 */
9
17
  export declare function hexToGL(hex: string): [number, number, number];
10
18
  /** DPR with supersampling for crisp circles */
11
19
  export declare function getCanvasDPR(): number;
@@ -1 +1 @@
1
- {"version":3,"file":"webgl.d.ts","sourceRoot":"","sources":["../../src/Chart/webgl.ts"],"names":[],"mappings":"AAkBA,sDAAsD;AACtD,wBAAgB,aAAa,CAC3B,EAAE,EAAE,qBAAqB,EACzB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,WAAW,GAAG,IAAI,CAYpB;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAC3B,EAAE,EAAE,qBAAqB,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,YAAY,GAAG,IAAI,CAkBrB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAE7D;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,iBAAiB,GACxB,qBAAqB,GAAG,IAAI,CAM9B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,qBAAqB,GAAG,IAAI,CAK5D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,kBAAkB,EAC7B,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAwC1B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,iPAO5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,QAAa,CAAC"}
1
+ {"version":3,"file":"webgl.d.ts","sourceRoot":"","sources":["../../src/Chart/webgl.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH,sDAAsD;AACtD,wBAAgB,aAAa,CAC3B,EAAE,EAAE,qBAAqB,EACzB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,WAAW,GAAG,IAAI,CAYpB;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAC3B,EAAE,EAAE,qBAAqB,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,YAAY,GAAG,IAAI,CAkBrB;AAED,kDAAkD;AAClD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAG7D;AAED,+CAA+C;AAC/C,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,iBAAiB,GACxB,qBAAqB,GAAG,IAAI,CAM9B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,qBAAqB,GAAG,IAAI,CAK5D;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,MAAM,CAOR;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,kBAAkB,EAC7B,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAwC1B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,iPAO5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,QAAa,CAAC"}
@@ -10,12 +10,8 @@
10
10
  * - Premultiplied alpha setup (correct compositing over page)
11
11
  * - Shader compilation helpers
12
12
  * - Smoothstep circle fragment for crisp point sprites
13
- * - Hex-to-GL color conversion via the shared @astryxdesign/core/utils/color
14
- * parsers (fallback instead of NaN)
15
13
  */
16
14
 
17
- import { parseHex, toGLFloats } from '@astryxdesign/core/utils';
18
-
19
15
  /** Compile a WebGL shader, returns null on failure */
20
16
  export function compileShader(gl, type, src) {
21
17
  const s = gl.createShader(type);
@@ -52,12 +48,10 @@ export function createProgram(gl, vertSrc, fragSrc) {
52
48
  return p;
53
49
  }
54
50
 
55
- /**
56
- * Parse a hex color (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`) to [r, g, b]
57
- * floats 0-1. Unparseable input returns a neutral fallback instead of NaN.
58
- */
51
+ /** Parse hex (#RRGGBB) to [r, g, b] floats 0-1 */
59
52
  export function hexToGL(hex) {
60
- return toGLFloats(parseHex(hex));
53
+ const n = parseInt(hex.replace('#', ''), 16);
54
+ return [(n >> 16) / 255, (n >> 8 & 0xff) / 255, (n & 0xff) / 255];
61
55
  }
62
56
 
63
57
  /** DPR with supersampling for crisp circles */
@@ -1 +1 @@
1
- {"version":3,"file":"ThreeDScatterGL.d.ts","sourceRoot":"","sources":["../../src/ThreeD/ThreeDScatterGL.tsx"],"names":[],"mappings":"AAmBA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuFD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,IAAQ,EACR,OAAc,GACf,EAAE,oBAAoB,sCAuJtB"}
1
+ {"version":3,"file":"ThreeDScatterGL.d.ts","sourceRoot":"","sources":["../../src/ThreeD/ThreeDScatterGL.tsx"],"names":[],"mappings":"AAkBA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4FD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,IAAQ,EACR,OAAc,GACf,EAAE,oBAAoB,sCAuJtB"}
@@ -14,9 +14,13 @@
14
14
  */
15
15
 
16
16
  import { useRef, useEffect, useMemo } from 'react';
17
- import { parseHex, toGLFloats } from '@astryxdesign/core/utils';
18
17
  import { use3D } from "./ThreeDContext.js";
19
18
  import { jsx as _jsx } from "react/jsx-runtime";
19
+ function hexToGL(hex) {
20
+ const n = parseInt(hex.replace('#', ''), 16);
21
+ return [(n >> 16) / 255, (n >> 8 & 0xff) / 255, (n & 0xff) / 255];
22
+ }
23
+
20
24
  // SYNC: Steps 1-5 match ThreeDChart.project() exactly.
21
25
  const VERT = `
22
26
  attribute vec3 a_position;
@@ -214,7 +218,7 @@ export function ThreeDScatterGL({
214
218
  const azRad = camera.azimuth * Math.PI / 180;
215
219
  const elRad = camera.elevation * Math.PI / 180;
216
220
  const scale = Math.min(width, height) * 0.35;
217
- const [r, g, b] = toGLFloats(parseHex(color));
221
+ const [r, g, b] = hexToGL(color);
218
222
  gl.uniform2f(gl.getUniformLocation(program, 'u_resolution'), width, height);
219
223
  gl.uniform2f(gl.getUniformLocation(program, 'u_center'), width / 2, height / 2);
220
224
  gl.uniform1f(gl.getUniformLocation(program, 'u_scale'), scale);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astryxdesign/lab",
3
- "version": "0.1.6-canary.e8d4c3f",
3
+ "version": "0.1.6-canary.eb23da7",
4
4
  "description": "Experimental Astryx components — published to npm only under the @canary dist-tag for early testing; never released as a stable (latest) version.",
5
5
  "author": "Meta Open Source",
6
6
  "license": "MIT",
@@ -45,7 +45,7 @@
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@stylexjs/stylex": ">=0.10.0",
48
- "@astryxdesign/core": "0.1.6-canary.e8d4c3f",
48
+ "@astryxdesign/core": "0.1.6-canary.eb23da7",
49
49
  "react": ">=19.0.0",
50
50
  "react-dom": ">=19.0.0"
51
51
  },
@@ -55,6 +55,7 @@
55
55
  "d3-shape": "^3.2.0"
56
56
  },
57
57
  "devDependencies": {
58
+ "@types/d3-array": "^3.2.2",
58
59
  "@types/d3-scale": "^4.0.9",
59
60
  "@types/d3-shape": "^3.1.8"
60
61
  },
@@ -20,7 +20,6 @@ import {
20
20
  } from 'react';
21
21
  import {useChart} from './ChartContext';
22
22
  import {xPixel} from './utils';
23
- import {hexToGL} from './webgl';
24
23
 
25
24
  export interface ChartDotGLInteractiveProps {
26
25
  /** Which data key for the y values */
@@ -38,6 +37,11 @@ export interface ChartDotGLInteractiveProps {
38
37
  renderTooltip?: (datum: Record<string, unknown>, index: number) => ReactNode;
39
38
  }
40
39
 
40
+ function hexToGL(hex: string): [number, number, number] {
41
+ const n = parseInt(hex.replace('#', ''), 16);
42
+ return [(n >> 16) / 255, ((n >> 8) & 0xff) / 255, (n & 0xff) / 255];
43
+ }
44
+
41
45
  /** Encode a point index as an RGB color (supports up to 16.7M points) */
42
46
  function indexToColor(i: number): [number, number, number] {
43
47
  // Add 1 so index 0 isn't black (which is our clear color)
@@ -10,7 +10,6 @@
10
10
  */
11
11
 
12
12
  import type {DefinedTheme} from '@astryxdesign/core/theme';
13
- import {parseHex, parseColor, formatColor} from '@astryxdesign/core/utils';
14
13
 
15
14
  // =============================================================================
16
15
  // Types
@@ -106,21 +105,17 @@ function pickFromRamp(stops: string[], n: number): string[] {
106
105
  );
107
106
  }
108
107
 
109
- /**
110
- * Apply an opacity to a concrete CSS color (hex — with or without the leading
111
- * `#` — `rgb()`/`rgba()`, named). The `opacity` argument always wins over any
112
- * alpha already in the color. Unparseable input (e.g. `var()`) is returned
113
- * unchanged.
114
- */
115
108
  function hexAlpha(hex: string, opacity: number): string {
116
- // parseColor requires the `#` for hex; fall back to parseHex so bare hex
117
- // strings (`0064E0`) keep working as they did before #3739.
118
- const rgba = parseColor(hex) ?? parseHex(hex);
119
- if (rgba === null) {
109
+ const match = hex.match(
110
+ /^#?([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})([0-9A-Fa-f]{2})$/,
111
+ );
112
+ if (!match) {
120
113
  return hex;
121
114
  }
122
- const a = Number.isFinite(opacity) ? Math.max(0, Math.min(1, opacity)) : 1;
123
- return formatColor({...rgba, a});
115
+ const r = parseInt(match[1], 16);
116
+ const g = parseInt(match[2], 16);
117
+ const b = parseInt(match[3], 16);
118
+ return `rgba(${r},${g},${b},${Math.max(0, Math.min(1, opacity))})`;
124
119
  }
125
120
 
126
121
  // =============================================================================
@@ -10,12 +10,8 @@
10
10
  * - Premultiplied alpha setup (correct compositing over page)
11
11
  * - Shader compilation helpers
12
12
  * - Smoothstep circle fragment for crisp point sprites
13
- * - Hex-to-GL color conversion via the shared @astryxdesign/core/utils/color
14
- * parsers (fallback instead of NaN)
15
13
  */
16
14
 
17
- import {parseHex, toGLFloats} from '@astryxdesign/core/utils';
18
-
19
15
  /** Compile a WebGL shader, returns null on failure */
20
16
  export function compileShader(
21
17
  gl: WebGLRenderingContext,
@@ -60,12 +56,10 @@ export function createProgram(
60
56
  return p;
61
57
  }
62
58
 
63
- /**
64
- * Parse a hex color (`#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`) to [r, g, b]
65
- * floats 0-1. Unparseable input returns a neutral fallback instead of NaN.
66
- */
59
+ /** Parse hex (#RRGGBB) to [r, g, b] floats 0-1 */
67
60
  export function hexToGL(hex: string): [number, number, number] {
68
- return toGLFloats(parseHex(hex));
61
+ const n = parseInt(hex.replace('#', ''), 16);
62
+ return [(n >> 16) / 255, ((n >> 8) & 0xff) / 255, (n & 0xff) / 255];
69
63
  }
70
64
 
71
65
  /** DPR with supersampling for crisp circles */
@@ -14,7 +14,6 @@
14
14
  */
15
15
 
16
16
  import {useRef, useEffect, useMemo} from 'react';
17
- import {parseHex, toGLFloats} from '@astryxdesign/core/utils';
18
17
  import {use3D} from './ThreeDContext';
19
18
 
20
19
  export interface ThreeDScatterGLProps {
@@ -23,6 +22,11 @@ export interface ThreeDScatterGLProps {
23
22
  opacity?: number;
24
23
  }
25
24
 
25
+ function hexToGL(hex: string): [number, number, number] {
26
+ const n = parseInt(hex.replace('#', ''), 16);
27
+ return [(n >> 16) / 255, ((n >> 8) & 0xff) / 255, (n & 0xff) / 255];
28
+ }
29
+
26
30
  // SYNC: Steps 1-5 match ThreeDChart.project() exactly.
27
31
  const VERT = `
28
32
  attribute vec3 a_position;
@@ -236,7 +240,7 @@ export function ThreeDScatterGL({
236
240
  const azRad = (camera.azimuth * Math.PI) / 180;
237
241
  const elRad = (camera.elevation * Math.PI) / 180;
238
242
  const scale = Math.min(width, height) * 0.35;
239
- const [r, g, b] = toGLFloats(parseHex(color));
243
+ const [r, g, b] = hexToGL(color);
240
244
 
241
245
  gl.uniform2f(gl.getUniformLocation(program, 'u_resolution'), width, height);
242
246
  gl.uniform2f(
@@ -1,28 +0,0 @@
1
- // Copyright (c) Meta Platforms, Inc. and affiliates.
2
-
3
- import {describe, expect, it} from 'vitest';
4
- import {getChartColorsFromResolver} from './getChartColors';
5
-
6
- describe('getChartColorsFromResolver alpha', () => {
7
- const colors = getChartColorsFromResolver(name => `resolved(${name})`);
8
-
9
- it('applies opacity to a hex color', () => {
10
- expect(colors.alpha('#0064E0', 0.2)).toBe('rgba(0, 100, 224, 0.2)');
11
- });
12
-
13
- it('applies opacity to bare hex without the leading # (pre-#3739 behavior)', () => {
14
- expect(colors.alpha('0064E0', 0.2)).toBe('rgba(0, 100, 224, 0.2)');
15
- });
16
-
17
- it('applies opacity to rgba() input, overriding its alpha', () => {
18
- expect(colors.alpha('rgba(0, 100, 224, 0.9)', 0.2)).toBe(
19
- 'rgba(0, 100, 224, 0.2)',
20
- );
21
- });
22
-
23
- it('preserves unparseable input unchanged', () => {
24
- expect(colors.alpha('var(--color-accent)', 0.5)).toBe(
25
- 'var(--color-accent)',
26
- );
27
- });
28
- });