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.
Files changed (139) hide show
  1. package/.claude/skills/bdg/SKILL.md +268 -0
  2. package/README.md +15 -1
  3. package/dist/commands/dom/a11y.js +2 -1
  4. package/dist/commands/dom/formInteraction.js +56 -25
  5. package/dist/commands/dom/helpers/keyAttributes.d.ts +20 -0
  6. package/dist/commands/dom/helpers/keyAttributes.js +54 -0
  7. package/dist/commands/dom/helpers/query.d.ts +1 -1
  8. package/dist/commands/dom/helpers/query.js +66 -19
  9. package/dist/commands/dom/helpers/runElementCommand.js +4 -3
  10. package/dist/commands/dom/helpers/screenshot.js +85 -12
  11. package/dist/commands/dom/index.d.ts +1 -0
  12. package/dist/commands/dom/index.js +8 -3
  13. package/dist/commands/dom/inspect.d.ts +15 -0
  14. package/dist/commands/dom/inspect.js +82 -0
  15. package/dist/commands/dom/layout.js +2 -2
  16. package/dist/commands/dom/listeners.js +2 -2
  17. package/dist/commands/dom/semanticUtils.d.ts +14 -1
  18. package/dist/commands/dom/semanticUtils.js +44 -3
  19. package/dist/commands/installSkill.d.ts +20 -0
  20. package/dist/commands/installSkill.js +87 -0
  21. package/dist/commands/network/list.js +13 -2
  22. package/dist/commands/optionBehaviors.js +48 -6
  23. package/dist/commands/page.d.ts +1 -1
  24. package/dist/commands/page.js +62 -3
  25. package/dist/commands/shared/commonOptions.d.ts +4 -0
  26. package/dist/commands/shared/commonOptions.js +9 -0
  27. package/dist/commands/shared/optionTypes.d.ts +21 -0
  28. package/dist/commands/shared/startHelpers.d.ts +66 -0
  29. package/dist/commands/shared/startHelpers.js +91 -10
  30. package/dist/commands/shared/validation.d.ts +11 -0
  31. package/dist/commands/shared/validation.js +16 -0
  32. package/dist/commands.js +3 -0
  33. package/dist/daemon/launcher.d.ts +8 -1
  34. package/dist/daemon/launcher.js +3 -1
  35. package/dist/daemon/session/Session.d.ts +7 -0
  36. package/dist/daemon/session/Session.js +23 -1
  37. package/dist/daemon/session/commandRegistry.d.ts +14 -1
  38. package/dist/daemon/session/commandRegistry.js +65 -9
  39. package/dist/daemon/session/interactions.d.ts +18 -5
  40. package/dist/daemon/session/interactions.js +22 -12
  41. package/dist/daemon.js +3565 -329
  42. package/dist/errors/messages.d.ts +85 -0
  43. package/dist/errors/messages.js +128 -1
  44. package/dist/index.js +2151 -960
  45. package/dist/ipc/client.d.ts +9 -0
  46. package/dist/ipc/client.js +13 -0
  47. package/dist/ipc/protocol/commands.d.ts +56 -1
  48. package/dist/ipc/protocol/commands.js +2 -0
  49. package/dist/ipc/protocol/domTypes.d.ts +35 -2
  50. package/dist/ipc/protocol/inspectTypes.d.ts +388 -0
  51. package/dist/ipc/protocol/inspectTypes.js +10 -0
  52. package/dist/runtime/dom/actionEffects.d.ts +94 -15
  53. package/dist/runtime/dom/actionEffects.js +173 -27
  54. package/dist/runtime/dom/actionEffectsScripts.d.ts +52 -14
  55. package/dist/runtime/dom/actionEffectsScripts.js +224 -32
  56. package/dist/runtime/dom/elementInfo.d.ts +26 -0
  57. package/dist/runtime/dom/elementInfo.js +65 -0
  58. package/dist/runtime/dom/eventListeners.js +14 -4
  59. package/dist/runtime/dom/formFillHelpers/fill.d.ts +3 -4
  60. package/dist/runtime/dom/formFillHelpers/fill.js +77 -28
  61. package/dist/runtime/dom/frameSelection.d.ts +11 -0
  62. package/dist/runtime/dom/frameSelection.js +20 -1
  63. package/dist/runtime/dom/frames.d.ts +38 -5
  64. package/dist/runtime/dom/frames.js +136 -21
  65. package/dist/runtime/dom/inspect.d.ts +28 -0
  66. package/dist/runtime/dom/inspect.js +557 -0
  67. package/dist/runtime/dom/inspectAllStyles.d.ts +62 -0
  68. package/dist/runtime/dom/inspectAllStyles.js +385 -0
  69. package/dist/runtime/dom/inspectCascade.d.ts +94 -0
  70. package/dist/runtime/dom/inspectCascade.js +371 -0
  71. package/dist/runtime/dom/inspectCascadeModel.d.ts +39 -0
  72. package/dist/runtime/dom/inspectCascadeModel.js +232 -0
  73. package/dist/runtime/dom/inspectHints.d.ts +62 -0
  74. package/dist/runtime/dom/inspectHints.js +305 -0
  75. package/dist/runtime/dom/inspectLayoutModel.d.ts +87 -0
  76. package/dist/runtime/dom/inspectLayoutModel.js +346 -0
  77. package/dist/runtime/dom/inspectModel.d.ts +74 -0
  78. package/dist/runtime/dom/inspectModel.js +184 -0
  79. package/dist/runtime/dom/inspectPaintModel.d.ts +157 -0
  80. package/dist/runtime/dom/inspectPaintModel.js +461 -0
  81. package/dist/runtime/dom/inspectRules.d.ts +37 -0
  82. package/dist/runtime/dom/inspectRules.js +101 -0
  83. package/dist/runtime/dom/inspectScripts.d.ts +132 -0
  84. package/dist/runtime/dom/inspectScripts.js +263 -0
  85. package/dist/runtime/dom/inspectTree.d.ts +40 -0
  86. package/dist/runtime/dom/inspectTree.js +134 -0
  87. package/dist/runtime/dom/inspectVariables.d.ts +33 -0
  88. package/dist/runtime/dom/inspectVariables.js +94 -0
  89. package/dist/runtime/dom/inspectWhyModel.d.ts +20 -0
  90. package/dist/runtime/dom/inspectWhyModel.js +134 -0
  91. package/dist/runtime/dom/layout.d.ts +5 -1
  92. package/dist/runtime/dom/layout.js +10 -3
  93. package/dist/runtime/dom/listenerPageScripts.d.ts +11 -5
  94. package/dist/runtime/dom/listenerPageScripts.js +95 -9
  95. package/dist/runtime/dom/listenerSummary.d.ts +4 -0
  96. package/dist/runtime/dom/listenerSummary.js +26 -9
  97. package/dist/runtime/dom/reactEventHelpers.d.ts +5 -0
  98. package/dist/runtime/dom/reactEventHelpers.js +12 -4
  99. package/dist/runtime/page/emulation.d.ts +20 -0
  100. package/dist/runtime/page/emulation.js +37 -0
  101. package/dist/telemetry/a11y.d.ts +10 -0
  102. package/dist/telemetry/a11y.js +78 -1
  103. package/dist/telemetry/console.d.ts +1 -0
  104. package/dist/telemetry/console.js +100 -5
  105. package/dist/telemetry/network.js +3 -1
  106. package/dist/types.d.ts +40 -0
  107. package/dist/ui/formatters/details.d.ts +8 -0
  108. package/dist/ui/formatters/details.js +59 -3
  109. package/dist/ui/formatters/dom.d.ts +2 -1
  110. package/dist/ui/formatters/dom.js +25 -9
  111. package/dist/ui/formatters/inspect.d.ts +39 -0
  112. package/dist/ui/formatters/inspect.js +596 -0
  113. package/dist/ui/formatters/installSkill.d.ts +11 -0
  114. package/dist/ui/formatters/installSkill.js +31 -0
  115. package/dist/ui/formatters/keyAttributes.d.ts +19 -0
  116. package/dist/ui/formatters/keyAttributes.js +84 -0
  117. package/dist/ui/formatters/layout.js +2 -2
  118. package/dist/ui/formatters/networkHeaders.d.ts +13 -0
  119. package/dist/ui/formatters/networkHeaders.js +23 -3
  120. package/dist/ui/formatters/networkList.d.ts +29 -1
  121. package/dist/ui/formatters/networkList.js +86 -20
  122. package/dist/ui/formatters/status.js +1 -1
  123. package/dist/ui/formatting.d.ts +9 -0
  124. package/dist/ui/formatting.js +6 -3
  125. package/dist/ui/messages/commands.d.ts +123 -7
  126. package/dist/ui/messages/commands.js +181 -10
  127. package/dist/ui/messages/networkMessages.d.ts +14 -0
  128. package/dist/ui/messages/networkMessages.js +18 -0
  129. package/dist/ui/messages/session.d.ts +14 -0
  130. package/dist/ui/messages/session.js +20 -0
  131. package/dist/utils/async.d.ts +9 -0
  132. package/dist/utils/async.js +17 -0
  133. package/dist/utils/color.d.ts +84 -0
  134. package/dist/utils/color.js +376 -0
  135. package/dist/utils/cssValues.d.ts +109 -0
  136. package/dist/utils/cssValues.js +236 -0
  137. package/dist/utils/selectorFilters.d.ts +12 -0
  138. package/dist/utils/selectorFilters.js +29 -0
  139. 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