browser-debugger-cli 0.11.0 → 0.13.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 +4 -4
- package/README.md +143 -79
- package/dist/commands/cdp.d.ts +22 -1
- package/dist/commands/cdp.js +100 -43
- package/dist/commands/console.d.ts +12 -0
- package/dist/commands/console.js +62 -12
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/DomElementResolver.d.ts +3 -1
- package/dist/commands/dom/DomElementResolver.js +10 -3
- package/dist/commands/dom/a11y.js +3 -2
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/eval.d.ts +3 -2
- package/dist/commands/dom/eval.js +11 -5
- package/dist/commands/dom/form.js +10 -9
- package/dist/commands/dom/formInteraction.js +42 -11
- package/dist/commands/dom/get.js +8 -8
- package/dist/commands/dom/helpers/index.d.ts +1 -1
- package/dist/commands/dom/helpers/index.js +1 -1
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/query.d.ts +27 -3
- package/dist/commands/dom/helpers/query.js +152 -64
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +169 -49
- package/dist/commands/dom/index.js +7 -2
- package/dist/commands/dom/query.d.ts +19 -2
- package/dist/commands/dom/query.js +37 -6
- package/dist/commands/dom/screenshot.js +12 -7
- package/dist/commands/dom/semanticUtils.d.ts +3 -2
- package/dist/commands/dom/semanticUtils.js +40 -9
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.d.ts +82 -19
- package/dist/commands/helpJson.js +112 -41
- package/dist/commands/helpTopic.d.ts +16 -1
- package/dist/commands/helpTopic.js +59 -1
- package/dist/commands/installSkill.d.ts +15 -5
- package/dist/commands/installSkill.js +86 -16
- package/dist/commands/network/list.js +22 -12
- package/dist/commands/optionBehaviors.js +53 -16
- 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/daemonErrorHandler.d.ts +5 -2
- package/dist/commands/shared/daemonErrorHandler.js +20 -9
- package/dist/commands/shared/dataFetcher.d.ts +12 -4
- package/dist/commands/shared/dataFetcher.js +12 -4
- package/dist/commands/shared/followMode.d.ts +9 -1
- package/dist/commands/shared/followMode.js +22 -4
- package/dist/commands/shared/optionTypes.d.ts +9 -2
- package/dist/commands/shared/outputFile.js +6 -1
- package/dist/commands/start.d.ts +20 -5
- package/dist/commands/start.js +84 -23
- package/dist/commands/stop.d.ts +11 -0
- package/dist/commands/stop.js +24 -1
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +2 -0
- package/dist/connection/cdp.d.ts +7 -0
- package/dist/connection/cdp.js +9 -0
- package/dist/connection/launcher.js +3 -2
- package/dist/daemon/SessionController.js +6 -1
- package/dist/daemon/launcher.d.ts +3 -2
- package/dist/daemon/launcher.js +47 -3
- package/dist/daemon/session/Session.d.ts +4 -1
- package/dist/daemon/session/Session.js +33 -2
- package/dist/daemon/session/TelemetryStore.d.ts +8 -1
- package/dist/daemon/session/TelemetryStore.js +13 -1
- package/dist/daemon/session/commandRegistry.js +36 -14
- package/dist/daemon/session/interactions.d.ts +2 -1
- package/dist/daemon/session/interactions.js +13 -1
- package/dist/daemon/session/plugins.js +19 -53
- package/dist/daemon/session/teardown.js +1 -1
- package/dist/daemon.js +9234 -7222
- package/dist/errors/messages.d.ts +88 -15
- package/dist/errors/messages.js +177 -27
- package/dist/index.js +19322 -13961
- package/dist/ipc/client.d.ts +22 -2
- package/dist/ipc/client.js +34 -5
- package/dist/ipc/protocol/auditTypes.d.ts +135 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +35 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +16 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +73 -8
- package/dist/ipc/session/types.d.ts +2 -0
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +9 -2
- package/dist/runtime/dom/actionEffects.js +30 -14
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +37 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +220 -0
- package/dist/runtime/dom/auditScripts.d.ts +113 -0
- package/dist/runtime/dom/auditScripts.js +148 -0
- package/dist/runtime/dom/elementGeometry.d.ts +16 -3
- package/dist/runtime/dom/elementGeometry.js +49 -10
- package/dist/runtime/dom/elementInfo.d.ts +74 -17
- package/dist/runtime/dom/elementInfo.js +187 -34
- package/dist/runtime/dom/evalHelpers.d.ts +12 -2
- package/dist/runtime/dom/evalHelpers.js +67 -7
- package/dist/runtime/dom/formDiscovery.d.ts +6 -2
- package/dist/runtime/dom/formDiscovery.js +20 -3
- package/dist/runtime/dom/formFillHelpers/fill.js +8 -12
- package/dist/runtime/dom/formFillHelpers/pressKey.js +2 -2
- package/dist/runtime/dom/formFillHelpers/shared.d.ts +16 -10
- package/dist/runtime/dom/formFillHelpers/shared.js +19 -52
- package/dist/runtime/dom/formSubmitHelpers.js +4 -3
- package/dist/runtime/dom/frameLayout.js +1 -0
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +92 -28
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +90 -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 +5 -1
- package/dist/runtime/dom/inspectModel.js +37 -10
- package/dist/runtime/dom/inspectPaintModel.d.ts +50 -22
- package/dist/runtime/dom/inspectPaintModel.js +182 -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 +112 -12
- package/dist/runtime/dom/inspectScripts.js +357 -32
- 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 +40 -16
- package/dist/runtime/dom/reactEventHelpers.d.ts +21 -4
- package/dist/runtime/dom/reactEventHelpers.js +90 -36
- package/dist/runtime/dom/targetNode.d.ts +18 -5
- package/dist/runtime/dom/targetNode.js +268 -8
- package/dist/runtime/dom/wait.js +2 -1
- package/dist/runtime/page/bdgWorld.d.ts +57 -0
- package/dist/runtime/page/bdgWorld.js +180 -0
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/replacedBuiltins.d.ts +28 -0
- package/dist/runtime/page/replacedBuiltins.js +136 -0
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/session/QueryCacheManager.d.ts +4 -1
- package/dist/session/QueryCacheManager.js +5 -2
- package/dist/session/chrome.d.ts +4 -1
- package/dist/session/chrome.js +7 -1
- package/dist/session/cleanup/staleSession.d.ts +21 -4
- package/dist/session/cleanup/staleSession.js +79 -9
- package/dist/session/cleanup/userCommands.d.ts +4 -1
- package/dist/session/cleanup/userCommands.js +10 -5
- package/dist/session/daemonSocket.d.ts +10 -0
- package/dist/session/daemonSocket.js +22 -0
- package/dist/session/lastSession.d.ts +6 -3
- package/dist/session/lastSession.js +11 -5
- package/dist/session/paths.d.ts +3 -1
- package/dist/session/paths.js +5 -5
- package/dist/session/portClaims.js +4 -3
- package/dist/session/sessionList.d.ts +13 -5
- package/dist/session/sessionList.js +31 -7
- package/dist/telemetry/a11y.js +2 -2
- package/dist/telemetry/console.d.ts +2 -1
- package/dist/telemetry/console.js +30 -21
- package/dist/telemetry/pageCrash.d.ts +26 -0
- package/dist/telemetry/pageCrash.js +53 -0
- package/dist/types.d.ts +20 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +115 -0
- package/dist/ui/formatters/cdp.d.ts +138 -0
- package/dist/ui/formatters/cdp.js +131 -0
- package/dist/ui/formatters/console/chronological.js +3 -1
- package/dist/ui/formatters/console/follow.d.ts +2 -1
- package/dist/ui/formatters/console/follow.js +2 -2
- package/dist/ui/formatters/console/json.d.ts +2 -2
- package/dist/ui/formatters/console/json.js +11 -5
- package/dist/ui/formatters/console/shared.d.ts +30 -0
- package/dist/ui/formatters/console/shared.js +16 -0
- package/dist/ui/formatters/console/summarize.d.ts +9 -2
- package/dist/ui/formatters/console/summarize.js +40 -9
- package/dist/ui/formatters/console.d.ts +2 -1
- package/dist/ui/formatters/console.js +7 -5
- package/dist/ui/formatters/details.js +3 -1
- package/dist/ui/formatters/dom.d.ts +2 -2
- package/dist/ui/formatters/dom.js +10 -8
- package/dist/ui/formatters/helpFormatters.js +1 -1
- package/dist/ui/formatters/inspect.js +50 -17
- package/dist/ui/formatters/installSkill.d.ts +9 -1
- package/dist/ui/formatters/installSkill.js +32 -6
- package/dist/ui/formatters/layout.js +2 -1
- package/dist/ui/formatters/networkList.d.ts +1 -1
- package/dist/ui/formatters/networkList.js +1 -2
- package/dist/ui/formatters/preview.d.ts +2 -0
- package/dist/ui/formatters/preview.js +17 -7
- package/dist/ui/formatters/sessions.d.ts +2 -2
- package/dist/ui/formatters/sessions.js +9 -2
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/logging/logger.d.ts +1 -1
- package/dist/ui/messages/commands.d.ts +168 -11
- package/dist/ui/messages/commands.js +245 -18
- package/dist/ui/messages/consoleMessages.d.ts +24 -0
- package/dist/ui/messages/consoleMessages.js +32 -0
- package/dist/ui/messages/preview.d.ts +12 -0
- package/dist/ui/messages/preview.js +18 -2
- package/dist/ui/messages/session.d.ts +13 -2
- package/dist/ui/messages/session.js +22 -3
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/directories.d.ts +34 -0
- package/dist/utils/directories.js +88 -0
- package/dist/utils/display.d.ts +16 -0
- package/dist/utils/display.js +42 -0
- package/dist/utils/exitCodes.d.ts +1 -0
- package/dist/utils/exitCodes.js +6 -0
- package/dist/utils/process.d.ts +12 -0
- package/dist/utils/process.js +25 -0
- 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 +3 -2
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* property, sides set by one declaration merged into the shorthand) and
|
|
5
5
|
* `--why` (every declaration of one property).
|
|
6
6
|
*/
|
|
7
|
-
import { normalizeProperty } from './inspectAllStyles.js';
|
|
8
|
-
import { physicalName, ownProperties, resolveCascade, shorthandLonghands, } from './inspectCascade.js';
|
|
7
|
+
import { collapsedValue, normalizeProperty } from './inspectAllStyles.js';
|
|
8
|
+
import { physicalName, ownProperties, ruleField, resolveCascade, shorthandLonghands, } from './inspectCascade.js';
|
|
9
9
|
import { formControlFontHints, inactiveHints, undefinedVariableHints, } from './inspectHints.js';
|
|
10
|
+
import { substituteVariables } from './inspectVariables.js';
|
|
10
11
|
import { buildWhy } from './inspectWhyModel.js';
|
|
11
12
|
const SIDES = ['top', 'right', 'bottom', 'left'];
|
|
12
13
|
const sides = (pattern) => SIDES.map((side) => pattern.replace('{side}', side));
|
|
@@ -33,6 +34,11 @@ const RULE_GROUPS = [
|
|
|
33
34
|
},
|
|
34
35
|
{ longhands: ['background-color'] },
|
|
35
36
|
{ longhands: ['background-image'] },
|
|
37
|
+
{ longhands: ['background-size'] },
|
|
38
|
+
{
|
|
39
|
+
shorthand: 'background-position',
|
|
40
|
+
longhands: ['background-position-x', 'background-position-y'],
|
|
41
|
+
},
|
|
36
42
|
{ longhands: ['color'] },
|
|
37
43
|
{ longhands: ['font-family'] },
|
|
38
44
|
{ longhands: ['font-size'] },
|
|
@@ -63,8 +69,10 @@ const RULE_GROUPS = [
|
|
|
63
69
|
* @returns `hints` (empty when checked and nothing found), `rules` and `why`
|
|
64
70
|
*/
|
|
65
71
|
export function buildCascadeFields(input) {
|
|
66
|
-
const ruleGroups = input.rules
|
|
67
|
-
|
|
72
|
+
const ruleGroups = input.rules
|
|
73
|
+
? (input.props?.map((name) => propertyGroup(name)) ?? RULE_GROUPS)
|
|
74
|
+
: [];
|
|
75
|
+
const whyGroup = input.why ? propertyGroup(input.why, input.whyLonghands) : undefined;
|
|
68
76
|
const wanted = [
|
|
69
77
|
...new Set([
|
|
70
78
|
...ownProperties(input.matched),
|
|
@@ -73,40 +81,59 @@ export function buildCascadeFields(input) {
|
|
|
73
81
|
]),
|
|
74
82
|
];
|
|
75
83
|
const cascade = resolveCascade(input.matched, wanted);
|
|
76
|
-
const
|
|
84
|
+
const found = collectHints(cascade, input);
|
|
85
|
+
const hints = input.hints === false ? undefined : found.map((hint) => toInspectHint(hint, input));
|
|
77
86
|
const rules = buildRules(ruleGroups, cascade, input);
|
|
78
87
|
return {
|
|
79
88
|
...(hints && { hints }),
|
|
80
89
|
...(rules.length > 0 && { rules }),
|
|
81
|
-
...(whyGroup && { why: buildWhy(whyGroup, cascade, input) }),
|
|
90
|
+
...(whyGroup && { why: withInactive(buildWhy(whyGroup, cascade, input, found), input) }),
|
|
82
91
|
};
|
|
83
92
|
}
|
|
84
93
|
/**
|
|
85
94
|
* The longhands a property name stands for.
|
|
86
95
|
*
|
|
87
96
|
* @param name - Shorthand, longhand, logical or custom property
|
|
97
|
+
* @param expanded - Its longhands as the browser expands it, for a
|
|
98
|
+
* shorthand bdg does not list (`transition`, `grid-template`)
|
|
88
99
|
* @returns Group (a shorthand with its longhands, or one physical longhand)
|
|
89
100
|
*/
|
|
90
|
-
function propertyGroup(name) {
|
|
91
|
-
const longhands = shorthandLonghands(name);
|
|
101
|
+
function propertyGroup(name, expanded) {
|
|
102
|
+
const longhands = shorthandLonghands(name) ?? (expanded && [...new Set(expanded.map(physicalName))]);
|
|
92
103
|
return longhands
|
|
93
104
|
? { shorthand: name, longhands: [...longhands] }
|
|
94
105
|
: { longhands: [physicalName(name)] };
|
|
95
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The `--why` answer with the rules that would set the property under a
|
|
109
|
+
* condition that does not apply now (on its first entry).
|
|
110
|
+
*
|
|
111
|
+
* @param whys - `--why` entries
|
|
112
|
+
* @param input - Page-side findings
|
|
113
|
+
* @returns Entries
|
|
114
|
+
*/
|
|
115
|
+
function withInactive(whys, input) {
|
|
116
|
+
const [first, ...rest] = whys;
|
|
117
|
+
if (!first || !input.whyInactive?.length)
|
|
118
|
+
return whys;
|
|
119
|
+
return [{ ...first, inactive: input.whyInactive }, ...rest];
|
|
120
|
+
}
|
|
96
121
|
/**
|
|
97
122
|
* Hints: declarations with no effect, and var() of unset custom properties;
|
|
98
123
|
* none without the computed style to check them against.
|
|
99
124
|
*
|
|
100
125
|
* @param cascade - Resolved properties
|
|
101
|
-
* @param input - Styles
|
|
102
|
-
* @returns Hints
|
|
126
|
+
* @param input - Styles
|
|
127
|
+
* @returns Hints with their declarations
|
|
103
128
|
*/
|
|
104
|
-
function
|
|
129
|
+
function collectHints(cascade, input) {
|
|
105
130
|
if (input.style['display'] === undefined)
|
|
106
131
|
return [];
|
|
132
|
+
const declaredDisplay = cascade.get('display')?.winner?.value;
|
|
107
133
|
const ctx = {
|
|
108
134
|
style: input.style,
|
|
109
135
|
parentStyle: input.parentStyle,
|
|
136
|
+
declaredDisplay: declaredDisplay === input.style['display'] ? undefined : declaredDisplay,
|
|
110
137
|
replaced: input.replaced,
|
|
111
138
|
formControl: input.formControl === true,
|
|
112
139
|
};
|
|
@@ -114,14 +141,26 @@ function buildHints(cascade, input) {
|
|
|
114
141
|
...inactiveHints(cascade, ctx),
|
|
115
142
|
...undefinedVariableHints(cascade, input.style),
|
|
116
143
|
...formControlFontHints(cascade, ctx),
|
|
117
|
-
]
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* A hint as the result shows it, with where its declaration is.
|
|
148
|
+
*
|
|
149
|
+
* @param hint - Hint
|
|
150
|
+
* @param input - The label function
|
|
151
|
+
* @returns Result hint
|
|
152
|
+
*/
|
|
153
|
+
function toInspectHint(hint, input) {
|
|
154
|
+
return {
|
|
118
155
|
kind: hint.kind,
|
|
119
156
|
property: hint.property,
|
|
120
157
|
value: hint.value,
|
|
121
158
|
reason: hint.reason,
|
|
122
159
|
fix: hint.fix,
|
|
160
|
+
...(hint.only && { only: hint.only }),
|
|
161
|
+
...(hint.variables && { variables: hint.variables }),
|
|
123
162
|
source: input.label(hint.declaration),
|
|
124
|
-
}
|
|
163
|
+
};
|
|
125
164
|
}
|
|
126
165
|
/**
|
|
127
166
|
* `--rules`: one row per property set by an author declaration; one row for
|
|
@@ -139,16 +178,22 @@ function buildRules(groups, cascade, input) {
|
|
|
139
178
|
const rows = groups.flatMap((group) => {
|
|
140
179
|
const resolutions = group.longhands.map((longhand) => cascade.get(longhand));
|
|
141
180
|
const winners = resolutions.map((resolution) => resolution?.winner);
|
|
142
|
-
const grouped = group.shorthand !== undefined ? groupedRow(
|
|
143
|
-
if (grouped)
|
|
144
|
-
|
|
181
|
+
const grouped = group.shorthand !== undefined ? groupedRow(winners) : undefined;
|
|
182
|
+
if (grouped) {
|
|
183
|
+
const longhands = shorthandLonghands(grouped.property) ?? group.longhands;
|
|
184
|
+
const partly = longhands.some((longhand) => {
|
|
185
|
+
const owner = cascade.get(longhand)?.winner;
|
|
186
|
+
return (owner !== undefined && !(sameSource(owner, grouped) && owner.via === grouped.property));
|
|
187
|
+
});
|
|
188
|
+
return [toRule(grouped.property, grouped, resolutions[0], input, longhands, partly)];
|
|
189
|
+
}
|
|
145
190
|
return group.longhands.flatMap((longhand, i) => {
|
|
146
191
|
const winner = winners[i];
|
|
147
|
-
if (!winner || winner.source.origin === 'user-agent')
|
|
192
|
+
if (!winner || (winner.source.origin === 'user-agent' && !input.props))
|
|
148
193
|
return [];
|
|
149
194
|
const unexpanded = winner.via !== undefined && winner.value === winner.written;
|
|
150
195
|
return unexpanded && winner.via
|
|
151
|
-
? [toRule(winner.via, winner, resolutions[i], input)]
|
|
196
|
+
? [toRule(winner.via, winner, resolutions[i], input, [longhand])]
|
|
152
197
|
: [toRule(longhand, winner, resolutions[i], input)];
|
|
153
198
|
});
|
|
154
199
|
});
|
|
@@ -163,28 +208,20 @@ function buildRules(groups, cascade, input) {
|
|
|
163
208
|
}
|
|
164
209
|
/**
|
|
165
210
|
* One row for a group of longhands that a single author declaration sets:
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* Chrome could not expand (`var()`) as written.
|
|
211
|
+
* that declaration as written (`padding: 4px 8px`; a wider shorthand like
|
|
212
|
+
* `border: none` once, not once per `border-width`/`-style`/`-color` group).
|
|
169
213
|
*
|
|
170
|
-
* @param shorthand - The group's shorthand
|
|
171
214
|
* @param winners - Winning declaration of each longhand
|
|
172
215
|
* @returns The row's declaration, or undefined when the sides differ in source
|
|
173
216
|
*/
|
|
174
|
-
function groupedRow(
|
|
217
|
+
function groupedRow(winners) {
|
|
175
218
|
const [first] = winners;
|
|
176
219
|
if (!first?.via || first.source.origin === 'user-agent')
|
|
177
220
|
return undefined;
|
|
178
221
|
const same = winners.every((w) => w && w.via === first.via && sameSource(w, first));
|
|
179
222
|
if (!same)
|
|
180
223
|
return undefined;
|
|
181
|
-
|
|
182
|
-
if (first.via === shorthand || first.value === written) {
|
|
183
|
-
return { ...first, property: first.via, value: written };
|
|
184
|
-
}
|
|
185
|
-
const values = winners.map((w) => w?.value ?? '');
|
|
186
|
-
const value = values.every((v) => v === first.value) ? first.value : values.join(' ');
|
|
187
|
-
return { ...first, property: shorthand, value };
|
|
224
|
+
return { ...first, property: first.via, value: first.written ?? first.value };
|
|
188
225
|
}
|
|
189
226
|
/**
|
|
190
227
|
* Whether two declarations come from the same place.
|
|
@@ -200,6 +237,26 @@ function sameSource(a, b) {
|
|
|
200
237
|
a.source.line === b.source.line &&
|
|
201
238
|
a.source.column === b.source.column);
|
|
202
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* The computed value of a row shown as a shorthand or logical property,
|
|
242
|
+
* from its longhands: as `--all` writes it (`padding 4 8`), else the
|
|
243
|
+
* longhands' values with runs of equal ones written once (`border` →
|
|
244
|
+
* `2px solid red`).
|
|
245
|
+
*
|
|
246
|
+
* @param property - Shorthand or logical property
|
|
247
|
+
* @param longhands - Its longhands
|
|
248
|
+
* @param style - Computed longhands
|
|
249
|
+
* @returns Value, or undefined when no longhand has one
|
|
250
|
+
*/
|
|
251
|
+
function shorthandValue(property, longhands, style) {
|
|
252
|
+
const collapsed = collapsedValue(property, style);
|
|
253
|
+
if (collapsed !== undefined)
|
|
254
|
+
return collapsed;
|
|
255
|
+
const values = longhands.map((longhand) => style[longhand]);
|
|
256
|
+
if (values.some((value) => value === undefined))
|
|
257
|
+
return undefined;
|
|
258
|
+
return values.filter((value, i) => i === 0 || value !== values[i - 1]).join(' ');
|
|
259
|
+
}
|
|
203
260
|
/**
|
|
204
261
|
* A `--rules` row.
|
|
205
262
|
*
|
|
@@ -207,21 +264,38 @@ function sameSource(a, b) {
|
|
|
207
264
|
* @param winner - Winning declaration
|
|
208
265
|
* @param resolution - Its resolution (for what it overrides)
|
|
209
266
|
* @param input - Computed style and the label function
|
|
267
|
+
* @param longhands - The longhands the row stands for (a shorthand's value comes from them)
|
|
268
|
+
* @param partly - Other declarations win some of its longhands: the value shown is
|
|
269
|
+
* the declaration's own, `var()` substituted, not the computed one
|
|
210
270
|
* @returns Rule
|
|
211
271
|
*/
|
|
212
|
-
function toRule(property, winner, resolution, input) {
|
|
213
|
-
const
|
|
272
|
+
function toRule(property, winner, resolution, input, longhands = [property], partly = false) {
|
|
273
|
+
const substituted = substituteVariables(winner.value, input.style);
|
|
274
|
+
const computed = partly
|
|
275
|
+
? substituted.includes('var(')
|
|
276
|
+
? undefined
|
|
277
|
+
: substituted
|
|
278
|
+
: (input.style[property] ?? shorthandValue(property, longhands, input.style));
|
|
214
279
|
const overrides = [
|
|
215
280
|
...new Set((resolution?.overridden ?? [])
|
|
216
281
|
.filter((d) => d.source.origin !== 'user-agent')
|
|
217
|
-
.map((d) => d.source.selector
|
|
282
|
+
.map((d) => d.source.selector !== undefined && d.source.selector === winner.source.selector
|
|
283
|
+
? input.label(d)
|
|
284
|
+
: (d.source.selector ?? d.source.kind))),
|
|
218
285
|
];
|
|
219
286
|
return {
|
|
220
287
|
property,
|
|
221
288
|
value: winner.value,
|
|
222
289
|
...(winner.value.includes('var(') &&
|
|
223
|
-
computed !== undefined && {
|
|
290
|
+
computed !== undefined && {
|
|
291
|
+
computed: `${normalizeProperty(property, computed)}${partly ? ' (partly overridden)' : ''}`,
|
|
292
|
+
}),
|
|
293
|
+
...(property === 'display' &&
|
|
294
|
+
computed !== undefined &&
|
|
295
|
+
computed !== winner.value &&
|
|
296
|
+
/^(inline|table-|ruby)/.test(winner.value) && { computed: `${computed} (blockified)` }),
|
|
224
297
|
source: input.label(winner),
|
|
298
|
+
...ruleField(winner),
|
|
225
299
|
...(overrides.length > 0 && { overrides }),
|
|
226
300
|
...(winner.ancestor !== undefined && { inherited: winner.ancestor }),
|
|
227
301
|
...(winner.important && { important: true }),
|
|
@@ -4,8 +4,10 @@
|
|
|
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 type { InspectHint } from '../../ipc/protocol/inspectTypes.js';
|
|
7
8
|
import type { Declaration, Resolution } from './inspectCascade.js';
|
|
8
9
|
import type { StyleMap } from './inspectLayoutModel.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?: {
|
|
@@ -49,7 +52,8 @@ export interface InspectRequest {
|
|
|
49
52
|
propValues?: InspectResult['props'];
|
|
50
53
|
}
|
|
51
54
|
/**
|
|
52
|
-
* The element's label: tag, id and the first classes
|
|
55
|
+
* The element's label: tag, id and the first classes ({@link isLabelClass}),
|
|
56
|
+
* with a count of the rest.
|
|
53
57
|
*
|
|
54
58
|
* @param raw - Tag, id and classes
|
|
55
59
|
* @returns e.g. `a.z-1.max-sm:hidden(+11)`, `input#user-name`
|
|
@@ -5,26 +5,29 @@
|
|
|
5
5
|
* tree. Pure: every input is plain data, so the rules are tested without a
|
|
6
6
|
* browser.
|
|
7
7
|
*/
|
|
8
|
+
import { isLabelClass } from './elementInfo.js';
|
|
8
9
|
import { allStyles } from './inspectAllStyles.js';
|
|
9
10
|
import { buildBox, buildLayout } from './inspectLayoutModel.js';
|
|
10
|
-
import { buildEffects, buildFills, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
|
|
11
|
+
import { buildEffects, buildFills, buildSvgPaint, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
|
|
11
12
|
import { buildTree, rowText } from './inspectTree.js';
|
|
12
13
|
import { hexColor, relativeLuminance } from '../../utils/color.js';
|
|
13
14
|
import { normalizeCssValue, round1 } from '../../utils/cssValues.js';
|
|
14
15
|
/** Classes shown in the label (the rest are counted) */
|
|
15
16
|
const LABEL_CLASSES = 2;
|
|
16
17
|
/**
|
|
17
|
-
* The element's label: tag, id and the first classes
|
|
18
|
+
* The element's label: tag, id and the first classes ({@link isLabelClass}),
|
|
19
|
+
* with a count of the rest.
|
|
18
20
|
*
|
|
19
21
|
* @param raw - Tag, id and classes
|
|
20
22
|
* @returns e.g. `a.z-1.max-sm:hidden(+11)`, `input#user-name`
|
|
21
23
|
*/
|
|
22
24
|
export function elementLabel(raw) {
|
|
23
|
-
const
|
|
25
|
+
const classes = raw.classes.filter(isLabelClass);
|
|
26
|
+
const shown = classes
|
|
24
27
|
.slice(0, LABEL_CLASSES)
|
|
25
28
|
.map((name) => `.${name}`)
|
|
26
29
|
.join('');
|
|
27
|
-
const more =
|
|
30
|
+
const more = classes.length - LABEL_CLASSES;
|
|
28
31
|
return `${raw.tag}${raw.id ? `#${raw.id}` : ''}${shown}${more > 0 ? `(+${more})` : ''}`;
|
|
29
32
|
}
|
|
30
33
|
/**
|
|
@@ -48,8 +51,33 @@ export function visibilityOf(layout, rendered) {
|
|
|
48
51
|
...(offscreen && { offscreen }),
|
|
49
52
|
...(layout.coveredBy && { coveredBy: layout.coveredBy }),
|
|
50
53
|
...(layout.coverTransparent && { coverTransparent: true }),
|
|
54
|
+
...(layout.masked && { masked: layout.masked }),
|
|
51
55
|
};
|
|
52
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* The header rectangle: border box size (and the box it covers on screen
|
|
59
|
+
* when a transform turns it), and the page position, or the
|
|
60
|
+
* viewport position of an element fixed to the viewport (it or a container
|
|
61
|
+
* is `position: fixed`: its page position changes with the scroll).
|
|
62
|
+
*
|
|
63
|
+
* @param layout - Layout measurements
|
|
64
|
+
* @param size - Border box size
|
|
65
|
+
* @param style - Computed styles (transforms)
|
|
66
|
+
* @returns Rectangle
|
|
67
|
+
*/
|
|
68
|
+
function headerRect(layout, size, style) {
|
|
69
|
+
const { width, height } = layout.bounds;
|
|
70
|
+
const transformed = ['transform', 'rotate', 'scale'].some((name) => (style[name] ?? 'none') !== 'none');
|
|
71
|
+
const turned = transformed && (Math.abs(width - size.w) > 1 || Math.abs(height - size.h) > 1);
|
|
72
|
+
const box = {
|
|
73
|
+
w: round1(size.w),
|
|
74
|
+
h: round1(size.h),
|
|
75
|
+
...(turned && { screen: { w: width, h: height } }),
|
|
76
|
+
};
|
|
77
|
+
return layout.fixed
|
|
78
|
+
? { x: layout.viewport.x, y: layout.viewport.y, ...box, in: 'viewport' }
|
|
79
|
+
: { x: layout.bounds.x, y: layout.bounds.y, ...box };
|
|
80
|
+
}
|
|
53
81
|
/** Below this relative luminance a page background counts as dark */
|
|
54
82
|
const DARK_LUMINANCE = 0.18;
|
|
55
83
|
/**
|
|
@@ -72,10 +100,7 @@ function header(sources, request) {
|
|
|
72
100
|
...(content && { content }),
|
|
73
101
|
...(raw.placeholder && { placeholder: rowText(raw.placeholder) }),
|
|
74
102
|
...(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
|
-
}),
|
|
103
|
+
...(size && layout && { rect: headerRect(layout, size, sources.style) }),
|
|
79
104
|
visibility: visibilityOf(layout, size !== undefined),
|
|
80
105
|
...(sources.colorScheme && { colorScheme: sources.colorScheme }),
|
|
81
106
|
...(sources.colorScheme === 'dark' && pageLooksDark(raw) && { theme: 'dark' }),
|
|
@@ -104,8 +129,9 @@ function pageLooksDark(raw) {
|
|
|
104
129
|
*/
|
|
105
130
|
function groups(sources) {
|
|
106
131
|
const { style, parentStyle, raw } = sources;
|
|
107
|
-
const text = buildText(style, parentStyle, raw, sources.fonts);
|
|
132
|
+
const text = buildText({ style, parentStyle, holderStyle: sources.holderStyle }, raw, sources.fonts);
|
|
108
133
|
const fills = buildFills(style);
|
|
134
|
+
const paint = buildSvgPaint(style, raw);
|
|
109
135
|
const strokes = buildStrokes(style);
|
|
110
136
|
const radius = buildRadius(style);
|
|
111
137
|
const outline = buildOutline(style);
|
|
@@ -120,6 +146,7 @@ function groups(sources) {
|
|
|
120
146
|
layout: buildLayout(style, parentStyle, raw),
|
|
121
147
|
...(text && { text }),
|
|
122
148
|
...(fills.length > 0 && { fills }),
|
|
149
|
+
...(paint && { paint }),
|
|
123
150
|
...(opacity !== 1 && { opacity }),
|
|
124
151
|
...(blend && blend !== 'normal' && { blend }),
|
|
125
152
|
...(strokes.length > 0 && { strokes }),
|
|
@@ -156,7 +183,7 @@ function treeFields(raw, limit) {
|
|
|
156
183
|
* @returns Properties and values
|
|
157
184
|
*/
|
|
158
185
|
function allFields(sources) {
|
|
159
|
-
const all = allStyles(sources.style, sources.raw.svg === true);
|
|
186
|
+
const all = allStyles(sources.style, sources.raw.svg === true, sources.raw.formControl);
|
|
160
187
|
for (const pseudo of sources.pseudo) {
|
|
161
188
|
const content = pseudo.style['content'];
|
|
162
189
|
if (content && content !== 'none' && content !== 'normal') {
|