browser-debugger-cli 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/bdg/SKILL.md +268 -0
- package/README.md +148 -74
- package/dist/commands/css.d.ts +13 -0
- package/dist/commands/css.js +53 -0
- package/dist/commands/dom/audit.d.ts +14 -0
- package/dist/commands/dom/audit.js +87 -0
- package/dist/commands/dom/formInteraction.js +36 -6
- package/dist/commands/dom/helpers/keyAttributes.d.ts +3 -2
- package/dist/commands/dom/helpers/keyAttributes.js +6 -4
- package/dist/commands/dom/helpers/screenshot.d.ts +1 -0
- package/dist/commands/dom/helpers/screenshot.js +158 -38
- package/dist/commands/dom/index.js +4 -1
- package/dist/commands/dom/screenshot.js +10 -6
- package/dist/commands/dom/wait.js +5 -3
- package/dist/commands/helpJson.js +1 -1
- package/dist/commands/installSkill.d.ts +20 -0
- package/dist/commands/installSkill.js +87 -0
- package/dist/commands/optionBehaviors.js +21 -6
- package/dist/commands/page.js +7 -4
- package/dist/commands/peek.d.ts +7 -0
- package/dist/commands/peek.js +65 -23
- package/dist/commands/shared/optionTypes.d.ts +5 -1
- package/dist/commands/start.d.ts +13 -0
- package/dist/commands/start.js +19 -2
- package/dist/commands/tail.d.ts +7 -1
- package/dist/commands/tail.js +13 -62
- package/dist/commands.js +5 -0
- package/dist/daemon/session/commandRegistry.js +7 -1
- package/dist/daemon/session/plugins.js +4 -52
- package/dist/daemon.js +7986 -6848
- package/dist/errors/messages.d.ts +50 -4
- package/dist/errors/messages.js +94 -5
- package/dist/index.js +709 -190
- package/dist/ipc/client.d.ts +4 -0
- package/dist/ipc/client.js +8 -0
- package/dist/ipc/protocol/auditTypes.d.ts +129 -0
- package/dist/ipc/protocol/auditTypes.js +6 -0
- package/dist/ipc/protocol/commands.d.ts +23 -0
- package/dist/ipc/protocol/commands.js +2 -0
- package/dist/ipc/protocol/domTypes.d.ts +4 -0
- package/dist/ipc/protocol/inspectTypes.d.ts +71 -8
- package/dist/runtime/css/search.d.ts +39 -0
- package/dist/runtime/css/search.js +122 -0
- package/dist/runtime/dom/actionEffects.d.ts +4 -1
- package/dist/runtime/dom/actionEffects.js +8 -4
- package/dist/runtime/dom/audit.d.ts +19 -0
- package/dist/runtime/dom/audit.js +36 -0
- package/dist/runtime/dom/auditModel.d.ts +45 -0
- package/dist/runtime/dom/auditModel.js +215 -0
- package/dist/runtime/dom/auditScripts.d.ts +107 -0
- package/dist/runtime/dom/auditScripts.js +112 -0
- package/dist/runtime/dom/elementGeometry.d.ts +8 -2
- package/dist/runtime/dom/elementGeometry.js +24 -8
- package/dist/runtime/dom/elementInfo.d.ts +3 -2
- package/dist/runtime/dom/elementInfo.js +8 -2
- package/dist/runtime/dom/formFillHelpers/fill.js +2 -2
- package/dist/runtime/dom/inspect.d.ts +7 -0
- package/dist/runtime/dom/inspect.js +88 -23
- package/dist/runtime/dom/inspectAllStyles.d.ts +16 -4
- package/dist/runtime/dom/inspectAllStyles.js +89 -7
- package/dist/runtime/dom/inspectCascade.d.ts +19 -2
- package/dist/runtime/dom/inspectCascade.js +214 -44
- package/dist/runtime/dom/inspectCascadeModel.d.ts +8 -0
- package/dist/runtime/dom/inspectCascadeModel.js +108 -34
- package/dist/runtime/dom/inspectHints.d.ts +26 -3
- package/dist/runtime/dom/inspectHints.js +125 -9
- package/dist/runtime/dom/inspectModel.d.ts +3 -0
- package/dist/runtime/dom/inspectModel.js +30 -7
- package/dist/runtime/dom/inspectPaintModel.d.ts +48 -22
- package/dist/runtime/dom/inspectPaintModel.js +180 -68
- package/dist/runtime/dom/inspectRules.d.ts +19 -0
- package/dist/runtime/dom/inspectRules.js +21 -5
- package/dist/runtime/dom/inspectScripts.d.ts +85 -12
- package/dist/runtime/dom/inspectScripts.js +314 -28
- package/dist/runtime/dom/inspectTree.js +10 -2
- package/dist/runtime/dom/inspectWhyModel.d.ts +2 -1
- package/dist/runtime/dom/inspectWhyModel.js +52 -10
- package/dist/runtime/dom/layout.js +31 -9
- package/dist/runtime/dom/reactEventHelpers.d.ts +7 -0
- package/dist/runtime/dom/reactEventHelpers.js +27 -9
- package/dist/runtime/page/emulation.d.ts +13 -4
- package/dist/runtime/page/emulation.js +69 -4
- package/dist/runtime/page/userAgent.d.ts +17 -0
- package/dist/runtime/page/userAgent.js +57 -0
- package/dist/types.d.ts +12 -0
- package/dist/ui/formatters/audit.d.ts +19 -0
- package/dist/ui/formatters/audit.js +106 -0
- package/dist/ui/formatters/dom.d.ts +1 -1
- package/dist/ui/formatters/dom.js +6 -3
- package/dist/ui/formatters/inspect.js +42 -15
- package/dist/ui/formatters/installSkill.d.ts +11 -0
- package/dist/ui/formatters/installSkill.js +31 -0
- package/dist/ui/formatters/status.js +1 -1
- package/dist/ui/messages/commands.d.ts +44 -7
- package/dist/ui/messages/commands.js +83 -11
- package/dist/ui/messages/preview.d.ts +6 -0
- package/dist/ui/messages/preview.js +9 -1
- package/dist/utils/cssValues.js +36 -4
- package/dist/utils/decisionTrees.js +0 -5
- package/dist/utils/suggestions.d.ts +4 -2
- package/dist/utils/suggestions.js +7 -5
- package/dist/utils/taskMappings.js +1 -1
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -567,13 +567,13 @@ var require_common = __commonJS({
|
|
|
567
567
|
}
|
|
568
568
|
}
|
|
569
569
|
}
|
|
570
|
-
function matchesTemplate(
|
|
570
|
+
function matchesTemplate(search2, template) {
|
|
571
571
|
let searchIndex = 0;
|
|
572
572
|
let templateIndex = 0;
|
|
573
573
|
let starIndex = -1;
|
|
574
574
|
let matchIndex2 = 0;
|
|
575
|
-
while (searchIndex <
|
|
576
|
-
if (templateIndex < template.length && (template[templateIndex] ===
|
|
575
|
+
while (searchIndex < search2.length) {
|
|
576
|
+
if (templateIndex < template.length && (template[templateIndex] === search2[searchIndex] || template[templateIndex] === "*")) {
|
|
577
577
|
if (template[templateIndex] === "*") {
|
|
578
578
|
starIndex = templateIndex;
|
|
579
579
|
matchIndex2 = searchIndex;
|
|
@@ -2412,6 +2412,9 @@ function pageNavigationText(navigation) {
|
|
|
2412
2412
|
const status = navigation.status === void 0 ? "" : ` (${navigation.status})`;
|
|
2413
2413
|
return `navigated to ${navigation.url}${status}`;
|
|
2414
2414
|
}
|
|
2415
|
+
function moreMessagesText(count) {
|
|
2416
|
+
return `(+${count} more)`;
|
|
2417
|
+
}
|
|
2415
2418
|
function newMessageText(message) {
|
|
2416
2419
|
return `"${message.text}" (${message.element})`;
|
|
2417
2420
|
}
|
|
@@ -2508,8 +2511,11 @@ function coverText(cover, transparent) {
|
|
|
2508
2511
|
function inspectCascadeNote(reason) {
|
|
2509
2512
|
return reason === "timeout" ? "CSS rules not read: the page's stylesheets took too long (hints wait 1 s; --rules and --why 5 s)" : "CSS rules not read: Chrome could not report the rules matching this element";
|
|
2510
2513
|
}
|
|
2511
|
-
function
|
|
2512
|
-
return
|
|
2514
|
+
function inspectPseudoOfNote(pseudo) {
|
|
2515
|
+
return `Inspected the element of ${pseudo}: pseudo-elements cannot be selected; ${pseudo} is on the pseudo line (content, size, position, inset, colors)`;
|
|
2516
|
+
}
|
|
2517
|
+
function inspectDarkThemeBadge(emulated = false) {
|
|
2518
|
+
return emulated ? "[dark theme, emulated; bdg page emulate --color-scheme light for light]" : "[dark theme from system; --color-scheme light for light]";
|
|
2513
2519
|
}
|
|
2514
2520
|
function inspectVisibilityBadges(visibility) {
|
|
2515
2521
|
const reason = (text) => text ? `: ${text}` : "";
|
|
@@ -2636,8 +2642,16 @@ function emptyElementLine(children, count) {
|
|
|
2636
2642
|
const more = count > children.length ? `, \u2026 ${count - children.length} more` : "";
|
|
2637
2643
|
return `No text; holds ${pluralize(count, "element")}: ${children.join(", ")}${more} (see its HTML with --raw)`;
|
|
2638
2644
|
}
|
|
2639
|
-
function
|
|
2640
|
-
return `
|
|
2645
|
+
function screenshotScaledNote(originalWidth, originalHeight, width, height) {
|
|
2646
|
+
return `scaled from ${originalWidth}\xD7${originalHeight} to ${width}\xD7${height}; --no-resize for full size`;
|
|
2647
|
+
}
|
|
2648
|
+
function screenshotGrownNote(box, captured, padding = 0) {
|
|
2649
|
+
const painted = { width: captured.width - 2 * padding, height: captured.height - 2 * padding };
|
|
2650
|
+
const grew = painted.width > box.width + 0.5 || painted.height > box.height + 0.5;
|
|
2651
|
+
const ink = `grown from ${box.width}\xD7${box.height} to ${painted.width}\xD7${painted.height} to include what it paints outside its box (overflowing content, shadows, outline)`;
|
|
2652
|
+
const pad = `${padding}px of page around it (--padding)`;
|
|
2653
|
+
if (!padding) return ink;
|
|
2654
|
+
return grew ? `${ink}, plus ${pad}` : `with ${pad}: ${captured.width}\xD7${captured.height}`;
|
|
2641
2655
|
}
|
|
2642
2656
|
function queryNextSteps(index) {
|
|
2643
2657
|
return `Next: bdg dom get ${index} (text), bdg dom get ${index} --raw (HTML), bdg dom layout ${index} (position)`;
|
|
@@ -2689,14 +2703,15 @@ function startCommandHelpMessage() {
|
|
|
2689
2703
|
function pageEmulateNothingError() {
|
|
2690
2704
|
return {
|
|
2691
2705
|
message: "Nothing to emulate",
|
|
2692
|
-
suggestion: "Give --viewport <WxH>, --color-scheme light|dark, or --reset, e.g. bdg page emulate --viewport 900x700"
|
|
2706
|
+
suggestion: "Give --viewport <WxH>, --mobile, --color-scheme light|dark, or --reset, e.g. bdg page emulate --viewport 900x700"
|
|
2693
2707
|
};
|
|
2694
2708
|
}
|
|
2695
2709
|
function pageEmulationLines(result) {
|
|
2696
2710
|
const size = (v) => `${v.width}x${v.height}`;
|
|
2697
2711
|
const { emulated } = result;
|
|
2712
|
+
const phone = emulated.viewport?.mobile ? " (phone: mobile layout, touch, mobile user agent)" : "";
|
|
2698
2713
|
return [
|
|
2699
|
-
["Viewport", emulated.viewport ? size(emulated.viewport) : "the browser window"],
|
|
2714
|
+
["Viewport", emulated.viewport ? `${size(emulated.viewport)}${phone}` : "the browser window"],
|
|
2700
2715
|
...result.viewport ? [["Layout", `${size(result.viewport)} (without scrollbars)`]] : [],
|
|
2701
2716
|
[
|
|
2702
2717
|
"Scheme",
|
|
@@ -2710,7 +2725,7 @@ function inspectAnimatingBadge(animating) {
|
|
|
2710
2725
|
function inspectMidTransitionNote() {
|
|
2711
2726
|
return "(mid-transition: inspect again for the final value)";
|
|
2712
2727
|
}
|
|
2713
|
-
var REQUIRED_FIELD_EMPTY_REASON, NO_VISIBLE_EFFECT, LAYOUT_REASONS, SCROLL_LOCKED_PREFIX, VIEWPORT_POSITION_HINTS, INSPECT_OUTPUT_LEGEND, COLLAPSED_LISTENERS_HEADING, NO_LISTENERS_HINT, NOTE_TYPES_SHOWN, POINTER_ACTION_DONE, POINTER_ACTION_NOUN, PAGE_ACTION_DONE, PAGE_INFO_DESCRIPTION, PAGE_EMULATE_DESCRIPTION, PAGE_ACTION_DESCRIPTIONS, CLICK_RESULT_WAIT_HELP, WAIT_HELP_EXAMPLES, FRAME_URL_MAX_LENGTH;
|
|
2728
|
+
var REQUIRED_FIELD_EMPTY_REASON, NO_VISIBLE_EFFECT, HOVER_OFF_DONE, HOVER_USAGE, LAYOUT_REASONS, SCROLL_LOCKED_PREFIX, VIEWPORT_POSITION_HINTS, INSPECT_OUTPUT_LEGEND, COLLAPSED_LISTENERS_HEADING, NO_LISTENERS_HINT, NOTE_TYPES_SHOWN, POINTER_ACTION_DONE, POINTER_ACTION_NOUN, PAGE_ACTION_DONE, PAGE_INFO_DESCRIPTION, PAGE_EMULATE_DESCRIPTION, PAGE_ACTION_DESCRIPTIONS, CLICK_RESULT_WAIT_HELP, WAIT_HELP_EXAMPLES, FRAME_URL_MAX_LENGTH, AUDIT_HELP_EXAMPLES, CSS_SEARCH_HELP_EXAMPLES;
|
|
2714
2729
|
var init_commands = __esm({
|
|
2715
2730
|
"src/ui/messages/commands.ts"() {
|
|
2716
2731
|
"use strict";
|
|
@@ -2720,6 +2735,8 @@ var init_commands = __esm({
|
|
|
2720
2735
|
init_strings();
|
|
2721
2736
|
REQUIRED_FIELD_EMPTY_REASON = "Required field is empty";
|
|
2722
2737
|
NO_VISIBLE_EFFECT = "no visible effect observed: no DOM change, requests or navigation within 300 ms";
|
|
2738
|
+
HOVER_OFF_DONE = "\u2713 Mouse moved off the page (hover styles and menus that close on mouseleave are gone)";
|
|
2739
|
+
HOVER_USAGE = "bdg dom hover <selector|index>, or bdg dom hover --off to move the mouse away";
|
|
2723
2740
|
LAYOUT_REASONS = {
|
|
2724
2741
|
/** Hidden: an `<option>` of a closed `<select>` has no box */
|
|
2725
2742
|
option: "not rendered (an <option> is shown by its <select>)",
|
|
@@ -2748,8 +2765,14 @@ Output notation:
|
|
|
2748
2765
|
in-parent distances to the parent's content edges (l t r b); sib: gaps to the sibling on each side
|
|
2749
2766
|
scroll WxH the content (pseudo-elements too) is larger than the box
|
|
2750
2767
|
16/24 font size / line height; 'webfont loaded' = drawn with a downloaded font;
|
|
2751
|
-
(rendered "X") = drawn with another font than declared (a fallback)
|
|
2768
|
+
(rendered "X") = drawn with another font than declared (a fallback);
|
|
2769
|
+
(resolves to "X") = the font a generic family (sans-serif, system-ui) became
|
|
2752
2770
|
contrast 4.47 WCAG ratio, rounded down, against the background behind the text
|
|
2771
|
+
text in X the text is drawn by descendant X (the one with most of it): its font, color, contrast
|
|
2772
|
+
truncated the text is cut off (overflow clip, ellipsis or line clamp); a "\u2026" in the header is
|
|
2773
|
+
only bdg shortening the text
|
|
2774
|
+
.a.b(+3) the first two classes and how many more the element has
|
|
2775
|
+
sizing content-box padding and border add to the CSS size (shown only then; border-box is not)
|
|
2753
2776
|
(+N not rendered) children with display: none (or not in the layout)
|
|
2754
2777
|
hints declarations on this element that have no effect, why, the fix and where they are
|
|
2755
2778
|
('none': checked, nothing found)
|
|
@@ -2803,6 +2826,21 @@ Sessions follow the system color scheme; start with --color-scheme light|dark to
|
|
|
2803
2826
|
" bdg dom wait --load # the page finished loading"
|
|
2804
2827
|
);
|
|
2805
2828
|
FRAME_URL_MAX_LENGTH = 100;
|
|
2829
|
+
AUDIT_HELP_EXAMPLES = `
|
|
2830
|
+
Examples:
|
|
2831
|
+
bdg dom audit All checks
|
|
2832
|
+
bdg dom audit contrast --level AAA Text below WCAG AAA, weakest first
|
|
2833
|
+
bdg dom audit overflow What scrolls sideways, cut-off text, scaled images
|
|
2834
|
+
bdg dom audit layers animations Fixed/sticky elements and running animations
|
|
2835
|
+
|
|
2836
|
+
Follow up on a finding with bdg dom inspect <element> (e.g. --why color).`;
|
|
2837
|
+
CSS_SEARCH_HELP_EXAMPLES = `
|
|
2838
|
+
Examples:
|
|
2839
|
+
bdg css search -- --brand Where a custom property is set and used (-- before a text
|
|
2840
|
+
that starts with -; options go before it:
|
|
2841
|
+
bdg css search --limit 50 -- --brand)
|
|
2842
|
+
bdg css search "oklch(" Rules that use oklch colors
|
|
2843
|
+
bdg css search ".btn-primary" Rules of a class, in every stylesheet`;
|
|
2806
2844
|
}
|
|
2807
2845
|
});
|
|
2808
2846
|
|
|
@@ -3248,6 +3286,7 @@ function noNodesFoundError(selector, context = {}) {
|
|
|
3248
3286
|
joinLines(
|
|
3249
3287
|
context.similar,
|
|
3250
3288
|
context.shadowHost && shadowBoundaryLine(context.shadowHost),
|
|
3289
|
+
shadowPseudoLine(selector),
|
|
3251
3290
|
`${hidden}Verify the CSS selector is correct.`,
|
|
3252
3291
|
note ? note : void 0
|
|
3253
3292
|
),
|
|
@@ -3256,6 +3295,13 @@ function noNodesFoundError(selector, context = {}) {
|
|
|
3256
3295
|
)
|
|
3257
3296
|
};
|
|
3258
3297
|
}
|
|
3298
|
+
function shadowPseudoLine(selector) {
|
|
3299
|
+
const part = /::part\(\s*([^)\s]+)\s*\)/.exec(selector)?.[1];
|
|
3300
|
+
if (part) {
|
|
3301
|
+
return `::part() styles a shadow part and selects nothing; bdg searches open shadow roots, so select the part itself: [part~="${part}"]`;
|
|
3302
|
+
}
|
|
3303
|
+
return /::slotted\(/.test(selector) ? "::slotted() selects nothing; select the slotted element itself (it is in the light DOM)" : void 0;
|
|
3304
|
+
}
|
|
3259
3305
|
function shellQuote(value) {
|
|
3260
3306
|
return `'${value.split("'").join(`'\\''`)}'`;
|
|
3261
3307
|
}
|
|
@@ -3285,6 +3331,12 @@ function elementZeroDimensionsError() {
|
|
|
3285
3331
|
suggestion: "It may be collapsed or empty; capture a visible ancestor instead"
|
|
3286
3332
|
};
|
|
3287
3333
|
}
|
|
3334
|
+
function hoverOffWithTargetError() {
|
|
3335
|
+
return {
|
|
3336
|
+
message: "--off moves the mouse off the page; it takes no element",
|
|
3337
|
+
suggestion: "Use bdg dom hover --off alone, or bdg dom hover <selector> to hover an element"
|
|
3338
|
+
};
|
|
3339
|
+
}
|
|
3288
3340
|
function missingArgumentError(usage) {
|
|
3289
3341
|
return {
|
|
3290
3342
|
message: "Missing required argument or flag",
|
|
@@ -3358,6 +3410,27 @@ function formInIframeError(iframeUrl, crossOrigin) {
|
|
|
3358
3410
|
suggestion: crossOrigin ? "Cross-origin iframes cannot be read or controlled; open the iframe URL directly: bdg <iframe url>" : 'dom form lists forms of the main document only; its fields are reachable directly: bdg dom query "input, select, textarea", then bdg dom fill <index> <value>'
|
|
3359
3411
|
};
|
|
3360
3412
|
}
|
|
3413
|
+
function skillSourceMissingError(source) {
|
|
3414
|
+
return {
|
|
3415
|
+
message: `The bdg skill is missing from the installed package (${source})`,
|
|
3416
|
+
suggestion: "Reinstall bdg: npm install -g browser-debugger-cli"
|
|
3417
|
+
};
|
|
3418
|
+
}
|
|
3419
|
+
function skillWriteFailedError(path6, reason) {
|
|
3420
|
+
return {
|
|
3421
|
+
message: `Could not write ${path6}: ${reason}`,
|
|
3422
|
+
suggestion: "Check the permissions of that directory, or install for the other agent only (--claude or --agents)"
|
|
3423
|
+
};
|
|
3424
|
+
}
|
|
3425
|
+
function intervalWithoutFollowError() {
|
|
3426
|
+
return {
|
|
3427
|
+
message: "--interval sets how often --follow refreshes",
|
|
3428
|
+
suggestion: "Add --follow, e.g. bdg peek --follow --interval 500"
|
|
3429
|
+
};
|
|
3430
|
+
}
|
|
3431
|
+
function unknownAuditCheckMessage(value, similar, checks) {
|
|
3432
|
+
return `Unknown check "${value}"${similar.length > 0 ? `; did you mean ${similar[0]}?` : ""} (checks: ${checks.join(", ")})`;
|
|
3433
|
+
}
|
|
3361
3434
|
var LAUNCHED_CHROME_DESCRIPTION, A11Y_NAME_QUERY_EXAMPLE, SCOPED_FILTER_EXAMPLES, PLAYWRIGHT_ONLY_SYNTAX, MALFORMED_SELECTOR_DETAILS, SESSION_DIR_SUGGESTION, SCROLL_PROBLEMS, VIA_LABEL_SUFFIX, NAME_QUERY_PLACEHOLDER, LABEL_WITHOUT_CONTROL;
|
|
3362
3435
|
var init_messages = __esm({
|
|
3363
3436
|
"src/errors/messages.ts"() {
|
|
@@ -3487,11 +3560,14 @@ function findSimilarNames(name, candidates) {
|
|
|
3487
3560
|
const maxDistance2 = Math.max(2, Math.floor(name.length / 5));
|
|
3488
3561
|
const minAffix = Math.max(4, Math.ceil(name.length / 3));
|
|
3489
3562
|
const typos = findMatches(name, others, maxDistance2, false).map((match) => match.value);
|
|
3490
|
-
const byAffix = (length) => others.map((candidate) => ({ candidate, length: length(candidate) })).filter((entry) => entry.length >=
|
|
3563
|
+
const byAffix = (length, min = minAffix) => others.map((candidate) => ({ candidate, length: length(candidate) })).filter((entry) => entry.length >= min).sort((a, b) => b.length - a.length).map((entry) => entry.candidate);
|
|
3491
3564
|
const ranked = [
|
|
3492
3565
|
...typos,
|
|
3493
3566
|
...byAffix((candidate) => commonSuffixLength(name, candidate)),
|
|
3494
|
-
...byAffix(
|
|
3567
|
+
...byAffix(
|
|
3568
|
+
(candidate) => commonPrefixLength(name, candidate),
|
|
3569
|
+
Math.max(minAffix, Math.ceil(name.length / 2))
|
|
3570
|
+
)
|
|
3495
3571
|
];
|
|
3496
3572
|
return [...new Set(ranked)].slice(0, MAX_SIMILAR_NAMES);
|
|
3497
3573
|
}
|
|
@@ -3962,6 +4038,12 @@ async function domListeners(params) {
|
|
|
3962
4038
|
async function domLayout(params) {
|
|
3963
4039
|
return sendCommand("dom_layout", params);
|
|
3964
4040
|
}
|
|
4041
|
+
async function domAudit(params) {
|
|
4042
|
+
return sendCommand("dom_audit", params);
|
|
4043
|
+
}
|
|
4044
|
+
async function cssSearch(params) {
|
|
4045
|
+
return sendCommand("css_search", params);
|
|
4046
|
+
}
|
|
3965
4047
|
async function domInspect(params) {
|
|
3966
4048
|
return sendCommand("dom_inspect", params);
|
|
3967
4049
|
}
|
|
@@ -4150,7 +4232,12 @@ var init_elementInfo = __esm({
|
|
|
4150
4232
|
doc = frame.ownerDocument;
|
|
4151
4233
|
}
|
|
4152
4234
|
const root = el.getRootNode();
|
|
4153
|
-
if (root.host)
|
|
4235
|
+
if (root.host) {
|
|
4236
|
+
const host = root.host;
|
|
4237
|
+
const label = describe(host) + (!host.id && host.classList.length ? '.' + host.classList[0] : '');
|
|
4238
|
+
const text = (host.textContent || '').replace(/\\s+/g, ' ').trim().slice(0, 30);
|
|
4239
|
+
parts.push('shadow root of <' + label + (text ? ' "' + text + '"' : '') + '>');
|
|
4240
|
+
}
|
|
4154
4241
|
return parts.join(' > ');
|
|
4155
4242
|
}`;
|
|
4156
4243
|
}
|
|
@@ -4159,8 +4246,9 @@ var init_elementInfo = __esm({
|
|
|
4159
4246
|
// src/commands/dom/helpers/keyAttributes.ts
|
|
4160
4247
|
function keyAttributes(tag, attributes, state = {}) {
|
|
4161
4248
|
const names = ATTRIBUTES_BY_TAG[tag];
|
|
4162
|
-
|
|
4163
|
-
|
|
4249
|
+
const part = attributes["part"];
|
|
4250
|
+
if (!names) return part ? { part } : void 0;
|
|
4251
|
+
const result = part ? { part } : {};
|
|
4164
4252
|
const type = state.type ?? attributes["type"];
|
|
4165
4253
|
if (type) result["type"] = type;
|
|
4166
4254
|
for (const name of names) {
|
|
@@ -4227,8 +4315,9 @@ function axisScroll(start, size, viewSize, range, aim) {
|
|
|
4227
4315
|
const scroll = Math.round(Math.min(range.forward, Math.max(-range.back, target))) || 0;
|
|
4228
4316
|
return visibleLength(start, size, viewSize, scroll) >= fully ? scroll : null;
|
|
4229
4317
|
}
|
|
4230
|
-
function classifyViewportPosition(
|
|
4231
|
-
if (
|
|
4318
|
+
function classifyViewportPosition(measured, viewport) {
|
|
4319
|
+
if (measured.hidden) return { inViewport: "hidden", hiddenReason: measured.hidden };
|
|
4320
|
+
const geometry = measured.content ? { ...measured, rect: measured.content } : measured;
|
|
4232
4321
|
const rect = edgesOf(geometry.rect);
|
|
4233
4322
|
const screen = edgesOf({ x: 0, y: 0, ...viewport });
|
|
4234
4323
|
const view = geometry.clip ? overlap(screen, edgesOf(geometry.clip)) : screen;
|
|
@@ -4473,7 +4562,20 @@ var init_elementGeometry = __esm({
|
|
|
4473
4562
|
}
|
|
4474
4563
|
return 'not rendered';
|
|
4475
4564
|
};
|
|
4476
|
-
const
|
|
4565
|
+
const shownChildren = (node) => {
|
|
4566
|
+
const s = styleOf(node);
|
|
4567
|
+
if (s.overflowX !== 'visible' || s.overflowY !== 'visible') return null;
|
|
4568
|
+
let union = null;
|
|
4569
|
+
for (const c of Array.from(node.children).slice(0, 50)) {
|
|
4570
|
+
const r = c.getBoundingClientRect();
|
|
4571
|
+
if (r.width <= 0 || r.height <= 0 || styleOf(c).visibility !== 'visible') continue;
|
|
4572
|
+
union = union
|
|
4573
|
+
? { left: Math.min(union.left, r.left), top: Math.min(union.top, r.top), right: Math.max(union.right, r.right), bottom: Math.max(union.bottom, r.bottom) }
|
|
4574
|
+
: { left: r.left, top: r.top, right: r.right, bottom: r.bottom };
|
|
4575
|
+
}
|
|
4576
|
+
return union;
|
|
4577
|
+
};
|
|
4578
|
+
const hiddenReason = (style, box, content) => {
|
|
4477
4579
|
if (style.display === 'none') return 'display: none';
|
|
4478
4580
|
if (style.display === 'contents') return 'display: contents (no box of its own)';
|
|
4479
4581
|
if (el.getClientRects().length === 0) {
|
|
@@ -4482,7 +4584,7 @@ var init_elementGeometry = __esm({
|
|
|
4482
4584
|
const skipped = skippedReason(el);
|
|
4483
4585
|
if (skipped) return skipped;
|
|
4484
4586
|
if (style.visibility !== 'visible') return 'visibility: ' + style.visibility;
|
|
4485
|
-
if (box.width === 0 || box.height === 0) return 'zero size';
|
|
4587
|
+
if ((box.width === 0 || box.height === 0) && !content) return 'zero size';
|
|
4486
4588
|
return null;
|
|
4487
4589
|
};
|
|
4488
4590
|
const isSticky = () => {
|
|
@@ -4505,7 +4607,8 @@ var init_elementGeometry = __esm({
|
|
|
4505
4607
|
let clipper = own.clipper;
|
|
4506
4608
|
let fixed = own.fixed;
|
|
4507
4609
|
let fixedBy = own.fixedBy;
|
|
4508
|
-
let
|
|
4610
|
+
let content = box.width === 0 || box.height === 0 ? shownChildren(el) : null;
|
|
4611
|
+
let hidden = hiddenReason(styleOf(el), box, content) || own.collapsed;
|
|
4509
4612
|
let x = 0;
|
|
4510
4613
|
let y = 0;
|
|
4511
4614
|
for (let view = el.ownerDocument.defaultView; view && view.frameElement; view = view.parent) {
|
|
@@ -4517,6 +4620,7 @@ var init_elementGeometry = __esm({
|
|
|
4517
4620
|
overlay = { right: !!unframed && unframed.right <= clip.right && overlay.right, bottom: !!unframed && unframed.bottom <= clip.bottom && overlay.bottom };
|
|
4518
4621
|
const offset = frameOffset(frame);
|
|
4519
4622
|
rect = shift(rect, offset.x, offset.y);
|
|
4623
|
+
content = shift(content, offset.x, offset.y);
|
|
4520
4624
|
const outer = ancestorClip(frame, rect, describe);
|
|
4521
4625
|
const inner = shift(clip, offset.x, offset.y);
|
|
4522
4626
|
clip = intersect(inner, outer.clip);
|
|
@@ -4535,7 +4639,7 @@ var init_elementGeometry = __esm({
|
|
|
4535
4639
|
const invisible = hidden ? null : invisibleReason(el, describe);
|
|
4536
4640
|
const lock = scrollLock(top, describe);
|
|
4537
4641
|
if (lock && fixedBy === top.document.body) fixed = false;
|
|
4538
|
-
return { rect: toBox(rect), clip: toBox(clip), clipOverlay: overlay, clipper: clipper, hidden: hidden, invisible: invisible, inert: isInert(), fixed: fixed, sticky: !fixed && isSticky(), pageScroll: scrollRange(top), scrollLock: lock, offset: { x: x, y: y } };
|
|
4642
|
+
return { rect: toBox(rect), content: toBox(content), clip: toBox(clip), clipOverlay: overlay, clipper: clipper, hidden: hidden, invisible: invisible, inert: isInert(), fixed: fixed, sticky: !fixed && isSticky(), pageScroll: scrollRange(top), scrollLock: lock, offset: { x: x, y: y } };
|
|
4539
4643
|
}`;
|
|
4540
4644
|
IN_VIEW_SLACK = 1;
|
|
4541
4645
|
}
|
|
@@ -5538,18 +5642,34 @@ var init_errors5 = __esm({
|
|
|
5538
5642
|
}
|
|
5539
5643
|
});
|
|
5540
5644
|
|
|
5645
|
+
// src/runtime/page/userAgent.ts
|
|
5646
|
+
var init_userAgent = __esm({
|
|
5647
|
+
"src/runtime/page/userAgent.ts"() {
|
|
5648
|
+
"use strict";
|
|
5649
|
+
init_errors();
|
|
5650
|
+
}
|
|
5651
|
+
});
|
|
5652
|
+
|
|
5541
5653
|
// src/runtime/page/emulation.ts
|
|
5542
5654
|
function viewportOverride(viewport, deviceScaleFactor = 0) {
|
|
5543
|
-
|
|
5655
|
+
const mobile = viewport.mobile === true;
|
|
5656
|
+
return {
|
|
5657
|
+
width: viewport.width,
|
|
5658
|
+
height: viewport.height,
|
|
5659
|
+
deviceScaleFactor: deviceScaleFactor || (mobile ? MOBILE_PIXEL_RATIO : 0),
|
|
5660
|
+
mobile
|
|
5661
|
+
};
|
|
5544
5662
|
}
|
|
5545
|
-
var log16;
|
|
5663
|
+
var log16, MOBILE_PIXEL_RATIO;
|
|
5546
5664
|
var init_emulation = __esm({
|
|
5547
5665
|
"src/runtime/page/emulation.ts"() {
|
|
5548
5666
|
"use strict";
|
|
5549
5667
|
init_elementGeometry();
|
|
5668
|
+
init_userAgent();
|
|
5550
5669
|
init_logging();
|
|
5551
5670
|
init_errors();
|
|
5552
5671
|
log16 = createLogger("session");
|
|
5672
|
+
MOBILE_PIXEL_RATIO = 3;
|
|
5553
5673
|
}
|
|
5554
5674
|
});
|
|
5555
5675
|
|
|
@@ -5726,13 +5846,18 @@ async function useUnitPixelRatio(devicePixelRatio, viewport) {
|
|
|
5726
5846
|
const sessionViewport = readSessionMetadata()?.viewport;
|
|
5727
5847
|
const size = sessionViewport ?? await windowSize(viewport);
|
|
5728
5848
|
await callCDP("Emulation.setDeviceMetricsOverride", viewportOverride(size, 1));
|
|
5729
|
-
return
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
}
|
|
5735
|
-
|
|
5849
|
+
return restoreSessionMetrics;
|
|
5850
|
+
}
|
|
5851
|
+
async function restoreSessionMetrics() {
|
|
5852
|
+
const sessionViewport = readSessionMetadata()?.viewport;
|
|
5853
|
+
if (!sessionViewport) {
|
|
5854
|
+
await callCDP("Emulation.clearDeviceMetricsOverride", {});
|
|
5855
|
+
return;
|
|
5856
|
+
}
|
|
5857
|
+
await callCDP("Emulation.setDeviceMetricsOverride", viewportOverride(sessionViewport));
|
|
5858
|
+
if (sessionViewport.mobile) {
|
|
5859
|
+
await callCDP("Emulation.setTouchEmulationEnabled", { enabled: true, maxTouchPoints: 5 });
|
|
5860
|
+
}
|
|
5736
5861
|
}
|
|
5737
5862
|
async function getElementBounds(ref) {
|
|
5738
5863
|
const response = await callCDP("DOM.getBoxModel", ref);
|
|
@@ -5876,21 +6001,61 @@ async function visibleViewport() {
|
|
|
5876
6001
|
function insideView(area, view) {
|
|
5877
6002
|
return area.x >= 0 && area.y >= 0 && area.x + area.width <= view.width && area.y + area.height <= view.height;
|
|
5878
6003
|
}
|
|
5879
|
-
async function measureInView(ref) {
|
|
6004
|
+
async function measureInView(ref, padding) {
|
|
5880
6005
|
const view = await visibleViewport();
|
|
5881
6006
|
let box = await getElementBounds(ref);
|
|
5882
|
-
let bounds = await captureArea(ref, box);
|
|
6007
|
+
let bounds = await captureArea(ref, box, padding);
|
|
5883
6008
|
const fits = bounds.width <= view.width && bounds.height <= view.height;
|
|
5884
|
-
if (fits
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
6009
|
+
if (!fits) {
|
|
6010
|
+
await keepLayoutWithoutScrollbars(view);
|
|
6011
|
+
box = await getElementBounds(ref);
|
|
6012
|
+
return { box, bounds: await captureArea(ref, box, padding), inView: false };
|
|
6013
|
+
}
|
|
6014
|
+
if (insideView(bounds, view)) return { box, bounds, inView: true };
|
|
6015
|
+
const scrolledFrom = await scrollPosition();
|
|
6016
|
+
const dx = bounds.x + bounds.width / 2 - view.width / 2;
|
|
6017
|
+
const dy = bounds.y + bounds.height / 2 - view.height / 2;
|
|
6018
|
+
await callCDP("Runtime.evaluate", { expression: `window.scrollBy(${dx}, ${dy})` });
|
|
6019
|
+
try {
|
|
5888
6020
|
box = await getElementBounds(ref);
|
|
5889
|
-
bounds = await captureArea(ref, box);
|
|
6021
|
+
bounds = await captureArea(ref, box, padding);
|
|
6022
|
+
} catch (error) {
|
|
6023
|
+
await restoreScrollPosition(scrolledFrom);
|
|
6024
|
+
throw error;
|
|
5890
6025
|
}
|
|
5891
|
-
return { box, bounds, inView: insideView(bounds, view) };
|
|
6026
|
+
return { box, bounds, inView: insideView(bounds, view), scrolledFrom };
|
|
5892
6027
|
}
|
|
5893
|
-
async function
|
|
6028
|
+
async function keepLayoutWithoutScrollbars(view) {
|
|
6029
|
+
const phone = readSessionMetadata()?.viewport?.mobile;
|
|
6030
|
+
await callCDP("Emulation.setScrollbarsHidden", { hidden: true });
|
|
6031
|
+
await callCDP(
|
|
6032
|
+
"Emulation.setDeviceMetricsOverride",
|
|
6033
|
+
viewportOverride({ ...view, ...phone && { mobile: true } }, 1)
|
|
6034
|
+
);
|
|
6035
|
+
}
|
|
6036
|
+
async function restoreViewport() {
|
|
6037
|
+
await callCDP("Emulation.setScrollbarsHidden", { hidden: false });
|
|
6038
|
+
await restoreSessionMetrics();
|
|
6039
|
+
}
|
|
6040
|
+
async function scrollPosition() {
|
|
6041
|
+
const response = await callCDP("Runtime.evaluate", {
|
|
6042
|
+
expression: "[window.scrollX, window.scrollY]",
|
|
6043
|
+
returnByValue: true
|
|
6044
|
+
});
|
|
6045
|
+
const value = response.data?.result?.result?.value;
|
|
6046
|
+
const [x, y] = Array.isArray(value) ? value : [];
|
|
6047
|
+
return { x: x ?? 0, y: y ?? 0 };
|
|
6048
|
+
}
|
|
6049
|
+
async function captureArea(ref, bounds, padding) {
|
|
6050
|
+
const area = await paintedArea(ref, bounds);
|
|
6051
|
+
return padding > 0 ? {
|
|
6052
|
+
x: area.x - padding,
|
|
6053
|
+
y: area.y - padding,
|
|
6054
|
+
width: area.width + 2 * padding,
|
|
6055
|
+
height: area.height + 2 * padding
|
|
6056
|
+
} : area;
|
|
6057
|
+
}
|
|
6058
|
+
async function paintedArea(ref, bounds) {
|
|
5894
6059
|
const objectGroup = `bdg-shot-${process.pid}`;
|
|
5895
6060
|
try {
|
|
5896
6061
|
const resolved = await callCDP("DOM.resolveNode", { ...ref, objectGroup });
|
|
@@ -5934,15 +6099,18 @@ async function captureElementScreenshot(outputPath, ref, options = {}) {
|
|
|
5934
6099
|
devicePixelRatio,
|
|
5935
6100
|
before?.visualViewport ?? { clientWidth: 800, clientHeight: 600 }
|
|
5936
6101
|
);
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
6102
|
+
const restore = async (wide, scrolledFrom2) => {
|
|
6103
|
+
await (wide ? restoreViewport() : restoreMetrics());
|
|
6104
|
+
if (scrolledFrom2) await restoreScrollPosition(scrolledFrom2);
|
|
6105
|
+
};
|
|
6106
|
+
let measured;
|
|
5940
6107
|
try {
|
|
5941
|
-
|
|
6108
|
+
measured = await measureInView(ref, options.padding ?? 0);
|
|
5942
6109
|
} catch (error) {
|
|
5943
|
-
await
|
|
6110
|
+
await restoreViewport();
|
|
5944
6111
|
throw error;
|
|
5945
6112
|
}
|
|
6113
|
+
const { box, bounds, inView, scrolledFrom } = measured;
|
|
5946
6114
|
const originalWidth = bounds.width;
|
|
5947
6115
|
const originalHeight = bounds.height;
|
|
5948
6116
|
const resized = shouldResize(originalWidth, originalHeight, noResize);
|
|
@@ -5968,7 +6136,7 @@ async function captureElementScreenshot(outputPath, ref, options = {}) {
|
|
|
5968
6136
|
});
|
|
5969
6137
|
screenshotResult = screenshotResponse.data?.result;
|
|
5970
6138
|
} finally {
|
|
5971
|
-
await
|
|
6139
|
+
await restore(!inView, scrolledFrom);
|
|
5972
6140
|
}
|
|
5973
6141
|
if (!screenshotResult?.data) {
|
|
5974
6142
|
throw new CDPConnectionError("No screenshot data returned", new Error("Empty response"));
|
|
@@ -5985,7 +6153,8 @@ async function captureElementScreenshot(outputPath, ref, options = {}) {
|
|
|
5985
6153
|
finalTokens: calculateImageTokens(finalWidth, finalHeight),
|
|
5986
6154
|
element: {
|
|
5987
6155
|
bounds: roundBounds(onPage(box)),
|
|
5988
|
-
...bounds !== box && { captured: roundBounds(clip) }
|
|
6156
|
+
...bounds !== box && { captured: roundBounds(clip) },
|
|
6157
|
+
...options.padding && { padding: options.padding }
|
|
5989
6158
|
}
|
|
5990
6159
|
};
|
|
5991
6160
|
if (quality !== void 0) {
|
|
@@ -6051,8 +6220,37 @@ var init_screenshot = __esm({
|
|
|
6051
6220
|
if (!clips(style)) walk(child);
|
|
6052
6221
|
}
|
|
6053
6222
|
};
|
|
6054
|
-
|
|
6055
|
-
|
|
6223
|
+
const ownStyle = view.getComputedStyle(this);
|
|
6224
|
+
if (!clips(ownStyle)) {
|
|
6225
|
+
walk(this);
|
|
6226
|
+
const scaleX = this.offsetWidth ? own.width / this.offsetWidth : 1;
|
|
6227
|
+
const scaleY = this.offsetHeight ? own.height / this.offsetHeight : 1;
|
|
6228
|
+
const wider = Math.max(0, this.scrollWidth - this.clientWidth) * scaleX;
|
|
6229
|
+
const taller = Math.max(0, this.scrollHeight - this.clientHeight) * scaleY;
|
|
6230
|
+
if (ownStyle.direction === 'rtl') reach.left = Math.min(reach.left, own.left - wider);
|
|
6231
|
+
else reach.right = Math.max(reach.right, own.right + wider);
|
|
6232
|
+
reach.bottom = Math.max(reach.bottom, own.bottom + taller);
|
|
6233
|
+
}
|
|
6234
|
+
const ink = { left: 0, top: 0, right: 0, bottom: 0 };
|
|
6235
|
+
const grow = (side, amount) => { ink[side] = Math.max(ink[side], amount); };
|
|
6236
|
+
for (const layer of ownStyle.boxShadow === 'none' ? [] : ownStyle.boxShadow.split(/,(?![^(]*\\))/)) {
|
|
6237
|
+
if (/\\binset\\b/.test(layer)) continue;
|
|
6238
|
+
const [x = 0, y = 0, blur = 0, spread = 0] = (layer.replace(/(rgba?|hsla?|color|oklch|lab|lch)\\([^)]*\\)/g, '').match(/-?[\\d.]+px/g) || []).map(parseFloat);
|
|
6239
|
+
grow('left', blur + spread - x);
|
|
6240
|
+
grow('right', blur + spread + x);
|
|
6241
|
+
grow('top', blur + spread - y);
|
|
6242
|
+
grow('bottom', blur + spread + y);
|
|
6243
|
+
}
|
|
6244
|
+
if (ownStyle.outlineStyle !== 'none') {
|
|
6245
|
+
const outline = parseFloat(ownStyle.outlineWidth) + parseFloat(ownStyle.outlineOffset);
|
|
6246
|
+
['left', 'top', 'right', 'bottom'].forEach((side) => grow(side, outline));
|
|
6247
|
+
}
|
|
6248
|
+
return {
|
|
6249
|
+
left: Math.max(own.left - reach.left, ink.left),
|
|
6250
|
+
top: Math.max(own.top - reach.top, ink.top),
|
|
6251
|
+
right: Math.max(reach.right - own.right, ink.right),
|
|
6252
|
+
bottom: Math.max(reach.bottom - own.bottom, ink.bottom)
|
|
6253
|
+
};
|
|
6056
6254
|
}`;
|
|
6057
6255
|
OVERFLOW_SLACK = 1;
|
|
6058
6256
|
}
|
|
@@ -8040,18 +8238,18 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
8040
8238
|
*
|
|
8041
8239
|
* @private
|
|
8042
8240
|
*/
|
|
8043
|
-
_prepareUserArgs(argv,
|
|
8241
|
+
_prepareUserArgs(argv, parseOptions2) {
|
|
8044
8242
|
if (argv !== void 0 && !Array.isArray(argv)) {
|
|
8045
8243
|
throw new Error("first parameter to parse must be array or undefined");
|
|
8046
8244
|
}
|
|
8047
|
-
|
|
8048
|
-
if (argv === void 0 &&
|
|
8245
|
+
parseOptions2 = parseOptions2 || {};
|
|
8246
|
+
if (argv === void 0 && parseOptions2.from === void 0) {
|
|
8049
8247
|
if (process2.versions?.electron) {
|
|
8050
|
-
|
|
8248
|
+
parseOptions2.from = "electron";
|
|
8051
8249
|
}
|
|
8052
8250
|
const execArgv = process2.execArgv ?? [];
|
|
8053
8251
|
if (execArgv.includes("-e") || execArgv.includes("--eval") || execArgv.includes("-p") || execArgv.includes("--print")) {
|
|
8054
|
-
|
|
8252
|
+
parseOptions2.from = "eval";
|
|
8055
8253
|
}
|
|
8056
8254
|
}
|
|
8057
8255
|
if (argv === void 0) {
|
|
@@ -8059,7 +8257,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
8059
8257
|
}
|
|
8060
8258
|
this.rawArgs = argv.slice();
|
|
8061
8259
|
let userArgs;
|
|
8062
|
-
switch (
|
|
8260
|
+
switch (parseOptions2.from) {
|
|
8063
8261
|
case void 0:
|
|
8064
8262
|
case "node":
|
|
8065
8263
|
this._scriptPath = argv[1];
|
|
@@ -8081,7 +8279,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
8081
8279
|
break;
|
|
8082
8280
|
default:
|
|
8083
8281
|
throw new Error(
|
|
8084
|
-
`unexpected parse option { from: '${
|
|
8282
|
+
`unexpected parse option { from: '${parseOptions2.from}' }`
|
|
8085
8283
|
);
|
|
8086
8284
|
}
|
|
8087
8285
|
if (!this._name && this._scriptPath)
|
|
@@ -8111,9 +8309,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
8111
8309
|
* @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron'
|
|
8112
8310
|
* @return {Command} `this` command for chaining
|
|
8113
8311
|
*/
|
|
8114
|
-
parse(argv,
|
|
8312
|
+
parse(argv, parseOptions2) {
|
|
8115
8313
|
this._prepareForParse();
|
|
8116
|
-
const userArgs = this._prepareUserArgs(argv,
|
|
8314
|
+
const userArgs = this._prepareUserArgs(argv, parseOptions2);
|
|
8117
8315
|
this._parseCommand([], userArgs);
|
|
8118
8316
|
return this;
|
|
8119
8317
|
}
|
|
@@ -8137,9 +8335,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
8137
8335
|
* @param {string} parseOptions.from - where the args are from: 'node', 'user', 'electron'
|
|
8138
8336
|
* @return {Promise}
|
|
8139
8337
|
*/
|
|
8140
|
-
async parseAsync(argv,
|
|
8338
|
+
async parseAsync(argv, parseOptions2) {
|
|
8141
8339
|
this._prepareForParse();
|
|
8142
|
-
const userArgs = this._prepareUserArgs(argv,
|
|
8340
|
+
const userArgs = this._prepareUserArgs(argv, parseOptions2);
|
|
8143
8341
|
await this._parseCommand([], userArgs);
|
|
8144
8342
|
return this;
|
|
8145
8343
|
}
|
|
@@ -9456,15 +9654,19 @@ init_schema();
|
|
|
9456
9654
|
// src/commands/optionBehaviors.ts
|
|
9457
9655
|
init_screenshotResize();
|
|
9458
9656
|
var TRIGGERED_REQUESTS_BEHAVIOR = "Requests (and WebSocket connections) that start after the action begins are returned as triggeredRequests (method, url, status, durationMs; pending when still running at return, loading when the response arrived but its body is still streaming; with resourceType; human output lists documents, XHR/fetch and WebSockets first (up to 10) and counts static assets on one line; absent when network telemetry is off). Attribution is by time: requests a page timer or poller starts meanwhile are listed too, whether or not the action caused them";
|
|
9459
|
-
var ACTION_EFFECTS_BEHAVIOR = 'The result also says what changed on the page: a navigation (Page: navigated to <url> (status), or URL changed to <url> (same document); JSON navigation { url, sameDocument, status }), and messages that appeared or changed in alert/status/aria-live elements or flash/error/toast-like classes (New text: "\u2026" (element); JSON messages [{ text, element }], at most 3; after a navigation every message on the new page counts; texts of only digits and time units, such as clocks and counters, are left out, but other text that changes on its own, such as a rotating banner, can show up). Both are absent when nothing changed. Cost: one page script sent before the action without waiting for it and one read after it, a few ms; when the page does not answer (a pending navigation) bdg waits at most 200 ms for the snapshot and 250 ms per read, and the navigation is still reported from CDP events';
|
|
9657
|
+
var ACTION_EFFECTS_BEHAVIOR = 'The result also says what changed on the page: a navigation (Page: navigated to <url> (status), or URL changed to <url> (same document); JSON navigation { url, sameDocument, status }), and messages that appeared or changed in alert/status/aria-live elements or flash/error/toast-like classes (New text: "\u2026" (element); JSON messages [{ text, element }], at most 3, with "(+N more)" and moreMessages for the rest; after a navigation every message on the new page counts; texts of only digits and time units, such as clocks and counters, are left out, but other text that changes on its own, such as a rotating banner, can show up). Both are absent when nothing changed. Cost: one page script sent before the action without waiting for it and one read after it, a few ms; when the page does not answer (a pending navigation) bdg waits at most 200 ms for the snapshot and 250 ms per read, and the navigation is still reported from CDP events';
|
|
9460
9658
|
var STILL_CHANGING_BEHAVIOR = 'When the page was still changing as the action returned, the status line says (page still changing), a note below it says what was pending and suggests bdg dom wait <selector>, and JSON has settled: false with pending { requests (document, fetch/XHR and script requests still running), navigation (a new page still loading), loading (a loading indicator that appeared, e.g. "div#loading"), domChanging (DOM changes kept coming in bursts over a second look 250 ms later; a single render, ticking text and style animations do not count), busy (the page did not answer within 250 ms: a long script) }; absent when the page looked settled (exit code stays 0). A result a timer renders later, with no DOM change, request or loading indicator before it, is not detected. Cost: nothing extra, except 250 ms plus one read when the DOM looked busy. Not checked with --no-wait';
|
|
9461
9659
|
var SHOWN_BEHAVIOR = `Elements the action showed are listed (Shown: <element> "<text>"; JSON shown [{ text, element }], at most 3, outermost first): elements with visible text added inside the target's form, search box, dialog or combobox (else its grandparent, or its parent when that is the body), and popups and messages added anywhere (tooltip, menu, listbox, dialog, alert, status roles, popover, aria-live, message-like classes); widgets elsewhere on the page and re-rendered elements whose text was there before do not count`;
|
|
9462
9660
|
var NO_WAIT_TRIGGERED_REQUESTS = "Returns immediately without waiting for network; triggeredRequests lists only requests bdg saw start before returning (often none yet; check bdg network list later)";
|
|
9463
9661
|
var OPTION_BEHAVIORS = {
|
|
9464
9662
|
"screenshot:--selector": {
|
|
9465
9663
|
default: "Captures the page (full page unless --no-full-page)",
|
|
9466
|
-
whenEnabled: 'Captures one element; the selector (or a query index) can also be given as the second argument: bdg dom screenshot out.png "#sel".
|
|
9467
|
-
automaticBehavior: "The capture covers the border box plus
|
|
9664
|
+
whenEnabled: 'Captures one element; the selector (or a query index) can also be given as the second argument: bdg dom screenshot out.png "#sel". With --index it picks that match of the selector (--selector ".item" --index 2). A positional and an option naming different elements exits 81',
|
|
9665
|
+
automaticBehavior: "The capture covers the border box plus what overflows it: uncleared floats, positioned children, text past a tight line height, and the element's own box shadows and outline (a focus ring); not what an overflow: hidden ancestor cuts off, nor fixed descendants. JSON element.bounds is the border box and element.captured the larger area when it grew, which human output notes. An element smaller than the viewport is scrolled into view for the capture and the page scroll put back afterwards; a larger one is captured with the page laid out at its width without scrollbars, so it does not shift"
|
|
9666
|
+
},
|
|
9667
|
+
"screenshot:--padding": {
|
|
9668
|
+
default: "The element capture is its painted area (border box, overflowing content, shadows, outline)",
|
|
9669
|
+
whenEnabled: "Adds that many CSS px of the page around the element capture on every side (0-500); without an element it exits 81"
|
|
9468
9670
|
},
|
|
9469
9671
|
"screenshot:--no-resize": {
|
|
9470
9672
|
default: `Images auto-resized to max ${MAX_EDGE_PX}px longest edge for Claude Vision optimization (~1,600 tokens)`,
|
|
@@ -9646,10 +9848,16 @@ var OPTION_BEHAVIORS = {
|
|
|
9646
9848
|
whenEnabled: "Lists every listener of framework roots individually",
|
|
9647
9849
|
tokenImpact: "On React pages --all adds a row per event type and phase (about 140 rows, 60 KB of JSON)"
|
|
9648
9850
|
},
|
|
9851
|
+
"audit:--level": {
|
|
9852
|
+
default: "Text must reach WCAG AA: 4.5, or 3 for large text (24px, or 18.66px bold); every text-drawing element is checked, composited like dom inspect",
|
|
9853
|
+
whenEnabled: "--level AAA asks 7, or 4.5 for large text",
|
|
9854
|
+
automaticBehavior: "One walk over the rendered elements (open shadow roots included, at most 20000; capped says when it stopped). Findings are sorted weakest first; --limit (default 20) lists that many per check and the rest are counted. Overflow leaves out content inside horizontal scrollers and visually-hidden 1px text; identical findings are grouped (\xD7N)",
|
|
9855
|
+
tokenImpact: "About one line per finding; --limit bounds it"
|
|
9856
|
+
},
|
|
9649
9857
|
"layout:--index": {
|
|
9650
9858
|
default: "Reports every match of the selector (human output lists the first 20, JSON up to 100 plus an omitted count); a numeric argument reports that cached query element",
|
|
9651
9859
|
whenEnabled: "Reports only the nth match (0-based); out of range exits 81",
|
|
9652
|
-
automaticBehavior: 'Coordinates are CSS px: bounds relative to the top-level page (iframe offsets and page scroll included), viewport relative to the visible area. Iframes and overflow containers (scroll lists, overflow: hidden) clip what counts as visible (clippedBy names the one cutting it off). scrollBy brings the whole element into view and is limited to how far the page can scroll: for an element out of view it centres it (aligns its start when it is larger than the viewport; human output says "to centre it"), for a partly visible one it is the smallest scroll that shows all of it (the part cut off at the top or bottom; the start of one larger than the viewport; "partly visible (87%); scroll up 5px to see all of it"). Elements a page script moves on scroll (floating menus) may move again after it; fixed and sticky elements (page scroll does not move them, or only until they stick) and ones beyond that range get offScreenReason instead, which says "page scrolling is locked (\u2026)" when the page cannot scroll because body/html is position: fixed or overflow: hidden, so in-flow content is not called fixed; a visible dialog (dialog[open], [aria-modal=true], [role=dialog|alertdialog]) is named as the likely cause ("likely by dialog div#consent"). page.viewport is the layout viewport without scrollbars, as dom scroll reports it; page.colorScheme is the prefers-color-scheme media feature the page sees (not the theme it renders). Content in a closed <details> or under content-visibility: hidden is hidden. coveredBy is the
|
|
9860
|
+
automaticBehavior: 'Coordinates are CSS px: bounds relative to the top-level page (iframe offsets and page scroll included), viewport relative to the visible area. Iframes and overflow containers (scroll lists, overflow: hidden) clip what counts as visible (clippedBy names the one cutting it off). scrollBy brings the whole element into view and is limited to how far the page can scroll: for an element out of view it centres it (aligns its start when it is larger than the viewport; human output says "to centre it"), for a partly visible one it is the smallest scroll that shows all of it (the part cut off at the top or bottom; the start of one larger than the viewport; "partly visible (87%); scroll up 5px to see all of it"). Elements a page script moves on scroll (floating menus) may move again after it; fixed and sticky elements (page scroll does not move them, or only until they stick) and ones beyond that range get offScreenReason instead, which says "page scrolling is locked (\u2026)" when the page cannot scroll because body/html is position: fixed or overflow: hidden, so in-flow content is not called fixed; a visible dialog (dialog[open], [aria-modal=true], [role=dialog|alertdialog]) is named as the likely cause ("likely by dialog div#consent"). page.viewport is the layout viewport without scrollbars, as dom scroll reports it; page.colorScheme is the prefers-color-scheme media feature the page sees (not the theme it renders). Content in a closed <details> or under content-visibility: hidden is hidden. coveredBy is the first element painted above it at the center of the largest visible box that paints there (the background of a sticky header rather than the transparent logo on it; inside a shadow host, what its shadow root paints), else the topmost one with coverTransparent (none for pointer-events: none, nor for an element of the same click target: an overlay inside the link, button or label the element is in, a link to the same URL, or the textless absolutely positioned overlay link spanning the card that holds plain content); inert elements are flagged, not hidden',
|
|
9653
9861
|
tokenImpact: 'About one line per element; a cheap alternative to screenshots for "where is it?"'
|
|
9654
9862
|
},
|
|
9655
9863
|
"inspect:--index": {
|
|
@@ -9724,11 +9932,11 @@ var OPTION_BEHAVIORS = {
|
|
|
9724
9932
|
},
|
|
9725
9933
|
"peek:-f": {
|
|
9726
9934
|
default: "Shows snapshot of current data",
|
|
9727
|
-
whenEnabled: "Continuous monitoring - refreshes every second
|
|
9935
|
+
whenEnabled: "Continuous monitoring (like tail -f): refreshes every second, or every --interval ms (100-60000). Replaces the deprecated bdg tail"
|
|
9728
9936
|
},
|
|
9729
9937
|
"peek:--follow": {
|
|
9730
9938
|
default: "Shows snapshot of current data",
|
|
9731
|
-
whenEnabled: "Continuous monitoring - refreshes every second
|
|
9939
|
+
whenEnabled: "Continuous monitoring (like tail -f): refreshes every second, or every --interval ms (100-60000). Replaces the deprecated bdg tail"
|
|
9732
9940
|
},
|
|
9733
9941
|
"peek:-v": {
|
|
9734
9942
|
default: "Compact output (truncated URLs, no resource types)",
|
|
@@ -9764,6 +9972,11 @@ var OPTION_BEHAVIORS = {
|
|
|
9764
9972
|
whenEnabled: "The page gets exactly that viewport (CSS px, e.g. 1280x800) for the whole session, through navigations and reloads (Emulation.setDeviceMetricsOverride at the display pixel ratio); a launched Chrome also opens its window at that size, so tabs the page opens get it too",
|
|
9765
9973
|
automaticBehavior: "Works with --chrome-ws-url: the override belongs to the session, and Chrome drops it when the session ends, so the attached browser gets its own size back. bdg status shows the resulting layout viewport without the scrollbar (Viewport: 1265\xD7800 (emulated 1280x800)). Invalid sizes (not WxH, a side outside 1-10000) exit 81"
|
|
9766
9974
|
},
|
|
9975
|
+
"bdg:--mobile": {
|
|
9976
|
+
default: "A desktop viewport: classic scrollbars take ~15px of the width, no touch, a desktop user agent",
|
|
9977
|
+
whenEnabled: "Emulates a phone for the whole session: a mobile viewport (390x844 unless --viewport) at pixel ratio 3 with mobile layout (meta viewport, overlay scrollbars, so 100vw fits), touch (pointer: coarse, maxTouchPoints 5) and an Android Chrome user agent with mobile client hints; bdg page emulate --mobile turns it on mid-session, --viewport WxH without --mobile or --reset turns it off",
|
|
9978
|
+
automaticBehavior: 'Screenshots keep the mobile layout and are taken at pixel ratio 1 (CSS px = image px); bdg status shows "(emulated 390x844, phone)"'
|
|
9979
|
+
},
|
|
9767
9980
|
"bdg:--color-scheme": {
|
|
9768
9981
|
default: "The page sees the system setting for prefers-color-scheme (headless Chrome follows the OS, so a dark OS renders dark pages); bdg status and dom layout show which one",
|
|
9769
9982
|
whenEnabled: "Emulates prefers-color-scheme: light or dark for the whole session (Emulation.setEmulatedMedia); other values exit 81 with a suggestion",
|
|
@@ -10619,11 +10832,6 @@ var DECISION_TREES = {
|
|
|
10619
10832
|
yesCommand: "peek --follow",
|
|
10620
10833
|
noAction: "next"
|
|
10621
10834
|
},
|
|
10622
|
-
{
|
|
10623
|
-
question: "Need continuous monitoring (like tail -f)?",
|
|
10624
|
-
yesCommand: "tail",
|
|
10625
|
-
noAction: "next"
|
|
10626
|
-
},
|
|
10627
10835
|
{
|
|
10628
10836
|
question: "Need quick preview of recent data?",
|
|
10629
10837
|
yesCommand: "peek",
|
|
@@ -10772,7 +10980,7 @@ var TASK_MAPPINGS = {
|
|
|
10772
10980
|
cdpAlternative: "Multiple IPC queries to session state"
|
|
10773
10981
|
},
|
|
10774
10982
|
live_monitoring: {
|
|
10775
|
-
commands: ["peek --follow"
|
|
10983
|
+
commands: ["peek --follow"],
|
|
10776
10984
|
description: "Monitor data collection in real-time",
|
|
10777
10985
|
cdpAlternative: "CDP event subscriptions with custom handler"
|
|
10778
10986
|
},
|
|
@@ -10879,7 +11087,7 @@ function convertCommand(command) {
|
|
|
10879
11087
|
}
|
|
10880
11088
|
function generateRuntimeState() {
|
|
10881
11089
|
const sessionActive = readLiveDaemonPid() !== null;
|
|
10882
|
-
const availableCommands = sessionActive ? ["peek", "
|
|
11090
|
+
const availableCommands = sessionActive ? ["peek", "details", "dom", "network", "console", "cdp", "status", "sessions", "stop"] : ["bdg <url>", "sessions", "cleanup", "--help", "--version"];
|
|
10883
11091
|
return {
|
|
10884
11092
|
sessionActive,
|
|
10885
11093
|
daemonRunning: sessionActive,
|
|
@@ -11241,6 +11449,8 @@ var COMMANDS = {
|
|
|
11241
11449
|
dom_form_discover: defineCommand(),
|
|
11242
11450
|
dom_listeners: defineCommand(),
|
|
11243
11451
|
dom_layout: defineCommand(),
|
|
11452
|
+
dom_audit: defineCommand(),
|
|
11453
|
+
css_search: defineCommand(),
|
|
11244
11454
|
dom_inspect: defineCommand(),
|
|
11245
11455
|
dom_wait: defineCommand()
|
|
11246
11456
|
};
|
|
@@ -11852,7 +12062,7 @@ function verboseCommandsMessage() {
|
|
|
11852
12062
|
return [
|
|
11853
12063
|
"Commands:",
|
|
11854
12064
|
` Full preview: ${sessionCommand("bdg peek --last 50")}`,
|
|
11855
|
-
` Watch live: ${sessionCommand("bdg
|
|
12065
|
+
` Watch live: ${sessionCommand("bdg peek --follow")}`,
|
|
11856
12066
|
` End session: ${sessionCommand("bdg stop")}`
|
|
11857
12067
|
].join("\n");
|
|
11858
12068
|
}
|
|
@@ -11869,6 +12079,9 @@ function connectionLostRetryMessage(timestamp, retryLabel) {
|
|
|
11869
12079
|
function connectionLostStopHintMessage() {
|
|
11870
12080
|
return "Press Ctrl+C to stop";
|
|
11871
12081
|
}
|
|
12082
|
+
function tailDeprecatedNotice() {
|
|
12083
|
+
return 'Note: "bdg tail" is deprecated and will be removed; use "bdg peek --follow" (same options: --last, --network, --console, --interval, --verbose)';
|
|
12084
|
+
}
|
|
11872
12085
|
|
|
11873
12086
|
// src/commands/shared/daemonErrorHandler.ts
|
|
11874
12087
|
init_exitCodes();
|
|
@@ -13675,9 +13888,17 @@ function registerFormInteractionCommands(program2) {
|
|
|
13675
13888
|
const action = options.double ? "double" : options.right ? "right" : "click";
|
|
13676
13889
|
await runPointerCommand(selectorOrIndex, options, action);
|
|
13677
13890
|
});
|
|
13678
|
-
domCommand.command("hover").description(
|
|
13679
|
-
|
|
13680
|
-
|
|
13891
|
+
domCommand.command("hover").description(
|
|
13892
|
+
"Move the mouse over an element (shows hover menus and tooltips); --off moves it off the page"
|
|
13893
|
+
).argument("[selectorOrIndex]", SELECTOR_OR_INDEX_ARGUMENT).option("--index <n>", "Element index if selector matches multiple (0-based)", integerOption(0)).option("--off", "Move the mouse off the page instead (closes menus that open on hover)").option("--strict", STRICT_OPTION_HELP).option("--no-wait", "Skip waiting for network stability after hovering").addOption(jsonOption()).action(
|
|
13894
|
+
async (selectorOrIndex, options) => {
|
|
13895
|
+
if (options.off || selectorOrIndex === void 0) {
|
|
13896
|
+
await hoverOff(selectorOrIndex, options);
|
|
13897
|
+
return;
|
|
13898
|
+
}
|
|
13899
|
+
await runPointerCommand(selectorOrIndex, options, "hover");
|
|
13900
|
+
}
|
|
13901
|
+
);
|
|
13681
13902
|
domCommand.command("submit").description("Submit a form by clicking submit button and waiting for completion").argument("<selectorOrIndex>", SELECTOR_OR_INDEX_ARGUMENT).option("--index <n>", "Element index if selector matches multiple (0-based)", integerOption(0)).option("--wait-navigation", "Wait for page navigation after submit").option(
|
|
13682
13903
|
"--wait-network <ms>",
|
|
13683
13904
|
"Wait for network idle after submit (milliseconds)",
|
|
@@ -13795,6 +14016,25 @@ function scrollOptionsProblem(selector, options) {
|
|
|
13795
14016
|
if (!selector && vertical + horizontal === 0) return "no-target";
|
|
13796
14017
|
return null;
|
|
13797
14018
|
}
|
|
14019
|
+
async function hoverOff(selectorOrIndex, options) {
|
|
14020
|
+
await runCommand(
|
|
14021
|
+
async () => {
|
|
14022
|
+
if (!options.off || selectorOrIndex !== void 0 || options.index !== void 0) {
|
|
14023
|
+
const err = options.off ? hoverOffWithTargetError() : missingArgumentError(HOVER_USAGE);
|
|
14024
|
+
return {
|
|
14025
|
+
success: false,
|
|
14026
|
+
error: err.message,
|
|
14027
|
+
exitCode: EXIT_CODES.INVALID_ARGUMENTS,
|
|
14028
|
+
errorContext: { suggestion: err.suggestion }
|
|
14029
|
+
};
|
|
14030
|
+
}
|
|
14031
|
+
await callCDP("Input.dispatchMouseEvent", { type: "mouseMoved", x: -1, y: -1 });
|
|
14032
|
+
return { success: true, data: { pointer: "off" } };
|
|
14033
|
+
},
|
|
14034
|
+
options,
|
|
14035
|
+
() => HOVER_OFF_DONE
|
|
14036
|
+
);
|
|
14037
|
+
}
|
|
13798
14038
|
async function runPointerCommand(selectorOrIndex, options, action) {
|
|
13799
14039
|
await runCommand(
|
|
13800
14040
|
() => options.double && options.right ? Promise.resolve({
|
|
@@ -13837,7 +14077,15 @@ function formatActionOutput(done, details, result, options = {}) {
|
|
|
13837
14077
|
fmt.blank();
|
|
13838
14078
|
fmt.keyValueList(details, keyWidth);
|
|
13839
14079
|
if (result.navigation) fmt.keyValue("Page", pageNavigationText(result.navigation), keyWidth);
|
|
13840
|
-
listRows(
|
|
14080
|
+
listRows(
|
|
14081
|
+
fmt,
|
|
14082
|
+
"New text",
|
|
14083
|
+
[
|
|
14084
|
+
...(result.messages ?? []).map(newMessageText),
|
|
14085
|
+
...result.moreMessages ? [moreMessagesText(result.moreMessages)] : []
|
|
14086
|
+
],
|
|
14087
|
+
keyWidth
|
|
14088
|
+
);
|
|
13841
14089
|
listRows(fmt, "Shown", (result.shown ?? []).map(shownElementText), keyWidth);
|
|
13842
14090
|
const omitted = result.triggeredRequestsOmitted;
|
|
13843
14091
|
const requests = formatTriggeredRequestLines(result.triggeredRequests ?? [], omitted);
|
|
@@ -14772,19 +15020,19 @@ function registerA11yCommands(domCmd) {
|
|
|
14772
15020
|
const a11y = domCmd.command("a11y").description("Accessibility tree inspection and semantic queries").argument(
|
|
14773
15021
|
"[search]",
|
|
14774
15022
|
'Quick search: index (describe), CSS selector (#id, .class), pattern with ":" (query), or name search'
|
|
14775
|
-
).enablePositionalOptions().addOption(jsonOption()).action(async (
|
|
14776
|
-
if (!
|
|
15023
|
+
).enablePositionalOptions().addOption(jsonOption()).action(async (search2, options) => {
|
|
15024
|
+
if (!search2) {
|
|
14777
15025
|
return a11y.help({ error: true });
|
|
14778
15026
|
}
|
|
14779
|
-
const isNumericIndex = /^\d+$/.test(
|
|
14780
|
-
const isCssSelector = /^[#.[]/u.test(
|
|
14781
|
-
const isPatternQuery = /^(role|name|description|desc)\s*[:=]/i.test(
|
|
15027
|
+
const isNumericIndex = /^\d+$/.test(search2);
|
|
15028
|
+
const isCssSelector = /^[#.[]/u.test(search2);
|
|
15029
|
+
const isPatternQuery = /^(role|name|description|desc)\s*[:=]/i.test(search2);
|
|
14782
15030
|
if (isNumericIndex || isCssSelector) {
|
|
14783
|
-
await handleA11yDescribe(
|
|
15031
|
+
await handleA11yDescribe(search2, options);
|
|
14784
15032
|
} else if (isPatternQuery) {
|
|
14785
|
-
await handleA11yQuery(
|
|
15033
|
+
await handleA11yQuery(search2, options);
|
|
14786
15034
|
} else {
|
|
14787
|
-
await handleA11yQuery(`name:${quoteQueryValue(
|
|
15035
|
+
await handleA11yQuery(`name:${quoteQueryValue(search2)}`, options);
|
|
14788
15036
|
}
|
|
14789
15037
|
});
|
|
14790
15038
|
a11y.command("tree").description("Dump full accessibility tree (filters ignored nodes)").addOption(jsonOption()).action(async (options) => {
|
|
@@ -14888,7 +15136,10 @@ function formatDomScreenshot(data) {
|
|
|
14888
15136
|
output += " (viewport only - page too tall)";
|
|
14889
15137
|
}
|
|
14890
15138
|
if (data.element?.captured) {
|
|
14891
|
-
output += ` (${screenshotGrownNote(data.element.bounds, data.element.captured)})`;
|
|
15139
|
+
output += ` (${screenshotGrownNote(data.element.bounds, data.element.captured, data.element.padding)})`;
|
|
15140
|
+
}
|
|
15141
|
+
if (data.resized && data.originalWidth !== void 0 && data.originalHeight !== void 0) {
|
|
15142
|
+
output += ` (${screenshotScaledNote(data.originalWidth, data.originalHeight, data.width, data.height)})`;
|
|
14892
15143
|
}
|
|
14893
15144
|
return output;
|
|
14894
15145
|
}
|
|
@@ -15676,11 +15927,11 @@ function inspectHeader(data) {
|
|
|
15676
15927
|
data.element,
|
|
15677
15928
|
data.content !== void 0 && `"${data.content}"`,
|
|
15678
15929
|
data.placeholder !== void 0 && `placeholder "${data.placeholder}"`,
|
|
15679
|
-
data.rect && `${data.rect.w}x${data.rect.h} @${data.rect.x},${data.rect.y}`,
|
|
15930
|
+
data.rect && `${data.rect.w}x${data.rect.h}${data.rect.screen ? ` (${data.rect.screen.w}x${data.rect.screen.h} on screen)` : ""} @${data.rect.x},${data.rect.y}${data.rect.in ? " (fixed: viewport position)" : ""}`,
|
|
15680
15931
|
kind && `[${kind}]`,
|
|
15681
15932
|
...inspectVisibilityBadges(data.visibility),
|
|
15682
15933
|
data.context && `in ${data.context}`,
|
|
15683
|
-
data.theme === "dark" && inspectDarkThemeBadge(),
|
|
15934
|
+
data.theme === "dark" && inspectDarkThemeBadge(data.themeFrom === "emulation"),
|
|
15684
15935
|
data.animating && inspectAnimatingBadge(data.animating)
|
|
15685
15936
|
].filter(Boolean).join(" ");
|
|
15686
15937
|
}
|
|
@@ -15755,6 +16006,7 @@ function parentLine(layout) {
|
|
|
15755
16006
|
function fontParts(text) {
|
|
15756
16007
|
const loaded = [
|
|
15757
16008
|
text.rendered && `rendered "${text.rendered}"`,
|
|
16009
|
+
text.resolved && `resolves to "${text.resolved}"`,
|
|
15758
16010
|
text.webfont && "webfont loaded"
|
|
15759
16011
|
].filter(Boolean);
|
|
15760
16012
|
const family = text.family && [text.family, loaded.length > 0 && `(${loaded.join(", ")})`];
|
|
@@ -15775,14 +16027,16 @@ function fontParts(text) {
|
|
|
15775
16027
|
function contrastText(contrast) {
|
|
15776
16028
|
if (!contrast) return void 0;
|
|
15777
16029
|
return [
|
|
15778
|
-
`contrast ${contrast.ratio} ${contrast.level}`,
|
|
15779
|
-
|
|
16030
|
+
contrast.approximate ? `contrast \u2248${contrast.ratio} ${contrast.level}` : `contrast ${contrast.ratio} ${contrast.level}`,
|
|
16031
|
+
`on ${contrast.background}`,
|
|
15780
16032
|
contrast.overImage && "(over image)",
|
|
15781
|
-
contrast.opacity !== void 0 && `(faded: opacity ${contrast.opacity})
|
|
16033
|
+
contrast.opacity !== void 0 && `(faded: opacity ${contrast.opacity})`,
|
|
16034
|
+
contrast.approximate && `(approximate: ${contrast.approximate.join(", ")})`
|
|
15782
16035
|
].filter(Boolean).join(" ");
|
|
15783
16036
|
}
|
|
15784
16037
|
function textLine(text) {
|
|
15785
16038
|
return groupLine("text", [
|
|
16039
|
+
text.holder && `in ${text.holder}`,
|
|
15786
16040
|
...fontParts(text),
|
|
15787
16041
|
text.color && `color ${text.color}`,
|
|
15788
16042
|
contrastText(text.contrast),
|
|
@@ -15793,17 +16047,25 @@ function textLine(text) {
|
|
|
15793
16047
|
text.whiteSpace && `ws ${text.whiteSpace}`,
|
|
15794
16048
|
text.overflow && `text-overflow ${text.overflow}`,
|
|
15795
16049
|
text.clamp && `clamp ${text.clamp}`,
|
|
16050
|
+
text.truncated && "truncated",
|
|
16051
|
+
text.gradientFill && "filled by its background (background-clip: text; no contrast)",
|
|
15796
16052
|
text.shadow && `shadow ${text.shadow}`,
|
|
15797
16053
|
text.features && `features ${text.features}`
|
|
15798
16054
|
]);
|
|
15799
16055
|
}
|
|
15800
16056
|
function fillText(fill) {
|
|
15801
16057
|
if (fill.type === "solid") return `bg ${fill.color}`;
|
|
15802
|
-
|
|
15803
|
-
|
|
16058
|
+
return [
|
|
16059
|
+
`bg-image ${fill.value}`,
|
|
16060
|
+
fill.size && `size ${fill.size}`,
|
|
16061
|
+
fill.position && `at ${fill.position}`
|
|
16062
|
+
].filter(Boolean).join(" ");
|
|
15804
16063
|
}
|
|
15805
16064
|
function fillLine(data) {
|
|
16065
|
+
const paint = data.paint;
|
|
15806
16066
|
return groupLine("fill", [
|
|
16067
|
+
paint && `fill ${paint.fill}`,
|
|
16068
|
+
paint && `stroke ${paint.stroke}${paint.strokeWidth !== void 0 ? ` ${paint.strokeWidth}` : ""}`,
|
|
15807
16069
|
...(data.fills ?? []).map(fillText),
|
|
15808
16070
|
data.opacity !== void 0 && `opacity ${data.opacity}`,
|
|
15809
16071
|
data.blend && `blend ${data.blend}`
|
|
@@ -15853,6 +16115,7 @@ function pseudoText(pseudo) {
|
|
|
15853
16115
|
pseudo.content !== void 0 && `content ${pseudo.content}`,
|
|
15854
16116
|
pseudo.display,
|
|
15855
16117
|
pseudo.position,
|
|
16118
|
+
pseudo.inset && `inset ${pseudo.inset}`,
|
|
15856
16119
|
pseudo.size && `${pseudo.size.w}x${pseudo.size.h}`,
|
|
15857
16120
|
pseudo.color && `color ${pseudo.color}`,
|
|
15858
16121
|
pseudo.fontStyle,
|
|
@@ -15874,7 +16137,9 @@ function treeRow(node) {
|
|
|
15874
16137
|
return [
|
|
15875
16138
|
node.element,
|
|
15876
16139
|
node.count !== void 0 && `\xD7${node.count}`,
|
|
15877
|
-
`${node.w}x${node.h}`,
|
|
16140
|
+
node.contents ? "(contents)" : `${node.w}x${node.h}`,
|
|
16141
|
+
node.shadow && "(shadow root)",
|
|
16142
|
+
node.via && `via ${node.via}`,
|
|
15878
16143
|
node.layout && `[${node.layout}]`,
|
|
15879
16144
|
node.text && `"${node.text}"`,
|
|
15880
16145
|
node.childCount !== void 0 && `(${node.childCount})`
|
|
@@ -15896,7 +16161,7 @@ function ruleScope(entry) {
|
|
|
15896
16161
|
}
|
|
15897
16162
|
function cascadeBlock(data) {
|
|
15898
16163
|
const hints = (data.hints ?? []).map(
|
|
15899
|
-
(hint) => `${hint.property}: ${hint.value} ${hint.kind === "not-inherited" ? "is the browser's" : "has no effect"}: ${hint.reason} \u2192 ${hint.fix} \xB7 in ${hint.source}`
|
|
16164
|
+
(hint) => `${hint.property}: ${hint.value} ${hint.kind === "not-inherited" ? "is the browser's" : hint.only ? `has no effect on ${hint.only.join(", ")}` : "has no effect"}: ${hint.reason} \u2192 ${hint.fix} \xB7 in ${hint.source}`
|
|
15900
16165
|
);
|
|
15901
16166
|
return [
|
|
15902
16167
|
...labelledLines("hints", data.hints?.length === 0 ? ["none"] : hints),
|
|
@@ -15927,7 +16192,8 @@ function whyLines(why, data) {
|
|
|
15927
16192
|
truncateByLength(value, CASCADE_VALUE_WIDTH) + resolved,
|
|
15928
16193
|
entry.important ? " !important" : "",
|
|
15929
16194
|
` ${entry.source}${entry.specificity ? ` [${entry.specificity.join(",")}]` : ""}${ruleScope(entry)}`,
|
|
15930
|
-
entry.status === "inherited" ? " (inherited)" : ""
|
|
16195
|
+
entry.status === "inherited" ? " (inherited)" : "",
|
|
16196
|
+
entry.note ? ` (${entry.note})` : ""
|
|
15931
16197
|
].join("");
|
|
15932
16198
|
});
|
|
15933
16199
|
const variables = (why.variables ?? []).map(
|
|
@@ -15936,8 +16202,12 @@ function whyLines(why, data) {
|
|
|
15936
16202
|
return [
|
|
15937
16203
|
`${why.property} = ${why.computed}${midTransition(why.property, data.animating) ? ` ${inspectMidTransitionNote()}` : ""}`,
|
|
15938
16204
|
...entries.slice(0, 1),
|
|
16205
|
+
...why.chain[0]?.rule ? [` in ${truncateByLength(why.chain[0].rule, RULE_LINE_WIDTH)}`] : [],
|
|
15939
16206
|
...variables,
|
|
15940
16207
|
...entries.slice(1),
|
|
16208
|
+
...(why.inactive ?? []).map(
|
|
16209
|
+
(rule) => ` - ${truncateByLength(rule.value, CASCADE_VALUE_WIDTH)} ${rule.selector} ${rule.condition} (does not apply now)`
|
|
16210
|
+
),
|
|
15941
16211
|
...why.chain.length === 0 ? [" no author declaration: the default or inherited value"] : []
|
|
15942
16212
|
];
|
|
15943
16213
|
}
|
|
@@ -15949,6 +16219,7 @@ function midTransition(property, animating) {
|
|
|
15949
16219
|
function levelsUp(levels) {
|
|
15950
16220
|
return levels === 1 ? "the parent" : `${levels} levels up`;
|
|
15951
16221
|
}
|
|
16222
|
+
var RULE_LINE_WIDTH = 120;
|
|
15952
16223
|
var CASCADE_VALUE_WIDTH = 60;
|
|
15953
16224
|
function treeBlock(data) {
|
|
15954
16225
|
if (!data.children && !data.hiddenChildren) return [];
|
|
@@ -15993,8 +16264,16 @@ function groupLines(data) {
|
|
|
15993
16264
|
function formatInspect(data) {
|
|
15994
16265
|
const body = data.props ? propLines(data.props) : data.all ? allLines(data.all) : groupLines(data);
|
|
15995
16266
|
const note = data.picked ? multipleMatchesWarning(data.count, inspectedMatchAction(data.picked, data.index)) : void 0;
|
|
15996
|
-
|
|
15997
|
-
return joinLines(inspectHeader(data), ...
|
|
16267
|
+
const pseudoNote = data.pseudoOf && inspectPseudoOfNote(data.pseudoOf);
|
|
16268
|
+
if (data.why) return joinLines(inspectHeader(data), ...cascadeBlock(data), note, pseudoNote);
|
|
16269
|
+
return joinLines(
|
|
16270
|
+
inspectHeader(data),
|
|
16271
|
+
...body,
|
|
16272
|
+
...cascadeBlock(data),
|
|
16273
|
+
...treeBlock(data),
|
|
16274
|
+
note,
|
|
16275
|
+
pseudoNote
|
|
16276
|
+
);
|
|
15998
16277
|
}
|
|
15999
16278
|
|
|
16000
16279
|
// src/commands/dom/inspect.ts
|
|
@@ -16063,6 +16342,125 @@ async function inspectTarget(selectorOrIndex, options) {
|
|
|
16063
16342
|
});
|
|
16064
16343
|
}
|
|
16065
16344
|
|
|
16345
|
+
// src/commands/dom/audit.ts
|
|
16346
|
+
init_messages();
|
|
16347
|
+
init_client();
|
|
16348
|
+
|
|
16349
|
+
// src/ipc/protocol/auditTypes.ts
|
|
16350
|
+
var AUDIT_CHECKS = ["contrast", "overflow", "layers", "animations"];
|
|
16351
|
+
|
|
16352
|
+
// src/ui/formatters/audit.ts
|
|
16353
|
+
init_strings();
|
|
16354
|
+
var TEXT_WIDTH = 50;
|
|
16355
|
+
function formatAudit(data) {
|
|
16356
|
+
const sections = [
|
|
16357
|
+
data.contrast && contrastSection(data.contrast),
|
|
16358
|
+
data.overflow && overflowSection(data.overflow),
|
|
16359
|
+
data.layers && layersSection(data.layers),
|
|
16360
|
+
data.animations && animationsSection(data.animations)
|
|
16361
|
+
].filter((section2) => section2 !== void 0);
|
|
16362
|
+
const capped = data.capped ? [`(stopped after ${data.walked} elements; the page has more)`] : [];
|
|
16363
|
+
return [...sections.flatMap((lines) => [...lines, ""]), ...capped].join("\n").trimEnd();
|
|
16364
|
+
}
|
|
16365
|
+
function contrastSection(contrast) {
|
|
16366
|
+
const head = `Contrast (${contrast.level}): ${contrast.failing} of ${contrast.checked} text elements below`;
|
|
16367
|
+
const rows = contrast.items.map(
|
|
16368
|
+
(item) => ` ${item.ratio.toFixed(2).padStart(5)} ${item.color} on ${item.background} ${item.element} "${truncateByLength(item.text, TEXT_WIDTH)}" ${item.size}px${item.weight >= 700 ? " bold" : ""}${item.inView ? "" : " (out of view)"}${item.approximate ? ` (approximate: ${item.approximate.join(", ")})` : ""}`
|
|
16369
|
+
);
|
|
16370
|
+
const more = contrast.failing - contrast.items.length;
|
|
16371
|
+
return [head, ...rows, ...more > 0 ? [` (+${more} more; --limit to list them)`] : []];
|
|
16372
|
+
}
|
|
16373
|
+
function overflowSection(overflow) {
|
|
16374
|
+
const head = overflow.scrollsSideways ? `Overflow: the page is ${overflow.pageWidth} wide in a ${overflow.viewportWidth} viewport (it scrolls sideways)` : `Overflow: nothing makes the page scroll sideways (${overflow.viewportWidth} wide)`;
|
|
16375
|
+
return [
|
|
16376
|
+
head,
|
|
16377
|
+
...overflow.wide.map(
|
|
16378
|
+
(item) => ` past the right edge: ${item.element} (to ${item.right}, ${item.width} wide)`
|
|
16379
|
+
),
|
|
16380
|
+
...overflow.truncated.map(
|
|
16381
|
+
(item) => ` cut off (${item.kind}): ${item.element}${times(item.count)} "${truncateByLength(item.text, TEXT_WIDTH)}"`
|
|
16382
|
+
),
|
|
16383
|
+
...overflow.scrollers.map(
|
|
16384
|
+
(item) => ` scrolls sideways inside: ${item.element} (${item.scrollWidth} of content in ${item.width})`
|
|
16385
|
+
),
|
|
16386
|
+
...overflow.images.map(
|
|
16387
|
+
(image) => ` image ${[image.upscaled && `upscaled ${image.scale}x${overflow.pixelRatio > 1 ? ` at pixel ratio ${overflow.pixelRatio}` : ""}`, image.distorted && "distorted"].filter(Boolean).join(", ")}: ${image.element}${times(image.count)} ${image.natural.w}x${image.natural.h} pixels drawn at ${image.rendered.w}x${image.rendered.h} CSS px`
|
|
16388
|
+
)
|
|
16389
|
+
];
|
|
16390
|
+
}
|
|
16391
|
+
function times(count) {
|
|
16392
|
+
return count ? ` \xD7${count}` : "";
|
|
16393
|
+
}
|
|
16394
|
+
function layersSection(layers) {
|
|
16395
|
+
if (layers.length === 0) return ["Layers: no fixed or sticky elements"];
|
|
16396
|
+
return [
|
|
16397
|
+
`Layers: ${layers.length} fixed or sticky`,
|
|
16398
|
+
...layers.map(
|
|
16399
|
+
(layer) => ` ${layer.position} z ${layer.zIndex} ${layer.element} ${layer.rect.w}x${layer.rect.h} at ${layer.rect.x},${layer.rect.y} of the viewport${layer.inView ? "" : " (not in view now)"}`
|
|
16400
|
+
)
|
|
16401
|
+
];
|
|
16402
|
+
}
|
|
16403
|
+
function animationsSection(animations) {
|
|
16404
|
+
if (animations.length === 0) return ["Animations: none running"];
|
|
16405
|
+
return [
|
|
16406
|
+
`Animations: ${animations.length} running`,
|
|
16407
|
+
...animations.map(
|
|
16408
|
+
(animation) => ` ${animation.name} on ${animation.element}${times(animation.count)} (${animation.type}, ${typeof animation.duration === "number" ? `${animation.duration}ms` : animation.duration}, ${animation.iterations === "infinite" ? "infinite" : `${animation.iterations}x`}${animation.scrollDriven ? ", scroll-driven" : ""})`
|
|
16409
|
+
)
|
|
16410
|
+
];
|
|
16411
|
+
}
|
|
16412
|
+
function formatCssSearch(data) {
|
|
16413
|
+
if (data.total === 0) return `"${data.query}" is not in the page's ${data.sheets} stylesheets`;
|
|
16414
|
+
const more = data.total - data.matches.length;
|
|
16415
|
+
return [
|
|
16416
|
+
`"${data.query}": ${data.total} matches in ${data.sheets} stylesheets`,
|
|
16417
|
+
...data.matches.flatMap((match) => [` ${match.source}`, ` ${match.text}`]),
|
|
16418
|
+
...more > 0 ? [` (+${more} more; --limit to list them)`] : []
|
|
16419
|
+
].join("\n");
|
|
16420
|
+
}
|
|
16421
|
+
|
|
16422
|
+
// src/commands/dom/audit.ts
|
|
16423
|
+
init_commands();
|
|
16424
|
+
init_exitCodes();
|
|
16425
|
+
init_suggestions();
|
|
16426
|
+
function registerAuditCommand(dom) {
|
|
16427
|
+
dom.command("audit").description(
|
|
16428
|
+
"Page-wide checks: text below WCAG contrast, sideways scroll and cut-off text, scaled images, fixed/sticky layers, animations"
|
|
16429
|
+
).argument("[checks...]", `Checks to run: ${AUDIT_CHECKS.join(", ")} (default: all)`, checkList).option("--level <level>", "WCAG level text must reach: AA or AAA (default: AA)", levelOption).option("--limit <n>", "Findings listed per check (default: 20)", integerOption(1, 500)).addOption(jsonOption()).addHelpText("after", AUDIT_HELP_EXAMPLES).action(async (checks, options) => {
|
|
16430
|
+
await runCommand(() => audit(checks, options), options, formatAudit);
|
|
16431
|
+
});
|
|
16432
|
+
}
|
|
16433
|
+
function checkList(value, previous = []) {
|
|
16434
|
+
const check = AUDIT_CHECKS.find((name) => name === value.trim().toLowerCase());
|
|
16435
|
+
if (!check) {
|
|
16436
|
+
throw new InvalidArgumentError(
|
|
16437
|
+
unknownAuditCheckMessage(value, findSimilar(value, [...AUDIT_CHECKS]), AUDIT_CHECKS)
|
|
16438
|
+
);
|
|
16439
|
+
}
|
|
16440
|
+
return previous.includes(check) ? previous : [...previous, check];
|
|
16441
|
+
}
|
|
16442
|
+
function levelOption(value) {
|
|
16443
|
+
const level = value.trim().toUpperCase();
|
|
16444
|
+
if (level === "AA" || level === "AAA") return level;
|
|
16445
|
+
throw new InvalidArgumentError("Use AA or AAA");
|
|
16446
|
+
}
|
|
16447
|
+
async function audit(checks, options) {
|
|
16448
|
+
const response = await domAudit({
|
|
16449
|
+
checks: checks && checks.length > 0 ? checks : [...AUDIT_CHECKS],
|
|
16450
|
+
...options.level && { level: options.level },
|
|
16451
|
+
...options.limit !== void 0 && { limit: options.limit }
|
|
16452
|
+
});
|
|
16453
|
+
if (response.status === "error" || !response.data) {
|
|
16454
|
+
return {
|
|
16455
|
+
success: false,
|
|
16456
|
+
error: response.error ?? "Failed to audit the page",
|
|
16457
|
+
exitCode: response.exitCode ?? EXIT_CODES.CDP_CONNECTION_FAILURE,
|
|
16458
|
+
...response.suggestion && { errorContext: { suggestion: response.suggestion } }
|
|
16459
|
+
};
|
|
16460
|
+
}
|
|
16461
|
+
return { success: true, data: response.data };
|
|
16462
|
+
}
|
|
16463
|
+
|
|
16066
16464
|
// src/commands/dom/layout.ts
|
|
16067
16465
|
init_client();
|
|
16068
16466
|
|
|
@@ -16382,13 +16780,17 @@ function buildElementScreenshotOptions(options) {
|
|
|
16382
16780
|
return filterDefined({
|
|
16383
16781
|
format: options.format,
|
|
16384
16782
|
quality: options.quality,
|
|
16385
|
-
noResize: options.resize === false
|
|
16783
|
+
noResize: options.resize === false,
|
|
16784
|
+
padding: options.padding
|
|
16386
16785
|
});
|
|
16387
16786
|
}
|
|
16388
16787
|
function hasElementTarget(options) {
|
|
16389
16788
|
return options.selector !== void 0 || options.index !== void 0;
|
|
16390
16789
|
}
|
|
16391
16790
|
async function resolveElementNodeId(options) {
|
|
16791
|
+
if (options.selector !== void 0 && options.index !== void 0) {
|
|
16792
|
+
return { backendNodeId: await selectMatch(options.selector, options.index) };
|
|
16793
|
+
}
|
|
16392
16794
|
if (options.index !== void 0) {
|
|
16393
16795
|
const resolver = DomElementResolver.getInstance();
|
|
16394
16796
|
const node = await resolver.getNodeIdForIndex(options.index);
|
|
@@ -16523,8 +16925,8 @@ function reportSequenceError(error, captured, json) {
|
|
|
16523
16925
|
}
|
|
16524
16926
|
function assertScreenshotOptions(outputPath, options) {
|
|
16525
16927
|
let message;
|
|
16526
|
-
if (options.
|
|
16527
|
-
message =
|
|
16928
|
+
if (options.padding !== void 0 && !hasElementTarget(options)) {
|
|
16929
|
+
message = "--padding applies to element captures; name an element (selector or index)";
|
|
16528
16930
|
} else if (options.quality !== void 0 && !options.follow && resolveImageFormat(outputPath, options.format) === "png") {
|
|
16529
16931
|
message = "--quality applies to JPEG only; this screenshot is a PNG";
|
|
16530
16932
|
}
|
|
@@ -16574,7 +16976,7 @@ function registerWaitCommand(dom) {
|
|
|
16574
16976
|
"Wait until elements appear, become visible, contain a text or are gone (or the page loads)"
|
|
16575
16977
|
).argument(
|
|
16576
16978
|
"[selector]",
|
|
16577
|
-
"CSS selector (:has-text, :visible allowed; shadow DOM and same-origin iframes searched);
|
|
16979
|
+
"CSS selector (:has-text, :visible allowed; shadow DOM and same-origin iframes searched); without one, waits for the page to load (--load)"
|
|
16578
16980
|
).option("--text <text>", "A match must contain this text (case-insensitive)").option("--visible", "Only count visible matches").option("--gone", "Wait until no element matches (none visible, with --visible)").option("--load", "Also wait for the page to finish loading (document.readyState complete)").option(
|
|
16579
16981
|
"--timeout <ms>",
|
|
16580
16982
|
"Give up after this many milliseconds (exit 102)",
|
|
@@ -16585,7 +16987,8 @@ function registerWaitCommand(dom) {
|
|
|
16585
16987
|
});
|
|
16586
16988
|
}
|
|
16587
16989
|
async function waitFor(selector, options) {
|
|
16588
|
-
const
|
|
16990
|
+
const load = options.load === true || selector === void 0 && options.text === void 0 && !options.gone && !options.visible;
|
|
16991
|
+
const needsSelector = options.text !== void 0 || options.gone === true || !load;
|
|
16589
16992
|
if (selector === void 0 && needsSelector) {
|
|
16590
16993
|
const err = waitTargetRequiredError();
|
|
16591
16994
|
return {
|
|
@@ -16599,7 +17002,7 @@ async function waitFor(selector, options) {
|
|
|
16599
17002
|
...filterDefined({ selector, text: options.text }),
|
|
16600
17003
|
...options.gone && { gone: true },
|
|
16601
17004
|
...options.visible && { visible: true },
|
|
16602
|
-
...
|
|
17005
|
+
...load && { load: true },
|
|
16603
17006
|
timeout: options.timeout
|
|
16604
17007
|
});
|
|
16605
17008
|
if (response.status === "error" || !response.data) {
|
|
@@ -16623,6 +17026,7 @@ function registerDomCommands(program2) {
|
|
|
16623
17026
|
registerFormCommand(dom);
|
|
16624
17027
|
registerListenersCommand(dom);
|
|
16625
17028
|
registerLayoutCommand(dom);
|
|
17029
|
+
registerAuditCommand(dom);
|
|
16626
17030
|
registerInspectCommand(dom);
|
|
16627
17031
|
registerWaitCommand(dom);
|
|
16628
17032
|
dom.command("query").description("Find elements by CSS selector").argument("<selector>", 'CSS selector (e.g., ".error", "#app", "button")').option("-j, --json", "Output as JSON").addHelpText("after", SELECTOR_SCOPE_HELP).action(async (selector, options) => {
|
|
@@ -16657,8 +17061,12 @@ function registerDomCommands(program2) {
|
|
|
16657
17061
|
"Element to capture: CSS selector or index from a query (same as --selector / --index)"
|
|
16658
17062
|
).option("--selector <selector>", "CSS selector for element capture").option(
|
|
16659
17063
|
"--index <number>",
|
|
16660
|
-
"Cached element index (0-based) from previous query",
|
|
17064
|
+
"Cached element index (0-based) from a previous query; with --selector, which match",
|
|
16661
17065
|
integerOption(0)
|
|
17066
|
+
).option(
|
|
17067
|
+
"--padding <px>",
|
|
17068
|
+
"Element capture: extra space around it (shadows and focus rings are included anyway)",
|
|
17069
|
+
integerOption(0, 500)
|
|
16662
17070
|
).option(
|
|
16663
17071
|
"--format <format>",
|
|
16664
17072
|
"Image format: png or jpeg/jpg (default: from the file extension, else png)",
|
|
@@ -16670,6 +17078,87 @@ function registerDomCommands(program2) {
|
|
|
16670
17078
|
);
|
|
16671
17079
|
}
|
|
16672
17080
|
|
|
17081
|
+
// src/commands/installSkill.ts
|
|
17082
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "fs";
|
|
17083
|
+
import { homedir as homedir3 } from "os";
|
|
17084
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
17085
|
+
init_errors3();
|
|
17086
|
+
init_messages();
|
|
17087
|
+
|
|
17088
|
+
// src/ui/formatters/installSkill.ts
|
|
17089
|
+
init_formatting();
|
|
17090
|
+
import { homedir as homedir2 } from "os";
|
|
17091
|
+
function tildePath(path6) {
|
|
17092
|
+
const home = homedir2();
|
|
17093
|
+
return path6.startsWith(`${home}/`) ? `~${path6.slice(home.length)}` : path6;
|
|
17094
|
+
}
|
|
17095
|
+
function formatInstalledSkills(data) {
|
|
17096
|
+
const fmt = new OutputFormatter().text("bdg skill:");
|
|
17097
|
+
for (const skill of data.skills) {
|
|
17098
|
+
fmt.text(` ${skill.target.padEnd(6)} ${skill.status.padEnd(9)} ${tildePath(skill.path)}`);
|
|
17099
|
+
}
|
|
17100
|
+
return fmt.hints("Next:", [
|
|
17101
|
+
"Start a new agent session to load it (running sessions keep the old list)",
|
|
17102
|
+
"After upgrading bdg, run bdg install-skill again"
|
|
17103
|
+
]).build();
|
|
17104
|
+
}
|
|
17105
|
+
|
|
17106
|
+
// src/commands/installSkill.ts
|
|
17107
|
+
init_errors();
|
|
17108
|
+
init_exitCodes();
|
|
17109
|
+
var SKILL_SOURCE_PATH = join6(PACKAGE_ROOT, ".claude", "skills", "bdg", "SKILL.md");
|
|
17110
|
+
var SKILL_ROOTS = {
|
|
17111
|
+
claude: join6(".claude", "skills"),
|
|
17112
|
+
agents: join6(".agents", "skills")
|
|
17113
|
+
};
|
|
17114
|
+
function installSkill(targets, home = homedir3(), source = SKILL_SOURCE_PATH) {
|
|
17115
|
+
if (!existsSync10(source)) {
|
|
17116
|
+
const err = skillSourceMissingError(source);
|
|
17117
|
+
throw new CommandError(
|
|
17118
|
+
err.message,
|
|
17119
|
+
{ suggestion: err.suggestion },
|
|
17120
|
+
EXIT_CODES.RESOURCE_NOT_FOUND
|
|
17121
|
+
);
|
|
17122
|
+
}
|
|
17123
|
+
const content = readFileSync8(source, "utf-8");
|
|
17124
|
+
return targets.map(
|
|
17125
|
+
(target) => writeSkill(target, join6(home, SKILL_ROOTS[target], "bdg", "SKILL.md"), content)
|
|
17126
|
+
);
|
|
17127
|
+
}
|
|
17128
|
+
function writeSkill(target, path6, content) {
|
|
17129
|
+
const existing = existsSync10(path6) ? readFileSync8(path6, "utf-8") : void 0;
|
|
17130
|
+
if (existing === content) {
|
|
17131
|
+
return { target, path: path6, status: "unchanged" };
|
|
17132
|
+
}
|
|
17133
|
+
try {
|
|
17134
|
+
mkdirSync3(dirname5(path6), { recursive: true });
|
|
17135
|
+
writeFileSync2(path6, content);
|
|
17136
|
+
} catch (caught) {
|
|
17137
|
+
const err = skillWriteFailedError(path6, getErrorMessage(caught));
|
|
17138
|
+
throw new CommandError(
|
|
17139
|
+
err.message,
|
|
17140
|
+
{ suggestion: err.suggestion },
|
|
17141
|
+
EXIT_CODES.PERMISSION_DENIED
|
|
17142
|
+
);
|
|
17143
|
+
}
|
|
17144
|
+
return { target, path: path6, status: existing === void 0 ? "installed" : "updated" };
|
|
17145
|
+
}
|
|
17146
|
+
function selectedTargets(options) {
|
|
17147
|
+
const picked = Object.keys(SKILL_ROOTS).filter((target) => options[target]);
|
|
17148
|
+
return picked.length > 0 ? picked : Object.keys(SKILL_ROOTS);
|
|
17149
|
+
}
|
|
17150
|
+
function registerInstallSkillCommand(program2) {
|
|
17151
|
+
program2.command("install-skill").description(
|
|
17152
|
+
"Install the bdg agent skill for Claude Code (~/.claude/skills) and agents reading ~/.agents/skills (Codex, Gemini CLI, ...); re-run after upgrading bdg"
|
|
17153
|
+
).option("--claude", "Only ~/.claude/skills (Claude Code)").option("--agents", "Only ~/.agents/skills (Codex, Gemini CLI and other agents)").addOption(jsonOption()).action(async (options) => {
|
|
17154
|
+
await runCommand(
|
|
17155
|
+
(opts) => Promise.resolve({ success: true, data: { skills: installSkill(selectedTargets(opts)) } }),
|
|
17156
|
+
options,
|
|
17157
|
+
formatInstalledSkills
|
|
17158
|
+
);
|
|
17159
|
+
});
|
|
17160
|
+
}
|
|
17161
|
+
|
|
16673
17162
|
// src/commands/network/har.ts
|
|
16674
17163
|
import * as fs13 from "fs";
|
|
16675
17164
|
import * as path5 from "path";
|
|
@@ -17434,7 +17923,7 @@ function appearanceLines(metadata, pageState) {
|
|
|
17434
17923
|
...viewport ? [
|
|
17435
17924
|
[
|
|
17436
17925
|
"Viewport",
|
|
17437
|
-
`${viewport.width}\xD7${viewport.height}${metadata.viewport ? ` (emulated ${metadata.viewport.width}x${metadata.viewport.height})` : ""}`
|
|
17926
|
+
`${viewport.width}\xD7${viewport.height}${metadata.viewport ? ` (emulated ${metadata.viewport.width}x${metadata.viewport.height}${metadata.viewport.mobile ? ", phone" : ""})` : ""}`
|
|
17438
17927
|
]
|
|
17439
17928
|
] : [],
|
|
17440
17929
|
...scheme ? [
|
|
@@ -18256,6 +18745,37 @@ function registerNetworkCommands(program2) {
|
|
|
18256
18745
|
registerDocumentCommand(networkCmd);
|
|
18257
18746
|
}
|
|
18258
18747
|
|
|
18748
|
+
// src/commands/css.ts
|
|
18749
|
+
init_client();
|
|
18750
|
+
init_commands();
|
|
18751
|
+
init_exitCodes();
|
|
18752
|
+
function registerCssCommands(program2) {
|
|
18753
|
+
const css = program2.command("css").description("The page's stylesheets: search <text>");
|
|
18754
|
+
css.command("search").description(
|
|
18755
|
+
"Find text in every stylesheet of the page (cross-origin ones too), with the rule and file:line"
|
|
18756
|
+
).argument(
|
|
18757
|
+
"<text>",
|
|
18758
|
+
"Text to find (case-insensitive), e.g. oklch( or .btn-primary; put -- before one that starts with - (-- --brand)"
|
|
18759
|
+
).option("--limit <n>", "Matches listed (default: 20)", integerOption(1, 500)).addOption(jsonOption()).addHelpText("after", CSS_SEARCH_HELP_EXAMPLES).action(async (text, options) => {
|
|
18760
|
+
await runCommand(() => search(text, options), options, formatCssSearch);
|
|
18761
|
+
});
|
|
18762
|
+
}
|
|
18763
|
+
async function search(text, options) {
|
|
18764
|
+
const response = await cssSearch({
|
|
18765
|
+
query: text,
|
|
18766
|
+
...options.limit !== void 0 && { limit: options.limit }
|
|
18767
|
+
});
|
|
18768
|
+
if (response.status === "error" || !response.data) {
|
|
18769
|
+
return {
|
|
18770
|
+
success: false,
|
|
18771
|
+
error: response.error ?? "Failed to search the stylesheets",
|
|
18772
|
+
exitCode: response.exitCode ?? EXIT_CODES.CDP_CONNECTION_FAILURE,
|
|
18773
|
+
...response.suggestion && { errorContext: { suggestion: response.suggestion } }
|
|
18774
|
+
};
|
|
18775
|
+
}
|
|
18776
|
+
return { success: true, data: response.data };
|
|
18777
|
+
}
|
|
18778
|
+
|
|
18259
18779
|
// src/commands/start.ts
|
|
18260
18780
|
import fs15 from "node:fs";
|
|
18261
18781
|
import os3 from "node:os";
|
|
@@ -18300,7 +18820,7 @@ var SessionDirError = class extends DaemonError {
|
|
|
18300
18820
|
// src/daemon/launcher.ts
|
|
18301
18821
|
import { spawn } from "child_process";
|
|
18302
18822
|
import fs14 from "fs";
|
|
18303
|
-
import { join as
|
|
18823
|
+
import { join as join8 } from "path";
|
|
18304
18824
|
init_messages();
|
|
18305
18825
|
init_paths();
|
|
18306
18826
|
init_logging();
|
|
@@ -18321,7 +18841,7 @@ async function launchDaemon() {
|
|
|
18321
18841
|
);
|
|
18322
18842
|
}
|
|
18323
18843
|
assertUsableSessionDir();
|
|
18324
|
-
const logPath =
|
|
18844
|
+
const logPath = join8(getSessionDir(), "daemon.log");
|
|
18325
18845
|
rotateLog(logPath);
|
|
18326
18846
|
const logFd = fs14.openSync(logPath, "a");
|
|
18327
18847
|
log24.debug(`Starting daemon: ${DAEMON_SCRIPT_PATH}`);
|
|
@@ -18382,7 +18902,7 @@ async function waitForDaemonReady(hasExited) {
|
|
|
18382
18902
|
}
|
|
18383
18903
|
if (hasExited() && !await isDaemonAlive()) {
|
|
18384
18904
|
throw new DaemonStartupError(
|
|
18385
|
-
`Daemon exited during startup. See ${
|
|
18905
|
+
`Daemon exited during startup. See ${join8(getSessionDir(), "daemon.log")}`,
|
|
18386
18906
|
"DAEMON_EXITED"
|
|
18387
18907
|
);
|
|
18388
18908
|
}
|
|
@@ -18729,8 +19249,17 @@ function applyCollectorOptions(command) {
|
|
|
18729
19249
|
).option(
|
|
18730
19250
|
"--color-scheme <scheme>",
|
|
18731
19251
|
"Emulate prefers-color-scheme for the session: light or dark (default: the system setting)"
|
|
19252
|
+
).option(
|
|
19253
|
+
"--mobile",
|
|
19254
|
+
`Emulate a phone for the session: mobile viewport (${MOBILE_VIEWPORT.width}x${MOBILE_VIEWPORT.height} unless --viewport), touch, mobile user agent`
|
|
18732
19255
|
);
|
|
18733
19256
|
}
|
|
19257
|
+
var MOBILE_VIEWPORT = { width: 390, height: 844 };
|
|
19258
|
+
function requestedViewport(viewport, mobile) {
|
|
19259
|
+
const size = viewport !== void 0 ? parseViewport(viewport) : void 0;
|
|
19260
|
+
if (!mobile) return size;
|
|
19261
|
+
return { ...size ?? MOBILE_VIEWPORT, mobile: true };
|
|
19262
|
+
}
|
|
18734
19263
|
var MAX_VIEWPORT_SIDE = 1e4;
|
|
18735
19264
|
var COLOR_SCHEMES = ["light", "dark"];
|
|
18736
19265
|
function parseViewport(value) {
|
|
@@ -18778,7 +19307,7 @@ function buildSessionOptions(options) {
|
|
|
18778
19307
|
quiet: options.quiet ?? false,
|
|
18779
19308
|
json: options.json ?? false,
|
|
18780
19309
|
chromeFlags,
|
|
18781
|
-
viewport: options.viewport
|
|
19310
|
+
viewport: requestedViewport(options.viewport, options.mobile),
|
|
18782
19311
|
colorScheme: options.colorScheme !== void 0 ? parseColorScheme(options.colorScheme) : void 0
|
|
18783
19312
|
};
|
|
18784
19313
|
}
|
|
@@ -19030,7 +19559,7 @@ async function showPageInfo(options) {
|
|
|
19030
19559
|
}
|
|
19031
19560
|
function emulationRequest(options) {
|
|
19032
19561
|
if (options.reset) return { reset: true };
|
|
19033
|
-
if (options.viewport === void 0 && options.colorScheme === void 0) {
|
|
19562
|
+
if (options.viewport === void 0 && options.colorScheme === void 0 && !options.mobile) {
|
|
19034
19563
|
const err = pageEmulateNothingError();
|
|
19035
19564
|
throw new CommandError(
|
|
19036
19565
|
err.message,
|
|
@@ -19038,8 +19567,9 @@ function emulationRequest(options) {
|
|
|
19038
19567
|
EXIT_CODES.INVALID_ARGUMENTS
|
|
19039
19568
|
);
|
|
19040
19569
|
}
|
|
19570
|
+
const viewport = requestedViewport(options.viewport, options.mobile);
|
|
19041
19571
|
return {
|
|
19042
|
-
...
|
|
19572
|
+
...viewport && { viewport },
|
|
19043
19573
|
...options.colorScheme !== void 0 && {
|
|
19044
19574
|
colorScheme: parseColorScheme(options.colorScheme)
|
|
19045
19575
|
}
|
|
@@ -19076,10 +19606,17 @@ function registerPageCommands(program2) {
|
|
|
19076
19606
|
).action(async (url, options) => {
|
|
19077
19607
|
await runPageAction("navigate", options, url);
|
|
19078
19608
|
});
|
|
19079
|
-
page.command("emulate").description(PAGE_EMULATE_DESCRIPTION).option(
|
|
19609
|
+
page.command("emulate").description(PAGE_EMULATE_DESCRIPTION).option(
|
|
19610
|
+
"--viewport <WxH>",
|
|
19611
|
+
"Viewport size in CSS px, e.g. 900x700 (a desktop one unless --mobile)"
|
|
19612
|
+
).option("--color-scheme <scheme>", "Emulate prefers-color-scheme: light or dark").option(
|
|
19613
|
+
"--mobile",
|
|
19614
|
+
"Emulate a phone: mobile viewport (390x844 unless --viewport), touch, mobile user agent"
|
|
19615
|
+
).addOption(
|
|
19080
19616
|
new Option("--reset", "Back to the browser window size and the system setting").conflicts([
|
|
19081
19617
|
"viewport",
|
|
19082
|
-
"colorScheme"
|
|
19618
|
+
"colorScheme",
|
|
19619
|
+
"mobile"
|
|
19083
19620
|
])
|
|
19084
19621
|
).addOption(jsonOption()).action(async (options) => {
|
|
19085
19622
|
await emulate(options);
|
|
@@ -19094,6 +19631,9 @@ function registerPageCommands(program2) {
|
|
|
19094
19631
|
}
|
|
19095
19632
|
|
|
19096
19633
|
// src/commands/peek.ts
|
|
19634
|
+
init_errors3();
|
|
19635
|
+
init_messages();
|
|
19636
|
+
init_exitCodes();
|
|
19097
19637
|
function parseOptions(options) {
|
|
19098
19638
|
const lastN = positiveIntRule({
|
|
19099
19639
|
name: "--last",
|
|
@@ -19103,7 +19643,21 @@ function parseOptions(options) {
|
|
|
19103
19643
|
allowZeroForAll: true
|
|
19104
19644
|
}).validate(options.last);
|
|
19105
19645
|
const resourceTypes = resourceTypeRule().validate(options.type);
|
|
19106
|
-
|
|
19646
|
+
if (options.interval !== void 0 && !options.follow) {
|
|
19647
|
+
const err = intervalWithoutFollowError();
|
|
19648
|
+
throw new CommandError(
|
|
19649
|
+
err.message,
|
|
19650
|
+
{ suggestion: err.suggestion },
|
|
19651
|
+
EXIT_CODES.INVALID_ARGUMENTS
|
|
19652
|
+
);
|
|
19653
|
+
}
|
|
19654
|
+
const interval = positiveIntRule({
|
|
19655
|
+
name: "--interval",
|
|
19656
|
+
min: 100,
|
|
19657
|
+
max: 6e4,
|
|
19658
|
+
default: 1e3
|
|
19659
|
+
}).validate(options.interval);
|
|
19660
|
+
return { lastN, resourceTypes, interval };
|
|
19107
19661
|
}
|
|
19108
19662
|
function peekSection(options) {
|
|
19109
19663
|
if (options.network) return "network";
|
|
@@ -19137,11 +19691,12 @@ function createPreviewOptions(base, resourceTypes, unfilteredCount) {
|
|
|
19137
19691
|
}
|
|
19138
19692
|
return options;
|
|
19139
19693
|
}
|
|
19140
|
-
async function runFollowMode3(options,
|
|
19694
|
+
async function runFollowMode3(options, parsed, baseOptions) {
|
|
19695
|
+
const { lastN, resourceTypes, interval } = parsed;
|
|
19141
19696
|
const showPreview = async () => {
|
|
19142
19697
|
const result = await fetchAndFilterPreview(lastN, resourceTypes, peekSection(options));
|
|
19143
19698
|
if (!result.success) {
|
|
19144
|
-
return followFetchFailure(result, { json: options.json, retryIntervalMs:
|
|
19699
|
+
return followFetchFailure(result, { json: options.json, retryIntervalMs: interval });
|
|
19145
19700
|
}
|
|
19146
19701
|
noteFollowConnected();
|
|
19147
19702
|
if (!options.json) console.clear();
|
|
@@ -19156,11 +19711,34 @@ async function runFollowMode3(options, lastN, resourceTypes, baseOptions) {
|
|
|
19156
19711
|
await setupFollowMode(showPreview, {
|
|
19157
19712
|
startMessage: followingPreviewMessage,
|
|
19158
19713
|
stopMessage: stoppedFollowingPreviewMessage,
|
|
19159
|
-
intervalMs:
|
|
19714
|
+
intervalMs: interval
|
|
19160
19715
|
});
|
|
19161
19716
|
}
|
|
19717
|
+
function parsePeekOptions(options) {
|
|
19718
|
+
try {
|
|
19719
|
+
return parseOptions(options);
|
|
19720
|
+
} catch (error) {
|
|
19721
|
+
handleValidationError(error, options.json ?? false);
|
|
19722
|
+
}
|
|
19723
|
+
}
|
|
19724
|
+
function previewDisplayOptions(options, lastN) {
|
|
19725
|
+
return {
|
|
19726
|
+
json: options.json,
|
|
19727
|
+
network: options.network,
|
|
19728
|
+
console: options.console,
|
|
19729
|
+
last: lastN,
|
|
19730
|
+
verbose: options.verbose,
|
|
19731
|
+
follow: options.follow
|
|
19732
|
+
};
|
|
19733
|
+
}
|
|
19734
|
+
async function followPreview(options) {
|
|
19735
|
+
const following = { ...options, follow: true };
|
|
19736
|
+
showBothSectionsWhenBothRequested(following);
|
|
19737
|
+
const parsed = parsePeekOptions(following);
|
|
19738
|
+
await runFollowMode3(following, parsed, previewDisplayOptions(following, parsed.lastN));
|
|
19739
|
+
}
|
|
19162
19740
|
function registerPeekCommand(program2) {
|
|
19163
|
-
program2.command("peek").description("Preview collected data without stopping the session").addOption(jsonOption()).option("-v, --verbose", "Use verbose output with full URLs and formatting", false).option("-n, --network", "Show only network requests", false).option("-c, --console", "Show only console messages", false).option("-f, --follow", "Watch for updates (like tail -f)", false).option("--last <count>", "Show last N items, 0 for all", "10").option(
|
|
19741
|
+
program2.command("peek").description("Preview collected data without stopping the session").addOption(jsonOption()).option("-v, --verbose", "Use verbose output with full URLs and formatting", false).option("-n, --network", "Show only network requests", false).option("-c, --console", "Show only console messages", false).option("-f, --follow", "Watch for updates (like tail -f)", false).option("--interval <ms>", "Refresh interval of --follow in ms, 100-60000 (default: 1000)").option("--last <count>", "Show last N items, 0 for all", "10").option(
|
|
19164
19742
|
"--type <types>",
|
|
19165
19743
|
"Filter network requests by resource type (comma-separated: Document,XHR,Fetch,etc.)"
|
|
19166
19744
|
).action(async (options) => {
|
|
@@ -19170,25 +19748,11 @@ function registerPeekCommand(program2) {
|
|
|
19170
19748
|
'Note: "bdg peek --network" is deprecated. Use "bdg network list" for enhanced filtering.'
|
|
19171
19749
|
);
|
|
19172
19750
|
}
|
|
19173
|
-
|
|
19174
|
-
|
|
19175
|
-
|
|
19176
|
-
const parsed = parseOptions(options);
|
|
19177
|
-
lastN = parsed.lastN;
|
|
19178
|
-
resourceTypes = parsed.resourceTypes;
|
|
19179
|
-
} catch (error) {
|
|
19180
|
-
handleValidationError(error, options.json ?? false);
|
|
19181
|
-
}
|
|
19182
|
-
const baseOptions = {
|
|
19183
|
-
json: options.json,
|
|
19184
|
-
network: options.network,
|
|
19185
|
-
console: options.console,
|
|
19186
|
-
last: lastN,
|
|
19187
|
-
verbose: options.verbose,
|
|
19188
|
-
follow: options.follow
|
|
19189
|
-
};
|
|
19751
|
+
const parsed = parsePeekOptions(options);
|
|
19752
|
+
const { lastN, resourceTypes } = parsed;
|
|
19753
|
+
const baseOptions = previewDisplayOptions(options, lastN);
|
|
19190
19754
|
if (options.follow) {
|
|
19191
|
-
await runFollowMode3(options,
|
|
19755
|
+
await runFollowMode3(options, parsed, baseOptions);
|
|
19192
19756
|
return;
|
|
19193
19757
|
}
|
|
19194
19758
|
await runCommand(
|
|
@@ -19657,62 +20221,14 @@ function registerStopCommand(program2) {
|
|
|
19657
20221
|
}
|
|
19658
20222
|
|
|
19659
20223
|
// src/commands/tail.ts
|
|
19660
|
-
function parseOptions2(options) {
|
|
19661
|
-
const lastRule = positiveIntRule({
|
|
19662
|
-
name: "--last",
|
|
19663
|
-
min: 1,
|
|
19664
|
-
max: MAX_LAST_ITEMS,
|
|
19665
|
-
default: 10,
|
|
19666
|
-
allowZeroForAll: true
|
|
19667
|
-
});
|
|
19668
|
-
const intervalRule = positiveIntRule({ name: "--interval", min: 100, max: 6e4, default: 1e3 });
|
|
19669
|
-
return {
|
|
19670
|
-
lastN: lastRule.validate(options.last),
|
|
19671
|
-
interval: intervalRule.validate(options.interval)
|
|
19672
|
-
};
|
|
19673
|
-
}
|
|
19674
|
-
function createPreviewOptions2(options, lastN) {
|
|
19675
|
-
return {
|
|
19676
|
-
json: options.json,
|
|
19677
|
-
network: options.network,
|
|
19678
|
-
console: options.console,
|
|
19679
|
-
last: lastN,
|
|
19680
|
-
verbose: options.verbose,
|
|
19681
|
-
follow: true,
|
|
19682
|
-
viewedAt: /* @__PURE__ */ new Date()
|
|
19683
|
-
};
|
|
19684
|
-
}
|
|
19685
20224
|
function registerTailCommand(program2) {
|
|
19686
|
-
program2.command("tail").description("
|
|
20225
|
+
program2.command("tail").description('Deprecated: use "bdg peek --follow" (same options)').addOption(jsonOption()).option("-v, --verbose", "Use verbose output with full URLs and formatting", false).option("-n, --network", "Show only network requests", false).option("-c, --console", "Show only console messages", false).option(
|
|
19687
20226
|
"--last <count>",
|
|
19688
20227
|
"Show last N items (network requests + console messages), 0 for all",
|
|
19689
20228
|
"10"
|
|
19690
20229
|
).option("--interval <ms>", "Update interval in milliseconds", "1000").action(async (options) => {
|
|
19691
|
-
|
|
19692
|
-
|
|
19693
|
-
let interval;
|
|
19694
|
-
try {
|
|
19695
|
-
const parsed = parseOptions2(options);
|
|
19696
|
-
lastN = parsed.lastN;
|
|
19697
|
-
interval = parsed.interval;
|
|
19698
|
-
} catch (error) {
|
|
19699
|
-
handleValidationError(error, options.json ?? false);
|
|
19700
|
-
}
|
|
19701
|
-
const showPreview = async () => {
|
|
19702
|
-
const result = await fetchPreviewOutput({ lastN });
|
|
19703
|
-
if (!result.success) {
|
|
19704
|
-
return followFetchFailure(result, { json: options.json, retryIntervalMs: interval });
|
|
19705
|
-
}
|
|
19706
|
-
noteFollowConnected();
|
|
19707
|
-
if (!options.json) console.clear();
|
|
19708
|
-
console.log(formatPreview(result.data, createPreviewOptions2(options, lastN)));
|
|
19709
|
-
return void 0;
|
|
19710
|
-
};
|
|
19711
|
-
await setupFollowMode(showPreview, {
|
|
19712
|
-
startMessage: followingPreviewMessage,
|
|
19713
|
-
stopMessage: stoppedFollowingPreviewMessage,
|
|
19714
|
-
intervalMs: interval
|
|
19715
|
-
});
|
|
20230
|
+
console.error(tailDeprecatedNotice());
|
|
20231
|
+
await followPreview({ ...options, follow: true });
|
|
19716
20232
|
});
|
|
19717
20233
|
}
|
|
19718
20234
|
|
|
@@ -19736,12 +20252,15 @@ var commandRegistry = [
|
|
|
19736
20252
|
registerDomCommands,
|
|
19737
20253
|
registerFormInteractionCommands,
|
|
19738
20254
|
registerPageCommands,
|
|
20255
|
+
registerCssCommands,
|
|
19739
20256
|
addCommandGroup("CDP Commands:"),
|
|
19740
20257
|
registerCdpCommand,
|
|
19741
20258
|
addCommandGroup("Network Commands:"),
|
|
19742
20259
|
registerNetworkCommands,
|
|
19743
20260
|
addCommandGroup("Console Commands:"),
|
|
19744
|
-
registerConsoleCommand
|
|
20261
|
+
registerConsoleCommand,
|
|
20262
|
+
addCommandGroup("Agent Setup:"),
|
|
20263
|
+
registerInstallSkillCommand
|
|
19745
20264
|
];
|
|
19746
20265
|
|
|
19747
20266
|
// src/index.ts
|