firefox-devtools-mcp 0.6.0 → 0.7.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/README.md +13 -1
- package/dist/index.js +100 -45
- package/dist/snapshot.injected.global.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -99,9 +99,21 @@ You can pass flags or environment variables (names on the right):
|
|
|
99
99
|
- Input: click/hover/fill/drag/upload/form fill
|
|
100
100
|
- Network: list/get (ID‑first, filters, always‑on capture)
|
|
101
101
|
- Console: list/clear
|
|
102
|
-
- Screenshot: page/by uid
|
|
102
|
+
- Screenshot: page/by uid (with optional `saveTo` for CLI environments)
|
|
103
103
|
- Utilities: accept/dismiss dialog, history back/forward, set viewport
|
|
104
104
|
|
|
105
|
+
### Screenshot optimization for Claude Code
|
|
106
|
+
|
|
107
|
+
When using screenshots in Claude Code CLI, the base64 image data can consume significant context.
|
|
108
|
+
Use the `saveTo` parameter to save screenshots to disk instead:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
screenshot_page({ saveTo: "/tmp/page.png" })
|
|
112
|
+
screenshot_by_uid({ uid: "abc123", saveTo: "/tmp/element.png" })
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The file can then be viewed with Claude Code's `Read` tool without impacting context size.
|
|
116
|
+
|
|
105
117
|
## Local development
|
|
106
118
|
|
|
107
119
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -14072,7 +14072,7 @@ var init_protocol = __esm({
|
|
|
14072
14072
|
return;
|
|
14073
14073
|
}
|
|
14074
14074
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
14075
|
-
await new Promise((
|
|
14075
|
+
await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
|
|
14076
14076
|
options?.signal?.throwIfAborted();
|
|
14077
14077
|
}
|
|
14078
14078
|
} catch (error2) {
|
|
@@ -14089,7 +14089,7 @@ var init_protocol = __esm({
|
|
|
14089
14089
|
*/
|
|
14090
14090
|
request(request, resultSchema, options) {
|
|
14091
14091
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
14092
|
-
return new Promise((
|
|
14092
|
+
return new Promise((resolve4, reject) => {
|
|
14093
14093
|
const earlyReject = (error2) => {
|
|
14094
14094
|
reject(error2);
|
|
14095
14095
|
};
|
|
@@ -14167,7 +14167,7 @@ var init_protocol = __esm({
|
|
|
14167
14167
|
if (!parseResult.success) {
|
|
14168
14168
|
reject(parseResult.error);
|
|
14169
14169
|
} else {
|
|
14170
|
-
|
|
14170
|
+
resolve4(parseResult.data);
|
|
14171
14171
|
}
|
|
14172
14172
|
} catch (error2) {
|
|
14173
14173
|
reject(error2);
|
|
@@ -14428,12 +14428,12 @@ var init_protocol = __esm({
|
|
|
14428
14428
|
}
|
|
14429
14429
|
} catch {
|
|
14430
14430
|
}
|
|
14431
|
-
return new Promise((
|
|
14431
|
+
return new Promise((resolve4, reject) => {
|
|
14432
14432
|
if (signal.aborted) {
|
|
14433
14433
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
14434
14434
|
return;
|
|
14435
14435
|
}
|
|
14436
|
-
const timeoutId = setTimeout(
|
|
14436
|
+
const timeoutId = setTimeout(resolve4, interval);
|
|
14437
14437
|
signal.addEventListener("abort", () => {
|
|
14438
14438
|
clearTimeout(timeoutId);
|
|
14439
14439
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -17460,7 +17460,7 @@ var require_compile = __commonJS({
|
|
|
17460
17460
|
const schOrFunc = root.refs[ref];
|
|
17461
17461
|
if (schOrFunc)
|
|
17462
17462
|
return schOrFunc;
|
|
17463
|
-
let _sch =
|
|
17463
|
+
let _sch = resolve4.call(this, root, ref);
|
|
17464
17464
|
if (_sch === void 0) {
|
|
17465
17465
|
const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
17466
17466
|
const { schemaId } = this.opts;
|
|
@@ -17487,7 +17487,7 @@ var require_compile = __commonJS({
|
|
|
17487
17487
|
function sameSchemaEnv(s1, s2) {
|
|
17488
17488
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
17489
17489
|
}
|
|
17490
|
-
function
|
|
17490
|
+
function resolve4(root, ref) {
|
|
17491
17491
|
let sch;
|
|
17492
17492
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
17493
17493
|
ref = sch;
|
|
@@ -18062,7 +18062,7 @@ var require_fast_uri = __commonJS({
|
|
|
18062
18062
|
}
|
|
18063
18063
|
return uri;
|
|
18064
18064
|
}
|
|
18065
|
-
function
|
|
18065
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
18066
18066
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
18067
18067
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
18068
18068
|
schemelessOptions.skipEscape = true;
|
|
@@ -18289,7 +18289,7 @@ var require_fast_uri = __commonJS({
|
|
|
18289
18289
|
var fastUri = {
|
|
18290
18290
|
SCHEMES,
|
|
18291
18291
|
normalize,
|
|
18292
|
-
resolve:
|
|
18292
|
+
resolve: resolve4,
|
|
18293
18293
|
resolveComponent,
|
|
18294
18294
|
equal,
|
|
18295
18295
|
serialize,
|
|
@@ -21944,12 +21944,12 @@ var init_stdio2 = __esm({
|
|
|
21944
21944
|
this.onclose?.();
|
|
21945
21945
|
}
|
|
21946
21946
|
send(message) {
|
|
21947
|
-
return new Promise((
|
|
21947
|
+
return new Promise((resolve4) => {
|
|
21948
21948
|
const json2 = serializeMessage(message);
|
|
21949
21949
|
if (this._stdout.write(json2)) {
|
|
21950
|
-
|
|
21950
|
+
resolve4();
|
|
21951
21951
|
} else {
|
|
21952
|
-
this._stdout.once("drain",
|
|
21952
|
+
this._stdout.once("drain", resolve4);
|
|
21953
21953
|
}
|
|
21954
21954
|
});
|
|
21955
21955
|
}
|
|
@@ -21963,7 +21963,7 @@ var init_constants = __esm({
|
|
|
21963
21963
|
"src/config/constants.ts"() {
|
|
21964
21964
|
"use strict";
|
|
21965
21965
|
SERVER_NAME = "firefox-devtools";
|
|
21966
|
-
SERVER_VERSION = "0.
|
|
21966
|
+
SERVER_VERSION = "0.7.0";
|
|
21967
21967
|
}
|
|
21968
21968
|
});
|
|
21969
21969
|
|
|
@@ -22791,7 +22791,7 @@ var init_dom = __esm({
|
|
|
22791
22791
|
*/
|
|
22792
22792
|
async waitForEventsAfterAction() {
|
|
22793
22793
|
await this.driver.executeScript("return new Promise(r => requestAnimationFrame(() => r()))");
|
|
22794
|
-
await new Promise((
|
|
22794
|
+
await new Promise((resolve4) => setTimeout(resolve4, 50));
|
|
22795
22795
|
}
|
|
22796
22796
|
// ============================================================================
|
|
22797
22797
|
// Screenshot
|
|
@@ -22820,7 +22820,7 @@ var init_dom = __esm({
|
|
|
22820
22820
|
'arguments[0].scrollIntoView({block: "center", inline: "center"});',
|
|
22821
22821
|
el
|
|
22822
22822
|
);
|
|
22823
|
-
await new Promise((
|
|
22823
|
+
await new Promise((resolve4) => setTimeout(resolve4, 100));
|
|
22824
22824
|
return await el.takeScreenshot();
|
|
22825
22825
|
}
|
|
22826
22826
|
};
|
|
@@ -23301,12 +23301,12 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23301
23301
|
* Take a snapshot of the current page
|
|
23302
23302
|
* Returns text and JSON with snapshotId, no DOM mutations
|
|
23303
23303
|
*/
|
|
23304
|
-
async takeSnapshot() {
|
|
23304
|
+
async takeSnapshot(options) {
|
|
23305
23305
|
const snapshotId = ++this.currentSnapshotId;
|
|
23306
23306
|
this.resolver.setSnapshotId(snapshotId);
|
|
23307
23307
|
this.resolver.clear();
|
|
23308
23308
|
logDebug(`Taking snapshot (ID: ${snapshotId})...`);
|
|
23309
|
-
const result = await this.executeInjectedScript(snapshotId);
|
|
23309
|
+
const result = await this.executeInjectedScript(snapshotId, options);
|
|
23310
23310
|
logDebug(
|
|
23311
23311
|
`Snapshot executeScript result: hasResult=${!!result}, hasTree=${!!result?.tree}, truncated=${result?.truncated || false}`
|
|
23312
23312
|
);
|
|
@@ -23319,6 +23319,10 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23319
23319
|
logDebug(` ... and ${result.debugLog.length - 20} more`);
|
|
23320
23320
|
}
|
|
23321
23321
|
}
|
|
23322
|
+
if (result?.selectorError) {
|
|
23323
|
+
logDebug(`Snapshot generation failed: ${result.selectorError}`);
|
|
23324
|
+
throw new Error(result.selectorError);
|
|
23325
|
+
}
|
|
23322
23326
|
if (!result?.tree) {
|
|
23323
23327
|
const errorMsg = "Unknown error";
|
|
23324
23328
|
logDebug(`Snapshot generation failed: ${errorMsg}`);
|
|
@@ -23362,7 +23366,7 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23362
23366
|
/**
|
|
23363
23367
|
* Execute bundled injected snapshot script
|
|
23364
23368
|
*/
|
|
23365
|
-
async executeInjectedScript(snapshotId) {
|
|
23369
|
+
async executeInjectedScript(snapshotId, options) {
|
|
23366
23370
|
const scriptSource = this.getInjectedScript();
|
|
23367
23371
|
const result = await this.driver.executeScript(
|
|
23368
23372
|
`
|
|
@@ -23374,10 +23378,11 @@ ${attemptedPaths.map((p) => ` - ${p}`).join("\n")}`
|
|
|
23374
23378
|
window.__createSnapshot = __SnapshotInjected.createSnapshot;
|
|
23375
23379
|
}
|
|
23376
23380
|
}
|
|
23377
|
-
// Call it
|
|
23378
|
-
return window.__createSnapshot(arguments[0]);
|
|
23381
|
+
// Call it with options
|
|
23382
|
+
return window.__createSnapshot(arguments[0], arguments[1]);
|
|
23379
23383
|
`,
|
|
23380
|
-
snapshotId
|
|
23384
|
+
snapshotId,
|
|
23385
|
+
options || {}
|
|
23381
23386
|
);
|
|
23382
23387
|
return result;
|
|
23383
23388
|
}
|
|
@@ -23649,11 +23654,11 @@ var init_firefox = __esm({
|
|
|
23649
23654
|
// ============================================================================
|
|
23650
23655
|
// Snapshot
|
|
23651
23656
|
// ============================================================================
|
|
23652
|
-
async takeSnapshot() {
|
|
23657
|
+
async takeSnapshot(options) {
|
|
23653
23658
|
if (!this.snapshot) {
|
|
23654
23659
|
throw new Error("Not connected");
|
|
23655
23660
|
}
|
|
23656
|
-
return await this.snapshot.takeSnapshot();
|
|
23661
|
+
return await this.snapshot.takeSnapshot(options);
|
|
23657
23662
|
}
|
|
23658
23663
|
resolveUidToSelector(uid) {
|
|
23659
23664
|
if (!this.snapshot) {
|
|
@@ -24506,13 +24511,24 @@ async function handleTakeSnapshot(args2) {
|
|
|
24506
24511
|
maxLines: requestedMaxLines = DEFAULT_SNAPSHOT_LINES,
|
|
24507
24512
|
includeAttributes = false,
|
|
24508
24513
|
includeText = true,
|
|
24509
|
-
maxDepth
|
|
24514
|
+
maxDepth,
|
|
24515
|
+
includeAll = false,
|
|
24516
|
+
selector
|
|
24510
24517
|
} = args2 || {};
|
|
24511
24518
|
const maxLines = Math.min(Math.max(1, requestedMaxLines), TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP);
|
|
24512
24519
|
const wasCapped = requestedMaxLines > TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP;
|
|
24513
24520
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
24514
24521
|
const firefox3 = await getFirefox2();
|
|
24515
|
-
const
|
|
24522
|
+
const snapshotOptions = {};
|
|
24523
|
+
if (includeAll) {
|
|
24524
|
+
snapshotOptions.includeAll = includeAll;
|
|
24525
|
+
}
|
|
24526
|
+
if (selector) {
|
|
24527
|
+
snapshotOptions.selector = selector;
|
|
24528
|
+
}
|
|
24529
|
+
const snapshot = await firefox3.takeSnapshot(
|
|
24530
|
+
Object.keys(snapshotOptions).length > 0 ? snapshotOptions : void 0
|
|
24531
|
+
);
|
|
24516
24532
|
const { formatSnapshotTree: formatSnapshotTree2 } = await Promise.resolve().then(() => (init_formatter(), formatter_exports));
|
|
24517
24533
|
const options = {
|
|
24518
24534
|
includeAttributes,
|
|
@@ -24526,6 +24542,12 @@ async function handleTakeSnapshot(args2) {
|
|
|
24526
24542
|
const truncated = lines.length > maxLines;
|
|
24527
24543
|
const displayLines = truncated ? lines.slice(0, maxLines) : lines;
|
|
24528
24544
|
let output = `\u{1F4F8} Snapshot (id=${snapshot.json.snapshotId})`;
|
|
24545
|
+
if (selector) {
|
|
24546
|
+
output += ` [selector: ${selector}]`;
|
|
24547
|
+
}
|
|
24548
|
+
if (includeAll) {
|
|
24549
|
+
output += " [includeAll: true]";
|
|
24550
|
+
}
|
|
24529
24551
|
if (wasCapped) {
|
|
24530
24552
|
output += ` [maxLines capped: ${TOKEN_LIMITS.MAX_SNAPSHOT_LINES_CAP}]`;
|
|
24531
24553
|
}
|
|
@@ -24606,6 +24628,14 @@ var init_snapshot2 = __esm({
|
|
|
24606
24628
|
maxDepth: {
|
|
24607
24629
|
type: "number",
|
|
24608
24630
|
description: "Max tree depth"
|
|
24631
|
+
},
|
|
24632
|
+
includeAll: {
|
|
24633
|
+
type: "boolean",
|
|
24634
|
+
description: "Include all visible elements without relevance filtering. Useful for Vue/Livewire apps (default: false)"
|
|
24635
|
+
},
|
|
24636
|
+
selector: {
|
|
24637
|
+
type: "string",
|
|
24638
|
+
description: 'CSS selector to scope snapshot to specific element (e.g., "#app")'
|
|
24609
24639
|
}
|
|
24610
24640
|
}
|
|
24611
24641
|
}
|
|
@@ -24903,33 +24933,48 @@ var init_input = __esm({
|
|
|
24903
24933
|
});
|
|
24904
24934
|
|
|
24905
24935
|
// src/tools/screenshot.ts
|
|
24906
|
-
|
|
24907
|
-
|
|
24908
|
-
|
|
24909
|
-
|
|
24910
|
-
|
|
24911
|
-
|
|
24912
|
-
|
|
24913
|
-
|
|
24914
|
-
|
|
24915
|
-
|
|
24916
|
-
}
|
|
24917
|
-
|
|
24936
|
+
import { writeFile, mkdir } from "fs/promises";
|
|
24937
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
24938
|
+
async function saveScreenshot(base64Png, saveTo) {
|
|
24939
|
+
const buffer = Buffer.from(base64Png, "base64");
|
|
24940
|
+
const resolvedPath = resolve2(saveTo);
|
|
24941
|
+
await mkdir(dirname2(resolvedPath), { recursive: true });
|
|
24942
|
+
await writeFile(resolvedPath, buffer);
|
|
24943
|
+
return successResponse(
|
|
24944
|
+
`Screenshot saved to: ${resolvedPath} (${(buffer.length / 1024).toFixed(1)}KB)`
|
|
24945
|
+
);
|
|
24946
|
+
}
|
|
24947
|
+
function imageResponse(base64Png) {
|
|
24948
|
+
return {
|
|
24949
|
+
content: [
|
|
24950
|
+
{
|
|
24951
|
+
type: "image",
|
|
24952
|
+
data: base64Png,
|
|
24953
|
+
mimeType: "image/png"
|
|
24954
|
+
}
|
|
24955
|
+
]
|
|
24956
|
+
};
|
|
24957
|
+
}
|
|
24958
|
+
async function handleScreenshotPage(args2) {
|
|
24918
24959
|
try {
|
|
24960
|
+
const { saveTo } = args2 ?? {};
|
|
24919
24961
|
const { getFirefox: getFirefox2 } = await init_index().then(() => index_exports);
|
|
24920
24962
|
const firefox3 = await getFirefox2();
|
|
24921
24963
|
const base64Png = await firefox3.takeScreenshotPage();
|
|
24922
24964
|
if (!base64Png || typeof base64Png !== "string") {
|
|
24923
24965
|
throw new Error("Invalid screenshot data");
|
|
24924
24966
|
}
|
|
24925
|
-
|
|
24967
|
+
if (saveTo) {
|
|
24968
|
+
return await saveScreenshot(base64Png, saveTo);
|
|
24969
|
+
}
|
|
24970
|
+
return imageResponse(base64Png);
|
|
24926
24971
|
} catch (error2) {
|
|
24927
24972
|
return errorResponse(error2);
|
|
24928
24973
|
}
|
|
24929
24974
|
}
|
|
24930
24975
|
async function handleScreenshotByUid(args2) {
|
|
24931
24976
|
try {
|
|
24932
|
-
const { uid } = args2;
|
|
24977
|
+
const { uid, saveTo } = args2;
|
|
24933
24978
|
if (!uid || typeof uid !== "string") {
|
|
24934
24979
|
throw new Error("uid required");
|
|
24935
24980
|
}
|
|
@@ -24940,7 +24985,10 @@ async function handleScreenshotByUid(args2) {
|
|
|
24940
24985
|
if (!base64Png || typeof base64Png !== "string") {
|
|
24941
24986
|
throw new Error("Invalid screenshot data");
|
|
24942
24987
|
}
|
|
24943
|
-
|
|
24988
|
+
if (saveTo) {
|
|
24989
|
+
return await saveScreenshot(base64Png, saveTo);
|
|
24990
|
+
}
|
|
24991
|
+
return imageResponse(base64Png);
|
|
24944
24992
|
} catch (error2) {
|
|
24945
24993
|
throw handleUidError(error2, uid);
|
|
24946
24994
|
}
|
|
@@ -24948,18 +24996,24 @@ async function handleScreenshotByUid(args2) {
|
|
|
24948
24996
|
return errorResponse(error2);
|
|
24949
24997
|
}
|
|
24950
24998
|
}
|
|
24951
|
-
var screenshotPageTool, screenshotByUidTool;
|
|
24999
|
+
var SAVE_TO_SCHEMA, screenshotPageTool, screenshotByUidTool;
|
|
24952
25000
|
var init_screenshot = __esm({
|
|
24953
25001
|
"src/tools/screenshot.ts"() {
|
|
24954
25002
|
"use strict";
|
|
24955
25003
|
init_response_helpers();
|
|
24956
25004
|
init_uid_helpers();
|
|
25005
|
+
SAVE_TO_SCHEMA = {
|
|
25006
|
+
type: "string",
|
|
25007
|
+
description: "Optional absolute file path to save the screenshot to instead of returning it as image data in the response. Use this in CLI environments (e.g. Claude Code) to avoid filling up the context window with large base64 image data. Example: '/tmp/screenshot.png'"
|
|
25008
|
+
};
|
|
24957
25009
|
screenshotPageTool = {
|
|
24958
25010
|
name: "screenshot_page",
|
|
24959
25011
|
description: "Capture page screenshot as base64 PNG.",
|
|
24960
25012
|
inputSchema: {
|
|
24961
25013
|
type: "object",
|
|
24962
|
-
properties: {
|
|
25014
|
+
properties: {
|
|
25015
|
+
saveTo: SAVE_TO_SCHEMA
|
|
25016
|
+
}
|
|
24963
25017
|
}
|
|
24964
25018
|
};
|
|
24965
25019
|
screenshotByUidTool = {
|
|
@@ -24971,7 +25025,8 @@ var init_screenshot = __esm({
|
|
|
24971
25025
|
uid: {
|
|
24972
25026
|
type: "string",
|
|
24973
25027
|
description: "Element UID from snapshot"
|
|
24974
|
-
}
|
|
25028
|
+
},
|
|
25029
|
+
saveTo: SAVE_TO_SCHEMA
|
|
24975
25030
|
},
|
|
24976
25031
|
required: ["uid"]
|
|
24977
25032
|
}
|
|
@@ -25565,7 +25620,7 @@ __export(index_exports, {
|
|
|
25565
25620
|
});
|
|
25566
25621
|
import { version as version2 } from "process";
|
|
25567
25622
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
25568
|
-
import { resolve as
|
|
25623
|
+
import { resolve as resolve3 } from "path";
|
|
25569
25624
|
import { realpathSync } from "fs";
|
|
25570
25625
|
function resetFirefox() {
|
|
25571
25626
|
if (firefox2) {
|
|
@@ -25746,7 +25801,7 @@ var init_index = __esm({
|
|
|
25746
25801
|
setViewportSizeTool
|
|
25747
25802
|
];
|
|
25748
25803
|
modulePath = fileURLToPath2(import.meta.url);
|
|
25749
|
-
scriptPath = process.argv[1] ?
|
|
25804
|
+
scriptPath = process.argv[1] ? resolve3(process.argv[1]) : "";
|
|
25750
25805
|
isMainModule = false;
|
|
25751
25806
|
try {
|
|
25752
25807
|
const realModulePath = realpathSync(modulePath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var __SnapshotInjected=(()=>{var
|
|
1
|
+
"use strict";var __SnapshotInjected=(()=>{var N=Object.defineProperty;var B=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var Q=(e,n)=>{for(var t in n)N(e,t,{get:n[t],enumerable:!0})},Y=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of J(n))!K.call(e,i)&&i!==t&&N(e,i,{get:()=>n[i],enumerable:!(r=B(n,i))||r.enumerable});return e};var Z=e=>Y(N({},"__esModule",{value:!0}),e);var ue={};Q(ue,{createSnapshot:()=>U});var T=["a","button","input","select","textarea","img","video","audio","iframe"],ee=["nav","main","section","article","header","footer","form"],te=["div","span","p","li","ul","ol"];function y(e){if(!e||e.nodeType!==Node.ELEMENT_NODE)return!1;let n=e;for(;n&&n!==document.documentElement;){try{let t=window.getComputedStyle(n),r=parseFloat(t.opacity);if(t.display==="none"||t.visibility==="hidden"||r===0||isNaN(r))return!1}catch{return!1}n=n.parentElement}return!0}function ne(e){let n="";for(let t=0;t<e.childNodes.length;t++){let r=e.childNodes[t];r&&r.nodeType===Node.TEXT_NODE&&(n+=r.textContent||"")}return n.trim()}function re(e){for(let n=0;n<e.children.length;n++){let t=e.children[n];if(t){let r=t.tagName.toLowerCase();if(T.indexOf(r)!==-1||t.hasAttribute("role"))return!0}}return!1}function R(e){if(!e||e.nodeType!==Node.ELEMENT_NODE||!y(e))return!1;let n=e.tagName.toLowerCase();if(T.indexOf(n)!==-1||e.hasAttribute("role")||e.hasAttribute("aria-label")||/^h[1-6]$/.test(n)||ee.indexOf(n)!==-1)return!0;if(te.indexOf(n)!==-1){let t=ne(e);if(t.length>0&&t.length<500||e.id||e.className||re(e))return!0}return!1}function $(e){if(e.tabIndex>=0)return!0;let t=e.tagName.toLowerCase();return["a","button","input","select","textarea"].indexOf(t)!==-1}function D(e){let n=e.tagName.toLowerCase();if(T.indexOf(n)!==-1)return!0;let t=e.getAttribute("role");return!!(t&&["button","link","menuitem","tab"].indexOf(t)!==-1||e.hasAttribute("onclick"))}var ie=100;function X(e){if(e.hasAttribute("aria-label"))return e.getAttribute("aria-label")||void 0;let t=e.id;if(t){let i=document.querySelector(`label[for="${t}"]`);if(i?.textContent)return i.textContent.trim()}if(e.hasAttribute("placeholder"))return e.getAttribute("placeholder")||void 0;if(e.hasAttribute("title"))return e.getAttribute("title")||void 0;if(e.hasAttribute("alt"))return e.getAttribute("alt")||void 0;let r=e.tagName.toLowerCase();if(["button","a","h1","h2","h3","h4","h5","h6"].indexOf(r)!==-1)return x(e)}function x(e){let n="";for(let r=0;r<e.childNodes.length;r++){let i=e.childNodes[r];i&&i.nodeType===Node.TEXT_NODE&&(n+=i.textContent||"")}let t=n.trim();if(t)return t.substring(0,ie)}function W(e){let n={},t=!1,r=["disabled","hidden","selected","expanded"];for(let o of r){let a=e.getAttribute(`aria-${o}`);a!==null&&(n[o]=a==="true",t=!0)}let i=["checked","pressed"];for(let o of i){let a=e.getAttribute(`aria-${o}`);a!==null&&(a==="mixed"?n[o]="mixed":n[o]=a==="true",t=!0)}let l=["autocomplete","haspopup","invalid","label","labelledby","describedby","controls"];for(let o of l){let a=e.getAttribute(`aria-${o}`);a&&(n[o]=a,t=!0)}let u=e.getAttribute("aria-level");if(u){let o=parseInt(u,10);isNaN(o)||(n.level=o,t=!0)}return t?n:void 0}function G(e){let n={};try{let t=window.getComputedStyle(e),r=parseFloat(t.opacity);n.visible=t.display!=="none"&&t.visibility!=="hidden"&&r!==0&&!isNaN(r)}catch{n.visible=!1}return n.accessible=n.visible&&!e.getAttribute("aria-hidden"),n.focusable=$(e),n.interactive=D(e),n}var oe=["id","data-testid","data-test-id"];function P(e){let n=[],t=e;for(;t&&t.nodeType===Node.ELEMENT_NODE;){let r=t.nodeName.toLowerCase(),i=!1;for(let a of oe){let d=t.getAttribute(a);if(d){a==="id"?r+="#"+CSS.escape(d):r+=`[${a}="${H(d)}"]`,n.unshift(r),i=!0;break}}if(i)break;let l=t.getAttribute("aria-label"),u=t.getAttribute("role");if(l&&u){r+=`[role="${u}"][aria-label="${H(l)}"]`,n.unshift(r),t=t.parentElement;continue}let o=t.parentElement?.children;if(o&&o.length>1){let a=1;for(let d=0;d<o.length;d++){let s=o[d];if(s){if(s===t)break;s.nodeName===t.nodeName&&a++}}(a>1||o.length>1&&o[0]!==t)&&(r+=`:nth-of-type(${a})`)}if(n.unshift(se(r)),t=t.parentElement,t&&t.nodeName.toLowerCase()==="body"){n.unshift("body");break}}return n.join(" > ")}function F(e){let n=e.id;if(n)return`//*[@id="${ae(n)}"]`;let t=[],r=e;for(;r&&r.nodeType===Node.ELEMENT_NODE;){let i=r.nodeName.toLowerCase(),l=1,u=r.previousElementSibling;for(;u;)u.nodeName.toLowerCase()===i&&l++,u=u.previousElementSibling;let o=r.parentElement,a=!1;o&&(a=Array.from(o.children).filter(h=>h.nodeName.toLowerCase()===i).length>1);let d=a?`${i}[${l}]`:i;if(t.unshift(d),r=r.parentElement,r&&r.nodeName.toLowerCase()==="html"){t.unshift("html");break}}return"/"+t.join("/")}function H(e){return e.replace(/"/g,'\\"').substring(0,64)}function ae(e){return e.indexOf('"')===-1||e.indexOf("'")===-1?e:`concat(${e.split('"').map((t,r,i)=>r===i.length-1?t?`"${t}"`:"":t?`"${t}",'"'`:`"'"`).filter(t=>t).join(",")})`}function se(e){return e.length<=64?e:e.substring(0,64)}var le=10,j=1e3;function V(e,n,t={}){let{includeAll:r=!1,includeIframes:i=!0}=t,l=0,u=[],o=!1;function a(s,h){if(h>le)return o=!0,{node:null,relevantChildren:[]};if(l>=j)return o=!0,{node:null,relevantChildren:[]};let b=s.tagName.toLowerCase(),C=b==="body"||b==="html",g;r?g=C||y(s):g=C||R(s);let E=[];if(b==="iframe"&&i&&g)try{let c=s,p=c.contentDocument||c.contentWindow?.document;if(p?.body){let f=a(p.body,h+1);f.node&&(f.node.isIframe=!0,f.node.frameSrc=c.src,E.push(f.node))}}catch{}else for(let c=0;c<s.children.length;c++){if(l>=j){o=!0;break}let p=s.children[c];if(!p)continue;let f=a(p,h+1);f.node?E.push(f.node):f.relevantChildren.length>0&&E.push(...f.relevantChildren)}if(!g)return{node:null,relevantChildren:E};let S=`${n}_${l++}`,q=P(s),z=F(s);u.push({uid:S,css:q,xpath:z});let A=s,v=s.getAttribute("role"),O=X(s),_=x(s),w=A.value,M=A.href,L=A.src,I=W(s),k=G(s),m={uid:S,tag:b,...v&&{role:v},...O&&{name:O},...w&&{value:w},...M&&{href:M},...L&&{src:L},..._&&{text:_},...I&&{aria:I},...k&&{computed:k},children:E};if(b==="iframe"&&i)try{let c=s;(c.contentDocument||c.contentWindow?.document)?.body||(m.isIframe=!0,m.frameSrc=c.src,m.crossOrigin=!0)}catch{m.isIframe=!0,m.frameSrc=s.src,m.crossOrigin=!0}return{node:m,relevantChildren:[]}}return{tree:a(e,0).node,uidMap:u,truncated:o}}function U(e,n){try{let t=document.body;if(n?.selector)try{let l=document.querySelector(n.selector);if(!l)return{tree:null,uidMap:[],truncated:!1,selectorError:`Selector "${n.selector}" not found`};t=l}catch{return{tree:null,uidMap:[],truncated:!1,selectorError:`Invalid selector syntax: "${n.selector}"`}}let r={includeIframes:n?.includeIframes??!0};n?.includeAll!==void 0&&(r.includeAll=n.includeAll);let i=V(t,e,r);if(!i.tree)throw new Error("Failed to generate tree");return i}catch{return{tree:null,uidMap:[],truncated:!1}}}typeof window<"u"&&(window.__createSnapshot=U);return Z(ue);})();
|