browser-debugger-cli 0.10.0 → 0.12.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/.claude/skills/bdg/SKILL.md +268 -0
- package/README.md +148 -74
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/formInteraction.js +36 -6
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +158 -38
- package/dist/commands/dom/index.js +4 -1
- package/dist/commands/dom/screenshot.js +10 -6
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.js +1 -1
- package/dist/commands/installSkill.d.ts +20 -0
- package/dist/commands/installSkill.js +87 -0
- package/dist/commands/optionBehaviors.js +21 -6
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/optionTypes.d.ts +5 -1
- package/dist/commands/start.d.ts +13 -0
- package/dist/commands/start.js +19 -2
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +5 -0
- package/dist/daemon/session/commandRegistry.js +7 -1
- package/dist/daemon/session/plugins.js +4 -52
- package/dist/daemon.js +7986 -6848
- package/dist/errors/messages.d.ts +50 -4
- package/dist/errors/messages.js +94 -5
- package/dist/index.js +709 -190
- package/dist/ipc/client.d.ts +4 -0
- package/dist/ipc/client.js +8 -0
- package/dist/ipc/protocol/auditTypes.d.ts +129 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +23 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +4 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +71 -8
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +4 -1
- package/dist/runtime/dom/actionEffects.js +8 -4
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +36 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +215 -0
- package/dist/runtime/dom/auditScripts.d.ts +107 -0
- package/dist/runtime/dom/auditScripts.js +112 -0
- package/dist/runtime/dom/elementGeometry.d.ts +8 -2
- package/dist/runtime/dom/elementGeometry.js +24 -8
- package/dist/runtime/dom/elementInfo.d.ts +3 -2
- package/dist/runtime/dom/elementInfo.js +8 -2
- package/dist/runtime/dom/formFillHelpers/fill.js +2 -2
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +88 -23
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +89 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +3 -0
- package/dist/runtime/dom/inspectModel.js +30 -7
- package/dist/runtime/dom/inspectPaintModel.d.ts +48 -22
- package/dist/runtime/dom/inspectPaintModel.js +180 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +85 -12
- package/dist/runtime/dom/inspectScripts.js +314 -28
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +31 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +7 -0
- package/dist/runtime/dom/reactEventHelpers.js +27 -9
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/types.d.ts +12 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +106 -0
- package/dist/ui/formatters/dom.d.ts +1 -1
- package/dist/ui/formatters/dom.js +6 -3
- package/dist/ui/formatters/inspect.js +42 -15
- package/dist/ui/formatters/installSkill.d.ts +11 -0
- package/dist/ui/formatters/installSkill.js +31 -0
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/messages/commands.d.ts +44 -7
- package/dist/ui/messages/commands.js +83 -11
- package/dist/ui/messages/preview.d.ts +6 -0
- package/dist/ui/messages/preview.js +9 -1
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +4 -2
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { Declaration, Resolution } from './inspectCascade.js';
|
|
8
8
|
import type { StyleMap } from './inspectLayoutModel.js';
|
|
9
|
+
import type { InspectHint } from '../../ipc/protocol/inspectTypes.js';
|
|
10
|
+
import type { VariableSetter } from './inspectScripts.js';
|
|
9
11
|
/** A declaration that has no effect */
|
|
10
12
|
export interface CssHint {
|
|
11
13
|
kind: 'inactive' | 'unset-variable' | 'not-inherited';
|
|
@@ -17,6 +19,10 @@ export interface CssHint {
|
|
|
17
19
|
reason: string;
|
|
18
20
|
/** What would make it work */
|
|
19
21
|
fix: string;
|
|
22
|
+
/** The longhands of a shorthand that have no effect, when the others do (`margin-top`, `margin-bottom`) */
|
|
23
|
+
only?: string[];
|
|
24
|
+
/** The custom properties that are not set (`unset-variable`) */
|
|
25
|
+
variables?: string[];
|
|
20
26
|
/** The declaration */
|
|
21
27
|
declaration: Declaration;
|
|
22
28
|
}
|
|
@@ -24,15 +30,19 @@ export interface CssHint {
|
|
|
24
30
|
interface HintContext {
|
|
25
31
|
style: StyleMap;
|
|
26
32
|
parentStyle: StyleMap | undefined;
|
|
33
|
+
/** `display` as the winning declaration wrote it, when it differs from the computed one (blockified) */
|
|
34
|
+
declaredDisplay?: string | undefined;
|
|
27
35
|
/** The element is replaced (img, input, video…) */
|
|
28
36
|
replaced: boolean;
|
|
29
37
|
/** The element is a form control (input, textarea, select, button) */
|
|
30
38
|
formControl?: boolean;
|
|
31
39
|
}
|
|
32
40
|
/**
|
|
33
|
-
* Declarations of the element that have no effect:
|
|
34
|
-
* declaration wins
|
|
35
|
-
*
|
|
41
|
+
* Declarations of the element that have no effect: the longhands a
|
|
42
|
+
* declaration wins that are inactive, unless they only restate a default.
|
|
43
|
+
* When only some of a `margin` shorthand's longhands are inactive
|
|
44
|
+
* (`margin: 8px 12px` on an inline element: the sides still move it), the
|
|
45
|
+
* hint names those; other partly inactive shorthands are not hinted.
|
|
36
46
|
*
|
|
37
47
|
* @param cascade - Resolved properties (winning declarations are checked)
|
|
38
48
|
* @param ctx - Computed styles of the element and its parent
|
|
@@ -49,6 +59,19 @@ export declare function inactiveHints(cascade: Map<string, Resolution>, ctx: Hin
|
|
|
49
59
|
* @returns Hints
|
|
50
60
|
*/
|
|
51
61
|
export declare function undefinedVariableHints(cascade: Map<string, Resolution>, style: StyleMap): CssHint[];
|
|
62
|
+
/**
|
|
63
|
+
* Unset-variable hints told where the page does set the variable, when it
|
|
64
|
+
* does: only in a rule that does not match now (`.btn:hover`: expected in
|
|
65
|
+
* the other state), only in `@keyframes` (while the animation runs), or to
|
|
66
|
+
* `inherit`/`initial`/an empty value in a rule that matches (nothing above
|
|
67
|
+
* gives it a value). A set variable is not a typo, so the "did you mean"
|
|
68
|
+
* suggestion goes.
|
|
69
|
+
*
|
|
70
|
+
* @param hints - Hints of the result
|
|
71
|
+
* @param setters - Where each variable is set, by name ({@link VARIABLE_SETTERS_JS})
|
|
72
|
+
* @returns Hints
|
|
73
|
+
*/
|
|
74
|
+
export declare function explainUnsetVariables(hints: readonly InspectHint[], setters: Readonly<Record<string, VariableSetter>>): InspectHint[];
|
|
52
75
|
/**
|
|
53
76
|
* A form control drawn in the browser's font while its parent uses another:
|
|
54
77
|
* controls do not inherit the font unless told to (a common oversight).
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
* checked against authored declarations only (never the browser's own
|
|
5
5
|
* styles), and `var()` references to custom properties that are not set.
|
|
6
6
|
*/
|
|
7
|
+
import { isDefaultValue } from './inspectAllStyles.js';
|
|
7
8
|
import { unsetVariables } from './inspectVariables.js';
|
|
8
9
|
import { findSimilarNames } from '../../utils/suggestions.js';
|
|
9
10
|
const display = (style) => style?.['display'] ?? 'inline';
|
|
10
11
|
const isFlex = (value) => /(^|-)flex$/.test(value);
|
|
11
12
|
const isGrid = (value) => /(^|-)grid$/.test(value);
|
|
12
13
|
const isFlexOrGrid = (value) => isFlex(value) || isGrid(value);
|
|
14
|
+
const horizontal = (style) => (style['writing-mode'] ?? 'horizontal-tb') === 'horizontal-tb';
|
|
13
15
|
const isMulticol = (style) => (style['column-count'] ?? 'auto') !== 'auto' || (style['column-width'] ?? 'auto') !== 'auto';
|
|
14
16
|
/** The checks, in DevTools' wording */
|
|
15
17
|
const RULES = [
|
|
@@ -120,7 +122,7 @@ const RULES = [
|
|
|
120
122
|
},
|
|
121
123
|
{
|
|
122
124
|
properties: ['margin-top', 'margin-bottom'],
|
|
123
|
-
inactive: ({ style, replaced }) => display(style) === 'inline' && !replaced
|
|
125
|
+
inactive: ({ style, replaced }) => display(style) === 'inline' && !replaced && horizontal(style)
|
|
124
126
|
? {
|
|
125
127
|
reason: 'display is inline (vertical margins do not move it)',
|
|
126
128
|
fix: 'use display: inline-block or block',
|
|
@@ -129,10 +131,10 @@ const RULES = [
|
|
|
129
131
|
},
|
|
130
132
|
{
|
|
131
133
|
properties: ['vertical-align'],
|
|
132
|
-
inactive: ({ style }) => /^(inline|
|
|
134
|
+
inactive: ({ style, parentStyle, declaredDisplay }) => /^(inline|table-cell|ruby)/.test(display(style))
|
|
133
135
|
? undefined
|
|
134
136
|
: {
|
|
135
|
-
reason: `display is ${display(style)}`,
|
|
137
|
+
reason: `display is ${display(style)}${blockified(declaredDisplay, style, parentStyle)}`,
|
|
136
138
|
fix: 'vertical-align works on inline and table-cell boxes; use flex alignment instead',
|
|
137
139
|
},
|
|
138
140
|
},
|
|
@@ -165,9 +167,58 @@ const RULES = [
|
|
|
165
167
|
},
|
|
166
168
|
];
|
|
167
169
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
170
|
+
* Why a declared inline-level display computes to a block-level one: the
|
|
171
|
+
* element is a flex or grid item, floated or positioned (CSS blockification).
|
|
172
|
+
*
|
|
173
|
+
* @param declared - `display` as written, when it differs from the computed one
|
|
174
|
+
* @param style - Computed styles
|
|
175
|
+
* @param parentStyle - Computed styles of the layout parent
|
|
176
|
+
* @returns ` (inline-flex blockified: a flex item)`, or empty
|
|
177
|
+
*/
|
|
178
|
+
function blockified(declared, style, parentStyle) {
|
|
179
|
+
if (!declared || declared === display(style) || !/^(inline|table-|ruby)/.test(declared))
|
|
180
|
+
return '';
|
|
181
|
+
const parent = display(parentStyle);
|
|
182
|
+
const cause = isFlex(parent)
|
|
183
|
+
? 'a flex item'
|
|
184
|
+
: isGrid(parent)
|
|
185
|
+
? 'a grid item'
|
|
186
|
+
: (style['float'] ?? 'none') !== 'none'
|
|
187
|
+
? 'floated'
|
|
188
|
+
: /^(absolute|fixed)$/.test(style['position'] ?? '')
|
|
189
|
+
? 'positioned'
|
|
190
|
+
: 'by its context';
|
|
191
|
+
return ` (${declared} blockified: ${cause})`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Shorthands whose inactive longhands are worth a hint while the others
|
|
195
|
+
* work: vertical margins of an inline element are a common mistake, while
|
|
196
|
+
* `gap` in a multi-column block or `grid-template-areas: none` are not
|
|
197
|
+
*/
|
|
198
|
+
const PARTIAL_SHORTHANDS = new Set(['margin', 'margin-block']);
|
|
199
|
+
/** Values that change nothing wherever they are written */
|
|
200
|
+
const NO_OP_KEYWORDS = new Set(['initial', 'unset', 'revert', 'revert-layer']);
|
|
201
|
+
/**
|
|
202
|
+
* Whether a declared longhand value is its default, so writing it has no
|
|
203
|
+
* effect in any case (`vertical-align: baseline`, `margin-top: 0` from a
|
|
204
|
+
* reset): such declarations are not worth a hint.
|
|
205
|
+
*
|
|
206
|
+
* @param declaration - Declaration of a longhand
|
|
207
|
+
* @returns True for a default value
|
|
208
|
+
*/
|
|
209
|
+
function isNoOp(declaration) {
|
|
210
|
+
const value = declaration.value.trim().toLowerCase();
|
|
211
|
+
if (NO_OP_KEYWORDS.has(value))
|
|
212
|
+
return true;
|
|
213
|
+
const zero = /^[+-]?0*\.?0+([a-z]+|%)?$/.test(value) ? '0px' : value;
|
|
214
|
+
return isDefaultValue(declaration.property, value) || isDefaultValue(declaration.property, zero);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Declarations of the element that have no effect: the longhands a
|
|
218
|
+
* declaration wins that are inactive, unless they only restate a default.
|
|
219
|
+
* When only some of a `margin` shorthand's longhands are inactive
|
|
220
|
+
* (`margin: 8px 12px` on an inline element: the sides still move it), the
|
|
221
|
+
* hint names those; other partly inactive shorthands are not hinted.
|
|
171
222
|
*
|
|
172
223
|
* @param cascade - Resolved properties (winning declarations are checked)
|
|
173
224
|
* @param ctx - Computed styles of the element and its parent
|
|
@@ -176,16 +227,21 @@ const RULES = [
|
|
|
176
227
|
export function inactiveHints(cascade, ctx) {
|
|
177
228
|
return [...ownWinnersByDeclaration(cascade).values()].flatMap((winners) => {
|
|
178
229
|
const checks = winners.map((declaration) => RULES.find((rule) => rule.properties.includes(declaration.property))?.inactive(ctx));
|
|
230
|
+
const inactive = winners.filter((declaration, i) => checks[i] && !isNoOp(declaration));
|
|
179
231
|
const [first] = winners;
|
|
180
|
-
const
|
|
181
|
-
|
|
232
|
+
const reason = checks.find(Boolean);
|
|
233
|
+
const partial = checks.some((check) => !check);
|
|
234
|
+
if (!first || !reason || inactive.length === 0)
|
|
235
|
+
return [];
|
|
236
|
+
if (partial && !PARTIAL_SHORTHANDS.has(first.via ?? ''))
|
|
182
237
|
return [];
|
|
183
238
|
return [
|
|
184
239
|
{
|
|
185
240
|
kind: 'inactive',
|
|
186
241
|
property: first.via ?? first.property,
|
|
187
242
|
value: first.written ?? first.value,
|
|
188
|
-
...
|
|
243
|
+
...reason,
|
|
244
|
+
...(partial && { only: inactive.map((declaration) => declaration.property) }),
|
|
189
245
|
declaration: first,
|
|
190
246
|
},
|
|
191
247
|
];
|
|
@@ -243,11 +299,71 @@ export function undefinedVariableHints(cascade, style) {
|
|
|
243
299
|
value,
|
|
244
300
|
reason: `${missing.join(', ')} is not set`,
|
|
245
301
|
fix: variableFix(missing[0] ?? '', style),
|
|
302
|
+
variables: missing,
|
|
246
303
|
declaration,
|
|
247
304
|
});
|
|
248
305
|
}
|
|
249
306
|
return hints;
|
|
250
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Unset-variable hints told where the page does set the variable, when it
|
|
310
|
+
* does: only in a rule that does not match now (`.btn:hover`: expected in
|
|
311
|
+
* the other state), only in `@keyframes` (while the animation runs), or to
|
|
312
|
+
* `inherit`/`initial`/an empty value in a rule that matches (nothing above
|
|
313
|
+
* gives it a value). A set variable is not a typo, so the "did you mean"
|
|
314
|
+
* suggestion goes.
|
|
315
|
+
*
|
|
316
|
+
* @param hints - Hints of the result
|
|
317
|
+
* @param setters - Where each variable is set, by name ({@link VARIABLE_SETTERS_JS})
|
|
318
|
+
* @returns Hints
|
|
319
|
+
*/
|
|
320
|
+
export function explainUnsetVariables(hints, setters) {
|
|
321
|
+
return hints.map((hint) => {
|
|
322
|
+
const variables = hint.variables ?? [];
|
|
323
|
+
const name = variables.find((variable) => setters[variable]);
|
|
324
|
+
const setter = name ? setters[name] : undefined;
|
|
325
|
+
if (!name || !setter)
|
|
326
|
+
return hint;
|
|
327
|
+
const explained = setterExplanation(name, setter);
|
|
328
|
+
if (variables.every((variable) => setters[variable]))
|
|
329
|
+
return { ...hint, ...explained };
|
|
330
|
+
return { ...hint, reason: `${hint.reason}; ${explained.reason}` };
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Why a variable that the page sets is unset here, and the fix.
|
|
335
|
+
*
|
|
336
|
+
* @param name - Variable
|
|
337
|
+
* @param setter - Where it is set
|
|
338
|
+
* @returns Reason and fix
|
|
339
|
+
*/
|
|
340
|
+
function setterExplanation(name, setter) {
|
|
341
|
+
const fallback = 'give var() a fallback';
|
|
342
|
+
if (setter.keyframes) {
|
|
343
|
+
return {
|
|
344
|
+
reason: `${name} is set only in @keyframes ${setter.keyframes} (while it runs)`,
|
|
345
|
+
fix: `${fallback} for when the animation is not running`,
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (setter.condition) {
|
|
349
|
+
return {
|
|
350
|
+
reason: `${name} is set only by ${setter.selector} under ${setter.condition}, which does not apply now`,
|
|
351
|
+
fix: `expected under other conditions; otherwise ${fallback}`,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (setter.matches) {
|
|
355
|
+
return {
|
|
356
|
+
reason: `${name} is set to ${setter.value === '' ? 'an empty value' : setter.value} by ${setter.selector}, and nothing above gives it a value`,
|
|
357
|
+
fix: `set ${name} on an ancestor, or ${fallback}`,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
reason: setter.matches === null
|
|
362
|
+
? `${name} is set only by ${setter.selector} (whether it applies here is not known)`
|
|
363
|
+
: `${name} is set only by ${setter.selector}, which does not match now`,
|
|
364
|
+
fix: `expected in that state; otherwise ${fallback}`,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
251
367
|
/**
|
|
252
368
|
* How to fix a `var()` of an unset custom property, naming a similar one
|
|
253
369
|
* that is set (a typo or a renamed token).
|
|
@@ -16,7 +16,10 @@ export interface InspectSources {
|
|
|
16
16
|
raw: RawInspect;
|
|
17
17
|
style: StyleMap;
|
|
18
18
|
parentStyle?: StyleMap;
|
|
19
|
+
/** Computed styles of the descendant that draws the text, when it is not the element */
|
|
20
|
+
holderStyle?: StyleMap;
|
|
19
21
|
pseudo: PseudoSource[];
|
|
22
|
+
/** Platform fonts of the text (of the descendant that draws it, when there is one) */
|
|
20
23
|
fonts: PlatformFont[];
|
|
21
24
|
/** Border box size from `DOM.getBoxModel`; absent when the element has no box */
|
|
22
25
|
size?: {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { allStyles } from './inspectAllStyles.js';
|
|
9
9
|
import { buildBox, buildLayout } from './inspectLayoutModel.js';
|
|
10
|
-
import { buildEffects, buildFills, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
|
|
10
|
+
import { buildEffects, buildFills, buildSvgPaint, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
|
|
11
11
|
import { buildTree, rowText } from './inspectTree.js';
|
|
12
12
|
import { hexColor, relativeLuminance } from '../../utils/color.js';
|
|
13
13
|
import { normalizeCssValue, round1 } from '../../utils/cssValues.js';
|
|
@@ -50,6 +50,30 @@ export function visibilityOf(layout, rendered) {
|
|
|
50
50
|
...(layout.coverTransparent && { coverTransparent: true }),
|
|
51
51
|
};
|
|
52
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* The header rectangle: border box size (and the box it covers on screen
|
|
55
|
+
* when a transform turns it), and the page position, or the
|
|
56
|
+
* viewport position of an element fixed to the viewport (it or a container
|
|
57
|
+
* is `position: fixed`: its page position changes with the scroll).
|
|
58
|
+
*
|
|
59
|
+
* @param layout - Layout measurements
|
|
60
|
+
* @param size - Border box size
|
|
61
|
+
* @param style - Computed styles (transforms)
|
|
62
|
+
* @returns Rectangle
|
|
63
|
+
*/
|
|
64
|
+
function headerRect(layout, size, style) {
|
|
65
|
+
const { width, height } = layout.bounds;
|
|
66
|
+
const transformed = ['transform', 'rotate', 'scale'].some((name) => (style[name] ?? 'none') !== 'none');
|
|
67
|
+
const turned = transformed && (Math.abs(width - size.w) > 1 || Math.abs(height - size.h) > 1);
|
|
68
|
+
const box = {
|
|
69
|
+
w: round1(size.w),
|
|
70
|
+
h: round1(size.h),
|
|
71
|
+
...(turned && { screen: { w: width, h: height } }),
|
|
72
|
+
};
|
|
73
|
+
return layout.fixed
|
|
74
|
+
? { x: layout.viewport.x, y: layout.viewport.y, ...box, in: 'viewport' }
|
|
75
|
+
: { x: layout.bounds.x, y: layout.bounds.y, ...box };
|
|
76
|
+
}
|
|
53
77
|
/** Below this relative luminance a page background counts as dark */
|
|
54
78
|
const DARK_LUMINANCE = 0.18;
|
|
55
79
|
/**
|
|
@@ -72,10 +96,7 @@ function header(sources, request) {
|
|
|
72
96
|
...(content && { content }),
|
|
73
97
|
...(raw.placeholder && { placeholder: rowText(raw.placeholder) }),
|
|
74
98
|
...(raw.context && { context: raw.context }),
|
|
75
|
-
...(size &&
|
|
76
|
-
layout && {
|
|
77
|
-
rect: { x: layout.bounds.x, y: layout.bounds.y, w: round1(size.w), h: round1(size.h) },
|
|
78
|
-
}),
|
|
99
|
+
...(size && layout && { rect: headerRect(layout, size, sources.style) }),
|
|
79
100
|
visibility: visibilityOf(layout, size !== undefined),
|
|
80
101
|
...(sources.colorScheme && { colorScheme: sources.colorScheme }),
|
|
81
102
|
...(sources.colorScheme === 'dark' && pageLooksDark(raw) && { theme: 'dark' }),
|
|
@@ -104,8 +125,9 @@ function pageLooksDark(raw) {
|
|
|
104
125
|
*/
|
|
105
126
|
function groups(sources) {
|
|
106
127
|
const { style, parentStyle, raw } = sources;
|
|
107
|
-
const text = buildText(style, parentStyle, raw, sources.fonts);
|
|
128
|
+
const text = buildText({ style, parentStyle, holderStyle: sources.holderStyle }, raw, sources.fonts);
|
|
108
129
|
const fills = buildFills(style);
|
|
130
|
+
const paint = buildSvgPaint(style, raw);
|
|
109
131
|
const strokes = buildStrokes(style);
|
|
110
132
|
const radius = buildRadius(style);
|
|
111
133
|
const outline = buildOutline(style);
|
|
@@ -120,6 +142,7 @@ function groups(sources) {
|
|
|
120
142
|
layout: buildLayout(style, parentStyle, raw),
|
|
121
143
|
...(text && { text }),
|
|
122
144
|
...(fills.length > 0 && { fills }),
|
|
145
|
+
...(paint && { paint }),
|
|
123
146
|
...(opacity !== 1 && { opacity }),
|
|
124
147
|
...(blend && blend !== 'normal' && { blend }),
|
|
125
148
|
...(strokes.length > 0 && { strokes }),
|
|
@@ -156,7 +179,7 @@ function treeFields(raw, limit) {
|
|
|
156
179
|
* @returns Properties and values
|
|
157
180
|
*/
|
|
158
181
|
function allFields(sources) {
|
|
159
|
-
const all = allStyles(sources.style, sources.raw.svg === true);
|
|
182
|
+
const all = allStyles(sources.style, sources.raw.svg === true, sources.raw.formControl);
|
|
160
183
|
for (const pseudo of sources.pseudo) {
|
|
161
184
|
const content = pseudo.style['content'];
|
|
162
185
|
if (content && content !== 'none' && content !== 'normal') {
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* backgrounds of the element and its ancestors composited over the page
|
|
8
8
|
* canvas).
|
|
9
9
|
*/
|
|
10
|
-
import type { InspectContrast, InspectEffect, InspectFill, InspectFx, InspectPseudo, InspectState, InspectStroke, InspectText, Sides } from '../../ipc/protocol/inspectTypes.js';
|
|
10
|
+
import type { InspectContrast, InspectEffect, InspectFill, InspectFx, InspectPseudo, InspectState, InspectStroke, InspectSvgPaint, InspectText, Sides } from '../../ipc/protocol/inspectTypes.js';
|
|
11
11
|
import type { StyleMap } from './inspectLayoutModel.js';
|
|
12
12
|
import type { RawBackground, RawInspect } from './inspectScripts.js';
|
|
13
13
|
import { type Rgba } from '../../utils/color.js';
|
|
@@ -18,20 +18,24 @@ export interface PlatformFont {
|
|
|
18
18
|
glyphCount: number;
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
* The font the text was rendered with
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* The font the text was rendered with and whether it is a web font. For a
|
|
22
|
+
* generic first family, the font it resolved to (`resolved`). Otherwise
|
|
23
|
+
* `rendered` only when it is a fallback: not a face of the first family
|
|
24
|
+
* (`DM Sans 9pt` is a face of `DM Sans`, `Liberation Sans` is not one of
|
|
25
|
+
* `Arial`), and the first family is not a web font the page loaded (whose
|
|
26
|
+
* file may give any internal name: `Copyright Klim Type Foundry`, or the
|
|
27
|
+
* local font a `src: local()` points at). Names that are not readable
|
|
28
|
+
* (sites that scramble a web font's internal name) are left out.
|
|
26
29
|
*
|
|
27
30
|
* @param family - First family of `font-family`
|
|
28
31
|
* @param fonts - Platform fonts of the text
|
|
29
|
-
* @
|
|
32
|
+
* @param familyLoaded - The first family is a loaded web font
|
|
33
|
+
* @returns `rendered`, `resolved` and `webfont` fields
|
|
30
34
|
*/
|
|
31
|
-
export declare function renderedFont(family: string, fonts: readonly PlatformFont[]): Pick<InspectText, 'rendered' | 'webfont'>;
|
|
35
|
+
export declare function renderedFont(family: string, fonts: readonly PlatformFont[], familyLoaded?: boolean): Pick<InspectText, 'rendered' | 'resolved' | 'webfont'>;
|
|
32
36
|
/**
|
|
33
37
|
* The background behind the element's text: its own and its ancestors'
|
|
34
|
-
* backgrounds composited
|
|
38
|
+
* backgrounds composited with their opacity over the page canvas.
|
|
35
39
|
*
|
|
36
40
|
* @param backgrounds - Backgrounds, the element's own first
|
|
37
41
|
* @param canvasDark - The page canvas is dark
|
|
@@ -43,35 +47,57 @@ export declare function effectiveBackground(backgrounds: readonly RawBackground[
|
|
|
43
47
|
overImage: boolean;
|
|
44
48
|
};
|
|
45
49
|
/**
|
|
46
|
-
* Contrast of the text color with the background behind it
|
|
50
|
+
* Contrast of the text color with the background behind it, both painted
|
|
51
|
+
* as the browser composites them ({@link paintOver}), with what makes the
|
|
52
|
+
* number approximate (blend modes, filters, content behind or on top).
|
|
47
53
|
*
|
|
48
54
|
* @param style - Computed styles (color, font size and weight)
|
|
49
|
-
* @param raw - Backgrounds
|
|
55
|
+
* @param raw - Backgrounds, the page canvas, opacity and paint risks
|
|
50
56
|
* @returns Ratio (rounded down to 2 decimals), level and background
|
|
51
57
|
*/
|
|
52
|
-
export declare function textContrast(style: StyleMap, raw: Pick<RawInspect, 'backgrounds' | 'canvasDark' | 'opacity'>): InspectContrast | undefined;
|
|
58
|
+
export declare function textContrast(style: StyleMap, raw: Pick<RawInspect, 'backgrounds' | 'canvasDark' | 'opacity' | 'paintRisks'>): InspectContrast | undefined;
|
|
59
|
+
/** Computed styles the text group reads */
|
|
60
|
+
export interface TextStyles {
|
|
61
|
+
/** The element's */
|
|
62
|
+
style: StyleMap;
|
|
63
|
+
/** Its layout parent's */
|
|
64
|
+
parentStyle?: StyleMap | undefined;
|
|
65
|
+
/** The descendant's that draws most of its text, when it is not the element */
|
|
66
|
+
holderStyle?: StyleMap | undefined;
|
|
67
|
+
}
|
|
53
68
|
/**
|
|
54
|
-
* The text group. Elements with text (or
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* differs
|
|
69
|
+
* The text group. Elements with text (or text fields) get the full group,
|
|
70
|
+
* read from whatever draws the text (the element, or the descendant with
|
|
71
|
+
* most of it, named in `holder`): font with the rendered font, size, color,
|
|
72
|
+
* alignment, contrast (not for text no one can see: not rendered,
|
|
73
|
+
* `opacity: 0` on it or an ancestor, `visibility: hidden`) and the
|
|
74
|
+
* non-default extras. Containers get only what differs from their parent,
|
|
75
|
+
* with the font their text was rendered in when that text has the
|
|
76
|
+
* container's family. Nothing for an element without text (an icon button,
|
|
77
|
+
* a checkbox).
|
|
60
78
|
*
|
|
61
|
-
* @param
|
|
62
|
-
* @param parentStyle - Computed styles of the layout parent
|
|
79
|
+
* @param styles - Computed styles of the element, its parent and its text holder
|
|
63
80
|
* @param raw - Page-side measurements
|
|
64
81
|
* @param fonts - Platform fonts of the text
|
|
65
82
|
* @returns Text group, or undefined when there is nothing to say
|
|
66
83
|
*/
|
|
67
|
-
export declare function buildText(
|
|
84
|
+
export declare function buildText(styles: TextStyles, raw: Pick<RawInspect, 'textual' | 'textHolder' | 'rendered' | 'hasText' | 'familyLoaded' | 'truncated' | 'backgrounds' | 'canvasDark' | 'opacity' | 'paintRisks'>, fonts: readonly PlatformFont[]): InspectText | undefined;
|
|
68
85
|
/**
|
|
69
|
-
* Background layers: images and gradients (top first),
|
|
86
|
+
* Background layers: images and gradients (top first), each with its own
|
|
87
|
+
* size and position when they are not the defaults, then the color.
|
|
70
88
|
*
|
|
71
89
|
* @param style - Computed styles
|
|
72
90
|
* @returns Fills
|
|
73
91
|
*/
|
|
74
92
|
export declare function buildFills(style: StyleMap): InspectFill[];
|
|
93
|
+
/**
|
|
94
|
+
* How an SVG element is painted: its `fill` and `stroke` (with the width).
|
|
95
|
+
*
|
|
96
|
+
* @param style - Computed styles
|
|
97
|
+
* @param raw - Whether it is an SVG element
|
|
98
|
+
* @returns Paint, or undefined for an HTML element
|
|
99
|
+
*/
|
|
100
|
+
export declare function buildSvgPaint(style: StyleMap, raw: Pick<RawInspect, 'svg'>): InspectSvgPaint | undefined;
|
|
75
101
|
/**
|
|
76
102
|
* Border sides that show (a style other than none/hidden and a width): one
|
|
77
103
|
* `all` stroke when the four are equal.
|