Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@zumer/snapdom-plugins

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@zumer/snapdom-plugins

Official plugins for SnapDOM

latest
Source
npmnpm
Version
2.2.0
Version published
Maintainers
1
Created
Source

@zumer/snapdom-plugins

Official plugins for SnapDOM — extend and transform DOM captures with zero core changes.

Install

npm install @zumer/snapdom-plugins

Usage

Import plugins individually (recommended for tree-shaking):

import { snapdom } from '@zumer/snapdom';
import { filter } from '@zumer/snapdom-plugins/filter';
import { timestampOverlay } from '@zumer/snapdom-plugins/timestamp-overlay';

const result = await snapdom(element, {
  plugins: [filter({ preset: 'grayscale' }), timestampOverlay()]
});
const png = await result.toPng();

Or import everything at once:

import { filter, timestampOverlay, replaceText } from '@zumer/snapdom-plugins';

CDN (no install):

import { snapdom } from 'https://esm.sh/@zumer/snapdom';
import { filter } from 'https://esm.sh/@zumer/snapdom-plugins/filter';

Plugins

filter

Applies CSS filter effects to the captured clone.

import { filter } from '@zumer/snapdom-plugins/filter';

snapdom(el, { plugins: [filter({ preset: 'grayscale' })] });
OptionTypeDefaultDescription
presetstring'grayscale' | 'sepia' | 'blur' | 'invert' | 'vintage' | 'dramatic'
filterstring''Raw CSS filter string, e.g. 'blur(2px) contrast(1.2)'

timestamp-overlay

Adds a translucent timestamp label to the captured clone.

import { timestampOverlay } from '@zumer/snapdom-plugins/timestamp-overlay';

snapdom(el, { plugins: [timestampOverlay({ position: 'top-right', format: 'date' })] });
OptionTypeDefaultDescription
formatstring | function'datetime''datetime' | 'date' | 'time' | 'iso' | custom (Date) => string
positionstring'bottom-right''top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
backgroundstring'rgba(0,0,0,0.6)'Label background color
colorstring'#fff'Label text color
fontSizenumber11Font size in px

replace-text

Find-and-replace text in the captured clone. Supports strings and regex.

import { replaceText } from '@zumer/snapdom-plugins/replace-text';

snapdom(el, {
  plugins: [replaceText({
    replacements: [
      { find: 'DRAFT', replace: 'APPROVED' },
      { find: /\d{4}-\d{2}-\d{2}/, replace: '[REDACTED]' }
    ]
  })]
});
OptionTypeDefaultDescription
replacementsArray<{ find: string|RegExp, replace: string }>[]List of find/replace pairs

color-tint

Tints the entire capture to a specified color using a mix-blend-mode overlay.

import { colorTint } from '@zumer/snapdom-plugins/color-tint';

snapdom(el, { plugins: [colorTint({ color: 'royalblue', opacity: 0.4 })] });
OptionTypeDefaultDescription
colorstring'red'Any CSS color value
opacitynumber1Overlay opacity (0–1)

ascii-export

Adds a toAscii() export method that converts captures to ASCII art.

import { asciiExport } from '@zumer/snapdom-plugins/ascii-export';

const result = await snapdom(el, { plugins: [asciiExport({ width: 100 })] });
const art = await result.toAscii();
console.log(art);
OptionTypeDefaultDescription
widthnumber80Character width of output
charsetstring' .:-=+*#%@'Characters from lightest to darkest
invertbooleanfalseInvert luminance mapping

pdf-image

Exports the capture as a PNG embedded in a downloadable PDF (A4). Adds a toPdfImage() method.

import { pdfImage } from '@zumer/snapdom-plugins/pdf-image';

const result = await snapdom(el, { plugins: [pdfImage({ orientation: 'landscape' })] });
await result.toPdfImage(); // triggers download
OptionTypeDefaultDescription
orientationstring'portrait''portrait' | 'landscape'
qualitynumber0.92JPEG quality (0–1)
filenamestring'capture.pdf'Download filename

html-in-canvas

