Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [Unreleased]

### Added
- Smart default snapshot scoping - snapshots automatically scope to `<main>` element (then `[role="main"]`, fallback to `<body>`), reducing output size by excluding navigation, headers, and footers. Use `--snapshot-full` to capture full page body when needed
- `--snapshot-compact` flag for token-efficient LLM consumption - applies four transforms: link collapsing (merges link + /url child into `link "Title" -> /path`), heading inlining (merges heading with single link child), decorative image removal (strips img nodes with empty or single-char alt text), and duplicate URL dedup (removes second occurrence at same depth scope). Applied after `--snapshot-depth` and before `--snapshot-collapse` in the pipeline
- `--snapshot-max-lines <N>` flag to truncate snapshot output to a maximum number of lines, with a `... (K more lines)` marker when lines are omitted
- `--snapshot-collapse` flag to collapse repeated consecutive siblings of the same ARIA type - keeps first 2 with subtrees, replaces the rest with `... (K more <type>)` markers. Works recursively on nested structures
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ This eliminates the common click-snapshot-check loop that wastes agent turns on
| `--snapshot-collapse` | Any action with snapshot | Collapse repeated siblings (keep first 2, summarize rest) |
| `--snapshot-text-only` | Any action with snapshot | Strip structural nodes, keep content only |
| `--max-field-length <N>` | `extract` | Max characters per field (default: 500, max: 2000) |
| `--snapshot-full` | Any action with snapshot | Use full page body (default: auto-scope to `<main>` content area) |
| `--no-snapshot` | Any action with snapshot | Omit snapshot from output entirely |

## Error Handling
Expand Down
1 change: 1 addition & 0 deletions commands/web-ctl.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> click <sel> --no-snapshot
node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> snapshot --snapshot-collapse
node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> snapshot --snapshot-compact
node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> snapshot --snapshot-text-only --snapshot-max-lines 50
node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> goto <url> --snapshot-full
node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> goto <url> --snapshot-collapse --snapshot-depth 4

# Macros
Expand Down
27 changes: 26 additions & 1 deletion scripts/web-ctl.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const BOOLEAN_FLAGS = new Set([
'--allow-evaluate', '--no-snapshot', '--wait-stable', '--vnc',
'--exact', '--accept', '--submit', '--dismiss', '--auto',
'--snapshot-collapse', '--snapshot-text-only', '--snapshot-compact',
'--snapshot-full',
]);

function validateSessionName(name) {
Expand Down Expand Up @@ -85,6 +86,26 @@ function resolveSelector(page, selector) {
return page.locator(selector);
}

/**
* Detect the main content area of the page.
* Tries <main>, then [role="main"], then falls back to <body>.
*
* @param {object} page - Playwright page object
* @returns {object} Playwright locator for the main content area
*/
async function detectMainContent(page) {
try {
const mainTag = page.locator('main').first();
const mainRole = page.locator('[role="main"]').first();
const [mainCount, roleCount] = await Promise.all([mainTag.count(), mainRole.count()]);
if (mainCount > 0) return mainTag;
if (roleCount > 0) return mainRole;
} catch {
// fall through to body
}
return page.locator('body');
}

/**
* Get accessibility tree snapshot formatted as text.
* Uses Playwright's ariaSnapshot API (page.accessibility was removed in v1.50+).
Expand All @@ -93,6 +114,7 @@ function resolveSelector(page, selector) {
* @param {object} [opts={}] - Snapshot options
* @param {boolean} [opts.noSnapshot] - Return null to omit snapshot entirely
* @param {string} [opts.snapshotSelector] - Scope snapshot to a DOM subtree
* @param {boolean} [opts.snapshotFull] - Use full page body (skip <main> auto-detection)
* @param {number} [opts.snapshotDepth] - Limit ARIA tree depth
* @param {boolean} [opts.snapshotCompact] - Compact format for token efficiency
* @param {boolean} [opts.snapshotCollapse] - Collapse repeated siblings
Expand All @@ -104,7 +126,9 @@ async function getSnapshot(page, opts = {}) {
try {
const root = opts.snapshotSelector
? resolveSelector(page, opts.snapshotSelector)
: page.locator('body');
: opts.snapshotFull
? page.locator('body')
: await detectMainContent(page);
const raw = await root.ariaSnapshot();
let result = raw;
if (opts.snapshotDepth) result = trimByDepth(result, opts.snapshotDepth);
Expand Down Expand Up @@ -1170,6 +1194,7 @@ Snapshot options (apply to any action that returns a snapshot):
remove decorative images, dedup URLs
--snapshot-collapse Collapse repeated siblings (show first 2)
--snapshot-text-only Strip structural nodes, keep content only
--snapshot-full Use full page body (skip <main> auto-detection)

Selector syntax:
role=button[name='Submit'] ARIA role selector
Expand Down
13 changes: 12 additions & 1 deletion skills/web-browse/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,9 @@ Auto-detect mode also returns the detected CSS selector, which can be reused wit

## Snapshot Control

All actions that return a snapshot support these flags to control output size:
All actions that return a snapshot support these flags to control output size.

By default, snapshots are auto-scoped to the main content area of the page. The tool looks for a `<main>` element, then `[role="main"]`, and falls back to `<body>` if neither exists. This automatically excludes navigation, headers, and footers from snapshots, reducing noise and token usage. Use `--snapshot-full` to capture the full page body when needed, or `--snapshot-selector` to scope to a specific element.

### --snapshot-depth N - Limit Tree Depth

Expand All @@ -366,6 +368,15 @@ node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> click "#btn" --snapshot-sel

Takes the snapshot from a specific DOM subtree instead of the full body. Accepts the same selector syntax as other actions.

### --snapshot-full - Full Page Snapshot

```bash
node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> goto <url> --snapshot-full
node ${PLUGIN_ROOT}/scripts/web-ctl.js run <session> snapshot --snapshot-full
```

Bypasses the default auto-scoping to `<main>` and captures the full page body instead. Use this when you need to see navigation, headers, footers, or other content outside the main content area.

### --no-snapshot - Omit Snapshot

```bash
Expand Down
Loading
Loading