browser-debugger-cli 0.9.0 → 0.10.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.
- package/README.md +4 -1
- package/dist/commands/dom/a11y.js +2 -1
- package/dist/commands/dom/formInteraction.js +56 -25
- package/dist/commands/dom/helpers/keyAttributes.d.ts +20 -0
- package/dist/commands/dom/helpers/keyAttributes.js +54 -0
- package/dist/commands/dom/helpers/query.d.ts +1 -1
- package/dist/commands/dom/helpers/query.js +66 -19
- package/dist/commands/dom/helpers/runElementCommand.js +4 -3
- package/dist/commands/dom/helpers/screenshot.js +85 -12
- package/dist/commands/dom/index.d.ts +1 -0
- package/dist/commands/dom/index.js +8 -3
- package/dist/commands/dom/inspect.d.ts +15 -0
- package/dist/commands/dom/inspect.js +82 -0
- package/dist/commands/dom/layout.js +2 -2
- package/dist/commands/dom/listeners.js +2 -2
- package/dist/commands/dom/semanticUtils.d.ts +14 -1
- package/dist/commands/dom/semanticUtils.js +44 -3
- package/dist/commands/network/list.js +13 -2
- package/dist/commands/optionBehaviors.js +48 -6
- package/dist/commands/page.d.ts +1 -1
- package/dist/commands/page.js +62 -3
- package/dist/commands/shared/commonOptions.d.ts +4 -0
- package/dist/commands/shared/commonOptions.js +9 -0
- package/dist/commands/shared/optionTypes.d.ts +21 -0
- package/dist/commands/shared/startHelpers.d.ts +66 -0
- package/dist/commands/shared/startHelpers.js +91 -10
- package/dist/commands/shared/validation.d.ts +11 -0
- package/dist/commands/shared/validation.js +16 -0
- package/dist/daemon/launcher.d.ts +8 -1
- package/dist/daemon/launcher.js +3 -1
- package/dist/daemon/session/Session.d.ts +7 -0
- package/dist/daemon/session/Session.js +23 -1
- package/dist/daemon/session/commandRegistry.d.ts +14 -1
- package/dist/daemon/session/commandRegistry.js +65 -9
- package/dist/daemon/session/interactions.d.ts +18 -5
- package/dist/daemon/session/interactions.js +22 -12
- package/dist/daemon.js +3565 -329
- package/dist/errors/messages.d.ts +69 -0
- package/dist/errors/messages.js +102 -1
- package/dist/index.js +2416 -1320
- package/dist/ipc/client.d.ts +9 -0
- package/dist/ipc/client.js +13 -0
- package/dist/ipc/protocol/commands.d.ts +56 -1
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +35 -2
- package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
- package/dist/ipc/protocol/inspectTypes.js +10 -0
- package/dist/runtime/dom/actionEffects.d.ts +94 -15
- package/dist/runtime/dom/actionEffects.js +173 -27
- package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -14
- package/dist/runtime/dom/actionEffectsScripts.js +224 -32
- package/dist/runtime/dom/elementInfo.d.ts +26 -0
- package/dist/runtime/dom/elementInfo.js +65 -0
- package/dist/runtime/dom/eventListeners.js +14 -4
- package/dist/runtime/dom/formFillHelpers/fill.d.ts +3 -4
- package/dist/runtime/dom/formFillHelpers/fill.js +77 -28
- package/dist/runtime/dom/frameSelection.d.ts +11 -0
- package/dist/runtime/dom/frameSelection.js +20 -1
- package/dist/runtime/dom/frames.d.ts +38 -5
- package/dist/runtime/dom/frames.js +136 -21
- package/dist/runtime/dom/inspect.d.ts +28 -0
- package/dist/runtime/dom/inspect.js +557 -0
- package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
- package/dist/runtime/dom/inspectAllStyles.js +385 -0
- package/dist/runtime/dom/inspectCascade.d.ts +94 -0
- package/dist/runtime/dom/inspectCascade.js +371 -0
- package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
- package/dist/runtime/dom/inspectCascadeModel.js +232 -0
- package/dist/runtime/dom/inspectHints.d.ts +62 -0
- package/dist/runtime/dom/inspectHints.js +305 -0
- package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
- package/dist/runtime/dom/inspectLayoutModel.js +346 -0
- package/dist/runtime/dom/inspectModel.d.ts +74 -0
- package/dist/runtime/dom/inspectModel.js +184 -0
- package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
- package/dist/runtime/dom/inspectPaintModel.js +461 -0
- package/dist/runtime/dom/inspectRules.d.ts +37 -0
- package/dist/runtime/dom/inspectRules.js +101 -0
- package/dist/runtime/dom/inspectScripts.d.ts +132 -0
- package/dist/runtime/dom/inspectScripts.js +263 -0
- package/dist/runtime/dom/inspectTree.d.ts +40 -0
- package/dist/runtime/dom/inspectTree.js +134 -0
- package/dist/runtime/dom/inspectVariables.d.ts +33 -0
- package/dist/runtime/dom/inspectVariables.js +94 -0
- package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
- package/dist/runtime/dom/inspectWhyModel.js +134 -0
- package/dist/runtime/dom/layout.d.ts +5 -1
- package/dist/runtime/dom/layout.js +10 -3
- package/dist/runtime/dom/listenerPageScripts.d.ts +11 -5
- package/dist/runtime/dom/listenerPageScripts.js +95 -9
- package/dist/runtime/dom/listenerSummary.d.ts +4 -0
- package/dist/runtime/dom/listenerSummary.js +26 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +5 -0
- package/dist/runtime/dom/reactEventHelpers.js +12 -4
- package/dist/runtime/page/emulation.d.ts +20 -0
- package/dist/runtime/page/emulation.js +37 -0
- package/dist/telemetry/a11y.d.ts +10 -0
- package/dist/telemetry/a11y.js +78 -1
- package/dist/telemetry/console.d.ts +1 -0
- package/dist/telemetry/console.js +100 -5
- package/dist/telemetry/network.js +3 -1
- package/dist/types.d.ts +32 -0
- package/dist/ui/formatters/details.d.ts +8 -0
- package/dist/ui/formatters/details.js +59 -3
- package/dist/ui/formatters/dom.d.ts +2 -1
- package/dist/ui/formatters/dom.js +25 -9
- package/dist/ui/formatters/inspect.d.ts +39 -0
- package/dist/ui/formatters/inspect.js +596 -0
- package/dist/ui/formatters/keyAttributes.d.ts +19 -0
- package/dist/ui/formatters/keyAttributes.js +84 -0
- package/dist/ui/formatters/layout.js +2 -2
- package/dist/ui/formatters/networkHeaders.d.ts +13 -0
- package/dist/ui/formatters/networkHeaders.js +23 -3
- package/dist/ui/formatters/networkList.d.ts +29 -1
- package/dist/ui/formatters/networkList.js +86 -20
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/formatting.d.ts +9 -0
- package/dist/ui/formatting.js +6 -3
- package/dist/ui/messages/commands.d.ts +123 -7
- package/dist/ui/messages/commands.js +181 -10
- package/dist/ui/messages/networkMessages.d.ts +14 -0
- package/dist/ui/messages/networkMessages.js +18 -0
- package/dist/ui/messages/session.d.ts +14 -0
- package/dist/ui/messages/session.js +20 -0
- package/dist/utils/async.d.ts +9 -0
- package/dist/utils/async.js +17 -0
- package/dist/utils/color.d.ts +84 -0
- package/dist/utils/color.js +376 -0
- package/dist/utils/cssValues.d.ts +109 -0
- package/dist/utils/cssValues.js +236 -0
- package/dist/utils/selectorFilters.d.ts +12 -0
- package/dist/utils/selectorFilters.js +29 -0
- package/package.json +1 -1
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS colors as `bdg dom inspect` reports them: parsed from the computed
|
|
3
|
+
* values Chrome returns (`rgb()`/`rgba()`, and `lab()`, `lch()`, `oklab()`,
|
|
4
|
+
* `oklch()` and `color()` as Tailwind v4 and other modern CSS leave them),
|
|
5
|
+
* converted to sRGB the way Chrome paints them on an sRGB screen (channels
|
|
6
|
+
* outside the gamut are clipped), and printed as short hex. Also the WCAG
|
|
7
|
+
* contrast math over alpha-composited backgrounds.
|
|
8
|
+
*
|
|
9
|
+
* Conversions follow the CSS Color 4 sample code (Bradford D50 → D65, the
|
|
10
|
+
* OKLab matrices of Björn Ottosson).
|
|
11
|
+
*/
|
|
12
|
+
const XYZ_D65_TO_LINEAR_SRGB = [
|
|
13
|
+
[3.2409699419045226, -1.537383177570094, -0.4986107602930034],
|
|
14
|
+
[-0.9692436362808796, 1.8759675015077202, 0.04155505740717559],
|
|
15
|
+
[0.05563007969699366, -0.20397695888897652, 1.0569715142428786],
|
|
16
|
+
];
|
|
17
|
+
const D50_TO_D65 = [
|
|
18
|
+
[0.955473421488075, -0.02309845494876471, 0.06325924320057072],
|
|
19
|
+
[-0.0283697093338637, 1.0099953980813041, 0.021041441191917323],
|
|
20
|
+
[0.012314014864481998, -0.020507649298898964, 1.330365926242124],
|
|
21
|
+
];
|
|
22
|
+
const LINEAR_P3_TO_XYZ_D65 = [
|
|
23
|
+
[0.4865709486482162, 0.26566769316909306, 0.1982172852343625],
|
|
24
|
+
[0.2289745640697488, 0.6917385218365064, 0.079286914093745],
|
|
25
|
+
[0, 0.04511338185890264, 1.043944368900976],
|
|
26
|
+
];
|
|
27
|
+
const LINEAR_REC2020_TO_XYZ_D65 = [
|
|
28
|
+
[0.6369580483012914, 0.14461690358620832, 0.1688809751641721],
|
|
29
|
+
[0.2627002120112671, 0.6779980715188708, 0.05930171646986196],
|
|
30
|
+
[0, 0.028072693049087428, 1.060985057710791],
|
|
31
|
+
];
|
|
32
|
+
const LINEAR_A98_TO_XYZ_D65 = [
|
|
33
|
+
[0.5766690429101305, 0.1855582379065463, 0.1882286462349947],
|
|
34
|
+
[0.29734497525053605, 0.6273635662554661, 0.07529145849399788],
|
|
35
|
+
[0.02703136138641234, 0.07068885253582723, 0.9913375368376388],
|
|
36
|
+
];
|
|
37
|
+
const LINEAR_PROPHOTO_TO_XYZ_D50 = [
|
|
38
|
+
[0.7977666449006423, 0.13518129740053308, 0.0313477341283922],
|
|
39
|
+
[0.2880748288194013, 0.711835234241873, 0.00008993693872564],
|
|
40
|
+
[0, 0, 0.8251046025104602],
|
|
41
|
+
];
|
|
42
|
+
/** D50 reference white (CSS Color 4) */
|
|
43
|
+
const D50_WHITE = [0.3457 / 0.3585, 1, (1 - 0.3457 - 0.3585) / 0.3585];
|
|
44
|
+
/**
|
|
45
|
+
* Multiply a 3×3 matrix by a vector.
|
|
46
|
+
*
|
|
47
|
+
* @param m - Matrix
|
|
48
|
+
* @param v - Vector
|
|
49
|
+
* @returns Product
|
|
50
|
+
*/
|
|
51
|
+
function multiply(m, v) {
|
|
52
|
+
return [
|
|
53
|
+
m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
|
|
54
|
+
m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
|
|
55
|
+
m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2],
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Apply a transfer function to each channel, keeping the sign.
|
|
60
|
+
*
|
|
61
|
+
* @param v - Channels
|
|
62
|
+
* @param fn - Transfer function for non-negative values
|
|
63
|
+
* @returns Transformed channels
|
|
64
|
+
*/
|
|
65
|
+
function perChannel(v, fn) {
|
|
66
|
+
return v.map((x) => Math.sign(x) * fn(Math.abs(x)));
|
|
67
|
+
}
|
|
68
|
+
/** sRGB (also Display P3) gamma-encoded → linear */
|
|
69
|
+
const srgbToLinear = (x) => x <= 0.04045 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
|
|
70
|
+
/** Linear → sRGB gamma-encoded */
|
|
71
|
+
const linearToSrgb = (x) => x <= 0.0031308 ? 12.92 * x : 1.055 * x ** (1 / 2.4) - 0.055;
|
|
72
|
+
/** Rec. 2020 gamma-encoded → linear */
|
|
73
|
+
const rec2020ToLinear = (x) => {
|
|
74
|
+
const alpha = 1.09929682680944;
|
|
75
|
+
const beta = 0.018053968510807;
|
|
76
|
+
return x < beta * 4.5 ? x / 4.5 : ((x + alpha - 1) / alpha) ** (1 / 0.45);
|
|
77
|
+
};
|
|
78
|
+
/** ProPhoto gamma-encoded → linear */
|
|
79
|
+
const prophotoToLinear = (x) => (x <= 16 / 512 ? x / 16 : x ** 1.8);
|
|
80
|
+
/**
|
|
81
|
+
* Gamma-encoded sRGB channels from XYZ (D65).
|
|
82
|
+
*
|
|
83
|
+
* @param xyz - XYZ relative to D65
|
|
84
|
+
* @returns sRGB channels (unclipped)
|
|
85
|
+
*/
|
|
86
|
+
function xyzD65ToSrgb(xyz) {
|
|
87
|
+
return perChannel(multiply(XYZ_D65_TO_LINEAR_SRGB, xyz), linearToSrgb);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* XYZ (D50) from CIE Lab.
|
|
91
|
+
*
|
|
92
|
+
* @param lab - L (0-100), a, b
|
|
93
|
+
* @returns XYZ relative to D50
|
|
94
|
+
*/
|
|
95
|
+
function labToXyzD50([l, a, b]) {
|
|
96
|
+
const kappa = 24389 / 27;
|
|
97
|
+
const epsilon = 216 / 24389;
|
|
98
|
+
const f1 = (l + 16) / 116;
|
|
99
|
+
const f0 = a / 500 + f1;
|
|
100
|
+
const f2 = f1 - b / 200;
|
|
101
|
+
const x = f0 ** 3 > epsilon ? f0 ** 3 : (116 * f0 - 16) / kappa;
|
|
102
|
+
const y = l > kappa * epsilon ? f1 ** 3 : l / kappa;
|
|
103
|
+
const z = f2 ** 3 > epsilon ? f2 ** 3 : (116 * f2 - 16) / kappa;
|
|
104
|
+
return [x * D50_WHITE[0], y * D50_WHITE[1], z * D50_WHITE[2]];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Gamma-encoded sRGB channels from OKLab.
|
|
108
|
+
*
|
|
109
|
+
* @param oklab - L (0-1), a, b
|
|
110
|
+
* @returns sRGB channels (unclipped)
|
|
111
|
+
*/
|
|
112
|
+
function oklabToSrgb([l, a, b]) {
|
|
113
|
+
const lms = [
|
|
114
|
+
(l + 0.3963377774 * a + 0.2158037573 * b) ** 3,
|
|
115
|
+
(l - 0.1055613458 * a - 0.0638541728 * b) ** 3,
|
|
116
|
+
(l - 0.0894841775 * a - 1.291485548 * b) ** 3,
|
|
117
|
+
];
|
|
118
|
+
const linear = [
|
|
119
|
+
4.0767416621 * lms[0] - 3.3077115913 * lms[1] + 0.2309699292 * lms[2],
|
|
120
|
+
-1.2684380046 * lms[0] + 2.6097574011 * lms[1] - 0.3413193965 * lms[2],
|
|
121
|
+
-0.0041960863 * lms[0] - 0.7034186147 * lms[1] + 1.707614701 * lms[2],
|
|
122
|
+
];
|
|
123
|
+
return perChannel(linear, linearToSrgb);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Cartesian a/b from a polar chroma and hue.
|
|
127
|
+
*
|
|
128
|
+
* @param lch - Lightness, chroma, hue in degrees
|
|
129
|
+
* @returns Lightness, a, b
|
|
130
|
+
*/
|
|
131
|
+
function polarToCartesian([l, c, h]) {
|
|
132
|
+
const radians = (h * Math.PI) / 180;
|
|
133
|
+
return [l, c * Math.cos(radians), c * Math.sin(radians)];
|
|
134
|
+
}
|
|
135
|
+
/** Gamma-encoded sRGB from the channels of a `color()` space */
|
|
136
|
+
const COLOR_SPACES = {
|
|
137
|
+
srgb: (v) => v,
|
|
138
|
+
'srgb-linear': (v) => perChannel(v, linearToSrgb),
|
|
139
|
+
'display-p3': (v) => xyzD65ToSrgb(multiply(LINEAR_P3_TO_XYZ_D65, perChannel(v, srgbToLinear))),
|
|
140
|
+
rec2020: (v) => xyzD65ToSrgb(multiply(LINEAR_REC2020_TO_XYZ_D65, perChannel(v, rec2020ToLinear))),
|
|
141
|
+
'a98-rgb': (v) => xyzD65ToSrgb(multiply(LINEAR_A98_TO_XYZ_D65, perChannel(v, (x) => x ** (563 / 256)))),
|
|
142
|
+
'prophoto-rgb': (v) => xyzD65ToSrgb(multiply(D50_TO_D65, multiply(LINEAR_PROPHOTO_TO_XYZ_D50, perChannel(v, prophotoToLinear)))),
|
|
143
|
+
xyz: (v) => xyzD65ToSrgb(v),
|
|
144
|
+
'xyz-d65': (v) => xyzD65ToSrgb(v),
|
|
145
|
+
'xyz-d50': (v) => xyzD65ToSrgb(multiply(D50_TO_D65, v)),
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* One color function argument as a number: `none` is 0, a percentage is
|
|
149
|
+
* scaled to `percentScale` (100% = percentScale), an angle is in degrees.
|
|
150
|
+
*
|
|
151
|
+
* @param token - Argument, e.g. `37.3%`, `0.2`, `none`, `120deg`
|
|
152
|
+
* @param percentScale - Value of 100%
|
|
153
|
+
* @returns Number, or NaN when it is not one
|
|
154
|
+
*/
|
|
155
|
+
function argument(token, percentScale) {
|
|
156
|
+
if (token === 'none')
|
|
157
|
+
return 0;
|
|
158
|
+
const match = /^(-?[\d.]+(?:e-?\d+)?)(%|deg|grad|rad|turn)?$/i.exec(token);
|
|
159
|
+
if (!match)
|
|
160
|
+
return NaN;
|
|
161
|
+
const value = Number(match[1]);
|
|
162
|
+
const unit = match[2]?.toLowerCase();
|
|
163
|
+
if (unit === '%')
|
|
164
|
+
return (value / 100) * percentScale;
|
|
165
|
+
if (unit === 'rad')
|
|
166
|
+
return (value * 180) / Math.PI;
|
|
167
|
+
if (unit === 'grad')
|
|
168
|
+
return value * 0.9;
|
|
169
|
+
if (unit === 'turn')
|
|
170
|
+
return value * 360;
|
|
171
|
+
return value;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Split the arguments of a color function into its channels and alpha
|
|
175
|
+
* (after a `/`, or the token after the channels in legacy `rgba(r, g, b, a)`).
|
|
176
|
+
*
|
|
177
|
+
* @param body - Text between the parentheses
|
|
178
|
+
* @param count - Tokens before the alpha (4 for `color()`: the space and three channels)
|
|
179
|
+
* @returns Channel tokens and the alpha token (when given)
|
|
180
|
+
*/
|
|
181
|
+
function splitArguments(body, count) {
|
|
182
|
+
const [main = '', alpha] = body.split('/').map((part) => part.trim());
|
|
183
|
+
const channels = main.split(/[\s,]+/).filter(Boolean);
|
|
184
|
+
if (alpha !== undefined)
|
|
185
|
+
return { channels, alpha };
|
|
186
|
+
const legacyAlpha = channels.length === count + 1 ? channels[count] : undefined;
|
|
187
|
+
if (legacyAlpha !== undefined)
|
|
188
|
+
return { channels: channels.slice(0, count), alpha: legacyAlpha };
|
|
189
|
+
return { channels };
|
|
190
|
+
}
|
|
191
|
+
/** How each color function maps its three channels to sRGB, with each channel's 100% */
|
|
192
|
+
const FUNCTIONS = {
|
|
193
|
+
rgb: { scales: [255, 255, 255], toSrgb: (v) => v.map((x) => x / 255) },
|
|
194
|
+
lab: {
|
|
195
|
+
scales: [100, 125, 125],
|
|
196
|
+
toSrgb: (v) => xyzD65ToSrgb(multiply(D50_TO_D65, labToXyzD50(v))),
|
|
197
|
+
},
|
|
198
|
+
lch: {
|
|
199
|
+
scales: [100, 150, 1],
|
|
200
|
+
toSrgb: (v) => xyzD65ToSrgb(multiply(D50_TO_D65, labToXyzD50(polarToCartesian(v)))),
|
|
201
|
+
},
|
|
202
|
+
oklab: { scales: [1, 0.4, 0.4], toSrgb: oklabToSrgb },
|
|
203
|
+
oklch: { scales: [1, 0.4, 1], toSrgb: (v) => oklabToSrgb(polarToCartesian(v)) },
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Parse a `#rgb`, `#rgba`, `#rrggbb` or `#rrggbbaa` color.
|
|
207
|
+
*
|
|
208
|
+
* @param hex - Hex digits without `#`
|
|
209
|
+
* @returns Color, or null when malformed
|
|
210
|
+
*/
|
|
211
|
+
function parseHex(hex) {
|
|
212
|
+
if (!/^([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i.test(hex))
|
|
213
|
+
return null;
|
|
214
|
+
const full = hex.length <= 4 ? [...hex].map((digit) => digit + digit).join('') : hex;
|
|
215
|
+
const channel = (i) => parseInt(full.slice(i * 2, i * 2 + 2), 16) / 255;
|
|
216
|
+
return { r: channel(0), g: channel(1), b: channel(2), a: full.length === 8 ? channel(3) : 1 };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Parse a functional color: `rgb()`/`rgba()`, `lab()`, `lch()`, `oklab()`,
|
|
220
|
+
* `oklch()` or `color(<space> …)`.
|
|
221
|
+
*
|
|
222
|
+
* @param name - Function name, lowercased
|
|
223
|
+
* @param body - Text between the parentheses
|
|
224
|
+
* @returns Color in sRGB, or null when it is not one
|
|
225
|
+
*/
|
|
226
|
+
function parseFunction(name, body) {
|
|
227
|
+
const { channels, alpha } = splitArguments(body, name === 'color' ? 4 : 3);
|
|
228
|
+
const a = alpha === undefined ? 1 : argument(alpha, 1);
|
|
229
|
+
if (name === 'color') {
|
|
230
|
+
const [space = '', ...rest] = channels;
|
|
231
|
+
const toSrgb = COLOR_SPACES[space.toLowerCase()];
|
|
232
|
+
const values = rest.map((token) => argument(token, 1));
|
|
233
|
+
if (!toSrgb || values.length !== 3)
|
|
234
|
+
return null;
|
|
235
|
+
return withAlpha(toSrgb(values), a);
|
|
236
|
+
}
|
|
237
|
+
const fn = FUNCTIONS[name === 'rgba' ? 'rgb' : name];
|
|
238
|
+
if (!fn || channels.length !== 3)
|
|
239
|
+
return null;
|
|
240
|
+
const values = channels.map((token, i) => argument(token, fn.scales[i] ?? 1));
|
|
241
|
+
return withAlpha(fn.toSrgb(values), a);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* A color from sRGB channels and an alpha, or null when a value is not a
|
|
245
|
+
* number. Channels outside sRGB (wide-gamut lab/oklch/color()) are clipped
|
|
246
|
+
* here, before any compositing, as Chrome paints them.
|
|
247
|
+
*
|
|
248
|
+
* @param rgb - sRGB channels
|
|
249
|
+
* @param a - Alpha
|
|
250
|
+
* @returns Color, or null
|
|
251
|
+
*/
|
|
252
|
+
function withAlpha(rgb, a) {
|
|
253
|
+
if ([...rgb, a].some((value) => Number.isNaN(value)))
|
|
254
|
+
return null;
|
|
255
|
+
const clamp = (value) => Math.min(1, Math.max(0, value));
|
|
256
|
+
return { r: clamp(rgb[0]), g: clamp(rgb[1]), b: clamp(rgb[2]), a: clamp(a) };
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Parse a CSS color as Chrome computes it.
|
|
260
|
+
*
|
|
261
|
+
* @param value - e.g. `rgba(0, 0, 0, 0.2)`, `oklch(0.373 0.034 259.733)`, `#fff`, `transparent`
|
|
262
|
+
* @returns The color in sRGB, or null for anything else (`currentcolor`, keywords, gradients)
|
|
263
|
+
*/
|
|
264
|
+
export function parseColor(value) {
|
|
265
|
+
const text = value.trim().toLowerCase();
|
|
266
|
+
if (text === 'transparent')
|
|
267
|
+
return { r: 0, g: 0, b: 0, a: 0 };
|
|
268
|
+
if (text.startsWith('#'))
|
|
269
|
+
return parseHex(text.slice(1));
|
|
270
|
+
const match = /^([a-z]+)\((.*)\)$/.exec(text);
|
|
271
|
+
return match?.[1] && match[2] !== undefined ? parseFunction(match[1], match[2]) : null;
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* A channel (0-1, clipped to the sRGB gamut) as two hex digits.
|
|
275
|
+
*
|
|
276
|
+
* @param channel - Channel value
|
|
277
|
+
* @returns e.g. `0a`
|
|
278
|
+
*/
|
|
279
|
+
function hexByte(channel) {
|
|
280
|
+
const byte = Math.round(Math.min(1, Math.max(0, channel)) * 255);
|
|
281
|
+
return byte.toString(16).padStart(2, '0');
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* A color as short hex: `#rgb` when every channel's digits repeat, else
|
|
285
|
+
* `#rrggbb`; `#rrggbbaa` when translucent; `transparent` at alpha 0.
|
|
286
|
+
*
|
|
287
|
+
* @param color - Color in sRGB
|
|
288
|
+
* @returns e.g. `#fff`, `#364153`, `#00000033`
|
|
289
|
+
*/
|
|
290
|
+
export function toHex(color) {
|
|
291
|
+
const alpha = Math.round(color.a * 255);
|
|
292
|
+
if (alpha === 0)
|
|
293
|
+
return 'transparent';
|
|
294
|
+
const rgb = [color.r, color.g, color.b].map(hexByte);
|
|
295
|
+
if (alpha < 255)
|
|
296
|
+
return `#${rgb.join('')}${alpha.toString(16).padStart(2, '0')}`;
|
|
297
|
+
const short = rgb.every((byte) => byte[0] === byte[1]);
|
|
298
|
+
return `#${short ? rgb.map((byte) => byte[0]).join('') : rgb.join('')}`;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* A computed color as short hex ({@link toHex}).
|
|
302
|
+
*
|
|
303
|
+
* @param value - Computed color
|
|
304
|
+
* @returns Hex, or the value unchanged when it is not a color
|
|
305
|
+
*/
|
|
306
|
+
export function hexColor(value) {
|
|
307
|
+
const color = parseColor(value);
|
|
308
|
+
return color ? toHex(color) : value;
|
|
309
|
+
}
|
|
310
|
+
/** Color functions and hex colors inside a longer value (shadows, gradients) */
|
|
311
|
+
const COLOR_IN_VALUE = /\b(?:rgba?|lab|lch|oklab|oklch|color)\([^()]*\)|#[0-9a-f]{3,8}\b/gi;
|
|
312
|
+
/**
|
|
313
|
+
* Replace every color inside a value with its hex ({@link toHex}).
|
|
314
|
+
*
|
|
315
|
+
* @param value - e.g. `rgba(0, 0, 0, 0.2) 0px 1px 2px 0px`
|
|
316
|
+
* @returns e.g. `#00000033 0px 1px 2px 0px`
|
|
317
|
+
*/
|
|
318
|
+
export function hexColorsIn(value) {
|
|
319
|
+
return value.replace(COLOR_IN_VALUE, (match) => hexColor(match));
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Paint a color over another (source-over alpha compositing).
|
|
323
|
+
*
|
|
324
|
+
* @param top - Color on top
|
|
325
|
+
* @param bottom - Color below
|
|
326
|
+
* @returns The color a user sees
|
|
327
|
+
*/
|
|
328
|
+
export function composite(top, bottom) {
|
|
329
|
+
const a = top.a + bottom.a * (1 - top.a);
|
|
330
|
+
if (a === 0)
|
|
331
|
+
return { r: 0, g: 0, b: 0, a: 0 };
|
|
332
|
+
const mix = (t, b) => (t * top.a + b * bottom.a * (1 - top.a)) / a;
|
|
333
|
+
return { r: mix(top.r, bottom.r), g: mix(top.g, bottom.g), b: mix(top.b, bottom.b), a };
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* WCAG relative luminance of an opaque color (clipped to sRGB).
|
|
337
|
+
*
|
|
338
|
+
* @param color - Color
|
|
339
|
+
* @returns Luminance 0-1
|
|
340
|
+
*/
|
|
341
|
+
export function relativeLuminance(color) {
|
|
342
|
+
const linear = [color.r, color.g, color.b].map((channel) => srgbToLinear(Math.min(1, Math.max(0, channel))));
|
|
343
|
+
return 0.2126 * (linear[0] ?? 0) + 0.7152 * (linear[1] ?? 0) + 0.0722 * (linear[2] ?? 0);
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* WCAG contrast ratio of text over an opaque background; translucent text
|
|
347
|
+
* is composited over the background first.
|
|
348
|
+
*
|
|
349
|
+
* @param text - Text color
|
|
350
|
+
* @param background - Opaque background color
|
|
351
|
+
* @returns Ratio 1-21
|
|
352
|
+
*/
|
|
353
|
+
export function contrastRatio(text, background) {
|
|
354
|
+
const lighter = relativeLuminance(composite(text, background));
|
|
355
|
+
const other = relativeLuminance(background);
|
|
356
|
+
const [high, low] = lighter > other ? [lighter, other] : [other, lighter];
|
|
357
|
+
return (high + 0.05) / (low + 0.05);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* WCAG 2 level a contrast ratio reaches. Large text (24px, or 18.66px bold)
|
|
361
|
+
* needs 4.5 for AAA and 3 for AA; other text 7 and 4.5.
|
|
362
|
+
*
|
|
363
|
+
* @param ratio - Contrast ratio
|
|
364
|
+
* @param fontSize - Font size in px
|
|
365
|
+
* @param fontWeight - Font weight
|
|
366
|
+
* @returns Level reached
|
|
367
|
+
*/
|
|
368
|
+
export function contrastLevel(ratio, fontSize, fontWeight) {
|
|
369
|
+
const large = fontSize >= 24 || (fontSize >= 18.66 && fontWeight >= 700);
|
|
370
|
+
if (ratio >= (large ? 4.5 : 7))
|
|
371
|
+
return 'AAA';
|
|
372
|
+
if (ratio >= 4.5)
|
|
373
|
+
return 'AA';
|
|
374
|
+
return large && ratio >= 3 ? 'AA large' : 'fail';
|
|
375
|
+
}
|
|
376
|
+
//# sourceMappingURL=color.js.map
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalization of computed CSS values for `bdg dom inspect`: px as numbers
|
|
3
|
+
* rounded to one decimal (no unit), colors as hex, 1-4 value shorthands,
|
|
4
|
+
* shadow layers, grid tracks with `repeat()`, transforms decomposed from the
|
|
5
|
+
* matrices Chrome computes, and the first font family.
|
|
6
|
+
*/
|
|
7
|
+
/** A length: px as a number, anything else (`auto`, `50%`, `10px 20px`) as text */
|
|
8
|
+
export type CssLength = number | string;
|
|
9
|
+
/**
|
|
10
|
+
* Round to one decimal (and drop `-0`).
|
|
11
|
+
*
|
|
12
|
+
* @param value - Number
|
|
13
|
+
* @returns e.g. 12.5, 0
|
|
14
|
+
*/
|
|
15
|
+
export declare function round1(value: number): number;
|
|
16
|
+
/**
|
|
17
|
+
* A px value as a number.
|
|
18
|
+
*
|
|
19
|
+
* @param value - Computed value, e.g. `12.5px`
|
|
20
|
+
* @returns Rounded number, or undefined when the value is not a single px length
|
|
21
|
+
*/
|
|
22
|
+
export declare function pxNumber(value: string | undefined): number | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* A length as {@link CssLength}.
|
|
25
|
+
*
|
|
26
|
+
* @param value - Computed value
|
|
27
|
+
* @returns Number for px, else the value as text
|
|
28
|
+
*/
|
|
29
|
+
export declare function cssLength(value: string | undefined): CssLength;
|
|
30
|
+
/**
|
|
31
|
+
* A computed value with px lengths as rounded unitless numbers and colors as
|
|
32
|
+
* hex, e.g. `rgba(0, 0, 0, 0.2) 0px 1.25px 2px` → `#00000033 0 1.3 2`.
|
|
33
|
+
*
|
|
34
|
+
* @param value - Computed value
|
|
35
|
+
* @returns Normalized value
|
|
36
|
+
*/
|
|
37
|
+
export declare function normalizeCssValue(value: string): string;
|
|
38
|
+
/**
|
|
39
|
+
* 1-4 values the way CSS shorthands write them (top, right, bottom, left).
|
|
40
|
+
*
|
|
41
|
+
* @param sides - Top, right, bottom, left
|
|
42
|
+
* @returns e.g. `10`, `10 0`, `10 0 1`, `1 2 3 4`
|
|
43
|
+
*/
|
|
44
|
+
export declare function sidesShorthand(sides: readonly CssLength[]): string;
|
|
45
|
+
/**
|
|
46
|
+
* Split a value at commas (or another separator) outside parentheses.
|
|
47
|
+
*
|
|
48
|
+
* @param value - e.g. `rgb(0, 0, 0) 0px 1px, rgb(1, 1, 1) 0px 2px`
|
|
49
|
+
* @param separator - Character to split at
|
|
50
|
+
* @returns Parts, trimmed
|
|
51
|
+
*/
|
|
52
|
+
export declare function splitTopLevel(value: string, separator?: string): string[];
|
|
53
|
+
/**
|
|
54
|
+
* The first family of a `font-family` list, without quotes.
|
|
55
|
+
*
|
|
56
|
+
* @param fontFamily - e.g. `"DM Sans", Arial, sans-serif`
|
|
57
|
+
* @returns e.g. `DM Sans`
|
|
58
|
+
*/
|
|
59
|
+
export declare function firstFontFamily(fontFamily: string): string;
|
|
60
|
+
/** One layer of a `box-shadow` or `text-shadow` */
|
|
61
|
+
export interface ShadowLayer {
|
|
62
|
+
x: number;
|
|
63
|
+
y: number;
|
|
64
|
+
blur: number;
|
|
65
|
+
/** Not for text shadows */
|
|
66
|
+
spread: number;
|
|
67
|
+
color: string;
|
|
68
|
+
inset: boolean;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The visible layers of a shadow: transparent layers and layers with no
|
|
72
|
+
* offset, blur or spread (Tailwind's ring placeholders) are left out.
|
|
73
|
+
*
|
|
74
|
+
* @param value - Computed `box-shadow` or `text-shadow`
|
|
75
|
+
* @returns Layers, outermost first
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseShadows(value: string): ShadowLayer[];
|
|
78
|
+
/**
|
|
79
|
+
* A shadow layer as text, e.g. `#00000033 0 1 2 0`, `inset #000 0 0 0 1`.
|
|
80
|
+
*
|
|
81
|
+
* @param layer - Shadow layer
|
|
82
|
+
* @param withSpread - Include the spread (box shadows)
|
|
83
|
+
* @returns Text
|
|
84
|
+
*/
|
|
85
|
+
export declare function shadowText(layer: ShadowLayer, withSpread?: boolean): string;
|
|
86
|
+
/**
|
|
87
|
+
* Grid tracks with runs of equal sizes written as `repeat()`.
|
|
88
|
+
*
|
|
89
|
+
* @param value - Computed `grid-template-columns`/`rows`, e.g. `100px 100px 100px 50px`
|
|
90
|
+
* @returns e.g. `repeat(3,100) 50`; `none` stays `none`
|
|
91
|
+
*/
|
|
92
|
+
export declare function compressTracks(value: string): string;
|
|
93
|
+
/**
|
|
94
|
+
* A computed transform in readable form: a 2D matrix as
|
|
95
|
+
* translate/rotate/scale, other matrices with rounded numbers.
|
|
96
|
+
*
|
|
97
|
+
* @param value - Computed `transform`, e.g. `matrix(1, 0, 0, 1, 10, 20)`
|
|
98
|
+
* @returns e.g. `translate(10,20)`; `none` when it does nothing
|
|
99
|
+
*/
|
|
100
|
+
export declare function readableTransform(value: string): string;
|
|
101
|
+
/**
|
|
102
|
+
* The file name of a URL in a CSS value: `url("https://x.com/a/b.png?v=1")` → `url(b.png)`;
|
|
103
|
+
* `data:` URLs become `url(data:…)`.
|
|
104
|
+
*
|
|
105
|
+
* @param value - Computed value with `url()`s
|
|
106
|
+
* @returns The value with short URLs
|
|
107
|
+
*/
|
|
108
|
+
export declare function shortUrls(value: string): string;
|
|
109
|
+
//# sourceMappingURL=cssValues.d.ts.map
|