Uses the experimental WICG drawElementImage API for direct DOM-to-canvas rendering where supported. Falls back gracefully.

import { htmlInCanvas } from '@zumer/snapdom-plugins/html-in-canvas';

snapdom(el, { plugins: [htmlInCanvas()] });

This plugin uses an experimental browser API and may not work in all environments.

agent-map

Produces a Set-of-Mark package for visual agents: an annotated screenshot with numbered badges on interactive elements, plus a compact JSON map from badge index → role / accessible name / bbox / state. One call, fully client-side.

import { agentMap } from '@zumer/snapdom-plugins/agent-map';

const result = await snapdom(el, { plugins: [agentMap()] });
const { image, map, dimensions } = await result.toAgentMap();

// image: data URL of the screenshot with numbered red badges overlaid
// map:   [{ i, n, r, b, s? }, …] — index, name, role, bbox, state
// Agent says "click element 2" → map[2].b gives [x, y, w, h]

Map entry shape (default fields: 'minimal'):

KeyTypeDescription
inumberIndex matching the badge drawn on the image
nstringAccessible name (aria-label → labelledby → alt → title → labels → textContent, truncated to 60 chars)
rstringARIA-style role (button, link, checkbox, radio, textbox, combobox, slider, heading, …) — derived from role attribute or implicit role of the element
b[x, y, w, h]Bounding box in pixels, scaled against maxImageWidth
sobject?State: included only when at least one key is meaningful — checked, disabled, focus, expanded, pressed, selected, value, open, selectedText, covered

Example map for a checkout form:

[
  { i: 0, n: 'Email',         r: 'textbox',  b: [28,  80, 280, 34], s: { value: 'ada@example.com' } },
  { i: 1, n: 'Send product updates', r: 'checkbox', b: [28, 134,  13, 13], s: { checked: true } },
  { i: 2, n: 'Apply coupon',  r: 'button',   b: [28, 176, 114, 38], s: { expanded: false } },
  { i: 3, n: 'Remove coupon', r: 'button',   b: [150, 176, 140, 38], s: { disabled: true } },
  { i: 4, n: 'Pay $53.90',    r: 'button',   b: [28, 220,  97, 38] }
]

Options

OptionTypeDefaultDescription
image'annotated' | 'raw' | false'annotated''annotated' overlays numbered badges; 'raw' skips badges; false skips image generation entirely (no canvas draw, no toDataURL — cheapest path).
fields'minimal' | 'full''minimal''full' adds t (raw text content) and a (meaningful attributes) per entry.
semanticbooleanfalseInclude non-interactive structural elements (headings, paragraphs, landmarks). Off by default — agents act on interactive.
maxImageWidthnumber1024Downscale target for the image; bboxes rescale to match.
imageFormat'png' | 'jpg' | 'webp''png'Image format (only used when image is rendered).
imageQualitynumber0.8Quality for lossy formats.
interactiveSelectorstringsee belowCSS selector for interactive elements.
semanticSelectorstringsee belowCSS selector for semantic elements (used when semantic: true).
labelStyleobject{}Override badge styles.

Defaults:

  • interactive: a[href], button, input, select, textarea, [role="button"|"link"|"tab"|"menuitem"|"checkbox"|"radio"|"switch"|"slider"|"combobox"|"textbox"], [tabindex]:not([tabindex="-1"]), summary, [contenteditable="true"]
  • semantic: h1–h6, nav, main, article, section, header, footer, figcaption, blockquote, legend, p

Per-call options override constructor options (e.g. result.toAgentMap({ image: false })).

When to use

  • Visual agents using Set-of-Mark prompting — one call gives you both the labelled image and the coordinate lookup table.
  • Computer-use / browser-agent harnesses that need click coordinates for a vision model's output.
  • Visual QA with an LLM judge — compare before/after captures with structured element identity.
  • Dataset generation for vision-LLM fine-tuning — (image, map) pairs.

Because it runs entirely in the browser, it works in contexts where Playwright / Puppeteer can't: Chrome extensions, SaaS web apps capturing the user's own page, Electron apps capturing their own window.

