Add fullscreen and preview modes
- Add --fullscreen flag for macOS native fullscreen (AppleScript) - Add --preview flag to highlight elements before actions - Add --preview-delay to configure highlight duration - Update README with new options Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,13 @@ browse -c ".cookie-accept" -c "a.nav-link" -q "h1" https://example.com
|
|||||||
|
|
||||||
# Interactive mode (visible browser)
|
# Interactive mode (visible browser)
|
||||||
browse -i --headed https://example.com
|
browse -i --headed https://example.com
|
||||||
|
|
||||||
|
# Fullscreen mode (macOS native fullscreen)
|
||||||
|
browse --fullscreen -i https://example.com
|
||||||
|
|
||||||
|
# Preview mode (highlights elements before actions)
|
||||||
|
browse -p -c "button.submit" https://example.com
|
||||||
|
browse -p --preview-delay 3000 -c ".nav-link" https://example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
## MCP Server (Standalone)
|
## MCP Server (Standalone)
|
||||||
@@ -185,9 +192,12 @@ Add to Claude Code's MCP config (`~/.claude/settings.json`):
|
|||||||
import { ClaudeBrowser } from '@saiden/browse';
|
import { ClaudeBrowser } from '@saiden/browse';
|
||||||
|
|
||||||
const browser = new ClaudeBrowser({
|
const browser = new ClaudeBrowser({
|
||||||
headless: true,
|
headless: true, // Set false to show browser window
|
||||||
width: 1280,
|
width: 1280,
|
||||||
height: 800,
|
height: 800,
|
||||||
|
fullscreen: false, // macOS native fullscreen (implies headless: false)
|
||||||
|
preview: false, // Highlight elements before actions
|
||||||
|
previewDelay: 2000, // Preview highlight duration in ms
|
||||||
});
|
});
|
||||||
|
|
||||||
await browser.launch();
|
await browser.launch();
|
||||||
|
|||||||
Vendored
+2
@@ -13,6 +13,8 @@ export declare class ClaudeBrowser {
|
|||||||
private interceptPatterns;
|
private interceptPatterns;
|
||||||
constructor(options?: BrowserOptions);
|
constructor(options?: BrowserOptions);
|
||||||
launch(): Promise<void>;
|
launch(): Promise<void>;
|
||||||
|
private enterFullscreen;
|
||||||
|
private previewAction;
|
||||||
private setupErrorListener;
|
private setupErrorListener;
|
||||||
private setupDialogListener;
|
private setupDialogListener;
|
||||||
private setupConsoleListener;
|
private setupConsoleListener;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}
|
||||||
Vendored
+95
@@ -1,5 +1,8 @@
|
|||||||
|
import { exec } from 'node:child_process';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
|
import { promisify } from 'node:util';
|
||||||
import { webkit } from 'playwright';
|
import { webkit } from 'playwright';
|
||||||
|
const execAsync = promisify(exec);
|
||||||
import * as image from './image.js';
|
import * as image from './image.js';
|
||||||
export class ClaudeBrowser {
|
export class ClaudeBrowser {
|
||||||
browser = null;
|
browser = null;
|
||||||
@@ -17,6 +20,9 @@ export class ClaudeBrowser {
|
|||||||
headless: options.headless ?? true,
|
headless: options.headless ?? true,
|
||||||
width: options.width ?? 1280,
|
width: options.width ?? 1280,
|
||||||
height: options.height ?? 800,
|
height: options.height ?? 800,
|
||||||
|
fullscreen: options.fullscreen ?? false,
|
||||||
|
preview: options.preview ?? false,
|
||||||
|
previewDelay: options.previewDelay ?? 2000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
async launch() {
|
async launch() {
|
||||||
@@ -32,6 +38,90 @@ export class ClaudeBrowser {
|
|||||||
this.setupNetworkListener(this.page);
|
this.setupNetworkListener(this.page);
|
||||||
this.setupErrorListener(this.page);
|
this.setupErrorListener(this.page);
|
||||||
this.setupDialogListener(this.page);
|
this.setupDialogListener(this.page);
|
||||||
|
if (this.options.fullscreen && !this.options.headless) {
|
||||||
|
await this.enterFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async enterFullscreen() {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
console.warn('Native fullscreen only supported on macOS');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// AppleScript to fullscreen the Playwright window by process name
|
||||||
|
const script = `
|
||||||
|
tell application "System Events"
|
||||||
|
tell process "Playwright"
|
||||||
|
set value of attribute "AXFullScreen" of window 1 to true
|
||||||
|
end tell
|
||||||
|
end tell
|
||||||
|
`;
|
||||||
|
// Retry logic: wait for window to appear, then fullscreen
|
||||||
|
const maxAttempts = 5;
|
||||||
|
const delayMs = 500;
|
||||||
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||||
|
try {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||||
|
await execAsync(`osascript -e '${script}'`);
|
||||||
|
return; // Success
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (attempt === maxAttempts) {
|
||||||
|
console.warn('Failed to enter fullscreen:', err.message);
|
||||||
|
}
|
||||||
|
// Window may not be ready yet, retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async previewAction(selector, action) {
|
||||||
|
if (!this.options.preview)
|
||||||
|
return;
|
||||||
|
const page = this.ensurePage();
|
||||||
|
const escapedSelector = JSON.stringify(selector);
|
||||||
|
const escapedAction = JSON.stringify(action);
|
||||||
|
// Highlight the element with a pulsing red border
|
||||||
|
const highlightScript = `
|
||||||
|
(() => {
|
||||||
|
const selector = ${escapedSelector};
|
||||||
|
const action = ${escapedAction};
|
||||||
|
const el = document.querySelector(selector);
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.id = '__claude_preview_overlay__';
|
||||||
|
overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.3);z-index:999998;pointer-events:none';
|
||||||
|
|
||||||
|
const label = document.createElement('div');
|
||||||
|
label.id = '__claude_preview_label__';
|
||||||
|
label.textContent = action + ': ' + selector;
|
||||||
|
label.style.cssText = 'position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#e11d48;color:white;padding:12px 24px;border-radius:8px;font-family:system-ui,sans-serif;font-size:16px;font-weight:600;z-index:1000001;box-shadow:0 4px 12px rgba(0,0,0,0.3)';
|
||||||
|
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
const highlight = document.createElement('div');
|
||||||
|
highlight.id = '__claude_preview_highlight__';
|
||||||
|
highlight.style.cssText = 'position:fixed;top:' + (rect.top - 4) + 'px;left:' + (rect.left - 4) + 'px;width:' + (rect.width + 8) + 'px;height:' + (rect.height + 8) + 'px;border:3px solid #e11d48;border-radius:4px;z-index:1000000;pointer-events:none;box-shadow:0 0 0 4px rgba(225,29,72,0.3);animation:__claude_pulse__ 1s ease-in-out infinite';
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = '__claude_preview_style__';
|
||||||
|
style.textContent = '@keyframes __claude_pulse__{0%,100%{box-shadow:0 0 0 4px rgba(225,29,72,0.3)}50%{box-shadow:0 0 0 8px rgba(225,29,72,0.5)}}';
|
||||||
|
|
||||||
|
document.head.appendChild(style);
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
document.body.appendChild(highlight);
|
||||||
|
document.body.appendChild(label);
|
||||||
|
})()
|
||||||
|
`;
|
||||||
|
await page.evaluate(highlightScript);
|
||||||
|
await page.waitForTimeout(this.options.previewDelay);
|
||||||
|
// Clean up highlight
|
||||||
|
const cleanupScript = `
|
||||||
|
(() => {
|
||||||
|
['__claude_preview_overlay__', '__claude_preview_highlight__', '__claude_preview_label__', '__claude_preview_style__']
|
||||||
|
.forEach(id => document.getElementById(id)?.remove());
|
||||||
|
})()
|
||||||
|
`;
|
||||||
|
await page.evaluate(cleanupScript);
|
||||||
}
|
}
|
||||||
setupErrorListener(page) {
|
setupErrorListener(page) {
|
||||||
page.on('pageerror', (error) => {
|
page.on('pageerror', (error) => {
|
||||||
@@ -155,12 +245,14 @@ export class ClaudeBrowser {
|
|||||||
}
|
}
|
||||||
async click(selector) {
|
async click(selector) {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
await this.previewAction(selector, 'CLICK');
|
||||||
await page.click(selector);
|
await page.click(selector);
|
||||||
await page.waitForLoadState('networkidle').catch(() => { });
|
await page.waitForLoadState('networkidle').catch(() => { });
|
||||||
return { url: page.url() };
|
return { url: page.url() };
|
||||||
}
|
}
|
||||||
async type(selector, text) {
|
async type(selector, text) {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
await this.previewAction(selector, `TYPE "${text}"`);
|
||||||
await page.fill(selector, text);
|
await page.fill(selector, text);
|
||||||
}
|
}
|
||||||
async query(selector) {
|
async query(selector) {
|
||||||
@@ -463,10 +555,13 @@ export class ClaudeBrowser {
|
|||||||
// Phase 7: Advanced Interactions
|
// Phase 7: Advanced Interactions
|
||||||
async hover(selector) {
|
async hover(selector) {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
await this.previewAction(selector, 'HOVER');
|
||||||
await page.hover(selector);
|
await page.hover(selector);
|
||||||
}
|
}
|
||||||
async select(selector, value) {
|
async select(selector, value) {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
const valueStr = Array.isArray(value) ? value.join(', ') : value;
|
||||||
|
await this.previewAction(selector, `SELECT "${valueStr}"`);
|
||||||
return page.selectOption(selector, value);
|
return page.selectOption(selector, value);
|
||||||
}
|
}
|
||||||
async keys(keys) {
|
async keys(keys) {
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+14
-1
@@ -16,6 +16,9 @@ const { values, positionals } = parseArgs({
|
|||||||
fullpage: { type: 'boolean', short: 'f', default: false },
|
fullpage: { type: 'boolean', short: 'f', default: false },
|
||||||
wait: { type: 'string', default: '2000' },
|
wait: { type: 'string', default: '2000' },
|
||||||
headed: { type: 'boolean', default: false },
|
headed: { type: 'boolean', default: false },
|
||||||
|
fullscreen: { type: 'boolean', default: false },
|
||||||
|
preview: { type: 'boolean', short: 'p', default: false },
|
||||||
|
'preview-delay': { type: 'string', default: '2000' },
|
||||||
interactive: { type: 'boolean', short: 'i', default: false },
|
interactive: { type: 'boolean', short: 'i', default: false },
|
||||||
query: { type: 'string', short: 'q' },
|
query: { type: 'string', short: 'q' },
|
||||||
json: { type: 'boolean', short: 'j', default: false },
|
json: { type: 'boolean', short: 'j', default: false },
|
||||||
@@ -40,6 +43,9 @@ Options:
|
|||||||
-f, --fullpage Capture full page scroll
|
-f, --fullpage Capture full page scroll
|
||||||
--wait <ms> Wait time after load (default: 2000)
|
--wait <ms> Wait time after load (default: 2000)
|
||||||
--headed Show browser window
|
--headed Show browser window
|
||||||
|
--fullscreen Launch in native fullscreen mode (macOS only, implies --headed)
|
||||||
|
-p, --preview Highlight elements before actions (click, type, etc.)
|
||||||
|
--preview-delay <ms> Preview highlight duration (default: 2000)
|
||||||
-i, --interactive Keep browser open for manual interaction
|
-i, --interactive Keep browser open for manual interaction
|
||||||
-q, --query <selector> Query elements by CSS selector and show attributes
|
-q, --query <selector> Query elements by CSS selector and show attributes
|
||||||
-j, --json Output query results as JSON
|
-j, --json Output query results as JSON
|
||||||
@@ -58,11 +64,13 @@ Examples:
|
|||||||
browse https://example.com
|
browse https://example.com
|
||||||
browse -o page.png -w 1920 -h 1080 https://example.com
|
browse -o page.png -w 1920 -h 1080 https://example.com
|
||||||
browse -i --headed https://example.com
|
browse -i --headed https://example.com
|
||||||
|
browse -i --fullscreen https://example.com
|
||||||
browse -q "a[href]" https://example.com
|
browse -q "a[href]" https://example.com
|
||||||
browse -q "img" -j https://example.com
|
browse -q "img" -j https://example.com
|
||||||
browse -c "button.submit" https://example.com
|
browse -c "button.submit" https://example.com
|
||||||
browse -t "input[name=q]=hello" -c "button[type=submit]" https://google.com
|
browse -t "input[name=q]=hello" -c "button[type=submit]" https://google.com
|
||||||
browse -c ".cookie-accept" -c "a.nav-link" -q "h1" https://example.com
|
browse -c ".cookie-accept" -c "a.nav-link" -q "h1" https://example.com
|
||||||
|
browse -p -c "button.submit" https://example.com
|
||||||
|
|
||||||
Image processing examples:
|
Image processing examples:
|
||||||
browse https://example.com --favicon ./favicons/
|
browse https://example.com --favicon ./favicons/
|
||||||
@@ -74,10 +82,15 @@ MCP Server (for Claude Code integration):
|
|||||||
browse-mcp # Run as MCP server (stdio transport)
|
browse-mcp # Run as MCP server (stdio transport)
|
||||||
`;
|
`;
|
||||||
function getViewportConfig() {
|
function getViewportConfig() {
|
||||||
|
const fullscreen = values.fullscreen;
|
||||||
|
const preview = values.preview;
|
||||||
return {
|
return {
|
||||||
headless: !values.headed,
|
headless: fullscreen || preview ? false : !values.headed,
|
||||||
width: Number.parseInt(values.width),
|
width: Number.parseInt(values.width),
|
||||||
height: Number.parseInt(values.height),
|
height: Number.parseInt(values.height),
|
||||||
|
fullscreen,
|
||||||
|
preview,
|
||||||
|
previewDelay: Number.parseInt(values['preview-delay']),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
async function processTypeActions(browser) {
|
async function processTypeActions(browser) {
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+3
@@ -2,6 +2,9 @@ export interface BrowserOptions {
|
|||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
fullscreen?: boolean;
|
||||||
|
preview?: boolean;
|
||||||
|
previewDelay?: number;
|
||||||
}
|
}
|
||||||
export interface ElementInfo {
|
export interface ElementInfo {
|
||||||
tag: string;
|
tag: string;
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@saiden/browse",
|
"name": "@saiden/browse",
|
||||||
"version": "0.2.13-pre.0",
|
"version": "0.2.13",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@saiden/browse",
|
"name": "@saiden/browse",
|
||||||
"version": "0.2.13-pre.0",
|
"version": "0.2.13",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@modelcontextprotocol/sdk": "^1.26.0",
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@saiden/browse",
|
"name": "@saiden/browse",
|
||||||
"version": "0.2.13-pre.0",
|
"version": "0.2.13",
|
||||||
"description": "Headless browser automation for Claude Code using Playwright WebKit",
|
"description": "Headless browser automation for Claude Code using Playwright WebKit",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 97 KiB |
+104
@@ -1,5 +1,9 @@
|
|||||||
|
import { exec } from 'node:child_process';
|
||||||
import { resolve } from 'node:path';
|
import { resolve } from 'node:path';
|
||||||
|
import { promisify } from 'node:util';
|
||||||
import { type Browser, type BrowserContext, type Page, type Route, webkit } from 'playwright';
|
import { type Browser, type BrowserContext, type Page, type Route, webkit } from 'playwright';
|
||||||
|
|
||||||
|
const execAsync = promisify(exec);
|
||||||
import * as image from './image.js';
|
import * as image from './image.js';
|
||||||
import type {
|
import type {
|
||||||
A11yNode,
|
A11yNode,
|
||||||
@@ -40,6 +44,9 @@ export class ClaudeBrowser {
|
|||||||
headless: options.headless ?? true,
|
headless: options.headless ?? true,
|
||||||
width: options.width ?? 1280,
|
width: options.width ?? 1280,
|
||||||
height: options.height ?? 800,
|
height: options.height ?? 800,
|
||||||
|
fullscreen: options.fullscreen ?? false,
|
||||||
|
preview: options.preview ?? false,
|
||||||
|
previewDelay: options.previewDelay ?? 2000,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +63,98 @@ export class ClaudeBrowser {
|
|||||||
this.setupNetworkListener(this.page);
|
this.setupNetworkListener(this.page);
|
||||||
this.setupErrorListener(this.page);
|
this.setupErrorListener(this.page);
|
||||||
this.setupDialogListener(this.page);
|
this.setupDialogListener(this.page);
|
||||||
|
|
||||||
|
if (this.options.fullscreen && !this.options.headless) {
|
||||||
|
await this.enterFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async enterFullscreen(): Promise<void> {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
console.warn('Native fullscreen only supported on macOS');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppleScript to fullscreen the Playwright window by process name
|
||||||
|
const script = `
|
||||||
|
tell application "System Events"
|
||||||
|
tell process "Playwright"
|
||||||
|
set value of attribute "AXFullScreen" of window 1 to true
|
||||||
|
end tell
|
||||||
|
end tell
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Retry logic: wait for window to appear, then fullscreen
|
||||||
|
const maxAttempts = 5;
|
||||||
|
const delayMs = 500;
|
||||||
|
|
||||||
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||||
|
try {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
||||||
|
await execAsync(`osascript -e '${script}'`);
|
||||||
|
return; // Success
|
||||||
|
} catch (err) {
|
||||||
|
if (attempt === maxAttempts) {
|
||||||
|
console.warn('Failed to enter fullscreen:', (err as Error).message);
|
||||||
|
}
|
||||||
|
// Window may not be ready yet, retry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async previewAction(selector: string, action: string): Promise<void> {
|
||||||
|
if (!this.options.preview) return;
|
||||||
|
|
||||||
|
const page = this.ensurePage();
|
||||||
|
const escapedSelector = JSON.stringify(selector);
|
||||||
|
const escapedAction = JSON.stringify(action);
|
||||||
|
|
||||||
|
// Highlight the element with a pulsing red border
|
||||||
|
const highlightScript = `
|
||||||
|
(() => {
|
||||||
|
const selector = ${escapedSelector};
|
||||||
|
const action = ${escapedAction};
|
||||||
|
const el = document.querySelector(selector);
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||||
|
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.id = '__claude_preview_overlay__';
|
||||||
|
overlay.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.3);z-index:999998;pointer-events:none';
|
||||||
|
|
||||||
|
const label = document.createElement('div');
|
||||||
|
label.id = '__claude_preview_label__';
|
||||||
|
label.textContent = action + ': ' + selector;
|
||||||
|
label.style.cssText = 'position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#e11d48;color:white;padding:12px 24px;border-radius:8px;font-family:system-ui,sans-serif;font-size:16px;font-weight:600;z-index:1000001;box-shadow:0 4px 12px rgba(0,0,0,0.3)';
|
||||||
|
|
||||||
|
const rect = el.getBoundingClientRect();
|
||||||
|
const highlight = document.createElement('div');
|
||||||
|
highlight.id = '__claude_preview_highlight__';
|
||||||
|
highlight.style.cssText = 'position:fixed;top:' + (rect.top - 4) + 'px;left:' + (rect.left - 4) + 'px;width:' + (rect.width + 8) + 'px;height:' + (rect.height + 8) + 'px;border:3px solid #e11d48;border-radius:4px;z-index:1000000;pointer-events:none;box-shadow:0 0 0 4px rgba(225,29,72,0.3);animation:__claude_pulse__ 1s ease-in-out infinite';
|
||||||
|
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.id = '__claude_preview_style__';
|
||||||
|
style.textContent = '@keyframes __claude_pulse__{0%,100%{box-shadow:0 0 0 4px rgba(225,29,72,0.3)}50%{box-shadow:0 0 0 8px rgba(225,29,72,0.5)}}';
|
||||||
|
|
||||||
|
document.head.appendChild(style);
|
||||||
|
document.body.appendChild(overlay);
|
||||||
|
document.body.appendChild(highlight);
|
||||||
|
document.body.appendChild(label);
|
||||||
|
})()
|
||||||
|
`;
|
||||||
|
|
||||||
|
await page.evaluate(highlightScript);
|
||||||
|
await page.waitForTimeout(this.options.previewDelay);
|
||||||
|
|
||||||
|
// Clean up highlight
|
||||||
|
const cleanupScript = `
|
||||||
|
(() => {
|
||||||
|
['__claude_preview_overlay__', '__claude_preview_highlight__', '__claude_preview_label__', '__claude_preview_style__']
|
||||||
|
.forEach(id => document.getElementById(id)?.remove());
|
||||||
|
})()
|
||||||
|
`;
|
||||||
|
await page.evaluate(cleanupScript);
|
||||||
}
|
}
|
||||||
|
|
||||||
private setupErrorListener(page: Page): void {
|
private setupErrorListener(page: Page): void {
|
||||||
@@ -191,6 +290,7 @@ export class ClaudeBrowser {
|
|||||||
|
|
||||||
async click(selector: string): Promise<{ url: string }> {
|
async click(selector: string): Promise<{ url: string }> {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
await this.previewAction(selector, 'CLICK');
|
||||||
await page.click(selector);
|
await page.click(selector);
|
||||||
await page.waitForLoadState('networkidle').catch(() => {});
|
await page.waitForLoadState('networkidle').catch(() => {});
|
||||||
return { url: page.url() };
|
return { url: page.url() };
|
||||||
@@ -198,6 +298,7 @@ export class ClaudeBrowser {
|
|||||||
|
|
||||||
async type(selector: string, text: string): Promise<void> {
|
async type(selector: string, text: string): Promise<void> {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
await this.previewAction(selector, `TYPE "${text}"`);
|
||||||
await page.fill(selector, text);
|
await page.fill(selector, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,11 +641,14 @@ export class ClaudeBrowser {
|
|||||||
// Phase 7: Advanced Interactions
|
// Phase 7: Advanced Interactions
|
||||||
async hover(selector: string): Promise<void> {
|
async hover(selector: string): Promise<void> {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
await this.previewAction(selector, 'HOVER');
|
||||||
await page.hover(selector);
|
await page.hover(selector);
|
||||||
}
|
}
|
||||||
|
|
||||||
async select(selector: string, value: string | string[]): Promise<string[]> {
|
async select(selector: string, value: string | string[]): Promise<string[]> {
|
||||||
const page = this.ensurePage();
|
const page = this.ensurePage();
|
||||||
|
const valueStr = Array.isArray(value) ? value.join(', ') : value;
|
||||||
|
await this.previewAction(selector, `SELECT "${valueStr}"`);
|
||||||
return page.selectOption(selector, value);
|
return page.selectOption(selector, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-1
@@ -19,6 +19,9 @@ const { values, positionals } = parseArgs({
|
|||||||
fullpage: { type: 'boolean', short: 'f', default: false },
|
fullpage: { type: 'boolean', short: 'f', default: false },
|
||||||
wait: { type: 'string', default: '2000' },
|
wait: { type: 'string', default: '2000' },
|
||||||
headed: { type: 'boolean', default: false },
|
headed: { type: 'boolean', default: false },
|
||||||
|
fullscreen: { type: 'boolean', default: false },
|
||||||
|
preview: { type: 'boolean', short: 'p', default: false },
|
||||||
|
'preview-delay': { type: 'string', default: '2000' },
|
||||||
interactive: { type: 'boolean', short: 'i', default: false },
|
interactive: { type: 'boolean', short: 'i', default: false },
|
||||||
query: { type: 'string', short: 'q' },
|
query: { type: 'string', short: 'q' },
|
||||||
json: { type: 'boolean', short: 'j', default: false },
|
json: { type: 'boolean', short: 'j', default: false },
|
||||||
@@ -44,6 +47,9 @@ Options:
|
|||||||
-f, --fullpage Capture full page scroll
|
-f, --fullpage Capture full page scroll
|
||||||
--wait <ms> Wait time after load (default: 2000)
|
--wait <ms> Wait time after load (default: 2000)
|
||||||
--headed Show browser window
|
--headed Show browser window
|
||||||
|
--fullscreen Launch in native fullscreen mode (macOS only, implies --headed)
|
||||||
|
-p, --preview Highlight elements before actions (click, type, etc.)
|
||||||
|
--preview-delay <ms> Preview highlight duration (default: 2000)
|
||||||
-i, --interactive Keep browser open for manual interaction
|
-i, --interactive Keep browser open for manual interaction
|
||||||
-q, --query <selector> Query elements by CSS selector and show attributes
|
-q, --query <selector> Query elements by CSS selector and show attributes
|
||||||
-j, --json Output query results as JSON
|
-j, --json Output query results as JSON
|
||||||
@@ -62,11 +68,13 @@ Examples:
|
|||||||
browse https://example.com
|
browse https://example.com
|
||||||
browse -o page.png -w 1920 -h 1080 https://example.com
|
browse -o page.png -w 1920 -h 1080 https://example.com
|
||||||
browse -i --headed https://example.com
|
browse -i --headed https://example.com
|
||||||
|
browse -i --fullscreen https://example.com
|
||||||
browse -q "a[href]" https://example.com
|
browse -q "a[href]" https://example.com
|
||||||
browse -q "img" -j https://example.com
|
browse -q "img" -j https://example.com
|
||||||
browse -c "button.submit" https://example.com
|
browse -c "button.submit" https://example.com
|
||||||
browse -t "input[name=q]=hello" -c "button[type=submit]" https://google.com
|
browse -t "input[name=q]=hello" -c "button[type=submit]" https://google.com
|
||||||
browse -c ".cookie-accept" -c "a.nav-link" -q "h1" https://example.com
|
browse -c ".cookie-accept" -c "a.nav-link" -q "h1" https://example.com
|
||||||
|
browse -p -c "button.submit" https://example.com
|
||||||
|
|
||||||
Image processing examples:
|
Image processing examples:
|
||||||
browse https://example.com --favicon ./favicons/
|
browse https://example.com --favicon ./favicons/
|
||||||
@@ -79,10 +87,15 @@ MCP Server (for Claude Code integration):
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
function getViewportConfig() {
|
function getViewportConfig() {
|
||||||
|
const fullscreen = values.fullscreen as boolean;
|
||||||
|
const preview = values.preview as boolean;
|
||||||
return {
|
return {
|
||||||
headless: !values.headed,
|
headless: fullscreen || preview ? false : !values.headed,
|
||||||
width: Number.parseInt(values.width as string),
|
width: Number.parseInt(values.width as string),
|
||||||
height: Number.parseInt(values.height as string),
|
height: Number.parseInt(values.height as string),
|
||||||
|
fullscreen,
|
||||||
|
preview,
|
||||||
|
previewDelay: Number.parseInt(values['preview-delay'] as string),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ export interface BrowserOptions {
|
|||||||
headless?: boolean;
|
headless?: boolean;
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
|
fullscreen?: boolean;
|
||||||
|
preview?: boolean;
|
||||||
|
previewDelay?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ElementInfo {
|
export interface ElementInfo {
|
||||||
|
|||||||
Reference in New Issue
Block a user