chrome-cdp-cli 2.0.4 → 2.1.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/dist/cli/ArgumentParser.js +172 -91
- package/dist/cli/CLIApplication.js +181 -57
- package/dist/cli/CommandRouter.js +98 -74
- package/dist/cli/CommandSchemaRegistry.js +506 -398
- package/dist/cli/EnhancedCLIInterface.js +2 -1
- package/dist/cli/HelpSystem.js +286 -256
- package/dist/handlers/ClickHandler.js +91 -27
- package/dist/handlers/InstallClaudeSkillHandler.js +220 -220
- package/dist/handlers/InstallCursorCommandHandler.js +60 -60
- package/dist/handlers/ListConsoleMessagesHandler.js +126 -178
- package/dist/handlers/ListNetworkRequestsHandler.js +128 -108
- package/dist/handlers/RestartProxyHandler.js +4 -4
- package/dist/handlers/TakeScreenshotHandler.js +70 -59
- package/dist/handlers/TakeSnapshotHandler.js +223 -165
- package/dist/handlers/index.js +0 -1
- package/dist/monitors/ConsoleMonitor.js +29 -0
- package/dist/monitors/NetworkMonitor.js +43 -19
- package/dist/proxy/server/CDPProxyServer.js +5 -1
- package/dist/proxy/server/CommandExecutionService.js +1 -1
- package/dist/proxy/server/ProxyAPIServer.js +11 -6
- package/package.json +3 -2
|
@@ -5,19 +5,20 @@ const fs_1 = require("fs");
|
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
class TakeSnapshotHandler {
|
|
7
7
|
constructor() {
|
|
8
|
-
this.name =
|
|
8
|
+
this.name = "dom";
|
|
9
|
+
this.aliases = ["snapshot"];
|
|
9
10
|
this.colors = {
|
|
10
|
-
reset:
|
|
11
|
-
bright:
|
|
12
|
-
dim:
|
|
13
|
-
red:
|
|
14
|
-
green:
|
|
15
|
-
yellow:
|
|
16
|
-
blue:
|
|
17
|
-
magenta:
|
|
18
|
-
cyan:
|
|
19
|
-
white:
|
|
20
|
-
gray:
|
|
11
|
+
reset: "\x1b[0m",
|
|
12
|
+
bright: "\x1b[1m",
|
|
13
|
+
dim: "\x1b[2m",
|
|
14
|
+
red: "\x1b[31m",
|
|
15
|
+
green: "\x1b[32m",
|
|
16
|
+
yellow: "\x1b[33m",
|
|
17
|
+
blue: "\x1b[34m",
|
|
18
|
+
magenta: "\x1b[35m",
|
|
19
|
+
cyan: "\x1b[36m",
|
|
20
|
+
white: "\x1b[37m",
|
|
21
|
+
gray: "\x1b[90m",
|
|
21
22
|
};
|
|
22
23
|
}
|
|
23
24
|
shouldUseColors(args) {
|
|
@@ -30,10 +31,10 @@ class TakeSnapshotHandler {
|
|
|
30
31
|
if (process.env.NO_COLOR) {
|
|
31
32
|
return false;
|
|
32
33
|
}
|
|
33
|
-
if (process.env.FORCE_COLOR ===
|
|
34
|
+
if (process.env.FORCE_COLOR === "0") {
|
|
34
35
|
return false;
|
|
35
36
|
}
|
|
36
|
-
if (process.env.FORCE_COLOR ===
|
|
37
|
+
if (process.env.FORCE_COLOR === "1" || process.env.FORCE_COLOR === "true") {
|
|
37
38
|
return true;
|
|
38
39
|
}
|
|
39
40
|
if (args.filename) {
|
|
@@ -47,13 +48,13 @@ class TakeSnapshotHandler {
|
|
|
47
48
|
return `${color}${text}${this.colors.reset}`;
|
|
48
49
|
}
|
|
49
50
|
colorTag(tagName, useColors) {
|
|
50
|
-
if ([
|
|
51
|
+
if (["button", "a", "input", "textarea", "select"].includes(tagName.toLowerCase())) {
|
|
51
52
|
return this.colorize(tagName.toUpperCase(), this.colors.green + this.colors.bright, useColors);
|
|
52
53
|
}
|
|
53
|
-
if ([
|
|
54
|
+
if (["h1", "h2", "h3", "h4", "h5", "h6"].includes(tagName.toLowerCase())) {
|
|
54
55
|
return this.colorize(tagName.toUpperCase(), this.colors.cyan + this.colors.bright, useColors);
|
|
55
56
|
}
|
|
56
|
-
if (tagName.toLowerCase() ===
|
|
57
|
+
if (tagName.toLowerCase() === "img") {
|
|
57
58
|
return this.colorize(tagName.toUpperCase(), this.colors.magenta, useColors);
|
|
58
59
|
}
|
|
59
60
|
return this.colorize(tagName.toUpperCase(), this.colors.cyan, useColors);
|
|
@@ -65,10 +66,10 @@ class TakeSnapshotHandler {
|
|
|
65
66
|
return this.colorize(`.${className}`, this.colors.yellow, useColors);
|
|
66
67
|
}
|
|
67
68
|
colorAttribute(name, value, useColors) {
|
|
68
|
-
if (name ===
|
|
69
|
+
if (name === "type") {
|
|
69
70
|
return this.colorize(`[${value}]`, this.colors.magenta, useColors);
|
|
70
71
|
}
|
|
71
|
-
if (name ===
|
|
72
|
+
if (name === "name") {
|
|
72
73
|
return this.colorize(`name="${value}"`, this.colors.magenta, useColors);
|
|
73
74
|
}
|
|
74
75
|
return `[${name}="${value}"]`;
|
|
@@ -90,12 +91,12 @@ class TakeSnapshotHandler {
|
|
|
90
91
|
}
|
|
91
92
|
async execute(client, args) {
|
|
92
93
|
const snapshotArgs = args;
|
|
93
|
-
if (snapshotArgs[
|
|
94
|
+
if (snapshotArgs["no-color"]) {
|
|
94
95
|
snapshotArgs.color = false;
|
|
95
96
|
}
|
|
96
97
|
try {
|
|
97
|
-
await client.send(
|
|
98
|
-
await client.send(
|
|
98
|
+
await client.send("DOM.enable");
|
|
99
|
+
await client.send("CSS.enable");
|
|
99
100
|
try {
|
|
100
101
|
return await this.captureWithDOMSnapshot(client, snapshotArgs);
|
|
101
102
|
}
|
|
@@ -106,16 +107,16 @@ class TakeSnapshotHandler {
|
|
|
106
107
|
catch (error) {
|
|
107
108
|
return {
|
|
108
109
|
success: false,
|
|
109
|
-
error: error instanceof Error ? error.message : String(error)
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
110
111
|
};
|
|
111
112
|
}
|
|
112
113
|
}
|
|
113
114
|
async captureWithDOMSnapshot(client, snapshotArgs) {
|
|
114
|
-
await client.send(
|
|
115
|
+
await client.send("DOMSnapshot.enable");
|
|
115
116
|
const params = this.buildSnapshotParams(snapshotArgs);
|
|
116
|
-
const response = await client.send(
|
|
117
|
+
const response = (await client.send("DOMSnapshot.captureSnapshot", params));
|
|
117
118
|
if (!response || !response.documents || response.documents.length === 0) {
|
|
118
|
-
throw new Error(
|
|
119
|
+
throw new Error("Failed to capture DOM snapshot: empty response");
|
|
119
120
|
}
|
|
120
121
|
const processedSnapshot = this.processSnapshot(response, snapshotArgs);
|
|
121
122
|
if (snapshotArgs.filename) {
|
|
@@ -125,23 +126,25 @@ class TakeSnapshotHandler {
|
|
|
125
126
|
data: {
|
|
126
127
|
message: `DOM snapshot saved to ${snapshotArgs.filename}`,
|
|
127
128
|
filename: snapshotArgs.filename,
|
|
128
|
-
format: snapshotArgs.format ||
|
|
129
|
+
format: snapshotArgs.format || "text",
|
|
129
130
|
documentsCount: response.documents.length,
|
|
130
|
-
nodesCount: response.documents[0]?.nodes?.nodeName?.length || 0
|
|
131
|
-
}
|
|
131
|
+
nodesCount: response.documents[0]?.nodes?.nodeName?.length || 0,
|
|
132
|
+
},
|
|
132
133
|
};
|
|
133
134
|
}
|
|
134
|
-
if (snapshotArgs.format !==
|
|
135
|
+
if (snapshotArgs.format !== "html" &&
|
|
136
|
+
typeof processedSnapshot === "object" &&
|
|
137
|
+
processedSnapshot !== null) {
|
|
135
138
|
const snapshotObj = processedSnapshot;
|
|
136
|
-
if (snapshotObj.snapshot && typeof snapshotObj.snapshot ===
|
|
139
|
+
if (snapshotObj.snapshot && typeof snapshotObj.snapshot === "string") {
|
|
137
140
|
return {
|
|
138
141
|
success: true,
|
|
139
142
|
data: {
|
|
140
143
|
snapshot: snapshotObj.snapshot,
|
|
141
|
-
format:
|
|
144
|
+
format: "text",
|
|
142
145
|
documentsCount: response.documents.length,
|
|
143
|
-
nodesCount: response.documents[0]?.nodes?.nodeName?.length || 0
|
|
144
|
-
}
|
|
146
|
+
nodesCount: response.documents[0]?.nodes?.nodeName?.length || 0,
|
|
147
|
+
},
|
|
145
148
|
};
|
|
146
149
|
}
|
|
147
150
|
}
|
|
@@ -149,26 +152,28 @@ class TakeSnapshotHandler {
|
|
|
149
152
|
success: true,
|
|
150
153
|
data: {
|
|
151
154
|
snapshot: processedSnapshot,
|
|
152
|
-
format: snapshotArgs.format ||
|
|
155
|
+
format: snapshotArgs.format || "text",
|
|
153
156
|
documentsCount: response.documents.length,
|
|
154
|
-
nodesCount: response.documents[0]?.nodes?.nodeName?.length || 0
|
|
155
|
-
}
|
|
157
|
+
nodesCount: response.documents[0]?.nodes?.nodeName?.length || 0,
|
|
158
|
+
},
|
|
156
159
|
};
|
|
157
160
|
}
|
|
158
161
|
async captureWithDOM(client, snapshotArgs) {
|
|
159
|
-
const docResponse = await client.send(
|
|
162
|
+
const docResponse = (await client.send("DOM.getDocument", {
|
|
163
|
+
depth: -1,
|
|
164
|
+
}));
|
|
160
165
|
if (!docResponse || !docResponse.root) {
|
|
161
|
-
throw new Error(
|
|
166
|
+
throw new Error("Failed to get document root");
|
|
162
167
|
}
|
|
163
168
|
const url = await this.getCurrentURL(client);
|
|
164
169
|
const title = await this.getCurrentTitle(client);
|
|
165
170
|
let processedSnapshot;
|
|
166
|
-
if (snapshotArgs.format ===
|
|
167
|
-
const htmlResponse = await client.send(
|
|
168
|
-
nodeId: docResponse.root.nodeId
|
|
169
|
-
});
|
|
171
|
+
if (snapshotArgs.format === "html") {
|
|
172
|
+
const htmlResponse = (await client.send("DOM.getOuterHTML", {
|
|
173
|
+
nodeId: docResponse.root.nodeId,
|
|
174
|
+
}));
|
|
170
175
|
if (!htmlResponse || !htmlResponse.outerHTML) {
|
|
171
|
-
throw new Error(
|
|
176
|
+
throw new Error("Failed to get document HTML");
|
|
172
177
|
}
|
|
173
178
|
processedSnapshot = htmlResponse.outerHTML;
|
|
174
179
|
}
|
|
@@ -178,7 +183,7 @@ class TakeSnapshotHandler {
|
|
|
178
183
|
processedSnapshot = {
|
|
179
184
|
url,
|
|
180
185
|
title,
|
|
181
|
-
snapshot: textSnapshot
|
|
186
|
+
snapshot: textSnapshot,
|
|
182
187
|
};
|
|
183
188
|
}
|
|
184
189
|
if (snapshotArgs.filename) {
|
|
@@ -188,19 +193,21 @@ class TakeSnapshotHandler {
|
|
|
188
193
|
data: {
|
|
189
194
|
message: `DOM snapshot saved to ${snapshotArgs.filename}`,
|
|
190
195
|
filename: snapshotArgs.filename,
|
|
191
|
-
format: snapshotArgs.format ||
|
|
192
|
-
}
|
|
196
|
+
format: snapshotArgs.format || "text",
|
|
197
|
+
},
|
|
193
198
|
};
|
|
194
199
|
}
|
|
195
|
-
if (snapshotArgs.format !==
|
|
200
|
+
if (snapshotArgs.format !== "html" &&
|
|
201
|
+
typeof processedSnapshot === "object" &&
|
|
202
|
+
processedSnapshot !== null) {
|
|
196
203
|
const snapshotObj = processedSnapshot;
|
|
197
|
-
if (snapshotObj.snapshot && typeof snapshotObj.snapshot ===
|
|
204
|
+
if (snapshotObj.snapshot && typeof snapshotObj.snapshot === "string") {
|
|
198
205
|
return {
|
|
199
206
|
success: true,
|
|
200
207
|
data: {
|
|
201
208
|
snapshot: snapshotObj.snapshot,
|
|
202
|
-
format:
|
|
203
|
-
}
|
|
209
|
+
format: "text",
|
|
210
|
+
},
|
|
204
211
|
};
|
|
205
212
|
}
|
|
206
213
|
}
|
|
@@ -208,49 +215,49 @@ class TakeSnapshotHandler {
|
|
|
208
215
|
success: true,
|
|
209
216
|
data: {
|
|
210
217
|
snapshot: processedSnapshot,
|
|
211
|
-
format: snapshotArgs.format ||
|
|
212
|
-
}
|
|
218
|
+
format: snapshotArgs.format || "text",
|
|
219
|
+
},
|
|
213
220
|
};
|
|
214
221
|
}
|
|
215
222
|
async getCurrentURL(client) {
|
|
216
223
|
try {
|
|
217
|
-
const result = await client.send(
|
|
218
|
-
expression:
|
|
219
|
-
returnByValue: true
|
|
220
|
-
});
|
|
221
|
-
return result.result?.value ||
|
|
224
|
+
const result = (await client.send("Runtime.evaluate", {
|
|
225
|
+
expression: "window.location.href",
|
|
226
|
+
returnByValue: true,
|
|
227
|
+
}));
|
|
228
|
+
return result.result?.value || "unknown";
|
|
222
229
|
}
|
|
223
230
|
catch {
|
|
224
|
-
return
|
|
231
|
+
return "unknown";
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
234
|
async getCurrentTitle(client) {
|
|
228
235
|
try {
|
|
229
|
-
const result = await client.send(
|
|
230
|
-
expression:
|
|
231
|
-
returnByValue: true
|
|
232
|
-
});
|
|
233
|
-
return result.result?.value ||
|
|
236
|
+
const result = (await client.send("Runtime.evaluate", {
|
|
237
|
+
expression: "document.title",
|
|
238
|
+
returnByValue: true,
|
|
239
|
+
}));
|
|
240
|
+
return result.result?.value || "unknown";
|
|
234
241
|
}
|
|
235
242
|
catch {
|
|
236
|
-
return
|
|
243
|
+
return "unknown";
|
|
237
244
|
}
|
|
238
245
|
}
|
|
239
246
|
buildSnapshotParams(args) {
|
|
240
247
|
const params = {};
|
|
241
248
|
if (args.includePaintOrder) {
|
|
242
|
-
console.log(
|
|
249
|
+
console.log("Paint order requested but not yet supported");
|
|
243
250
|
}
|
|
244
251
|
return params;
|
|
245
252
|
}
|
|
246
253
|
processSnapshot(response, args) {
|
|
247
|
-
if (args.format ===
|
|
254
|
+
if (args.format === "html") {
|
|
248
255
|
return this.convertToHTML(response);
|
|
249
256
|
}
|
|
250
257
|
const doc = response.documents[0];
|
|
251
258
|
if (!doc) {
|
|
252
259
|
return {
|
|
253
|
-
error:
|
|
260
|
+
error: "No documents found",
|
|
254
261
|
};
|
|
255
262
|
}
|
|
256
263
|
const useColors = this.shouldUseColors(args);
|
|
@@ -258,17 +265,17 @@ class TakeSnapshotHandler {
|
|
|
258
265
|
const result = {
|
|
259
266
|
url: doc.documentURL,
|
|
260
267
|
title: doc.title,
|
|
261
|
-
snapshot: textSnapshot
|
|
268
|
+
snapshot: textSnapshot,
|
|
262
269
|
};
|
|
263
270
|
return result;
|
|
264
271
|
}
|
|
265
272
|
createTextSnapshot(doc, strings, useColors = true) {
|
|
266
273
|
const nodes = doc.nodes;
|
|
267
274
|
if (!nodes.nodeName || !nodes.nodeType) {
|
|
268
|
-
return
|
|
275
|
+
return "Empty document";
|
|
269
276
|
}
|
|
270
277
|
const nodeTree = this.buildNodeTree(doc, strings);
|
|
271
|
-
let output = this.colorPageTitle(doc.title ||
|
|
278
|
+
let output = this.colorPageTitle(doc.title || "Untitled", useColors) + "\n";
|
|
272
279
|
const bodyNode = this.findBodyNode(nodeTree);
|
|
273
280
|
if (bodyNode) {
|
|
274
281
|
output += this.renderNodeAsText(bodyNode, 0, false, [], useColors);
|
|
@@ -292,8 +299,8 @@ class TakeSnapshotHandler {
|
|
|
292
299
|
nodeType: nodes.nodeType[i],
|
|
293
300
|
nodeName: nodes.nodeName[i].toLowerCase(),
|
|
294
301
|
children: [],
|
|
295
|
-
textContent:
|
|
296
|
-
attributes: {}
|
|
302
|
+
textContent: "",
|
|
303
|
+
attributes: {},
|
|
297
304
|
};
|
|
298
305
|
if (nodes.nodeValue?.[i]) {
|
|
299
306
|
node.textContent = nodes.nodeValue[i].trim();
|
|
@@ -301,7 +308,7 @@ class TakeSnapshotHandler {
|
|
|
301
308
|
if (nodes.textValue?.index.includes(i)) {
|
|
302
309
|
const textIndex = nodes.textValue.index.indexOf(i);
|
|
303
310
|
const stringIndex = nodes.textValue.value[textIndex];
|
|
304
|
-
if (typeof stringIndex ===
|
|
311
|
+
if (typeof stringIndex === "number" && strings[stringIndex]) {
|
|
305
312
|
node.textContent = strings[stringIndex].trim();
|
|
306
313
|
}
|
|
307
314
|
}
|
|
@@ -310,7 +317,18 @@ class TakeSnapshotHandler {
|
|
|
310
317
|
for (let j = 0; j < attrs.length; j += 2) {
|
|
311
318
|
const name = strings[parseInt(attrs[j])];
|
|
312
319
|
const value = strings[parseInt(attrs[j + 1])];
|
|
313
|
-
if ([
|
|
320
|
+
if ([
|
|
321
|
+
"id",
|
|
322
|
+
"class",
|
|
323
|
+
"type",
|
|
324
|
+
"name",
|
|
325
|
+
"href",
|
|
326
|
+
"src",
|
|
327
|
+
"alt",
|
|
328
|
+
"placeholder",
|
|
329
|
+
"value",
|
|
330
|
+
"title",
|
|
331
|
+
].includes(name)) {
|
|
314
332
|
node.attributes[name] = value;
|
|
315
333
|
}
|
|
316
334
|
}
|
|
@@ -318,7 +336,7 @@ class TakeSnapshotHandler {
|
|
|
318
336
|
if (nodes.inputValue?.index.includes(i)) {
|
|
319
337
|
const inputIndex = nodes.inputValue.index.indexOf(i);
|
|
320
338
|
const stringIndex = nodes.inputValue.value[inputIndex];
|
|
321
|
-
if (typeof stringIndex ===
|
|
339
|
+
if (typeof stringIndex === "number") {
|
|
322
340
|
node.inputValue = strings[stringIndex];
|
|
323
341
|
}
|
|
324
342
|
}
|
|
@@ -361,31 +379,31 @@ class TakeSnapshotHandler {
|
|
|
361
379
|
}
|
|
362
380
|
return null;
|
|
363
381
|
};
|
|
364
|
-
return findNode(nodeTree,
|
|
382
|
+
return findNode(nodeTree, "body") || findNode(nodeTree, "main") || null;
|
|
365
383
|
}
|
|
366
384
|
shouldIncludeNode(node) {
|
|
367
|
-
const skipTags = [
|
|
385
|
+
const skipTags = ["script", "style", "meta", "link", "head", "noscript"];
|
|
368
386
|
if (skipTags.includes(node.nodeName)) {
|
|
369
387
|
return false;
|
|
370
388
|
}
|
|
371
389
|
if (node.nodeType === 3) {
|
|
372
|
-
const text = (node.textContent ||
|
|
390
|
+
const text = (node.textContent || "").trim();
|
|
373
391
|
return text.length > 0;
|
|
374
392
|
}
|
|
375
393
|
return true;
|
|
376
394
|
}
|
|
377
395
|
renderNodeAsText(node, depth, isLast = false, parentIsLast = [], useColors = true) {
|
|
378
396
|
if (!this.shouldIncludeNode(node)) {
|
|
379
|
-
return
|
|
397
|
+
return "";
|
|
380
398
|
}
|
|
381
|
-
let indent =
|
|
399
|
+
let indent = "";
|
|
382
400
|
for (let i = 0; i < parentIsLast.length; i++) {
|
|
383
|
-
const symbol = parentIsLast[i] ?
|
|
401
|
+
const symbol = parentIsLast[i] ? " " : "│ ";
|
|
384
402
|
indent += this.colorTreeSymbol(symbol, useColors);
|
|
385
403
|
}
|
|
386
|
-
const prefixSymbol = depth > 0 ? (isLast ?
|
|
404
|
+
const prefixSymbol = depth > 0 ? (isLast ? "└── " : "├── ") : "";
|
|
387
405
|
const prefix = this.colorTreeSymbol(prefixSymbol, useColors);
|
|
388
|
-
let output =
|
|
406
|
+
let output = "";
|
|
389
407
|
if (node.nodeType === 3) {
|
|
390
408
|
if (node.textContent) {
|
|
391
409
|
const truncatedText = this.truncateText(node.textContent.trim(), 40);
|
|
@@ -400,28 +418,30 @@ class TakeSnapshotHandler {
|
|
|
400
418
|
attrs.push(this.colorId(node.attributes.id, useColors));
|
|
401
419
|
}
|
|
402
420
|
if (node.attributes.class) {
|
|
403
|
-
const classes = node.attributes.class
|
|
421
|
+
const classes = node.attributes.class
|
|
422
|
+
.split(/\s+/)
|
|
423
|
+
.filter((c) => c.trim().length > 0);
|
|
404
424
|
classes.forEach((cls) => {
|
|
405
425
|
attrs.push(this.colorClass(cls.trim(), useColors));
|
|
406
426
|
});
|
|
407
427
|
}
|
|
408
428
|
if (node.attributes.type) {
|
|
409
|
-
attrs.push(this.colorAttribute(
|
|
429
|
+
attrs.push(this.colorAttribute("type", node.attributes.type, useColors));
|
|
410
430
|
}
|
|
411
431
|
if (node.attributes.name) {
|
|
412
|
-
attrs.push(this.colorAttribute(
|
|
432
|
+
attrs.push(this.colorAttribute("name", node.attributes.name, useColors));
|
|
413
433
|
}
|
|
414
434
|
if (attrs.length > 0) {
|
|
415
|
-
description += `(${attrs.join(
|
|
435
|
+
description += `(${attrs.join(" ")})`;
|
|
416
436
|
}
|
|
417
|
-
if (node.nodeName ===
|
|
437
|
+
if (node.nodeName === "img" && node.attributes.alt) {
|
|
418
438
|
const altText = this.truncateText(node.attributes.alt, 40);
|
|
419
439
|
description += `: ${this.colorText(altText, useColors)}`;
|
|
420
440
|
}
|
|
421
|
-
else if (node.nodeName ===
|
|
441
|
+
else if (node.nodeName === "a" && node.attributes.href) {
|
|
422
442
|
description += `: ${this.colorUrl(node.attributes.href, useColors)}`;
|
|
423
443
|
}
|
|
424
|
-
else if ([
|
|
444
|
+
else if (["input", "textarea"].includes(node.nodeName)) {
|
|
425
445
|
if (node.attributes.placeholder) {
|
|
426
446
|
const placeholderText = this.truncateText(node.attributes.placeholder, 40);
|
|
427
447
|
description += `: ${this.colorText(placeholderText, useColors)}`;
|
|
@@ -430,7 +450,7 @@ class TakeSnapshotHandler {
|
|
|
430
450
|
const inputText = this.truncateText(node.inputValue, 40);
|
|
431
451
|
description += `: ${this.colorText(inputText, useColors)}`;
|
|
432
452
|
}
|
|
433
|
-
else if (node.nodeName ===
|
|
453
|
+
else if (node.nodeName === "textarea") {
|
|
434
454
|
const textContent = this.extractTextContent(node);
|
|
435
455
|
if (textContent) {
|
|
436
456
|
const truncatedText = this.truncateText(textContent, 40);
|
|
@@ -438,10 +458,11 @@ class TakeSnapshotHandler {
|
|
|
438
458
|
}
|
|
439
459
|
}
|
|
440
460
|
if (node.checked) {
|
|
441
|
-
description += ` ${this.colorSpecial(
|
|
461
|
+
description += ` ${this.colorSpecial("[checked]", useColors)}`;
|
|
442
462
|
}
|
|
443
463
|
}
|
|
444
|
-
else if (node.nodeName ===
|
|
464
|
+
else if (node.nodeName === "button" ||
|
|
465
|
+
["h1", "h2", "h3", "h4", "h5", "h6"].includes(node.nodeName)) {
|
|
445
466
|
const textContent = this.extractTextContent(node);
|
|
446
467
|
if (textContent) {
|
|
447
468
|
const truncatedText = this.truncateText(textContent, 40);
|
|
@@ -449,7 +470,7 @@ class TakeSnapshotHandler {
|
|
|
449
470
|
}
|
|
450
471
|
}
|
|
451
472
|
output += `${indent}${prefix}${description}\n`;
|
|
452
|
-
if (node.nodeName ===
|
|
473
|
+
if (node.nodeName === "textarea") {
|
|
453
474
|
return output;
|
|
454
475
|
}
|
|
455
476
|
const meaningfulChildren = node.children.filter((child) => this.shouldIncludeNode(child));
|
|
@@ -457,7 +478,7 @@ class TakeSnapshotHandler {
|
|
|
457
478
|
const textContent = this.extractTextContent(node);
|
|
458
479
|
if (textContent && textContent.trim().length > 0) {
|
|
459
480
|
const truncatedText = this.truncateText(textContent.trim(), 40);
|
|
460
|
-
const treeSymbol = this.colorTreeSymbol(
|
|
481
|
+
const treeSymbol = this.colorTreeSymbol("│ └── ", useColors);
|
|
461
482
|
output += `${indent}${treeSymbol}${this.colorText(truncatedText, useColors)}\n`;
|
|
462
483
|
return output;
|
|
463
484
|
}
|
|
@@ -475,27 +496,27 @@ class TakeSnapshotHandler {
|
|
|
475
496
|
return output;
|
|
476
497
|
}
|
|
477
498
|
extractTextContent(node) {
|
|
478
|
-
let text =
|
|
499
|
+
let text = "";
|
|
479
500
|
if (node.nodeType === 3) {
|
|
480
|
-
return node.textContent ||
|
|
501
|
+
return node.textContent || "";
|
|
481
502
|
}
|
|
482
503
|
if (node.textContent) {
|
|
483
|
-
text += node.textContent +
|
|
504
|
+
text += node.textContent + " ";
|
|
484
505
|
}
|
|
485
506
|
for (const child of node.children) {
|
|
486
|
-
text += this.extractTextContent(child) +
|
|
507
|
+
text += this.extractTextContent(child) + " ";
|
|
487
508
|
}
|
|
488
|
-
return text.trim().replace(/\s+/g,
|
|
509
|
+
return text.trim().replace(/\s+/g, " ");
|
|
489
510
|
}
|
|
490
511
|
truncateText(text, maxLength) {
|
|
491
512
|
if (!text)
|
|
492
|
-
return
|
|
513
|
+
return "";
|
|
493
514
|
if (text.length <= maxLength)
|
|
494
515
|
return text;
|
|
495
|
-
return text.substring(0, maxLength) +
|
|
516
|
+
return text.substring(0, maxLength) + "...";
|
|
496
517
|
}
|
|
497
518
|
buildTextFromDOMNode(root, _url, title, useColors = true) {
|
|
498
|
-
let output = this.colorPageTitle(title ||
|
|
519
|
+
let output = this.colorPageTitle(title || "Untitled", useColors) + "\n";
|
|
499
520
|
const bodyNode = this.findBodyInDOMTree(root);
|
|
500
521
|
if (bodyNode) {
|
|
501
522
|
output += this.renderDOMNodeAsText(bodyNode, 0, false, [], useColors);
|
|
@@ -512,10 +533,10 @@ class TakeSnapshotHandler {
|
|
|
512
533
|
return output;
|
|
513
534
|
}
|
|
514
535
|
findBodyInDOMTree(node) {
|
|
515
|
-
if (node.nodeName && node.nodeName.toLowerCase() ===
|
|
536
|
+
if (node.nodeName && node.nodeName.toLowerCase() === "body") {
|
|
516
537
|
return node;
|
|
517
538
|
}
|
|
518
|
-
if (node.nodeName && node.nodeName.toLowerCase() ===
|
|
539
|
+
if (node.nodeName && node.nodeName.toLowerCase() === "main") {
|
|
519
540
|
return node;
|
|
520
541
|
}
|
|
521
542
|
if (node.children) {
|
|
@@ -530,38 +551,38 @@ class TakeSnapshotHandler {
|
|
|
530
551
|
shouldIncludeDOMNode(node) {
|
|
531
552
|
if (!node)
|
|
532
553
|
return false;
|
|
533
|
-
const nodeName = node.nodeName ? node.nodeName.toLowerCase() :
|
|
534
|
-
const skipTags = [
|
|
554
|
+
const nodeName = node.nodeName ? node.nodeName.toLowerCase() : "";
|
|
555
|
+
const skipTags = ["script", "style", "meta", "link", "head", "noscript"];
|
|
535
556
|
if (skipTags.includes(nodeName)) {
|
|
536
557
|
return false;
|
|
537
558
|
}
|
|
538
559
|
if (node.nodeType === 3) {
|
|
539
|
-
const text = node.nodeValue ||
|
|
560
|
+
const text = node.nodeValue || "";
|
|
540
561
|
return text.trim().length > 0;
|
|
541
562
|
}
|
|
542
563
|
return true;
|
|
543
564
|
}
|
|
544
565
|
renderDOMNodeAsText(node, depth, isLast = false, parentIsLast = [], useColors = true) {
|
|
545
566
|
if (!this.shouldIncludeDOMNode(node)) {
|
|
546
|
-
return
|
|
567
|
+
return "";
|
|
547
568
|
}
|
|
548
|
-
let indent =
|
|
569
|
+
let indent = "";
|
|
549
570
|
for (let i = 0; i < parentIsLast.length; i++) {
|
|
550
|
-
const symbol = parentIsLast[i] ?
|
|
571
|
+
const symbol = parentIsLast[i] ? " " : "│ ";
|
|
551
572
|
indent += this.colorTreeSymbol(symbol, useColors);
|
|
552
573
|
}
|
|
553
|
-
const prefixSymbol = depth > 0 ? (isLast ?
|
|
574
|
+
const prefixSymbol = depth > 0 ? (isLast ? "└── " : "├── ") : "";
|
|
554
575
|
const prefix = this.colorTreeSymbol(prefixSymbol, useColors);
|
|
555
|
-
let output =
|
|
576
|
+
let output = "";
|
|
556
577
|
if (node.nodeType === 3) {
|
|
557
|
-
const text = (node.nodeValue ||
|
|
578
|
+
const text = (node.nodeValue || "").trim();
|
|
558
579
|
if (text) {
|
|
559
580
|
const truncatedText = this.truncateText(text, 40);
|
|
560
581
|
output += `${indent}${prefix}${this.colorText(truncatedText, useColors)}\n`;
|
|
561
582
|
}
|
|
562
583
|
}
|
|
563
584
|
else if (node.nodeType === 1) {
|
|
564
|
-
const nodeName = (node.nodeName ||
|
|
585
|
+
const nodeName = (node.nodeName || "").toLowerCase();
|
|
565
586
|
const tag = this.colorTag(nodeName, useColors);
|
|
566
587
|
let description = tag;
|
|
567
588
|
const attrs = [];
|
|
@@ -569,18 +590,31 @@ class TakeSnapshotHandler {
|
|
|
569
590
|
for (let i = 0; i < node.attributes.length; i += 2) {
|
|
570
591
|
const name = node.attributes[i];
|
|
571
592
|
const value = node.attributes[i + 1];
|
|
572
|
-
if ([
|
|
573
|
-
|
|
593
|
+
if ([
|
|
594
|
+
"id",
|
|
595
|
+
"class",
|
|
596
|
+
"type",
|
|
597
|
+
"name",
|
|
598
|
+
"href",
|
|
599
|
+
"src",
|
|
600
|
+
"alt",
|
|
601
|
+
"placeholder",
|
|
602
|
+
"value",
|
|
603
|
+
"title",
|
|
604
|
+
].includes(name)) {
|
|
605
|
+
if (name === "id") {
|
|
574
606
|
attrs.push(this.colorId(value, useColors));
|
|
575
607
|
}
|
|
576
|
-
else if (name ===
|
|
577
|
-
const classes = value
|
|
608
|
+
else if (name === "class") {
|
|
609
|
+
const classes = value
|
|
610
|
+
.split(/\s+/)
|
|
611
|
+
.filter((c) => c.trim().length > 0);
|
|
578
612
|
classes.forEach((cls) => {
|
|
579
613
|
attrs.push(this.colorClass(cls.trim(), useColors));
|
|
580
614
|
});
|
|
581
615
|
}
|
|
582
|
-
else if (name ===
|
|
583
|
-
attrs.push(this.colorAttribute(
|
|
616
|
+
else if (name === "type") {
|
|
617
|
+
attrs.push(this.colorAttribute("type", value, useColors));
|
|
584
618
|
}
|
|
585
619
|
else {
|
|
586
620
|
attrs.push(this.colorAttribute(name, value, useColors));
|
|
@@ -589,30 +623,31 @@ class TakeSnapshotHandler {
|
|
|
589
623
|
}
|
|
590
624
|
}
|
|
591
625
|
if (attrs.length > 0) {
|
|
592
|
-
description += `(${attrs.join(
|
|
626
|
+
description += `(${attrs.join(" ")})`;
|
|
593
627
|
}
|
|
594
|
-
if (nodeName ===
|
|
595
|
-
const altIndex = node.attributes.indexOf(
|
|
628
|
+
if (nodeName === "img" && node.attributes) {
|
|
629
|
+
const altIndex = node.attributes.indexOf("alt");
|
|
596
630
|
if (altIndex >= 0 && altIndex + 1 < node.attributes.length) {
|
|
597
631
|
const altText = this.truncateText(node.attributes[altIndex + 1], 40);
|
|
598
632
|
description += `: ${this.colorText(altText, useColors)}`;
|
|
599
633
|
}
|
|
600
634
|
}
|
|
601
|
-
else if (nodeName ===
|
|
602
|
-
const hrefIndex = node.attributes.indexOf(
|
|
635
|
+
else if (nodeName === "a" && node.attributes) {
|
|
636
|
+
const hrefIndex = node.attributes.indexOf("href");
|
|
603
637
|
if (hrefIndex >= 0 && hrefIndex + 1 < node.attributes.length) {
|
|
604
638
|
description += `: ${this.colorUrl(node.attributes[hrefIndex + 1], useColors)}`;
|
|
605
639
|
}
|
|
606
640
|
}
|
|
607
|
-
else if ([
|
|
641
|
+
else if (["input", "textarea"].includes(nodeName)) {
|
|
608
642
|
if (node.attributes) {
|
|
609
|
-
const placeholderIndex = node.attributes.indexOf(
|
|
610
|
-
if (placeholderIndex >= 0 &&
|
|
643
|
+
const placeholderIndex = node.attributes.indexOf("placeholder");
|
|
644
|
+
if (placeholderIndex >= 0 &&
|
|
645
|
+
placeholderIndex + 1 < node.attributes.length) {
|
|
611
646
|
const placeholderText = this.truncateText(node.attributes[placeholderIndex + 1], 40);
|
|
612
647
|
description += `: ${this.colorText(placeholderText, useColors)}`;
|
|
613
648
|
}
|
|
614
649
|
}
|
|
615
|
-
if (nodeName ===
|
|
650
|
+
if (nodeName === "textarea") {
|
|
616
651
|
const textContent = this.extractTextFromDOMNode(node);
|
|
617
652
|
if (textContent) {
|
|
618
653
|
const truncatedText = this.truncateText(textContent, 40);
|
|
@@ -620,7 +655,8 @@ class TakeSnapshotHandler {
|
|
|
620
655
|
}
|
|
621
656
|
}
|
|
622
657
|
}
|
|
623
|
-
else if (nodeName ===
|
|
658
|
+
else if (nodeName === "button" ||
|
|
659
|
+
["h1", "h2", "h3", "h4", "h5", "h6"].includes(nodeName)) {
|
|
624
660
|
const textContent = this.extractTextFromDOMNode(node);
|
|
625
661
|
if (textContent) {
|
|
626
662
|
const truncatedText = this.truncateText(textContent, 40);
|
|
@@ -628,7 +664,7 @@ class TakeSnapshotHandler {
|
|
|
628
664
|
}
|
|
629
665
|
}
|
|
630
666
|
output += `${indent}${prefix}${description}\n`;
|
|
631
|
-
if (nodeName ===
|
|
667
|
+
if (nodeName === "textarea") {
|
|
632
668
|
return output;
|
|
633
669
|
}
|
|
634
670
|
if (node.children) {
|
|
@@ -637,7 +673,7 @@ class TakeSnapshotHandler {
|
|
|
637
673
|
const textContent = this.extractTextFromDOMNode(node);
|
|
638
674
|
if (textContent && textContent.trim().length > 0) {
|
|
639
675
|
const truncatedText = this.truncateText(textContent.trim(), 40);
|
|
640
|
-
const treeSymbol = this.colorTreeSymbol(
|
|
676
|
+
const treeSymbol = this.colorTreeSymbol("│ └── ", useColors);
|
|
641
677
|
output += `${indent}${treeSymbol}${this.colorText(truncatedText, useColors)}\n`;
|
|
642
678
|
return output;
|
|
643
679
|
}
|
|
@@ -657,20 +693,20 @@ class TakeSnapshotHandler {
|
|
|
657
693
|
}
|
|
658
694
|
extractTextFromDOMNode(node) {
|
|
659
695
|
if (node.nodeType === 3) {
|
|
660
|
-
return (node.nodeValue ||
|
|
696
|
+
return (node.nodeValue || "").trim();
|
|
661
697
|
}
|
|
662
|
-
let text =
|
|
698
|
+
let text = "";
|
|
663
699
|
if (node.children) {
|
|
664
700
|
for (const child of node.children) {
|
|
665
|
-
text += this.extractTextFromDOMNode(child) +
|
|
701
|
+
text += this.extractTextFromDOMNode(child) + " ";
|
|
666
702
|
}
|
|
667
703
|
}
|
|
668
|
-
return text.trim().replace(/\s+/g,
|
|
704
|
+
return text.trim().replace(/\s+/g, " ");
|
|
669
705
|
}
|
|
670
706
|
convertToHTML(response) {
|
|
671
707
|
const doc = response.documents[0];
|
|
672
708
|
if (!doc)
|
|
673
|
-
return
|
|
709
|
+
return "";
|
|
674
710
|
const tree = this.buildDOMTree(doc, response.strings);
|
|
675
711
|
return this.renderNodeAsHTML(tree[0], 0);
|
|
676
712
|
}
|
|
@@ -689,7 +725,7 @@ class TakeSnapshotHandler {
|
|
|
689
725
|
nodeName: nodes.nodeName[i],
|
|
690
726
|
nodeValue: nodes.nodeValue?.[i],
|
|
691
727
|
backendNodeId: nodes.backendNodeId?.[i],
|
|
692
|
-
children: []
|
|
728
|
+
children: [],
|
|
693
729
|
};
|
|
694
730
|
if (nodes.attributes?.[i]) {
|
|
695
731
|
const attrs = nodes.attributes[i];
|
|
@@ -703,14 +739,14 @@ class TakeSnapshotHandler {
|
|
|
703
739
|
if (nodes.textValue?.index.includes(i)) {
|
|
704
740
|
const textIndex = nodes.textValue.index.indexOf(i);
|
|
705
741
|
const stringIndex = nodes.textValue.value[textIndex];
|
|
706
|
-
if (typeof stringIndex ===
|
|
742
|
+
if (typeof stringIndex === "number") {
|
|
707
743
|
node.textValue = strings[stringIndex];
|
|
708
744
|
}
|
|
709
745
|
}
|
|
710
746
|
if (nodes.inputValue?.index.includes(i)) {
|
|
711
747
|
const inputIndex = nodes.inputValue.index.indexOf(i);
|
|
712
748
|
const stringIndex = nodes.inputValue.value[inputIndex];
|
|
713
|
-
if (typeof stringIndex ===
|
|
749
|
+
if (typeof stringIndex === "number") {
|
|
714
750
|
node.inputValue = strings[stringIndex];
|
|
715
751
|
}
|
|
716
752
|
}
|
|
@@ -742,14 +778,14 @@ class TakeSnapshotHandler {
|
|
|
742
778
|
}
|
|
743
779
|
renderNodeAsHTML(node, depth) {
|
|
744
780
|
if (!node)
|
|
745
|
-
return
|
|
746
|
-
const indent =
|
|
781
|
+
return "";
|
|
782
|
+
const indent = " ".repeat(depth);
|
|
747
783
|
if (node.nodeType === 3) {
|
|
748
|
-
const text = node.nodeValue || node.textValue ||
|
|
749
|
-
return text.trim() ? `${indent}${text.trim()}\n` :
|
|
784
|
+
const text = node.nodeValue || node.textValue || "";
|
|
785
|
+
return text.trim() ? `${indent}${text.trim()}\n` : "";
|
|
750
786
|
}
|
|
751
787
|
if (node.nodeType === 8) {
|
|
752
|
-
return `${indent}<!-- ${node.nodeValue ||
|
|
788
|
+
return `${indent}<!-- ${node.nodeValue || ""} -->\n`;
|
|
753
789
|
}
|
|
754
790
|
if (node.nodeType === 1) {
|
|
755
791
|
const tagName = node.nodeName.toLowerCase();
|
|
@@ -759,14 +795,29 @@ class TakeSnapshotHandler {
|
|
|
759
795
|
html += ` ${name}="${value}"`;
|
|
760
796
|
}
|
|
761
797
|
}
|
|
762
|
-
const selfClosing = [
|
|
798
|
+
const selfClosing = [
|
|
799
|
+
"area",
|
|
800
|
+
"base",
|
|
801
|
+
"br",
|
|
802
|
+
"col",
|
|
803
|
+
"embed",
|
|
804
|
+
"hr",
|
|
805
|
+
"img",
|
|
806
|
+
"input",
|
|
807
|
+
"link",
|
|
808
|
+
"meta",
|
|
809
|
+
"param",
|
|
810
|
+
"source",
|
|
811
|
+
"track",
|
|
812
|
+
"wbr",
|
|
813
|
+
];
|
|
763
814
|
if (selfClosing.includes(tagName)) {
|
|
764
|
-
html +=
|
|
815
|
+
html += " />\n";
|
|
765
816
|
return html;
|
|
766
817
|
}
|
|
767
|
-
html +=
|
|
818
|
+
html += ">";
|
|
768
819
|
if (node.children && node.children.length > 0) {
|
|
769
|
-
html +=
|
|
820
|
+
html += "\n";
|
|
770
821
|
for (const child of node.children) {
|
|
771
822
|
html += this.renderNodeAsHTML(child, depth + 1);
|
|
772
823
|
}
|
|
@@ -777,27 +828,28 @@ class TakeSnapshotHandler {
|
|
|
777
828
|
}
|
|
778
829
|
return html;
|
|
779
830
|
}
|
|
780
|
-
return
|
|
831
|
+
return "";
|
|
781
832
|
}
|
|
782
833
|
async saveSnapshot(snapshotData, filename, format) {
|
|
783
834
|
try {
|
|
784
835
|
const dir = (0, path_1.dirname)(filename);
|
|
785
836
|
await fs_1.promises.mkdir(dir, { recursive: true });
|
|
786
837
|
let content;
|
|
787
|
-
if (format ===
|
|
838
|
+
if (format === "html") {
|
|
788
839
|
content = snapshotData;
|
|
789
840
|
}
|
|
790
|
-
else if (format ===
|
|
791
|
-
if (typeof snapshotData ===
|
|
841
|
+
else if (format === "text" || !format) {
|
|
842
|
+
if (typeof snapshotData === "object" && snapshotData !== null) {
|
|
792
843
|
const snapshotObj = snapshotData;
|
|
793
|
-
if (snapshotObj.snapshot &&
|
|
844
|
+
if (snapshotObj.snapshot &&
|
|
845
|
+
typeof snapshotObj.snapshot === "string") {
|
|
794
846
|
content = snapshotObj.snapshot;
|
|
795
847
|
}
|
|
796
848
|
else {
|
|
797
849
|
content = JSON.stringify(snapshotData, null, 2);
|
|
798
850
|
}
|
|
799
851
|
}
|
|
800
|
-
else if (typeof snapshotData ===
|
|
852
|
+
else if (typeof snapshotData === "string") {
|
|
801
853
|
content = snapshotData;
|
|
802
854
|
}
|
|
803
855
|
else {
|
|
@@ -807,33 +859,39 @@ class TakeSnapshotHandler {
|
|
|
807
859
|
else {
|
|
808
860
|
content = JSON.stringify(snapshotData, null, 2);
|
|
809
861
|
}
|
|
810
|
-
await fs_1.promises.writeFile(filename, content,
|
|
862
|
+
await fs_1.promises.writeFile(filename, content, "utf-8");
|
|
811
863
|
}
|
|
812
864
|
catch (error) {
|
|
813
865
|
throw new Error(`Failed to save DOM snapshot to "${filename}": ${error instanceof Error ? error.message : String(error)}`);
|
|
814
866
|
}
|
|
815
867
|
}
|
|
816
868
|
validateArgs(args) {
|
|
817
|
-
if (typeof args !==
|
|
869
|
+
if (typeof args !== "object" || args === null) {
|
|
818
870
|
return false;
|
|
819
871
|
}
|
|
820
872
|
const snapshotArgs = args;
|
|
821
|
-
if (snapshotArgs.filename !== undefined &&
|
|
873
|
+
if (snapshotArgs.filename !== undefined &&
|
|
874
|
+
typeof snapshotArgs.filename !== "string") {
|
|
822
875
|
return false;
|
|
823
876
|
}
|
|
824
|
-
if (snapshotArgs.format !== undefined &&
|
|
877
|
+
if (snapshotArgs.format !== undefined &&
|
|
878
|
+
!["json", "html", "text"].includes(snapshotArgs.format)) {
|
|
825
879
|
return false;
|
|
826
880
|
}
|
|
827
|
-
if (snapshotArgs.includeStyles !== undefined &&
|
|
881
|
+
if (snapshotArgs.includeStyles !== undefined &&
|
|
882
|
+
typeof snapshotArgs.includeStyles !== "boolean") {
|
|
828
883
|
return false;
|
|
829
884
|
}
|
|
830
|
-
if (snapshotArgs.includeAttributes !== undefined &&
|
|
885
|
+
if (snapshotArgs.includeAttributes !== undefined &&
|
|
886
|
+
typeof snapshotArgs.includeAttributes !== "boolean") {
|
|
831
887
|
return false;
|
|
832
888
|
}
|
|
833
|
-
if (snapshotArgs.includePaintOrder !== undefined &&
|
|
889
|
+
if (snapshotArgs.includePaintOrder !== undefined &&
|
|
890
|
+
typeof snapshotArgs.includePaintOrder !== "boolean") {
|
|
834
891
|
return false;
|
|
835
892
|
}
|
|
836
|
-
if (snapshotArgs.includeTextIndex !== undefined &&
|
|
893
|
+
if (snapshotArgs.includeTextIndex !== undefined &&
|
|
894
|
+
typeof snapshotArgs.includeTextIndex !== "boolean") {
|
|
837
895
|
return false;
|
|
838
896
|
}
|
|
839
897
|
return true;
|