html-export

Adds a toHtml() export that returns the capture as a self-contained, re-renderable HTML document (clone + inlined styles/fonts) instead of pixels. It unwraps the SVG <foreignObject> SnapDOM already produced, so the markup and CSS match the capture byte-for-byte — nothing is rasterized.

import { htmlExport } from '@zumer/snapdom-plugins/html-export';

const result = await snapdom(el, { plugins: [htmlExport()] });
const html = await result.toHtml();              // full <!DOCTYPE html> string

// Or download a .html file:
await result.toHtml({ download: 'snapshot.html' });
OptionTypeDefaultDescription
fullDocumentbooleantrueWrap output in <!DOCTYPE html>…; if false, return just <style> + the fragment
filenamestring'capture.html'Download filename when opts.download is true

Per-call opts: download (boolean \| stringtrue triggers download, a string sets the filename), plus fullDocument / filename overrides. Returns the HTML string.

gif-export

Adds a toGif() export that records an animated GIF by re-capturing the live element over time and encoding the frames. The GIF89a encoder (median-cut quantization + LZW) is built in — no dependencies. Returns a Blob (image/gif).

import { gifExport } from '@zumer/snapdom-plugins/gif-export';

const result = await snapdom(el, { plugins: [gifExport({ fps: 12, duration: 3000 })] });
const blob = await result.toGif();

// Or download directly:
await result.toGif({ download: 'animation.gif' });
OptionTypeDefaultDescription
fpsnumber10Frames per second
durationnumber2000Total duration in ms (ignored if frames is set)
framesnumberExplicit frame count (overrides duration)
maxColorsnumber256Palette size per frame (2–256)
backgroundstring'#ffffff'Color composited under transparent pixels
scalenumber1Capture scale
repeatnumber0Loop count (0 = forever, -1 = play once)
filenamestring'capture.gif'Download filename

Per-call opts override any constructor option, plus download (boolean \| string). Each frame is captured live, so CSS animations / dynamic content are recorded as they play.

video-export

Adds a toMp4() export that records a video by re-capturing the live element over time and encoding the frames with the native MediaRecorder. Returns a Blob whose type reflects what was actually produced.

import { videoExport } from '@zumer/snapdom-plugins/video-export';

const result = await snapdom(el, { plugins: [videoExport({ fps: 30, duration: 4000 })] });
const blob = await result.toMp4();

// Or download directly:
await result.toMp4({ download: true });

Codec reality: MediaRecorder output depends on the browser. Safari produces MP4 (H.264); Chromium typically produces WebM (VP8/VP9). When MP4 isn't supported the plugin falls back to WebM, warns in the console, and the downloaded file extension is set to .mp4 / .webm accordingly.

OptionTypeDefaultDescription
fpsnumber10Frames per second
durationnumber2000Total duration in ms (ignored if frames is set)
framesnumberExplicit frame count (overrides duration)
backgroundstring'#ffffff'Color composited under transparent pixels
scalenumber1Capture scale
bitratenumbervideoBitsPerSecond passed to MediaRecorder
filenamestringDownload filename (extension auto-set to .mp4 / .webm)

Requires MediaRecorder (unavailable in some headless environments). Per-call opts override any constructor option, plus download (boolean \| string).

Plugin registration

Global (applies to all captures):

import { snapdom } from '@zumer/snapdom';
import { filter } from '@zumer/snapdom-plugins/filter';

snapdom.plugins(filter({ preset: 'sepia' }));

Per-capture (overrides global for that call):

const result = await snapdom(element, {
  plugins: [filter({ preset: 'dramatic' })]
});

Per-capture plugins run before global ones. Duplicate plugin names are skipped automatically.

Build your own plugin

Use the template to scaffold a new plugin in seconds:

npx degit zumerlab/snapdom/packages/plugin-template my-plugin

See PLUGIN_SPEC.md for the full hook specification and CONTRIBUTING_PLUGINS.md to get your plugin listed on the community page.

License

MIT

Keywords

snapdom

FAQs

Package last updated on 29 May 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts