chrome-cdp-cli 1.7.8 → 1.8.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.
@@ -373,6 +373,9 @@ class CLIInterface {
373
373
  }
374
374
  if (result.data && typeof result.data === 'object') {
375
375
  const data = result.data;
376
+ if (data.snapshot && typeof data.snapshot === 'string') {
377
+ return data.snapshot;
378
+ }
376
379
  if (data.messages && Array.isArray(data.messages)) {
377
380
  output += dataSourceInfo;
378
381
  if (data.messages.length === 0) {
@@ -47,6 +47,20 @@ class TakeSnapshotHandler {
47
47
  }
48
48
  };
49
49
  }
50
+ if (snapshotArgs.format !== 'html' && typeof processedSnapshot === 'object' && processedSnapshot !== null) {
51
+ const snapshotObj = processedSnapshot;
52
+ if (snapshotObj.snapshot && typeof snapshotObj.snapshot === 'string') {
53
+ return {
54
+ success: true,
55
+ data: {
56
+ snapshot: snapshotObj.snapshot,
57
+ format: 'text',
58
+ documentsCount: response.documents.length,
59
+ nodesCount: response.documents[0]?.nodes?.nodeName?.length || 0
60
+ }
61
+ };
62
+ }
63
+ }
50
64
  return {
51
65
  success: true,
52
66
  data: {
@@ -62,29 +76,24 @@ class TakeSnapshotHandler {
62
76
  if (!docResponse || !docResponse.root) {
63
77
  throw new Error('Failed to get document root');
64
78
  }
65
- const htmlResponse = await client.send('DOM.getOuterHTML', {
66
- nodeId: docResponse.root.nodeId
67
- });
68
- if (!htmlResponse || !htmlResponse.outerHTML) {
69
- throw new Error('Failed to get document HTML');
70
- }
79
+ const url = await this.getCurrentURL(client);
80
+ const title = await this.getCurrentTitle(client);
71
81
  let processedSnapshot;
72
82
  if (snapshotArgs.format === 'html') {
83
+ const htmlResponse = await client.send('DOM.getOuterHTML', {
84
+ nodeId: docResponse.root.nodeId
85
+ });
86
+ if (!htmlResponse || !htmlResponse.outerHTML) {
87
+ throw new Error('Failed to get document HTML');
88
+ }
73
89
  processedSnapshot = htmlResponse.outerHTML;
74
90
  }
75
91
  else {
92
+ const textSnapshot = this.buildTextFromDOMNode(docResponse.root, url, title);
76
93
  processedSnapshot = {
77
- metadata: {
78
- captureTime: new Date().toISOString(),
79
- method: 'DOM.getOuterHTML',
80
- documentsCount: 1
81
- },
82
- documents: [{
83
- url: await this.getCurrentURL(client),
84
- title: await this.getCurrentTitle(client),
85
- html: htmlResponse.outerHTML,
86
- domTree: docResponse.root
87
- }]
94
+ url,
95
+ title,
96
+ snapshot: textSnapshot
88
97
  };
89
98
  }
90
99
  if (snapshotArgs.filename) {
@@ -98,6 +107,18 @@ class TakeSnapshotHandler {
98
107
  }
99
108
  };
100
109
  }
110
+ if (snapshotArgs.format !== 'html' && typeof processedSnapshot === 'object' && processedSnapshot !== null) {
111
+ const snapshotObj = processedSnapshot;
112
+ if (snapshotObj.snapshot && typeof snapshotObj.snapshot === 'string') {
113
+ return {
114
+ success: true,
115
+ data: {
116
+ snapshot: snapshotObj.snapshot,
117
+ format: 'text'
118
+ }
119
+ };
120
+ }
121
+ }
101
122
  return {
102
123
  success: true,
103
124
  data: {
@@ -141,25 +162,418 @@ class TakeSnapshotHandler {
141
162
  if (args.format === 'html') {
142
163
  return this.convertToHTML(response);
143
164
  }
144
- return {
145
- metadata: {
146
- captureTime: new Date().toISOString(),
147
- documentsCount: response.documents.length,
148
- stringsCount: response.strings.length
149
- },
150
- documents: response.documents.map(doc => ({
151
- url: doc.documentURL,
152
- title: doc.title,
153
- baseURL: doc.baseURL,
154
- language: doc.contentLanguage,
155
- encoding: doc.encodingName,
156
- frameId: doc.frameId,
157
- domTree: this.buildDOMTree(doc, response.strings),
158
- layout: this.processLayoutInfo(doc.layout, response.strings),
159
- textBoxes: doc.textBoxes
160
- })),
161
- strings: response.strings
165
+ const doc = response.documents[0];
166
+ if (!doc) {
167
+ return {
168
+ error: 'No documents found'
169
+ };
170
+ }
171
+ const textSnapshot = this.createTextSnapshot(doc, response.strings);
172
+ const result = {
173
+ url: doc.documentURL,
174
+ title: doc.title,
175
+ snapshot: textSnapshot
176
+ };
177
+ return result;
178
+ }
179
+ createTextSnapshot(doc, strings) {
180
+ const nodes = doc.nodes;
181
+ if (!nodes.nodeName || !nodes.nodeType) {
182
+ return 'Empty document';
183
+ }
184
+ const nodeTree = this.buildNodeTree(doc, strings);
185
+ let output = `PAGE: ${doc.title || 'Untitled'}\n`;
186
+ const bodyNode = this.findBodyNode(nodeTree);
187
+ if (bodyNode) {
188
+ output += this.renderNodeAsText(bodyNode, 0);
189
+ }
190
+ else {
191
+ for (const node of nodeTree) {
192
+ if (this.shouldIncludeNode(node)) {
193
+ output += this.renderNodeAsText(node, 0);
194
+ }
195
+ }
196
+ }
197
+ return output;
198
+ }
199
+ buildNodeTree(doc, strings) {
200
+ const nodes = doc.nodes;
201
+ const nodeCount = nodes.nodeName.length;
202
+ const nodeMap = new Map();
203
+ for (let i = 0; i < nodeCount; i++) {
204
+ const node = {
205
+ index: i,
206
+ nodeType: nodes.nodeType[i],
207
+ nodeName: nodes.nodeName[i].toLowerCase(),
208
+ children: [],
209
+ textContent: '',
210
+ attributes: {}
211
+ };
212
+ if (nodes.nodeValue?.[i]) {
213
+ node.textContent = nodes.nodeValue[i].trim();
214
+ }
215
+ if (nodes.textValue?.index.includes(i)) {
216
+ const textIndex = nodes.textValue.index.indexOf(i);
217
+ const stringIndex = nodes.textValue.value[textIndex];
218
+ if (typeof stringIndex === 'number' && strings[stringIndex]) {
219
+ node.textContent = strings[stringIndex].trim();
220
+ }
221
+ }
222
+ if (nodes.attributes?.[i]) {
223
+ const attrs = nodes.attributes[i];
224
+ for (let j = 0; j < attrs.length; j += 2) {
225
+ const name = strings[parseInt(attrs[j])];
226
+ const value = strings[parseInt(attrs[j + 1])];
227
+ if (['id', 'class', 'type', 'name', 'href', 'src', 'alt', 'placeholder', 'value', 'title'].includes(name)) {
228
+ node.attributes[name] = value;
229
+ }
230
+ }
231
+ }
232
+ if (nodes.inputValue?.index.includes(i)) {
233
+ const inputIndex = nodes.inputValue.index.indexOf(i);
234
+ const stringIndex = nodes.inputValue.value[inputIndex];
235
+ if (typeof stringIndex === 'number') {
236
+ node.inputValue = strings[stringIndex];
237
+ }
238
+ }
239
+ if (nodes.inputChecked?.index.includes(i)) {
240
+ node.checked = true;
241
+ }
242
+ if (nodes.optionSelected?.index.includes(i)) {
243
+ node.selected = true;
244
+ }
245
+ if (nodes.isClickable?.index.includes(i)) {
246
+ node.clickable = true;
247
+ }
248
+ nodeMap.set(i, node);
249
+ }
250
+ const tree = [];
251
+ for (let i = 0; i < nodeCount; i++) {
252
+ const parentIndex = nodes.parentIndex?.[i];
253
+ const node = nodeMap.get(i);
254
+ if (parentIndex !== undefined && parentIndex >= 0) {
255
+ const parent = nodeMap.get(parentIndex);
256
+ if (parent) {
257
+ parent.children.push(node);
258
+ }
259
+ }
260
+ else {
261
+ tree.push(node);
262
+ }
263
+ }
264
+ return tree;
265
+ }
266
+ findBodyNode(nodeTree) {
267
+ const findNode = (nodes, tagName) => {
268
+ for (const node of nodes) {
269
+ if (node.nodeName === tagName) {
270
+ return node;
271
+ }
272
+ const found = findNode(node.children, tagName);
273
+ if (found)
274
+ return found;
275
+ }
276
+ return null;
162
277
  };
278
+ return findNode(nodeTree, 'body') || findNode(nodeTree, 'main') || null;
279
+ }
280
+ shouldIncludeNode(node) {
281
+ const skipTags = ['script', 'style', 'meta', 'link', 'head', 'noscript'];
282
+ if (skipTags.includes(node.nodeName)) {
283
+ return false;
284
+ }
285
+ if (node.nodeType === 3) {
286
+ const text = (node.textContent || '').trim();
287
+ return text.length > 0;
288
+ }
289
+ return true;
290
+ }
291
+ renderNodeAsText(node, depth, isLast = false, parentIsLast = []) {
292
+ if (!this.shouldIncludeNode(node)) {
293
+ return '';
294
+ }
295
+ let indent = '';
296
+ for (let i = 0; i < parentIsLast.length; i++) {
297
+ indent += parentIsLast[i] ? ' ' : '│ ';
298
+ }
299
+ const prefix = depth > 0 ? (isLast ? '└── ' : '├── ') : '';
300
+ let output = '';
301
+ if (node.nodeType === 3) {
302
+ if (node.textContent) {
303
+ const truncatedText = this.truncateText(node.textContent.trim(), 40);
304
+ output += `${indent}${prefix}"${truncatedText}"\n`;
305
+ }
306
+ }
307
+ else if (node.nodeType === 1) {
308
+ const tag = node.nodeName.toUpperCase();
309
+ let description = tag;
310
+ const attrs = [];
311
+ if (node.attributes.id)
312
+ attrs.push(`#${node.attributes.id}`);
313
+ if (node.attributes.class) {
314
+ const classes = node.attributes.class.split(/\s+/).filter((c) => c.trim().length > 0);
315
+ classes.forEach((cls) => {
316
+ attrs.push(`.${cls.trim()}`);
317
+ });
318
+ }
319
+ if (node.attributes.type)
320
+ attrs.push(`[${node.attributes.type}]`);
321
+ if (node.attributes.name)
322
+ attrs.push(`name="${node.attributes.name}"`);
323
+ if (attrs.length > 0) {
324
+ description += `(${attrs.join(' ')})`;
325
+ }
326
+ if (node.nodeName === 'img' && node.attributes.alt) {
327
+ const altText = this.truncateText(node.attributes.alt, 40);
328
+ description += `: "${altText}"`;
329
+ }
330
+ else if (node.nodeName === 'a' && node.attributes.href) {
331
+ description += `: "${node.attributes.href}"`;
332
+ }
333
+ else if (['input', 'textarea'].includes(node.nodeName)) {
334
+ if (node.attributes.placeholder) {
335
+ const placeholderText = this.truncateText(node.attributes.placeholder, 40);
336
+ description += `: "${placeholderText}"`;
337
+ }
338
+ else if (node.inputValue) {
339
+ const inputText = this.truncateText(node.inputValue, 40);
340
+ description += `: "${inputText}"`;
341
+ }
342
+ else if (node.nodeName === 'textarea') {
343
+ const textContent = this.extractTextContent(node);
344
+ if (textContent) {
345
+ const truncatedText = this.truncateText(textContent, 40);
346
+ description += `: "${truncatedText}"`;
347
+ }
348
+ }
349
+ if (node.checked)
350
+ description += ' [checked]';
351
+ }
352
+ else if (node.nodeName === 'button' || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(node.nodeName)) {
353
+ const textContent = this.extractTextContent(node);
354
+ if (textContent) {
355
+ const truncatedText = this.truncateText(textContent, 40);
356
+ description += `: "${truncatedText}"`;
357
+ }
358
+ }
359
+ output += `${indent}${prefix}${description}\n`;
360
+ if (node.nodeName === 'textarea') {
361
+ return output;
362
+ }
363
+ const meaningfulChildren = node.children.filter((child) => this.shouldIncludeNode(child));
364
+ if (meaningfulChildren.length === 0) {
365
+ const textContent = this.extractTextContent(node);
366
+ if (textContent && textContent.trim().length > 0) {
367
+ const truncatedText = this.truncateText(textContent.trim(), 40);
368
+ output += `${indent}│ └── "${truncatedText}"\n`;
369
+ return output;
370
+ }
371
+ return output;
372
+ }
373
+ else {
374
+ const newParentIsLast = [...parentIsLast, isLast];
375
+ for (let i = 0; i < meaningfulChildren.length; i++) {
376
+ const child = meaningfulChildren[i];
377
+ const childIsLast = i === meaningfulChildren.length - 1;
378
+ output += this.renderNodeAsText(child, depth + 1, childIsLast, newParentIsLast);
379
+ }
380
+ }
381
+ }
382
+ return output;
383
+ }
384
+ extractTextContent(node) {
385
+ let text = '';
386
+ if (node.nodeType === 3) {
387
+ return node.textContent || '';
388
+ }
389
+ if (node.textContent) {
390
+ text += node.textContent + ' ';
391
+ }
392
+ for (const child of node.children) {
393
+ text += this.extractTextContent(child) + ' ';
394
+ }
395
+ return text.trim().replace(/\s+/g, ' ');
396
+ }
397
+ truncateText(text, maxLength) {
398
+ if (!text)
399
+ return '';
400
+ if (text.length <= maxLength)
401
+ return text;
402
+ return text.substring(0, maxLength) + '...';
403
+ }
404
+ buildTextFromDOMNode(root, _url, title) {
405
+ let output = `PAGE: ${title || 'Untitled'}\n`;
406
+ const bodyNode = this.findBodyInDOMTree(root);
407
+ if (bodyNode) {
408
+ output += this.renderDOMNodeAsText(bodyNode, 0);
409
+ }
410
+ else {
411
+ if (root.children) {
412
+ for (const child of root.children) {
413
+ if (this.shouldIncludeDOMNode(child)) {
414
+ output += this.renderDOMNodeAsText(child, 0);
415
+ }
416
+ }
417
+ }
418
+ }
419
+ return output;
420
+ }
421
+ findBodyInDOMTree(node) {
422
+ if (node.nodeName && node.nodeName.toLowerCase() === 'body') {
423
+ return node;
424
+ }
425
+ if (node.nodeName && node.nodeName.toLowerCase() === 'main') {
426
+ return node;
427
+ }
428
+ if (node.children) {
429
+ for (const child of node.children) {
430
+ const found = this.findBodyInDOMTree(child);
431
+ if (found)
432
+ return found;
433
+ }
434
+ }
435
+ return null;
436
+ }
437
+ shouldIncludeDOMNode(node) {
438
+ if (!node)
439
+ return false;
440
+ const nodeName = node.nodeName ? node.nodeName.toLowerCase() : '';
441
+ const skipTags = ['script', 'style', 'meta', 'link', 'head', 'noscript'];
442
+ if (skipTags.includes(nodeName)) {
443
+ return false;
444
+ }
445
+ if (node.nodeType === 3) {
446
+ const text = node.nodeValue || '';
447
+ return text.trim().length > 0;
448
+ }
449
+ return true;
450
+ }
451
+ renderDOMNodeAsText(node, depth, isLast = false, parentIsLast = []) {
452
+ if (!this.shouldIncludeDOMNode(node)) {
453
+ return '';
454
+ }
455
+ let indent = '';
456
+ for (let i = 0; i < parentIsLast.length; i++) {
457
+ indent += parentIsLast[i] ? ' ' : '│ ';
458
+ }
459
+ const prefix = depth > 0 ? (isLast ? '└── ' : '├── ') : '';
460
+ let output = '';
461
+ if (node.nodeType === 3) {
462
+ const text = (node.nodeValue || '').trim();
463
+ if (text) {
464
+ const truncatedText = this.truncateText(text, 40);
465
+ output += `${indent}${prefix}"${truncatedText}"\n`;
466
+ }
467
+ }
468
+ else if (node.nodeType === 1) {
469
+ const tag = (node.nodeName || '').toUpperCase();
470
+ let description = tag;
471
+ const attrs = [];
472
+ if (node.attributes) {
473
+ for (let i = 0; i < node.attributes.length; i += 2) {
474
+ const name = node.attributes[i];
475
+ const value = node.attributes[i + 1];
476
+ if (['id', 'class', 'type', 'name', 'href', 'src', 'alt', 'placeholder', 'value', 'title'].includes(name)) {
477
+ if (name === 'id')
478
+ attrs.push(`#${value}`);
479
+ else if (name === 'class') {
480
+ const classes = value.split(/\s+/).filter((c) => c.trim().length > 0);
481
+ classes.forEach((cls) => {
482
+ attrs.push(`.${cls.trim()}`);
483
+ });
484
+ }
485
+ else if (name === 'type')
486
+ attrs.push(`[${value}]`);
487
+ else
488
+ attrs.push(`${name}="${value}"`);
489
+ }
490
+ }
491
+ }
492
+ if (attrs.length > 0) {
493
+ description += `(${attrs.join(' ')})`;
494
+ }
495
+ const nodeName = (node.nodeName || '').toLowerCase();
496
+ if (nodeName === 'img' && node.attributes) {
497
+ const altIndex = node.attributes.indexOf('alt');
498
+ if (altIndex >= 0 && altIndex + 1 < node.attributes.length) {
499
+ const altText = this.truncateText(node.attributes[altIndex + 1], 40);
500
+ description += `: "${altText}"`;
501
+ }
502
+ }
503
+ else if (nodeName === 'a' && node.attributes) {
504
+ const hrefIndex = node.attributes.indexOf('href');
505
+ if (hrefIndex >= 0 && hrefIndex + 1 < node.attributes.length) {
506
+ description += `: "${node.attributes[hrefIndex + 1]}"`;
507
+ }
508
+ }
509
+ else if (['input', 'textarea'].includes(nodeName)) {
510
+ if (node.attributes) {
511
+ const placeholderIndex = node.attributes.indexOf('placeholder');
512
+ if (placeholderIndex >= 0 && placeholderIndex + 1 < node.attributes.length) {
513
+ const placeholderText = this.truncateText(node.attributes[placeholderIndex + 1], 40);
514
+ description += `: "${placeholderText}"`;
515
+ }
516
+ }
517
+ if (nodeName === 'textarea') {
518
+ const textContent = this.extractTextFromDOMNode(node);
519
+ if (textContent) {
520
+ const truncatedText = this.truncateText(textContent, 40);
521
+ description += `: "${truncatedText}"`;
522
+ }
523
+ }
524
+ }
525
+ else if (nodeName === 'button' || ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(nodeName)) {
526
+ const textContent = this.extractTextFromDOMNode(node);
527
+ if (textContent) {
528
+ const truncatedText = this.truncateText(textContent, 40);
529
+ description += `: "${truncatedText}"`;
530
+ }
531
+ }
532
+ output += `${indent}${prefix}${description}\n`;
533
+ if (nodeName === 'textarea') {
534
+ return output;
535
+ }
536
+ if (node.children) {
537
+ const meaningfulChildren = node.children.filter((child) => this.shouldIncludeDOMNode(child));
538
+ if (meaningfulChildren.length === 0) {
539
+ const textContent = this.extractTextFromDOMNode(node);
540
+ if (textContent && textContent.trim().length > 0) {
541
+ const truncatedText = this.truncateText(textContent.trim(), 40);
542
+ output += `${indent}│ └── "${truncatedText}"\n`;
543
+ return output;
544
+ }
545
+ return output;
546
+ }
547
+ else {
548
+ const newParentIsLast = [...parentIsLast, isLast];
549
+ for (let i = 0; i < meaningfulChildren.length; i++) {
550
+ const child = meaningfulChildren[i];
551
+ const childIsLast = i === meaningfulChildren.length - 1;
552
+ output += this.renderDOMNodeAsText(child, depth + 1, childIsLast, newParentIsLast);
553
+ }
554
+ }
555
+ }
556
+ }
557
+ return output;
558
+ }
559
+ extractTextFromDOMNode(node) {
560
+ if (node.nodeType === 3) {
561
+ return (node.nodeValue || '').trim();
562
+ }
563
+ let text = '';
564
+ if (node.children) {
565
+ for (const child of node.children) {
566
+ text += this.extractTextFromDOMNode(child) + ' ';
567
+ }
568
+ }
569
+ return text.trim().replace(/\s+/g, ' ');
570
+ }
571
+ convertToHTML(response) {
572
+ const doc = response.documents[0];
573
+ if (!doc)
574
+ return '';
575
+ const tree = this.buildDOMTree(doc, response.strings);
576
+ return this.renderNodeAsHTML(tree[0], 0);
163
577
  }
164
578
  buildDOMTree(doc, strings) {
165
579
  const nodes = doc.nodes;
@@ -227,27 +641,6 @@ class TakeSnapshotHandler {
227
641
  }
228
642
  return tree;
229
643
  }
230
- processLayoutInfo(layout, strings) {
231
- return {
232
- nodeCount: layout.nodeIndex.length,
233
- styles: layout.styles.map(styleArray => styleArray.map(styleIndex => strings[parseInt(styleIndex)])),
234
- bounds: layout.bounds,
235
- text: layout.text,
236
- paintOrders: layout.paintOrders,
237
- offsetRects: layout.offsetRects,
238
- scrollRects: layout.scrollRects,
239
- clientRects: layout.clientRects,
240
- blendedBackgroundColors: layout.blendedBackgroundColors,
241
- textColorOpacities: layout.textColorOpacities
242
- };
243
- }
244
- convertToHTML(response) {
245
- const doc = response.documents[0];
246
- if (!doc)
247
- return '';
248
- const tree = this.buildDOMTree(doc, response.strings);
249
- return this.renderNodeAsHTML(tree[0], 0);
250
- }
251
644
  renderNodeAsHTML(node, depth) {
252
645
  if (!node)
253
646
  return '';
@@ -312,7 +705,7 @@ class TakeSnapshotHandler {
312
705
  if (snapshotArgs.filename !== undefined && typeof snapshotArgs.filename !== 'string') {
313
706
  return false;
314
707
  }
315
- if (snapshotArgs.format !== undefined && !['json', 'html'].includes(snapshotArgs.format)) {
708
+ if (snapshotArgs.format !== undefined && !['json', 'html', 'text'].includes(snapshotArgs.format)) {
316
709
  return false;
317
710
  }
318
711
  if (snapshotArgs.includeStyles !== undefined && typeof snapshotArgs.includeStyles !== 'boolean') {
@@ -331,47 +724,58 @@ class TakeSnapshotHandler {
331
724
  }
332
725
  getHelp() {
333
726
  return `
334
- snapshot - Capture a DOM snapshot including DOM tree structure, computed styles, and layout information
727
+ snapshot - Capture a text-based DOM representation optimized for LLM understanding
335
728
 
336
729
  Usage:
337
730
  snapshot
338
- snapshot --filename dom-snapshot.json
731
+ snapshot --filename page-structure.txt
339
732
  snapshot --format html --filename page-structure.html
340
- snapshot --include-paint-order --filename detailed-snapshot.json
341
733
 
342
734
  Arguments:
343
735
  --filename <path> Output filename (if not provided, returns data directly)
344
- --format <json|html> Output format (default: json)
736
+ --format <json|html> Output format (default: json with text snapshot)
345
737
  --include-styles Include computed styles (default: true)
346
738
  --include-attributes Include DOM attributes (default: true)
347
739
  --include-paint-order Include paint order information (default: false)
348
- --include-text-index Include text node indices (default: false)
740
+ --include-text-index Include additional DOM tree data (default: false)
741
+
742
+ Output Format:
743
+ The default JSON output contains a text-based representation of the page structure
744
+ that LLMs can easily understand, showing:
745
+ - Page hierarchy with indentation
746
+ - Element types and key attributes
747
+ - Text content and form values
748
+ - Interactive elements (buttons, links, forms)
749
+ - Semantic structure (headings, lists, etc.)
349
750
 
350
- Output Formats:
351
- json: Structured JSON with DOM tree, layout info, and computed styles
352
- html: Reconstructed HTML representation of the DOM
751
+ Example Output:
752
+ PAGE: Example Site (https://example.com)
753
+ ├── HEADER
754
+ │ ├── IMG(#logo): "Company Logo"
755
+ │ └── NAV
756
+ │ ├── A: "https://example.com/home"
757
+ │ └── "Home"
758
+ ├── MAIN
759
+ │ ├── H1: "Welcome to Our Site"
760
+ │ ├── P
761
+ │ │ └── "This is the main content..."
762
+ │ └── FORM
763
+ │ ├── INPUT[email](name="email"): "Enter your email"
764
+ │ └── BUTTON: "Submit"
353
765
 
354
766
  Examples:
355
- # Basic DOM snapshot as JSON
356
- snapshot --filename dom-snapshot.json
767
+ # Get text-based page structure
768
+ snapshot
769
+
770
+ # Save to file
771
+ snapshot --filename page-structure.json
357
772
 
358
773
  # HTML representation
359
774
  snapshot --format html --filename page-structure.html
360
775
 
361
- # Detailed snapshot with paint order
362
- snapshot --include-paint-order --filename detailed-snapshot.json
363
-
364
- # Return data directly (no file)
365
- snapshot --format json
366
-
367
776
  Note:
368
- DOM snapshots capture the complete DOM tree structure including:
369
- - Element hierarchy and attributes
370
- - Computed CSS styles for each element
371
- - Layout information (bounds, positioning)
372
- - Text content and form values
373
- - Visibility and clickability information
374
- - Paint order and rendering details (optional)
777
+ This format provides a "text screenshot" that LLMs can easily parse to understand
778
+ page layout, content, and interactive elements without overwhelming detail.
375
779
  `;
376
780
  }
377
781
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chrome-cdp-cli",
3
- "version": "1.7.8",
3
+ "version": "1.8.0",
4
4
  "description": "Browser automation CLI via Chrome DevTools Protocol. Designed for developers and AI assistants - combines dedicated commands for common tasks with flexible JavaScript execution for complex scenarios. Features: element interaction, screenshots, DOM snapshots, console/network monitoring. Built-in IDE integration for Cursor and Claude.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",