feat: add darkMode and hideSelectors to Node.js and Python SDKs
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled

This commit is contained in:
OpenClaw Dev 2026-03-04 15:07:20 +01:00
parent 96d21aa63b
commit 90c1e7da44
3 changed files with 125 additions and 0 deletions

View file

@ -33,6 +33,10 @@ export interface ScreenshotOptions {
delay?: number;
/** Page load event to wait for (default: domcontentloaded) */
waitUntil?: "load" | "domcontentloaded" | "networkidle0" | "networkidle2";
/** Emulate dark mode (prefers-color-scheme: dark) */
darkMode?: boolean;
/** CSS selectors to hide before capture (max 10, each max 200 chars) */
hideSelectors?: string | string[];
}
export interface SnapAPIConfig {
@ -108,6 +112,18 @@ export class SnapAPI {
* format: 'png',
* deviceScale: 2
* });
*
* // Dark mode with hidden elements
* const darkScreenshot = await snap.capture({
* url: 'https://example.com',
* darkMode: true,
* hideSelectors: ['.ads', '.popup', '.cookie-banner']
* });
*
* // Hide single selector
* const clean = await snap.capture('https://example.com', {
* hideSelectors: '.advertisement'
* });
* ```
*
* @throws {SnapAPIError} When the API returns an error response

View file

@ -130,6 +130,98 @@ describe('SnapAPI', () => {
);
});
it('sends darkMode parameter correctly', async () => {
const mockArrayBuffer = new ArrayBuffer(100);
mockFetch.mockResolvedValueOnce({
ok: true,
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
});
await snap.capture('https://example.com', {
darkMode: true,
});
expect(mockFetch).toHaveBeenCalledWith(
'https://snapapi.eu/v1/screenshot',
expect.objectContaining({
body: JSON.stringify({
url: 'https://example.com',
darkMode: true,
}),
})
);
});
it('sends hideSelectors as string correctly', async () => {
const mockArrayBuffer = new ArrayBuffer(100);
mockFetch.mockResolvedValueOnce({
ok: true,
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
});
await snap.capture('https://example.com', {
hideSelectors: '.ads',
});
expect(mockFetch).toHaveBeenCalledWith(
'https://snapapi.eu/v1/screenshot',
expect.objectContaining({
body: JSON.stringify({
url: 'https://example.com',
hideSelectors: '.ads',
}),
})
);
});
it('sends hideSelectors as array correctly', async () => {
const mockArrayBuffer = new ArrayBuffer(100);
mockFetch.mockResolvedValueOnce({
ok: true,
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
});
await snap.capture('https://example.com', {
hideSelectors: ['.ads', '.popup', '.banner'],
});
expect(mockFetch).toHaveBeenCalledWith(
'https://snapapi.eu/v1/screenshot',
expect.objectContaining({
body: JSON.stringify({
url: 'https://example.com',
hideSelectors: ['.ads', '.popup', '.banner'],
}),
})
);
});
it('sends both darkMode and hideSelectors together', async () => {
const mockArrayBuffer = new ArrayBuffer(100);
mockFetch.mockResolvedValueOnce({
ok: true,
arrayBuffer: () => Promise.resolve(mockArrayBuffer),
});
await snap.capture('https://example.com', {
darkMode: true,
hideSelectors: ['.ads', '.tracking'],
format: 'png',
});
expect(mockFetch).toHaveBeenCalledWith(
'https://snapapi.eu/v1/screenshot',
expect.objectContaining({
body: JSON.stringify({
url: 'https://example.com',
darkMode: true,
hideSelectors: ['.ads', '.tracking'],
format: 'png',
}),
})
);
});
it('works with ScreenshotOptions object form', async () => {
const mockArrayBuffer = new ArrayBuffer(100);
mockFetch.mockResolvedValueOnce({

View file

@ -32,6 +32,8 @@ class ScreenshotOptions:
device_scale: Optional[float] = None
delay: Optional[int] = None
wait_until: Optional[str] = None
dark_mode: Optional[bool] = None
hide_selectors: Optional[list] = None
def to_dict(self) -> dict:
"""Convert to API request body (camelCase keys)."""
@ -46,6 +48,8 @@ class ScreenshotOptions:
"device_scale": "deviceScale",
"delay": "delay",
"wait_until": "waitUntil",
"dark_mode": "darkMode",
"hide_selectors": "hideSelectors",
}
return {
mapping[k]: v
@ -98,6 +102,8 @@ class SnapAPI:
device_scale: Optional[float] = None,
delay: Optional[int] = None,
wait_until: Optional[str] = None,
dark_mode: Optional[bool] = None,
hide_selectors: Optional[list] = None,
options: Optional[ScreenshotOptions] = None,
) -> bytes:
"""Capture a screenshot.
@ -113,6 +119,8 @@ class SnapAPI:
device_scale: Device pixel ratio (1-3).
delay: Extra delay in ms (0-5000).
wait_until: Load event (domcontentloaded, load, networkidle0, networkidle2).
dark_mode: Emulate dark mode (prefers-color-scheme: dark).
hide_selectors: CSS selectors to hide before capture (max 10, each max 200 chars).
options: ScreenshotOptions object (alternative to keyword args).
Returns:
@ -141,6 +149,13 @@ class SnapAPI:
full_page=True,
device_scale=2,
)
# Dark mode with hidden elements
dark_screenshot = snap.capture(
"https://example.com",
dark_mode=True,
hide_selectors=['.ads', '.popup', '.cookie-banner']
)
"""
if options:
body = options.to_dict()
@ -160,6 +175,8 @@ class SnapAPI:
device_scale=device_scale,
delay=delay,
wait_until=wait_until,
dark_mode=dark_mode,
hide_selectors=hide_selectors,
)
body = opts.to_dict()