Add clip parameter for viewport cropping
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled

- Add clip object parameter to crop rectangular areas from screenshots
- Support POST body: clip {x, y, width, height} number fields
- Support GET query: clipX, clipY, clipW, clipH params
- Validation: all fields required, x/y >=0, width/height >0, max 3840x2160
- Mutually exclusive with fullPage and selector
- Update OpenAPI docs with clip examples
- Update Node.js and Python SDK READMEs with clip usage
- Add comprehensive test coverage (11 new tests)
- Tests: missing fields, negative coords, zero dimensions, max limits, mutual exclusivity
This commit is contained in:
SnapAPI Developer 2026-03-05 15:13:12 +01:00
parent 9290c759da
commit 3e9336ae67
4 changed files with 109 additions and 0 deletions

View file

@ -95,6 +95,30 @@ export const screenshotRouter = Router();
* maxLength: 500
* description: Custom User-Agent string for HTTP requests (max 500 chars, no newlines allowed)
* example: "Mozilla/5.0 (compatible; SnapAPI/1.0)"
* clip:
* type: object
* description: Crop a rectangular area from the screenshot (mutually exclusive with fullPage and selector)
* required: [x, y, width, height]
* properties:
* x:
* type: integer
* minimum: 0
* description: X coordinate of the top-left corner (pixels)
* y:
* type: integer
* minimum: 0
* description: Y coordinate of the top-left corner (pixels)
* width:
* type: integer
* minimum: 1
* maximum: 3840
* description: Width of the clipping area (pixels)
* height:
* type: integer
* minimum: 1
* maximum: 2160
* description: Height of the clipping area (pixels)
* example: { "x": 100, "y": 50, "width": 800, "height": 600 }
* hideSelectors:
* oneOf:
* - type: string
@ -128,6 +152,9 @@ export const screenshotRouter = Router();
* custom_user_agent:
* summary: Custom User-Agent
* value: { "url": "https://example.com", "userAgent": "Mozilla/5.0 (compatible; SnapAPI/1.0)" }
* clipped:
* summary: Crop a specific area
* value: { "url": "https://example.com", "clip": { "x": 100, "y": 50, "width": 800, "height": 600 } }
* responses:
* 200:
* description: Screenshot image binary
@ -288,6 +315,36 @@ export const screenshotRouter = Router();
* maxLength: 500
* description: Custom User-Agent string for HTTP requests (max 500 chars, no newlines allowed)
* example: "Mozilla/5.0 (compatible; SnapAPI/1.0)"
* - name: clipX
* in: query
* schema:
* type: integer
* minimum: 0
* description: X coordinate of the clipping area (pixels, used with clipY, clipW, clipH - mutually exclusive with fullPage and selector)
* example: 100
* - name: clipY
* in: query
* schema:
* type: integer
* minimum: 0
* description: Y coordinate of the clipping area (pixels, used with clipX, clipW, clipH)
* example: 50
* - name: clipW
* in: query
* schema:
* type: integer
* minimum: 1
* maximum: 3840
* description: Width of the clipping area (pixels, used with clipX, clipY, clipH)
* example: 800
* - name: clipH
* in: query
* schema:
* type: integer
* minimum: 1
* maximum: 2160
* description: Height of the clipping area (pixels, used with clipX, clipY, clipW)
* example: 600
* - name: darkMode
* in: query
* schema:

View file

@ -99,6 +99,11 @@ export async function takeScreenshot(opts: ScreenshotOptions): Promise<Screensho
throw new Error("selector and fullPage are mutually exclusive");
}
// Check mutual exclusivity of clip with fullPage and selector
if (opts.clip && (opts.fullPage || opts.selector)) {
throw new Error("clip is mutually exclusive with fullPage and selector");
}
const format = opts.format || "png";
const width = Math.min(opts.width || 1280, MAX_WIDTH);
const height = Math.min(opts.height || 800, MAX_HEIGHT);
@ -164,6 +169,7 @@ export async function takeScreenshot(opts: ScreenshotOptions): Promise<Screensho
screenshotOpts.fullPage = fullPage;
}
if (quality !== undefined) screenshotOpts.quality = quality;
if (opts.clip) screenshotOpts.clip = opts.clip;
let result: any;
if ((opts as any).selector) {