You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
/** Fallback parser that defers to the browser by assigning `input` to a 2D canvas `fillStyle` and re-reading the normalized value. Handles named colors, `hsl()`, etc. Returns `[0, 0, 0]` if the value is invalid or no canvas context is available. */
/** Parse any CSS color string into an `RGB` triple. Tries hex and `rgb()` fast paths, then falls back to a canvas-based parser for named colors, `hsl()`, etc. Results are memoized per input. */
/** Format a single channel as a clamped, zero-padded two-digit hex byte. */
79
+
functiontoHex(c: number): string{
80
+
constv=Math.max(0,Math.min(255,Math.round(c)));
81
+
returnv.toString(16).padStart(2,"0");
82
+
}
83
+
84
+
/** Format an `RGB` triple as a `#rrggbb` hex string. Channels are clamped to `[0, 255]`. */
85
+
exportfunctionrgbToHex([r,g,b]: RGB): string{
86
+
return`#${toHex(r)}${toHex(g)}${toHex(b)}`;
87
+
}
88
+
89
+
/** Convert sRGB to HSL. Output `h` is in degrees `[0, 360)`; `s` and `l` are in `[0, 1]`. */
90
+
exportfunctionrgbToHsl([r,g,b]: RGB): HSL{
91
+
constrn=r/255,
92
+
gn=g/255,
93
+
bn=b/255;
94
+
constmax=Math.max(rn,gn,bn);
95
+
constmin=Math.min(rn,gn,bn);
96
+
constl=(max+min)/2;
97
+
constd=max-min;
98
+
leth=0;
99
+
lets=0;
100
+
if(d!==0){
101
+
s=l>0.5 ? d/(2-max-min) : d/(max+min);
102
+
if(max===rn)h=((gn-bn)/d+(gn<bn ? 6 : 0))*60;
103
+
elseif(max===gn)h=((bn-rn)/d+2)*60;
104
+
elseh=((rn-gn)/d+4)*60;
105
+
}
106
+
return[h,s,l];
107
+
}
108
+
109
+
/** Convert HSL to sRGB. `h` is wrapped into `[0, 360)`; `s` and `l` are expected in `[0, 1]`. Output channels are rounded to integers in `[0, 255]`. */
110
+
exportfunctionhslToRgb([h,s,l]: HSL): RGB{
111
+
consthn=(((h%360)+360)%360)/360;
112
+
if(s===0){
113
+
constv=Math.round(l*255);
114
+
return[v,v,v];
115
+
}
116
+
constq=l<0.5 ? l*(1+s) : l+s-l*s;
117
+
constp=2*l-q;
118
+
constf=(t: number): number=>{
119
+
if(t<0)t+=1;
120
+
if(t>1)t-=1;
121
+
if(t<1/6)returnp+(q-p)*6*t;
122
+
if(t<1/2)returnq;
123
+
if(t<2/3)returnp+(q-p)*(2/3-t)*6;
124
+
returnp;
125
+
};
126
+
return[
127
+
Math.round(f(hn+1/3)*255),
128
+
Math.round(f(hn)*255),
129
+
Math.round(f(hn-1/3)*255),
130
+
];
131
+
}
132
+
133
+
/**
134
+
* Blend two `RGB` colors using LRGB (gamma-naive linear) interpolation,
135
+
* matching chroma-js's default `mix` mode. `f` is the weight of `b` in `[0, 1]`
// AFAICT `chroma-js` has no alpha-aware blending? So we need a function to get
21
-
// the color of a heatmap cell over the background.
151
+
/** Composite a premultiplied-style `RGBA` cell color over `source` (default white) and return the resulting opaque `RGB`. Used to flatten heatmap cells against the background. */
22
152
exportfunctionrgbaToRgb(
23
153
[r,g,b,a]: [number,number,number,number],
24
154
source: [number,number,number]=[255,255,255],
@@ -30,7 +160,7 @@ export function rgbaToRgb(
30
160
return[f(0,r),f(1,g),f(2,b)];
31
161
}
32
162
33
-
// Chroma does this but why bother?
163
+
/** Pick a readable foreground (`#161616` or `#ffffff`) for the given background using a perceptual luminance threshold. */
/** Build a CSS `linear-gradient` that fans `rgb` ±15° in hue, used as the negative-value swatch in column color pickers. */
176
+
functionmake_gradient(rgb: RGB): string{
177
+
const[h,s,l]=rgbToHsl(rgb);
178
+
const[r,g,b]=rgb;
179
+
const[r1,g1,b1]=hslToRgb([h-15,s,l]);
180
+
const[r2,g2,b2]=hslToRgb([h+15,s,l]);
53
181
return`linear-gradient(to right top,rgb(${r1},${g1},${b1}),rgb(${r},${g},${b}) 50%,rgb(${r2},${g2},${b2}))`;
54
182
}
55
183
184
+
/** Precompute the tuple of derived color strings (RGB channels, gradient, opaque/transparent rgba) cached on the model for a configured plugin color. */
0 commit comments