browser-debugger-cli 0.9.0 → 0.11.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 +15 -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/installSkill.d.ts +20 -0
- package/dist/commands/installSkill.js +87 -0
- 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/commands.js +3 -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 +85 -0
- package/dist/errors/messages.js +128 -1
- package/dist/index.js +2151 -960
- 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 +40 -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/installSkill.d.ts +11 -0
- package/dist/ui/formatters/installSkill.js +31 -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 +2 -1
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Box and layout groups of `bdg dom inspect`, from computed styles.
|
|
3
|
+
*
|
|
4
|
+
* Core facts are always there (display, padding/margin/border as computed,
|
|
5
|
+
* even when they equal the UA default); the rest only when it applies
|
|
6
|
+
* (container fields for flex and grid containers, item fields in a flex or
|
|
7
|
+
* grid parent) and is not a no-op default. Sizing is named like Figma's
|
|
8
|
+
* auto layout: hug (sized by the content), fill (stretched by the parent)
|
|
9
|
+
* or fixed, from the computed (not resolved) width and height.
|
|
10
|
+
*/
|
|
11
|
+
import { compressTracks, cssLength, round1 } from '../../utils/cssValues.js';
|
|
12
|
+
const SIDES = ['top', 'right', 'bottom', 'left'];
|
|
13
|
+
/**
|
|
14
|
+
* The four sides of a box property.
|
|
15
|
+
*
|
|
16
|
+
* @param style - Computed styles
|
|
17
|
+
* @param name - e.g. `padding-{side}`, `border-{side}-width`
|
|
18
|
+
* @returns Top, right, bottom, left
|
|
19
|
+
*/
|
|
20
|
+
export function sides(style, name) {
|
|
21
|
+
return SIDES.map((side) => cssLength(style[name.replace('{side}', side)]));
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether a computed value is a no-op for a size limit.
|
|
25
|
+
*
|
|
26
|
+
* @param value - Computed min/max width or height
|
|
27
|
+
* @returns True for `auto`, `none` and 0
|
|
28
|
+
*/
|
|
29
|
+
function noLimit(value) {
|
|
30
|
+
return value === undefined || value === 'auto' || value === 'none' || value === '0px';
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Min or max sizes that constrain the box.
|
|
34
|
+
*
|
|
35
|
+
* @param style - Computed styles
|
|
36
|
+
* @param kind - `min` or `max`
|
|
37
|
+
* @returns Width and height limits, or undefined when there are none
|
|
38
|
+
*/
|
|
39
|
+
function sizeLimits(style, kind) {
|
|
40
|
+
const w = style[`${kind}-width`];
|
|
41
|
+
const h = style[`${kind}-height`];
|
|
42
|
+
const limits = {
|
|
43
|
+
...(!noLimit(w) && { w: cssLength(w) }),
|
|
44
|
+
...(!noLimit(h) && { h: cssLength(h) }),
|
|
45
|
+
};
|
|
46
|
+
return Object.keys(limits).length > 0 ? limits : undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The box group: padding, margin, border widths, box-sizing, size limits,
|
|
50
|
+
* overflow and the content size when it overflows.
|
|
51
|
+
*
|
|
52
|
+
* @param style - Computed styles
|
|
53
|
+
* @param raw - Page-side measurements
|
|
54
|
+
* @returns Box group
|
|
55
|
+
*/
|
|
56
|
+
export function buildBox(style, raw) {
|
|
57
|
+
const overflowX = style['overflow-x'] ?? 'visible';
|
|
58
|
+
const overflowY = style['overflow-y'] ?? 'visible';
|
|
59
|
+
const overflow = overflowX === 'visible' && overflowY === 'visible'
|
|
60
|
+
? undefined
|
|
61
|
+
: overflowX === overflowY
|
|
62
|
+
? overflowX
|
|
63
|
+
: `${overflowX} ${overflowY}`;
|
|
64
|
+
const min = sizeLimits(style, 'min');
|
|
65
|
+
const max = sizeLimits(style, 'max');
|
|
66
|
+
return {
|
|
67
|
+
padding: sides(style, 'padding-{side}'),
|
|
68
|
+
margin: sides(style, 'margin-{side}'),
|
|
69
|
+
border: sides(style, 'border-{side}-width'),
|
|
70
|
+
sizing: style['box-sizing'] ?? 'content-box',
|
|
71
|
+
...(min && { min }),
|
|
72
|
+
...(max && { max }),
|
|
73
|
+
...(overflow && { overflow }),
|
|
74
|
+
...(raw.scroll && { scroll: { w: raw.scroll.w, h: raw.scroll.h } }),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Row and column gap, when there is one.
|
|
79
|
+
*
|
|
80
|
+
* @param style - Computed styles
|
|
81
|
+
* @returns One length when both are equal, else row and column
|
|
82
|
+
*/
|
|
83
|
+
function gapOf(style) {
|
|
84
|
+
const toLength = (value) => value === undefined || value === 'normal' ? 0 : cssLength(value);
|
|
85
|
+
const row = toLength(style['row-gap']);
|
|
86
|
+
const column = toLength(style['column-gap']);
|
|
87
|
+
if (row === 0 && column === 0)
|
|
88
|
+
return undefined;
|
|
89
|
+
return row === column ? row : [row, column];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Whether a display value makes a flex or grid container.
|
|
93
|
+
*
|
|
94
|
+
* @param display - Computed display
|
|
95
|
+
* @returns `flex`, `grid` or undefined
|
|
96
|
+
*/
|
|
97
|
+
export function containerKind(display) {
|
|
98
|
+
if (display === undefined)
|
|
99
|
+
return undefined;
|
|
100
|
+
if (/(^|-)flex$/.test(display) || display === '-webkit-box')
|
|
101
|
+
return 'flex';
|
|
102
|
+
return /(^|-)grid$/.test(display) ? 'grid' : undefined;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Flex or grid container fields that are not no-op defaults (the flex
|
|
106
|
+
* direction is always there).
|
|
107
|
+
*
|
|
108
|
+
* @param style - Computed styles
|
|
109
|
+
* @returns Container fields, empty for other displays
|
|
110
|
+
*/
|
|
111
|
+
export function buildContainer(style) {
|
|
112
|
+
const kind = containerKind(style['display']);
|
|
113
|
+
if (!kind)
|
|
114
|
+
return {};
|
|
115
|
+
const justify = style['justify-content'];
|
|
116
|
+
const align = style['align-items'];
|
|
117
|
+
const wrap = style['flex-wrap'];
|
|
118
|
+
const gap = gapOf(style);
|
|
119
|
+
const columns = style['grid-template-columns'] ?? 'none';
|
|
120
|
+
const rows = style['grid-template-rows'] ?? 'none';
|
|
121
|
+
return {
|
|
122
|
+
...(kind === 'flex' && { direction: style['flex-direction'] ?? 'row' }),
|
|
123
|
+
...(kind === 'flex' && wrap && wrap !== 'nowrap' && { wrap }),
|
|
124
|
+
...(kind === 'grid' && columns !== 'none' && { columns: compressTracks(columns) }),
|
|
125
|
+
...(kind === 'grid' && rows !== 'none' && { rows: compressTracks(rows) }),
|
|
126
|
+
...(justify && !['normal', 'flex-start', 'start'].includes(justify) && { justify }),
|
|
127
|
+
...(align && !['normal', 'stretch'].includes(align) && { align }),
|
|
128
|
+
...(gap !== undefined && { gap }),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Position fields: position unless static, inset unless all `auto` (or all
|
|
133
|
+
* 0/auto for relative), z-index unless `auto`.
|
|
134
|
+
*
|
|
135
|
+
* @param style - Computed styles
|
|
136
|
+
* @returns Position fields
|
|
137
|
+
*/
|
|
138
|
+
function positionFields(style) {
|
|
139
|
+
const position = style['position'] ?? 'static';
|
|
140
|
+
if (position === 'static')
|
|
141
|
+
return {};
|
|
142
|
+
const inset = sides(style, '{side}');
|
|
143
|
+
const allAuto = inset.every((value) => value === 'auto');
|
|
144
|
+
const noOffset = inset.every((value) => value === 'auto' || value === 0);
|
|
145
|
+
const z = style['z-index'];
|
|
146
|
+
return {
|
|
147
|
+
position,
|
|
148
|
+
...(!allAuto && !(position === 'relative' && noOffset) && { inset }),
|
|
149
|
+
...(z && z !== 'auto' && { z: /^-?\d+$/.test(z) ? Number(z) : z }),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Fields of an element laid out by a flex or grid parent: flex
|
|
154
|
+
* grow/shrink/basis (unless `0 1 auto`), self alignment, order and grid area.
|
|
155
|
+
*
|
|
156
|
+
* @param style - Computed styles
|
|
157
|
+
* @param parentDisplay - Display of the layout parent
|
|
158
|
+
* @returns Item fields, empty in other parents
|
|
159
|
+
*/
|
|
160
|
+
function itemFields(style, parentDisplay) {
|
|
161
|
+
const parentKind = containerKind(parentDisplay);
|
|
162
|
+
if (!parentKind)
|
|
163
|
+
return {};
|
|
164
|
+
const flex = `${style['flex-grow'] ?? 0} ${style['flex-shrink'] ?? 1} ${cssLength(style['flex-basis'] ?? 'auto')}`;
|
|
165
|
+
const self = style['align-self'];
|
|
166
|
+
const justifySelf = style['justify-self'];
|
|
167
|
+
const order = Number(style['order'] ?? 0);
|
|
168
|
+
const placement = ['grid-row-start', 'grid-column-start', 'grid-row-end', 'grid-column-end'].map((name) => style[name] ?? 'auto');
|
|
169
|
+
const selfAlign = [self, parentKind === 'grid' ? justifySelf : undefined]
|
|
170
|
+
.filter((value) => value && value !== 'auto')
|
|
171
|
+
.join(' ');
|
|
172
|
+
return {
|
|
173
|
+
...(parentKind === 'flex' && flex !== '0 1 auto' && { flex }),
|
|
174
|
+
...(selfAlign && { self: selfAlign }),
|
|
175
|
+
...(order !== 0 && { order }),
|
|
176
|
+
...(parentKind === 'grid' &&
|
|
177
|
+
placement.some((value) => value !== 'auto') && { area: placement.join(' / ') }),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The parent line: its description, display, container fields and its
|
|
182
|
+
* text alignment (when not the start).
|
|
183
|
+
*
|
|
184
|
+
* @param parentStyle - Computed styles of the layout parent
|
|
185
|
+
* @param element - Its short description
|
|
186
|
+
* @returns Parent fields
|
|
187
|
+
*/
|
|
188
|
+
function buildParent(parentStyle, element) {
|
|
189
|
+
const textAlign = parentStyle['text-align'];
|
|
190
|
+
return {
|
|
191
|
+
element,
|
|
192
|
+
display: parentStyle['display'] ?? 'block',
|
|
193
|
+
...buildContainer(parentStyle),
|
|
194
|
+
...(textAlign && !['start', 'left', '-webkit-auto'].includes(textAlign) && { textAlign }),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Whether an item stretches along the cross axis of its flex or grid parent.
|
|
199
|
+
*
|
|
200
|
+
* @param self - The item's `align-self`/`justify-self`
|
|
201
|
+
* @param items - The parent's `align-items`/`justify-items`
|
|
202
|
+
* @returns True for `stretch` and `normal` (stretch for non-replaced items)
|
|
203
|
+
*/
|
|
204
|
+
function stretches(self, items) {
|
|
205
|
+
const effective = self === 'auto' ? (items ?? 'normal') : self;
|
|
206
|
+
return effective === 'normal' || effective === 'stretch';
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* How the element is sized along an axis inside a flex parent.
|
|
210
|
+
*
|
|
211
|
+
* @param input - Sizing inputs
|
|
212
|
+
* @param automatic - The size is `auto` (or content-based)
|
|
213
|
+
* @returns Sizing mode
|
|
214
|
+
*/
|
|
215
|
+
function flexItemSizing(input, automatic) {
|
|
216
|
+
const mainAxis = (input.parentDirection ?? 'row').startsWith('row') ? 'w' : 'h';
|
|
217
|
+
if (input.axis === mainAxis) {
|
|
218
|
+
if (input.flexGrow > 0)
|
|
219
|
+
return 'fill';
|
|
220
|
+
if (!['auto', 'content'].includes(input.flexBasis))
|
|
221
|
+
return 'fixed';
|
|
222
|
+
return automatic ? 'hug' : 'fixed';
|
|
223
|
+
}
|
|
224
|
+
if (automatic && input.size === 'auto' && stretches(input.alignSelf, input.parentAlignItems)) {
|
|
225
|
+
return 'fill';
|
|
226
|
+
}
|
|
227
|
+
return automatic ? 'hug' : input.size === '100%' ? 'fill' : 'fixed';
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Sizing of a replaced element with an automatic size: its intrinsic size
|
|
231
|
+
* (hug), unless it grows along a flex main axis or is stretched explicitly
|
|
232
|
+
* (`normal` alignment does not stretch replaced elements).
|
|
233
|
+
*
|
|
234
|
+
* @param input - Computed size, display and the parent's layout
|
|
235
|
+
* @returns Sizing mode
|
|
236
|
+
*/
|
|
237
|
+
function replacedSizing(input) {
|
|
238
|
+
const kind = containerKind(input.parentDisplay);
|
|
239
|
+
const mainAxis = (input.parentDirection ?? 'row').startsWith('row') ? 'w' : 'h';
|
|
240
|
+
if (kind === 'flex' && input.axis === mainAxis && input.flexGrow > 0)
|
|
241
|
+
return 'fill';
|
|
242
|
+
const self = input.axis === 'w' && kind === 'grid' ? input.justifySelf : input.alignSelf;
|
|
243
|
+
return kind && self === 'stretch' ? 'fill' : 'hug';
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* How the element is sized along an axis: `fill` when its parent stretches
|
|
247
|
+
* it (a flex item that grows, a stretched cross axis or grid cell, a block's
|
|
248
|
+
* width in normal flow, 100%), `hug` when its content sizes it (`auto` and
|
|
249
|
+
* the content keywords elsewhere), `fixed` for a length.
|
|
250
|
+
*
|
|
251
|
+
* @param input - Computed size, display and the parent's layout
|
|
252
|
+
* @returns Sizing mode
|
|
253
|
+
*/
|
|
254
|
+
export function deriveSizing(input) {
|
|
255
|
+
const automatic = /^(auto|fit-content|max-content|min-content)$/.test(input.size) || input.size === '';
|
|
256
|
+
if (input.display === 'inline')
|
|
257
|
+
return 'hug';
|
|
258
|
+
if (input.replaced && automatic)
|
|
259
|
+
return replacedSizing(input);
|
|
260
|
+
if (input.position === 'absolute' || input.position === 'fixed')
|
|
261
|
+
return automatic ? 'hug' : 'fixed';
|
|
262
|
+
const parentKind = containerKind(input.parentDisplay);
|
|
263
|
+
if (parentKind === 'flex')
|
|
264
|
+
return flexItemSizing(input, automatic);
|
|
265
|
+
if (parentKind === 'grid' && input.size === 'auto') {
|
|
266
|
+
const fills = input.axis === 'w'
|
|
267
|
+
? stretches(input.justifySelf, input.parentJustifyItems)
|
|
268
|
+
: stretches(input.alignSelf, input.parentAlignItems);
|
|
269
|
+
return fills ? 'fill' : 'hug';
|
|
270
|
+
}
|
|
271
|
+
if (input.size === '100%')
|
|
272
|
+
return 'fill';
|
|
273
|
+
if (!automatic)
|
|
274
|
+
return 'fixed';
|
|
275
|
+
const blockLevel = !/^inline|^table$/.test(input.display) && input.float === 'none';
|
|
276
|
+
return input.axis === 'w' && input.size === 'auto' && blockLevel ? 'fill' : 'hug';
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Sizing along both axes.
|
|
280
|
+
*
|
|
281
|
+
* @param style - Computed styles
|
|
282
|
+
* @param parentStyle - Computed styles of the layout parent
|
|
283
|
+
* @param raw - Computed (not resolved) width and height, and whether the element is replaced
|
|
284
|
+
* @returns Width and height sizing
|
|
285
|
+
*/
|
|
286
|
+
function sizingOf(style, parentStyle, raw) {
|
|
287
|
+
const typed = raw.typed;
|
|
288
|
+
const base = {
|
|
289
|
+
...(raw.replaced && { replaced: true }),
|
|
290
|
+
display: style['display'] ?? 'inline',
|
|
291
|
+
position: style['position'] ?? 'static',
|
|
292
|
+
float: style['float'] ?? 'none',
|
|
293
|
+
flexGrow: Number(style['flex-grow'] ?? 0),
|
|
294
|
+
flexBasis: style['flex-basis'] ?? 'auto',
|
|
295
|
+
alignSelf: style['align-self'] ?? 'auto',
|
|
296
|
+
justifySelf: style['justify-self'] ?? 'auto',
|
|
297
|
+
...(parentStyle && {
|
|
298
|
+
parentDisplay: parentStyle['display'] ?? 'block',
|
|
299
|
+
parentDirection: parentStyle['flex-direction'] ?? 'row',
|
|
300
|
+
parentAlignItems: parentStyle['align-items'] ?? 'normal',
|
|
301
|
+
parentJustifyItems: parentStyle['justify-items'] ?? 'normal',
|
|
302
|
+
}),
|
|
303
|
+
};
|
|
304
|
+
return {
|
|
305
|
+
w: deriveSizing({ ...base, size: typed.width, axis: 'w' }),
|
|
306
|
+
h: deriveSizing({ ...base, size: typed.height, axis: 'h' }),
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Round each value of a record of distances.
|
|
311
|
+
*
|
|
312
|
+
* @param distances - Distances in CSS px
|
|
313
|
+
* @returns Rounded distances
|
|
314
|
+
*/
|
|
315
|
+
function rounded(distances) {
|
|
316
|
+
return Object.fromEntries(Object.entries(distances).map(([key, value]) => [key, round1(value)]));
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* The layout group: display, position, container and item fields, valign
|
|
320
|
+
* for inline displays, float, sizing, and the parent with the element's
|
|
321
|
+
* place in it (offsets from its content edges, gaps to the siblings).
|
|
322
|
+
*
|
|
323
|
+
* @param style - Computed styles
|
|
324
|
+
* @param parentStyle - Computed styles of the layout parent (none for the root)
|
|
325
|
+
* @param raw - Page-side measurements
|
|
326
|
+
* @returns Layout group
|
|
327
|
+
*/
|
|
328
|
+
export function buildLayout(style, parentStyle, raw) {
|
|
329
|
+
const display = style['display'] ?? 'inline';
|
|
330
|
+
const valign = style['vertical-align'];
|
|
331
|
+
const float = style['float'];
|
|
332
|
+
const siblings = raw.siblings && Object.keys(raw.siblings).length > 0 ? raw.siblings : undefined;
|
|
333
|
+
return {
|
|
334
|
+
display,
|
|
335
|
+
...positionFields(style),
|
|
336
|
+
...buildContainer(style),
|
|
337
|
+
...itemFields(style, parentStyle?.['display']),
|
|
338
|
+
...(/^inline|table-cell/.test(display) && valign && valign !== 'baseline' && { valign }),
|
|
339
|
+
...(float && float !== 'none' && { float }),
|
|
340
|
+
...(display !== 'none' && { sizing: sizingOf(style, parentStyle, raw) }),
|
|
341
|
+
...(parentStyle && raw.parent && { parent: buildParent(parentStyle, raw.parent) }),
|
|
342
|
+
...(raw.inParent && { inParent: rounded(raw.inParent) }),
|
|
343
|
+
...(siblings && { siblings: rounded(siblings) }),
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
//# sourceMappingURL=inspectLayoutModel.js.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assemble the `bdg dom inspect` result from what CDP and the page-side walk
|
|
3
|
+
* read: the header (label, text, rect, what keeps it from being seen), then
|
|
4
|
+
* the groups (or, with `--all`/`--props`, the property lists) and the child
|
|
5
|
+
* tree. Pure: every input is plain data, so the rules are tested without a
|
|
6
|
+
* browser.
|
|
7
|
+
*/
|
|
8
|
+
import type { Protocol } from '../../connection/typed-cdp.js';
|
|
9
|
+
import type { ElementLayout } from '../../ipc/protocol/domTypes.js';
|
|
10
|
+
import type { InspectResult, InspectVisibility } from '../../ipc/protocol/inspectTypes.js';
|
|
11
|
+
import { type StyleMap } from './inspectLayoutModel.js';
|
|
12
|
+
import { type PlatformFont, type PseudoSource } from './inspectPaintModel.js';
|
|
13
|
+
import type { RawInspect } from './inspectScripts.js';
|
|
14
|
+
/** What one inspect read */
|
|
15
|
+
export interface InspectSources {
|
|
16
|
+
raw: RawInspect;
|
|
17
|
+
style: StyleMap;
|
|
18
|
+
parentStyle?: StyleMap;
|
|
19
|
+
pseudo: PseudoSource[];
|
|
20
|
+
fonts: PlatformFont[];
|
|
21
|
+
/** Border box size from `DOM.getBoxModel`; absent when the element has no box */
|
|
22
|
+
size?: {
|
|
23
|
+
w: number;
|
|
24
|
+
h: number;
|
|
25
|
+
};
|
|
26
|
+
/** The element as `dom layout` measures it */
|
|
27
|
+
layout?: ElementLayout;
|
|
28
|
+
colorScheme?: 'light' | 'dark';
|
|
29
|
+
/** Matched rules for the cascade fields; `timeout` or `failed` when they were not read */
|
|
30
|
+
matched?: Protocol.CSS.GetMatchedStylesForNodeResponse | 'timeout' | 'failed';
|
|
31
|
+
/** `--rules` was asked for */
|
|
32
|
+
rules?: boolean;
|
|
33
|
+
/** `--why` property */
|
|
34
|
+
why?: string;
|
|
35
|
+
/** `--props` names, custom property patterns expanded (`--rules` covers only these) */
|
|
36
|
+
props?: string[];
|
|
37
|
+
/** `--no-hints` */
|
|
38
|
+
hints?: false;
|
|
39
|
+
}
|
|
40
|
+
/** What the command asked for */
|
|
41
|
+
export interface InspectRequest {
|
|
42
|
+
selector: string;
|
|
43
|
+
index: number;
|
|
44
|
+
count: number;
|
|
45
|
+
/** Tree rows shown at most */
|
|
46
|
+
treeLimit: number;
|
|
47
|
+
all?: boolean;
|
|
48
|
+
/** `--props` values (from `selectedProps`) */
|
|
49
|
+
propValues?: InspectResult['props'];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The element's label: tag, id and the first classes, with a count of the rest.
|
|
53
|
+
*
|
|
54
|
+
* @param raw - Tag, id and classes
|
|
55
|
+
* @returns e.g. `a.z-1.max-sm:hidden(+11)`, `input#user-name`
|
|
56
|
+
*/
|
|
57
|
+
export declare function elementLabel(raw: Pick<RawInspect, 'tag' | 'id' | 'classes'>): string;
|
|
58
|
+
/**
|
|
59
|
+
* Why the element cannot be seen, from `dom layout`'s measurements.
|
|
60
|
+
*
|
|
61
|
+
* @param layout - Layout measurements
|
|
62
|
+
* @param rendered - The element has a box
|
|
63
|
+
* @returns Visibility fields
|
|
64
|
+
*/
|
|
65
|
+
export declare function visibilityOf(layout: ElementLayout | undefined, rendered: boolean): InspectVisibility;
|
|
66
|
+
/**
|
|
67
|
+
* The `bdg dom inspect` result.
|
|
68
|
+
*
|
|
69
|
+
* @param sources - What CDP and the page-side walk read
|
|
70
|
+
* @param request - Selector, index, match count and options
|
|
71
|
+
* @returns Inspect result
|
|
72
|
+
*/
|
|
73
|
+
export declare function buildInspectResult(sources: InspectSources, request: InspectRequest): InspectResult;
|
|
74
|
+
//# sourceMappingURL=inspectModel.d.ts.map
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Assemble the `bdg dom inspect` result from what CDP and the page-side walk
|
|
3
|
+
* read: the header (label, text, rect, what keeps it from being seen), then
|
|
4
|
+
* the groups (or, with `--all`/`--props`, the property lists) and the child
|
|
5
|
+
* tree. Pure: every input is plain data, so the rules are tested without a
|
|
6
|
+
* browser.
|
|
7
|
+
*/
|
|
8
|
+
import { allStyles } from './inspectAllStyles.js';
|
|
9
|
+
import { buildBox, buildLayout } from './inspectLayoutModel.js';
|
|
10
|
+
import { buildEffects, buildFills, buildFx, buildOutline, buildPseudo, buildRadius, buildState, buildStrokes, buildText, effectiveBackground, } from './inspectPaintModel.js';
|
|
11
|
+
import { buildTree, rowText } from './inspectTree.js';
|
|
12
|
+
import { hexColor, relativeLuminance } from '../../utils/color.js';
|
|
13
|
+
import { normalizeCssValue, round1 } from '../../utils/cssValues.js';
|
|
14
|
+
/** Classes shown in the label (the rest are counted) */
|
|
15
|
+
const LABEL_CLASSES = 2;
|
|
16
|
+
/**
|
|
17
|
+
* The element's label: tag, id and the first classes, with a count of the rest.
|
|
18
|
+
*
|
|
19
|
+
* @param raw - Tag, id and classes
|
|
20
|
+
* @returns e.g. `a.z-1.max-sm:hidden(+11)`, `input#user-name`
|
|
21
|
+
*/
|
|
22
|
+
export function elementLabel(raw) {
|
|
23
|
+
const shown = raw.classes
|
|
24
|
+
.slice(0, LABEL_CLASSES)
|
|
25
|
+
.map((name) => `.${name}`)
|
|
26
|
+
.join('');
|
|
27
|
+
const more = raw.classes.length - LABEL_CLASSES;
|
|
28
|
+
return `${raw.tag}${raw.id ? `#${raw.id}` : ''}${shown}${more > 0 ? `(+${more})` : ''}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Why the element cannot be seen, from `dom layout`'s measurements.
|
|
32
|
+
*
|
|
33
|
+
* @param layout - Layout measurements
|
|
34
|
+
* @param rendered - The element has a box
|
|
35
|
+
* @returns Visibility fields
|
|
36
|
+
*/
|
|
37
|
+
export function visibilityOf(layout, rendered) {
|
|
38
|
+
if (!rendered)
|
|
39
|
+
return { notRendered: true, ...(layout?.hiddenReason && { hidden: layout.hiddenReason }) };
|
|
40
|
+
if (!layout)
|
|
41
|
+
return {};
|
|
42
|
+
const hidden = layout.inViewport === 'hidden' ? layout.hiddenReason : layout.invisible;
|
|
43
|
+
const offscreen = ['above', 'below', 'left', 'right'].includes(layout.inViewport)
|
|
44
|
+
? layout.inViewport
|
|
45
|
+
: undefined;
|
|
46
|
+
return {
|
|
47
|
+
...(hidden && { hidden }),
|
|
48
|
+
...(offscreen && { offscreen }),
|
|
49
|
+
...(layout.coveredBy && { coveredBy: layout.coveredBy }),
|
|
50
|
+
...(layout.coverTransparent && { coverTransparent: true }),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/** Below this relative luminance a page background counts as dark */
|
|
54
|
+
const DARK_LUMINANCE = 0.18;
|
|
55
|
+
/**
|
|
56
|
+
* Header fields: label, text, placeholder, context, rect, visibility and
|
|
57
|
+
* the color scheme.
|
|
58
|
+
*
|
|
59
|
+
* @param sources - What was read
|
|
60
|
+
* @param request - Selector, index and match count
|
|
61
|
+
* @returns Header part of the result
|
|
62
|
+
*/
|
|
63
|
+
function header(sources, request) {
|
|
64
|
+
const { raw, size, layout } = sources;
|
|
65
|
+
const content = raw.textual && raw.content ? rowText(raw.content) : undefined;
|
|
66
|
+
return {
|
|
67
|
+
success: true,
|
|
68
|
+
selector: request.selector,
|
|
69
|
+
count: request.count,
|
|
70
|
+
index: request.index,
|
|
71
|
+
element: elementLabel(raw),
|
|
72
|
+
...(content && { content }),
|
|
73
|
+
...(raw.placeholder && { placeholder: rowText(raw.placeholder) }),
|
|
74
|
+
...(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
|
+
}),
|
|
79
|
+
visibility: visibilityOf(layout, size !== undefined),
|
|
80
|
+
...(sources.colorScheme && { colorScheme: sources.colorScheme }),
|
|
81
|
+
...(sources.colorScheme === 'dark' && pageLooksDark(raw) && { theme: 'dark' }),
|
|
82
|
+
...(raw.animating.length > 0 && { animating: raw.animating }),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Whether the page renders dark: a dark canvas (its color-scheme), or a dark
|
|
87
|
+
* background on the page's root (the backgrounds of body and html).
|
|
88
|
+
*
|
|
89
|
+
* @param raw - Backgrounds from the element up to the root, and the canvas
|
|
90
|
+
* @returns True for a dark page
|
|
91
|
+
*/
|
|
92
|
+
function pageLooksDark(raw) {
|
|
93
|
+
if (raw.canvasDark)
|
|
94
|
+
return true;
|
|
95
|
+
const root = effectiveBackground(raw.backgrounds.slice(-2), false).color;
|
|
96
|
+
return relativeLuminance(root) < DARK_LUMINANCE;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The style groups: box, layout, text, fills, strokes, radius, outline,
|
|
100
|
+
* effects, fx, state and pseudo-elements.
|
|
101
|
+
*
|
|
102
|
+
* @param sources - What was read
|
|
103
|
+
* @returns Group fields
|
|
104
|
+
*/
|
|
105
|
+
function groups(sources) {
|
|
106
|
+
const { style, parentStyle, raw } = sources;
|
|
107
|
+
const text = buildText(style, parentStyle, raw, sources.fonts);
|
|
108
|
+
const fills = buildFills(style);
|
|
109
|
+
const strokes = buildStrokes(style);
|
|
110
|
+
const radius = buildRadius(style);
|
|
111
|
+
const outline = buildOutline(style);
|
|
112
|
+
const effects = buildEffects(style);
|
|
113
|
+
const fx = buildFx(style);
|
|
114
|
+
const state = buildState(style, raw);
|
|
115
|
+
const pseudo = buildPseudo(sources.pseudo, style, raw);
|
|
116
|
+
const opacity = Number(style['opacity'] ?? 1);
|
|
117
|
+
const blend = style['mix-blend-mode'];
|
|
118
|
+
return {
|
|
119
|
+
box: buildBox(style, raw),
|
|
120
|
+
layout: buildLayout(style, parentStyle, raw),
|
|
121
|
+
...(text && { text }),
|
|
122
|
+
...(fills.length > 0 && { fills }),
|
|
123
|
+
...(opacity !== 1 && { opacity }),
|
|
124
|
+
...(blend && blend !== 'normal' && { blend }),
|
|
125
|
+
...(strokes.length > 0 && { strokes }),
|
|
126
|
+
...(radius && { radius }),
|
|
127
|
+
...(outline && { outline }),
|
|
128
|
+
...(effects.length > 0 && { effects }),
|
|
129
|
+
...(fx && { fx }),
|
|
130
|
+
...(state && { state }),
|
|
131
|
+
...(pseudo && { pseudo }),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The child tree fields.
|
|
136
|
+
*
|
|
137
|
+
* @param raw - Page-side walk
|
|
138
|
+
* @param limit - Rows shown at most
|
|
139
|
+
* @returns Children, hidden count and rows left out
|
|
140
|
+
*/
|
|
141
|
+
function treeFields(raw, limit) {
|
|
142
|
+
if (!raw.tree)
|
|
143
|
+
return {};
|
|
144
|
+
const { children, moreRows } = buildTree(raw.tree, limit, raw.treeSkipped);
|
|
145
|
+
return {
|
|
146
|
+
...(children.length > 0 && { children }),
|
|
147
|
+
...(raw.hiddenChildren > 0 && { hiddenChildren: raw.hiddenChildren }),
|
|
148
|
+
...(moreRows > 0 && { moreRows }),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The `--all` list: the element's own non-default longhands, plus its
|
|
153
|
+
* generated pseudo-elements' content and the placeholder color.
|
|
154
|
+
*
|
|
155
|
+
* @param sources - What was read
|
|
156
|
+
* @returns Properties and values
|
|
157
|
+
*/
|
|
158
|
+
function allFields(sources) {
|
|
159
|
+
const all = allStyles(sources.style, sources.raw.svg === true);
|
|
160
|
+
for (const pseudo of sources.pseudo) {
|
|
161
|
+
const content = pseudo.style['content'];
|
|
162
|
+
if (content && content !== 'none' && content !== 'normal') {
|
|
163
|
+
all[`${pseudo.type}`] = normalizeCssValue(content);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (sources.raw.placeholderColor)
|
|
167
|
+
all['::placeholder'] = `color ${hexColor(sources.raw.placeholderColor)}`;
|
|
168
|
+
return all;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The `bdg dom inspect` result.
|
|
172
|
+
*
|
|
173
|
+
* @param sources - What CDP and the page-side walk read
|
|
174
|
+
* @param request - Selector, index, match count and options
|
|
175
|
+
* @returns Inspect result
|
|
176
|
+
*/
|
|
177
|
+
export function buildInspectResult(sources, request) {
|
|
178
|
+
const result = header(sources, request);
|
|
179
|
+
if (request.propValues)
|
|
180
|
+
return { ...result, props: request.propValues };
|
|
181
|
+
const body = request.all ? { all: allFields(sources) } : groups(sources);
|
|
182
|
+
return { ...result, ...body, ...treeFields(sources.raw, request.treeLimit) };
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=inspectModel.js.map
|