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,596 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Human output of `bdg dom inspect`: a header line, then one line per group
|
|
3
|
+
* in Figma's order with DevTools' names (box, layout, parent, text, fill,
|
|
4
|
+
* border, fx, state, pseudo), then the child tree. Fields within a line are
|
|
5
|
+
* separated by ` · `; lengths are CSS px without the unit.
|
|
6
|
+
*
|
|
7
|
+
* ```
|
|
8
|
+
* button#buy.btn "Buy now" 120x41 @20,20 [flex] [prefers light]
|
|
9
|
+
* box p 12 24 · b 1
|
|
10
|
+
* layout inline-flex row · gap 8
|
|
11
|
+
* parent div.card flex column · gap 16 · in-parent l0 t0 r180 b0 · sib b16
|
|
12
|
+
* text Inter (webfont) 600 16/24 · color #fff · contrast 5.17 AA
|
|
13
|
+
* fill bg #1c69e3
|
|
14
|
+
* border 1 solid #32447f · radius 8
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { containerKind } from '../../runtime/dom/inspectLayoutModel.js';
|
|
18
|
+
import { joinLines } from '../formatting.js';
|
|
19
|
+
import { inspectCascadeNote, inspectAnimatingBadge, inspectMidTransitionNote, inspectDarkThemeBadge, inspectedMatchAction, inspectVisibilityBadges, multipleMatchesWarning, } from '../messages/commands.js';
|
|
20
|
+
import { shadowText, sidesShorthand } from '../../utils/cssValues.js';
|
|
21
|
+
import { truncateByLength } from '../../utils/strings.js';
|
|
22
|
+
/** Longest parent label on the parent line (CSS-module class names run long) */
|
|
23
|
+
const PARENT_LABEL_MAX = 40;
|
|
24
|
+
/** Width of the group label column */
|
|
25
|
+
const LABEL_WIDTH = 7;
|
|
26
|
+
/** Width a `--all` line wraps at */
|
|
27
|
+
const ALL_LINE_WIDTH = 100;
|
|
28
|
+
/**
|
|
29
|
+
* One group line: the label, then its fields joined with ` · `.
|
|
30
|
+
*
|
|
31
|
+
* @param label - Group name
|
|
32
|
+
* @param parts - Fields (empty ones left out)
|
|
33
|
+
* @returns The line, or undefined when there are no fields
|
|
34
|
+
*/
|
|
35
|
+
function groupLine(label, parts) {
|
|
36
|
+
const shown = parts.filter((part) => Boolean(part));
|
|
37
|
+
return shown.length > 0 ? `${label.padEnd(LABEL_WIDTH)}${shown.join(' · ')}` : undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The header: label, text, size and position, layout badge, what keeps it
|
|
41
|
+
* from being seen, where it lives and the color scheme.
|
|
42
|
+
*
|
|
43
|
+
* @param data - Inspect result
|
|
44
|
+
* @returns Header line
|
|
45
|
+
*/
|
|
46
|
+
export function inspectHeader(data) {
|
|
47
|
+
const display = data.layout?.display ?? data.all?.['display'];
|
|
48
|
+
const kind = containerKind(display);
|
|
49
|
+
return [
|
|
50
|
+
data.element,
|
|
51
|
+
data.content !== undefined && `"${data.content}"`,
|
|
52
|
+
data.placeholder !== undefined && `placeholder "${data.placeholder}"`,
|
|
53
|
+
data.rect && `${data.rect.w}x${data.rect.h} @${data.rect.x},${data.rect.y}`,
|
|
54
|
+
kind && `[${kind}]`,
|
|
55
|
+
...inspectVisibilityBadges(data.visibility),
|
|
56
|
+
data.context && `in ${data.context}`,
|
|
57
|
+
data.theme === 'dark' && inspectDarkThemeBadge(),
|
|
58
|
+
data.animating && inspectAnimatingBadge(data.animating),
|
|
59
|
+
]
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(' ');
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Whether all four sides are 0.
|
|
65
|
+
*
|
|
66
|
+
* @param sides - Sides
|
|
67
|
+
* @returns True when nothing is set
|
|
68
|
+
*/
|
|
69
|
+
function zero(sides) {
|
|
70
|
+
return sides.every((side) => side === 0);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Min/max size limits, e.g. `min-w 100`.
|
|
74
|
+
*
|
|
75
|
+
* @param kind - `min` or `max`
|
|
76
|
+
* @param limits - Width and height limits
|
|
77
|
+
* @returns Fields
|
|
78
|
+
*/
|
|
79
|
+
function limitParts(kind, limits) {
|
|
80
|
+
if (!limits)
|
|
81
|
+
return [];
|
|
82
|
+
return [
|
|
83
|
+
limits.w !== undefined ? `${kind}-w ${limits.w}` : '',
|
|
84
|
+
limits.h !== undefined ? `${kind}-h ${limits.h}` : '',
|
|
85
|
+
].filter(Boolean);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The box line: margin, padding, border widths (when not all 0), content-box
|
|
89
|
+
* sizing (when padding or border make the box larger than its CSS size),
|
|
90
|
+
* size limits, overflow and the scroll size.
|
|
91
|
+
*
|
|
92
|
+
* @param box - Box group
|
|
93
|
+
* @returns Line
|
|
94
|
+
*/
|
|
95
|
+
function boxLine(box) {
|
|
96
|
+
const padded = !zero(box.padding) || !zero(box.border);
|
|
97
|
+
return groupLine('box', [
|
|
98
|
+
!zero(box.margin) && `m ${sidesShorthand(box.margin)}`,
|
|
99
|
+
!zero(box.padding) && `p ${sidesShorthand(box.padding)}`,
|
|
100
|
+
!zero(box.border) && `b ${sidesShorthand(box.border)}`,
|
|
101
|
+
padded && box.sizing === 'content-box' && 'sizing content-box',
|
|
102
|
+
...limitParts('min', box.min),
|
|
103
|
+
...limitParts('max', box.max),
|
|
104
|
+
box.overflow && `overflow ${box.overflow}`,
|
|
105
|
+
box.scroll && `scroll ${box.scroll.w}x${box.scroll.h}`,
|
|
106
|
+
]);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Container fields as words, e.g. `wrap wrap`, `cols repeat(3,100)`, `gap 16`.
|
|
110
|
+
*
|
|
111
|
+
* @param container - Container fields
|
|
112
|
+
* @returns Fields
|
|
113
|
+
*/
|
|
114
|
+
function containerParts(container) {
|
|
115
|
+
const gap = Array.isArray(container.gap) ? container.gap.join(' ') : container.gap;
|
|
116
|
+
return [
|
|
117
|
+
container.wrap && `wrap ${container.wrap}`,
|
|
118
|
+
container.columns && `cols ${container.columns}`,
|
|
119
|
+
container.rows && `rows ${container.rows}`,
|
|
120
|
+
container.justify && `justify ${container.justify}`,
|
|
121
|
+
container.align && `align ${container.align}`,
|
|
122
|
+
gap !== undefined && `gap ${gap}`,
|
|
123
|
+
].filter((part) => Boolean(part));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Display with the flex direction, e.g. `flex column`.
|
|
127
|
+
*
|
|
128
|
+
* @param display - Display
|
|
129
|
+
* @param direction - Flex direction
|
|
130
|
+
* @returns Words
|
|
131
|
+
*/
|
|
132
|
+
function displayWords(display, direction) {
|
|
133
|
+
return direction ? `${display} ${direction}` : display;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The layout line: display, position, container and item fields.
|
|
137
|
+
*
|
|
138
|
+
* @param layout - Layout group
|
|
139
|
+
* @returns Line
|
|
140
|
+
*/
|
|
141
|
+
function layoutLine(layout) {
|
|
142
|
+
return groupLine('layout', [
|
|
143
|
+
displayWords(layout.display, layout.direction),
|
|
144
|
+
layout.position && `position ${layout.position}`,
|
|
145
|
+
layout.inset && `inset ${sidesShorthand(layout.inset)}`,
|
|
146
|
+
layout.z !== undefined && `z ${layout.z}`,
|
|
147
|
+
...containerParts(layout),
|
|
148
|
+
layout.flex && `flex ${layout.flex}`,
|
|
149
|
+
layout.self && `self ${layout.self}`,
|
|
150
|
+
layout.order !== undefined && `order ${layout.order}`,
|
|
151
|
+
layout.area && `area ${layout.area}`,
|
|
152
|
+
layout.valign && `valign ${layout.valign}`,
|
|
153
|
+
layout.float && `float ${layout.float}`,
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* The parent line: the parent's label, display and layout, then the
|
|
158
|
+
* element's offsets from its content edges and the gaps to its siblings.
|
|
159
|
+
*
|
|
160
|
+
* @param layout - Layout group
|
|
161
|
+
* @returns Line
|
|
162
|
+
*/
|
|
163
|
+
function parentLine(layout) {
|
|
164
|
+
const parent = layout.parent;
|
|
165
|
+
const offsets = layout.inParent;
|
|
166
|
+
const gaps = Object.entries(layout.siblings ?? {}).map(([side, gap]) => `${side[0]}${gap}`);
|
|
167
|
+
const parentWords = parent && [
|
|
168
|
+
truncateByLength(parent.element, PARENT_LABEL_MAX),
|
|
169
|
+
displayWords(parent.display, parent.direction),
|
|
170
|
+
...containerParts(parent),
|
|
171
|
+
parent.textAlign && `text-align ${parent.textAlign}`,
|
|
172
|
+
];
|
|
173
|
+
return groupLine('parent', [
|
|
174
|
+
parentWords?.filter(Boolean).join(' '),
|
|
175
|
+
offsets && `in-parent l${offsets.left} t${offsets.top} r${offsets.right} b${offsets.bottom}`,
|
|
176
|
+
gaps.length > 0 && `sib ${gaps.join(' ')}`,
|
|
177
|
+
]);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Font, weight and size as one phrase, e.g. `Inter (webfont) 600 italic 16/24`
|
|
181
|
+
* or `Inter (rendered "Helvetica") 400 14/normal` when the text was drawn in
|
|
182
|
+
* another font (a fallback, or the face's own name); for a container only
|
|
183
|
+
* the fields it has.
|
|
184
|
+
*
|
|
185
|
+
* @param text - Text group
|
|
186
|
+
* @returns Fields
|
|
187
|
+
*/
|
|
188
|
+
function fontParts(text) {
|
|
189
|
+
const loaded = [
|
|
190
|
+
text.rendered && `rendered "${text.rendered}"`,
|
|
191
|
+
text.webfont && 'webfont loaded',
|
|
192
|
+
].filter(Boolean);
|
|
193
|
+
const family = text.family && [text.family, loaded.length > 0 && `(${loaded.join(', ')})`];
|
|
194
|
+
const size = text.size !== undefined &&
|
|
195
|
+
(text.lineHeight !== undefined ? `${text.size}/${text.lineHeight}` : `${text.size}`);
|
|
196
|
+
if (family && text.weight !== undefined && size) {
|
|
197
|
+
return [
|
|
198
|
+
[...family, text.weight, text.style, size]
|
|
199
|
+
.filter((part) => part !== undefined && part !== false && part !== '')
|
|
200
|
+
.join(' '),
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
return [
|
|
204
|
+
family && `font ${family.filter(Boolean).join(' ')}`,
|
|
205
|
+
text.weight !== undefined && `weight ${text.weight}`,
|
|
206
|
+
text.style && `style ${text.style}`,
|
|
207
|
+
size && `size ${size}`,
|
|
208
|
+
!size && text.lineHeight !== undefined && `line-height ${text.lineHeight}`,
|
|
209
|
+
].filter((part) => Boolean(part));
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* A contrast as words, e.g. `contrast 4.47 fail on #fff (faded: opacity 0.4)`.
|
|
213
|
+
*
|
|
214
|
+
* @param contrast - Contrast
|
|
215
|
+
* @returns Words, or undefined
|
|
216
|
+
*/
|
|
217
|
+
function contrastText(contrast) {
|
|
218
|
+
if (!contrast)
|
|
219
|
+
return undefined;
|
|
220
|
+
return [
|
|
221
|
+
`contrast ${contrast.ratio} ${contrast.level}`,
|
|
222
|
+
contrast.inherited && `on ${contrast.background}`,
|
|
223
|
+
contrast.overImage && '(over image)',
|
|
224
|
+
contrast.opacity !== undefined && `(faded: opacity ${contrast.opacity})`,
|
|
225
|
+
]
|
|
226
|
+
.filter(Boolean)
|
|
227
|
+
.join(' ');
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* The text line: font, color, contrast and the non-default extras.
|
|
231
|
+
*
|
|
232
|
+
* @param text - Text group
|
|
233
|
+
* @returns Line
|
|
234
|
+
*/
|
|
235
|
+
function textLine(text) {
|
|
236
|
+
return groupLine('text', [
|
|
237
|
+
...fontParts(text),
|
|
238
|
+
text.color && `color ${text.color}`,
|
|
239
|
+
contrastText(text.contrast),
|
|
240
|
+
text.align && `align ${text.align}`,
|
|
241
|
+
text.transform && `transform ${text.transform}`,
|
|
242
|
+
text.tracking !== undefined && `tracking ${text.tracking}`,
|
|
243
|
+
text.decoration && `decoration ${text.decoration}`,
|
|
244
|
+
text.whiteSpace && `ws ${text.whiteSpace}`,
|
|
245
|
+
text.overflow && `text-overflow ${text.overflow}`,
|
|
246
|
+
text.clamp && `clamp ${text.clamp}`,
|
|
247
|
+
text.shadow && `shadow ${text.shadow}`,
|
|
248
|
+
text.features && `features ${text.features}`,
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* A fill as words.
|
|
253
|
+
*
|
|
254
|
+
* @param fill - Fill
|
|
255
|
+
* @returns e.g. `bg #fff`, `bg-image url(hero.png) size cover`
|
|
256
|
+
*/
|
|
257
|
+
function fillText(fill) {
|
|
258
|
+
if (fill.type === 'solid')
|
|
259
|
+
return `bg ${fill.color}`;
|
|
260
|
+
if (fill.type === 'gradient')
|
|
261
|
+
return `bg-image ${fill.value}`;
|
|
262
|
+
return `bg-image ${fill.value}${fill.size ? ` size ${fill.size}` : ''}`;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* The fill line: backgrounds, opacity and blend mode.
|
|
266
|
+
*
|
|
267
|
+
* @param data - Inspect result
|
|
268
|
+
* @returns Line
|
|
269
|
+
*/
|
|
270
|
+
function fillLine(data) {
|
|
271
|
+
return groupLine('fill', [
|
|
272
|
+
...(data.fills ?? []).map(fillText),
|
|
273
|
+
data.opacity !== undefined && `opacity ${data.opacity}`,
|
|
274
|
+
data.blend && `blend ${data.blend}`,
|
|
275
|
+
]);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* The border line: strokes, radius and outline.
|
|
279
|
+
*
|
|
280
|
+
* @param data - Inspect result
|
|
281
|
+
* @returns Line
|
|
282
|
+
*/
|
|
283
|
+
function borderLine(data) {
|
|
284
|
+
const outline = data.outline;
|
|
285
|
+
return groupLine('border', [
|
|
286
|
+
...(data.strokes ?? []).map((s) => `${s.side === 'all' ? '' : `${s.side} `}${s.width} ${s.style} ${s.color}`),
|
|
287
|
+
data.radius && `radius ${sidesShorthand(data.radius)}`,
|
|
288
|
+
outline &&
|
|
289
|
+
`outline ${outline.width} ${outline.style} ${outline.color}${outline.offset ? ` offset ${outline.offset}` : ''}`,
|
|
290
|
+
]);
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* The fx line: shadows, transform, filters, clip, mask and animation.
|
|
294
|
+
*
|
|
295
|
+
* @param data - Inspect result
|
|
296
|
+
* @returns Line
|
|
297
|
+
*/
|
|
298
|
+
function fxLine(data) {
|
|
299
|
+
const shadows = (data.effects ?? []).map((e) => shadowText({ ...e, inset: e.type === 'inner-shadow' }));
|
|
300
|
+
const fx = data.fx ?? {};
|
|
301
|
+
return groupLine('fx', [
|
|
302
|
+
shadows.length > 0 && `shadow ${shadows.join(', ')}`,
|
|
303
|
+
fx.transform && `transform ${fx.transform}`,
|
|
304
|
+
fx.filter && `filter ${fx.filter}`,
|
|
305
|
+
fx.backdrop && `backdrop ${fx.backdrop}`,
|
|
306
|
+
fx.clip && `clip ${fx.clip}`,
|
|
307
|
+
fx.mask && `mask ${fx.mask}`,
|
|
308
|
+
fx.animation && `animation ${fx.animation}`,
|
|
309
|
+
]);
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* The state line.
|
|
313
|
+
*
|
|
314
|
+
* @param data - Inspect result
|
|
315
|
+
* @returns Line
|
|
316
|
+
*/
|
|
317
|
+
function stateLine(data) {
|
|
318
|
+
const state = data.state ?? {};
|
|
319
|
+
return groupLine('state', [
|
|
320
|
+
state.cursor && `cursor ${state.cursor}`,
|
|
321
|
+
state.pointerEvents && `pointer-events ${state.pointerEvents}`,
|
|
322
|
+
state.visibility && `visibility ${state.visibility}`,
|
|
323
|
+
state.userSelect && `select ${state.userSelect}`,
|
|
324
|
+
state.appearance && `appearance ${state.appearance}`,
|
|
325
|
+
]);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* One pseudo-element as words.
|
|
329
|
+
*
|
|
330
|
+
* @param pseudo - Pseudo-element
|
|
331
|
+
* @returns e.g. `::before content "★" absolute 25x15 color #f00`
|
|
332
|
+
*/
|
|
333
|
+
function pseudoText(pseudo) {
|
|
334
|
+
const shadows = (pseudo.effects ?? []).map((e) => shadowText({ ...e, inset: e.type === 'inner-shadow' }));
|
|
335
|
+
return [
|
|
336
|
+
pseudo.type,
|
|
337
|
+
pseudo.content !== undefined && `content ${pseudo.content}`,
|
|
338
|
+
pseudo.display,
|
|
339
|
+
pseudo.position,
|
|
340
|
+
pseudo.size && `${pseudo.size.w}x${pseudo.size.h}`,
|
|
341
|
+
pseudo.color && `color ${pseudo.color}`,
|
|
342
|
+
pseudo.fontStyle,
|
|
343
|
+
pseudo.fontWeight !== undefined && `weight ${pseudo.fontWeight}`,
|
|
344
|
+
pseudo.contrast && `· ${contrastText(pseudo.contrast)}`,
|
|
345
|
+
...(pseudo.fills ?? []).map(fillText),
|
|
346
|
+
pseudo.radius && `radius ${pseudo.radius}`,
|
|
347
|
+
shadows.length > 0 && `shadow ${shadows.join(', ')}`,
|
|
348
|
+
pseudo.transform && `transform ${pseudo.transform}`,
|
|
349
|
+
pseudo.opacity !== undefined && `opacity ${pseudo.opacity}`,
|
|
350
|
+
]
|
|
351
|
+
.filter(Boolean)
|
|
352
|
+
.join(' ');
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Pseudo-element lines (the label on the first only).
|
|
356
|
+
*
|
|
357
|
+
* @param pseudo - Pseudo-elements
|
|
358
|
+
* @returns Lines
|
|
359
|
+
*/
|
|
360
|
+
function pseudoLines(pseudo) {
|
|
361
|
+
return (pseudo ?? []).map((entry, i) => `${(i === 0 ? 'pseudo' : '').padEnd(LABEL_WIDTH)}${pseudoText(entry)}`);
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* One tree row.
|
|
365
|
+
*
|
|
366
|
+
* @param node - Row
|
|
367
|
+
* @returns e.g. `li.card ×33 266x107`, `div.card-body 286x190 [flex] (3)`
|
|
368
|
+
*/
|
|
369
|
+
function treeRow(node) {
|
|
370
|
+
return [
|
|
371
|
+
node.element,
|
|
372
|
+
node.count !== undefined && `×${node.count}`,
|
|
373
|
+
`${node.w}x${node.h}`,
|
|
374
|
+
node.layout && `[${node.layout}]`,
|
|
375
|
+
node.text && `"${node.text}"`,
|
|
376
|
+
node.childCount !== undefined && `(${node.childCount})`,
|
|
377
|
+
]
|
|
378
|
+
.filter(Boolean)
|
|
379
|
+
.join(' ');
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Tree rows, indented by depth, with the hidden children of each level.
|
|
383
|
+
*
|
|
384
|
+
* @param nodes - Rows
|
|
385
|
+
* @param hidden - Hidden children at this level
|
|
386
|
+
* @param depth - Indentation level
|
|
387
|
+
* @returns Lines
|
|
388
|
+
*/
|
|
389
|
+
function treeLines(nodes, hidden, depth) {
|
|
390
|
+
const indent = ' '.repeat(depth);
|
|
391
|
+
const lines = nodes.flatMap((node) => [
|
|
392
|
+
`${indent}${treeRow(node)}`,
|
|
393
|
+
...treeLines(node.children ?? [], node.hiddenChildren, depth + 1),
|
|
394
|
+
]);
|
|
395
|
+
return hidden ? [...lines, `${indent}(+${hidden} not rendered)`] : lines;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Lines under a label shown on the first only (`hints a`, ` b`).
|
|
399
|
+
*
|
|
400
|
+
* @param label - Group label
|
|
401
|
+
* @param lines - Lines
|
|
402
|
+
* @returns Labelled lines
|
|
403
|
+
*/
|
|
404
|
+
function labelledLines(label, lines) {
|
|
405
|
+
return lines.map((line, i) => `${i === 0 ? label.padEnd(6) : ''.padEnd(6)} ${line}`);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Where a rule applies when it is not always: its layer and condition.
|
|
409
|
+
*
|
|
410
|
+
* @param entry - Layer and media/container condition
|
|
411
|
+
* @returns e.g. ` @media (min-width: 80rem) layer utilities`, or empty
|
|
412
|
+
*/
|
|
413
|
+
function ruleScope(entry) {
|
|
414
|
+
return [entry.condition && ` @${entry.condition}`, entry.layer && ` layer ${entry.layer}`]
|
|
415
|
+
.filter(Boolean)
|
|
416
|
+
.join('');
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Hints, `--rules` and `--why` lines, and a note when the cascade was not read in time.
|
|
420
|
+
*
|
|
421
|
+
* @param data - Inspect result
|
|
422
|
+
* @returns Lines
|
|
423
|
+
*/
|
|
424
|
+
function cascadeBlock(data) {
|
|
425
|
+
const hints = (data.hints ?? []).map((hint) => `${hint.property}: ${hint.value} ${hint.kind === 'not-inherited' ? "is the browser's" : 'has no effect'}: ${hint.reason} → ${hint.fix} · in ${hint.source}`);
|
|
426
|
+
return [
|
|
427
|
+
...labelledLines('hints', data.hints?.length === 0 ? ['none'] : hints),
|
|
428
|
+
...labelledLines('rules', (data.rules ?? []).map(ruleLine)),
|
|
429
|
+
...labelledLines('why', (data.why ?? []).flatMap((why) => whyLines(why, data))),
|
|
430
|
+
...(data.cascade ? [inspectCascadeNote(data.cascade)] : []),
|
|
431
|
+
];
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* A `--rules` line: property, value as written (= computed for `var()`),
|
|
435
|
+
* source, scope, inheritance and what it beats.
|
|
436
|
+
*
|
|
437
|
+
* @param rule - Rule
|
|
438
|
+
* @returns Line
|
|
439
|
+
*/
|
|
440
|
+
function ruleLine(rule) {
|
|
441
|
+
return [
|
|
442
|
+
`${rule.property} ${truncateByLength(rule.value, CASCADE_VALUE_WIDTH)}`,
|
|
443
|
+
rule.computed !== undefined && ` = ${truncateByLength(rule.computed, CASCADE_VALUE_WIDTH)}`,
|
|
444
|
+
`${rule.important ? ' !important' : ''} ← ${rule.source}`,
|
|
445
|
+
ruleScope(rule),
|
|
446
|
+
rule.inherited !== undefined && ` (inherited from ${levelsUp(rule.inherited)})`,
|
|
447
|
+
rule.overrides && ` over ${rule.overrides.join(', ')}`,
|
|
448
|
+
]
|
|
449
|
+
.filter(Boolean)
|
|
450
|
+
.join('');
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* `--why` lines of one property: the computed value, each declaration
|
|
454
|
+
* (`✓` the winner, `✗` the ones it beats) and where the winner's custom
|
|
455
|
+
* properties are set.
|
|
456
|
+
*
|
|
457
|
+
* @param why - Why
|
|
458
|
+
* @param data - Inspect result (for running transitions)
|
|
459
|
+
* @returns Lines
|
|
460
|
+
*/
|
|
461
|
+
function whyLines(why, data) {
|
|
462
|
+
const entries = why.chain.map((entry) => {
|
|
463
|
+
const value = `${entry.via ? `${entry.via}: ` : ''}${entry.value}`;
|
|
464
|
+
const resolved = entry.unset
|
|
465
|
+
? ` = invalid: ${entry.unset.join(', ')} not set`
|
|
466
|
+
: entry.resolved !== undefined
|
|
467
|
+
? ` = ${truncateByLength(entry.resolved, CASCADE_VALUE_WIDTH)}`
|
|
468
|
+
: '';
|
|
469
|
+
return [
|
|
470
|
+
` ${entry.status === 'overridden' ? '✗' : '✓'} `,
|
|
471
|
+
truncateByLength(value, CASCADE_VALUE_WIDTH) + resolved,
|
|
472
|
+
entry.important ? ' !important' : '',
|
|
473
|
+
` ${entry.source}${entry.specificity ? ` [${entry.specificity.join(',')}]` : ''}${ruleScope(entry)}`,
|
|
474
|
+
entry.status === 'inherited' ? ' (inherited)' : '',
|
|
475
|
+
].join('');
|
|
476
|
+
});
|
|
477
|
+
const variables = (why.variables ?? []).map((variable) => ` ${variable.name}: ${truncateByLength(variable.value, CASCADE_VALUE_WIDTH)} ${variable.source}${variable.inherited !== undefined ? ` (inherited from ${levelsUp(variable.inherited)})` : ''}`);
|
|
478
|
+
return [
|
|
479
|
+
`${why.property} = ${why.computed}${midTransition(why.property, data.animating) ? ` ${inspectMidTransitionNote()}` : ''}`,
|
|
480
|
+
...entries.slice(0, 1),
|
|
481
|
+
...variables,
|
|
482
|
+
...entries.slice(1),
|
|
483
|
+
...(why.chain.length === 0 ? [' no author declaration: the default or inherited value'] : []),
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Whether a property is being transitioned (or animated) right now.
|
|
488
|
+
*
|
|
489
|
+
* @param property - Longhand or shorthand asked about
|
|
490
|
+
* @param animating - Running transitions' properties and animations' names
|
|
491
|
+
* @returns True when its value is mid-way
|
|
492
|
+
*/
|
|
493
|
+
function midTransition(property, animating) {
|
|
494
|
+
return (animating ?? []).some((name) => name === 'all' ||
|
|
495
|
+
name === property ||
|
|
496
|
+
property.startsWith(`${name}-`) ||
|
|
497
|
+
name.startsWith(`${property}-`));
|
|
498
|
+
}
|
|
499
|
+
/**
|
|
500
|
+
* How far up the ancestor an inherited value comes from is.
|
|
501
|
+
*
|
|
502
|
+
* @param levels - Ancestor levels
|
|
503
|
+
* @returns e.g. `the parent`, `3 levels up`
|
|
504
|
+
*/
|
|
505
|
+
function levelsUp(levels) {
|
|
506
|
+
return levels === 1 ? 'the parent' : `${levels} levels up`;
|
|
507
|
+
}
|
|
508
|
+
/** Longest declared value shown in `--rules` and `--why` lines (font stacks run long) */
|
|
509
|
+
const CASCADE_VALUE_WIDTH = 60;
|
|
510
|
+
/**
|
|
511
|
+
* The tree block.
|
|
512
|
+
*
|
|
513
|
+
* @param data - Inspect result
|
|
514
|
+
* @returns Lines (none without children)
|
|
515
|
+
*/
|
|
516
|
+
function treeBlock(data) {
|
|
517
|
+
if (!data.children && !data.hiddenChildren)
|
|
518
|
+
return [];
|
|
519
|
+
return [
|
|
520
|
+
'tree',
|
|
521
|
+
...treeLines(data.children ?? [], data.hiddenChildren, 1),
|
|
522
|
+
...(data.moreRows ? [` … +${data.moreRows} more`] : []),
|
|
523
|
+
];
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* `--all` properties packed into lines of about {@link ALL_LINE_WIDTH} characters.
|
|
527
|
+
*
|
|
528
|
+
* @param all - Properties and values
|
|
529
|
+
* @returns Lines
|
|
530
|
+
*/
|
|
531
|
+
function allLines(all) {
|
|
532
|
+
const lines = [];
|
|
533
|
+
let current = [];
|
|
534
|
+
for (const [name, value] of Object.entries(all)) {
|
|
535
|
+
current.push(`${name} ${value}`);
|
|
536
|
+
if (current.join(' · ').length > ALL_LINE_WIDTH) {
|
|
537
|
+
lines.push(current.join(' · '));
|
|
538
|
+
current = [];
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (current.length > 0)
|
|
542
|
+
lines.push(current.join(' · '));
|
|
543
|
+
return lines.map((line, i) => `${(i === 0 ? 'all' : '').padEnd(LABEL_WIDTH)}${line}`);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* `--props` lines: the computed value, and the normalized one when it differs;
|
|
547
|
+
* a custom property no rule sets is `(not set)`.
|
|
548
|
+
*
|
|
549
|
+
* @param props - Properties asked for
|
|
550
|
+
* @returns Lines
|
|
551
|
+
*/
|
|
552
|
+
function propLines(props) {
|
|
553
|
+
if (Object.keys(props).length === 0)
|
|
554
|
+
return ['(no matching custom properties)'];
|
|
555
|
+
return Object.entries(props).map(([name, prop]) => `${name}: ${prop.computed || (name.startsWith('--') ? '(not set)' : '(empty)')}${prop.value !== prop.computed ? ` = ${prop.value}` : ''}`);
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* The style group lines.
|
|
559
|
+
*
|
|
560
|
+
* @param data - Inspect result
|
|
561
|
+
* @returns Lines
|
|
562
|
+
*/
|
|
563
|
+
function groupLines(data) {
|
|
564
|
+
return [
|
|
565
|
+
data.box && boxLine(data.box),
|
|
566
|
+
data.layout && layoutLine(data.layout),
|
|
567
|
+
data.layout && parentLine(data.layout),
|
|
568
|
+
data.text && textLine(data.text),
|
|
569
|
+
fillLine(data),
|
|
570
|
+
borderLine(data),
|
|
571
|
+
fxLine(data),
|
|
572
|
+
stateLine(data),
|
|
573
|
+
...pseudoLines(data.pseudo),
|
|
574
|
+
];
|
|
575
|
+
}
|
|
576
|
+
/**
|
|
577
|
+
* Format `bdg dom inspect` output (`--why` answers only its question:
|
|
578
|
+
* the header, hints and the declarations, no style groups or tree).
|
|
579
|
+
*
|
|
580
|
+
* @param data - Inspect result
|
|
581
|
+
* @returns Formatted output
|
|
582
|
+
*/
|
|
583
|
+
export function formatInspect(data) {
|
|
584
|
+
const body = data.props
|
|
585
|
+
? propLines(data.props)
|
|
586
|
+
: data.all
|
|
587
|
+
? allLines(data.all)
|
|
588
|
+
: groupLines(data);
|
|
589
|
+
const note = data.picked
|
|
590
|
+
? multipleMatchesWarning(data.count, inspectedMatchAction(data.picked, data.index))
|
|
591
|
+
: undefined;
|
|
592
|
+
if (data.why)
|
|
593
|
+
return joinLines(inspectHeader(data), ...cascadeBlock(data), note);
|
|
594
|
+
return joinLines(inspectHeader(data), ...body, ...cascadeBlock(data), ...treeBlock(data), note);
|
|
595
|
+
}
|
|
596
|
+
//# sourceMappingURL=inspect.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { InstalledSkill } from '../../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Format where the skill was installed, one line per agent.
|
|
4
|
+
*
|
|
5
|
+
* @param data - Install results
|
|
6
|
+
* @returns Human-readable summary
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatInstalledSkills(data: {
|
|
9
|
+
skills: InstalledSkill[];
|
|
10
|
+
}): string;
|
|
11
|
+
//# sourceMappingURL=installSkill.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { homedir } from 'os';
|
|
2
|
+
import { OutputFormatter } from '../formatting.js';
|
|
3
|
+
/**
|
|
4
|
+
* Shorten a path under the home directory to `~/...`.
|
|
5
|
+
*
|
|
6
|
+
* @param path - Absolute path
|
|
7
|
+
* @returns Path as the user would type it
|
|
8
|
+
*/
|
|
9
|
+
function tildePath(path) {
|
|
10
|
+
const home = homedir();
|
|
11
|
+
return path.startsWith(`${home}/`) ? `~${path.slice(home.length)}` : path;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Format where the skill was installed, one line per agent.
|
|
15
|
+
*
|
|
16
|
+
* @param data - Install results
|
|
17
|
+
* @returns Human-readable summary
|
|
18
|
+
*/
|
|
19
|
+
export function formatInstalledSkills(data) {
|
|
20
|
+
const fmt = new OutputFormatter().text('bdg skill:');
|
|
21
|
+
for (const skill of data.skills) {
|
|
22
|
+
fmt.text(` ${skill.target.padEnd(6)} ${skill.status.padEnd(9)} ${tildePath(skill.path)}`);
|
|
23
|
+
}
|
|
24
|
+
return fmt
|
|
25
|
+
.hints('Next:', [
|
|
26
|
+
'Start a new agent session to load it (running sessions keep the old list)',
|
|
27
|
+
'After upgrading bdg, run bdg install-skill again',
|
|
28
|
+
])
|
|
29
|
+
.build();
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=installSkill.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Key attributes of an element (`KeyAttributes`) shortened for one line of
|
|
3
|
+
* human output: `dom query` shows them in the element's tag, `dom get` after
|
|
4
|
+
* its role. `--json` has the full values.
|
|
5
|
+
*/
|
|
6
|
+
import type { KeyAttributes } from '../../types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Key attributes as `name="value"` items, values shortened: an image's
|
|
9
|
+
* file name, an iframe's host, links without their scheme, the rest cut in
|
|
10
|
+
* the middle at 40 characters. `checked` is shown as a bare word, and left
|
|
11
|
+
* out when false.
|
|
12
|
+
*
|
|
13
|
+
* @param tag - Element tag
|
|
14
|
+
* @param attributes - Key attributes
|
|
15
|
+
* @param leaveOut - Names not to show (already shown elsewhere)
|
|
16
|
+
* @returns Items in attribute order, e.g. `['src="…/logo.png"', 'alt="Logo"']`
|
|
17
|
+
*/
|
|
18
|
+
export declare function keyAttributeItems(tag: string, attributes: KeyAttributes | undefined, leaveOut?: ReadonlySet<string>): string[];
|
|
19
|
+
//# sourceMappingURL=keyAttributes.d.ts.map
|