@zumer/snapdom-plugins 2.2.2 → 3.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +192 -50
- package/agent-map.d.ts +31 -0
- package/agent-map.js +248 -88
- package/ascii-export.d.ts +20 -0
- package/ascii-export.js +17 -12
- package/capture-frames.js +85 -0
- package/color-tint.d.ts +18 -0
- package/color-tint.js +7 -4
- package/context-export.d.ts +25 -0
- package/context-export.js +283 -0
- package/filter.d.ts +17 -0
- package/filter.js +10 -3
- package/gif-export.d.ts +29 -0
- package/gif-export.js +15 -21
- package/html-export.d.ts +31 -0
- package/html-export.js +62 -16
- package/index.d.ts +19 -0
- package/index.js +2 -1
- package/package.json +11 -4
- package/pdf-image.d.ts +18 -0
- package/pdf-image.js +14 -14
- package/privacy-policy.js +55 -0
- package/redact-clone.js +150 -0
- package/redact-inputs.d.ts +43 -0
- package/redact-inputs.js +175 -0
- package/replace-text.d.ts +20 -0
- package/replace-text.js +6 -0
- package/timestamp-overlay.d.ts +19 -0
- package/timestamp-overlay.js +4 -1
- package/video-export.d.ts +28 -0
- package/video-export.js +70 -52
- package/html-in-canvas.js +0 -139
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ZumerLab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
# @zumer/snapdom-plugins
|
|
2
2
|
|
|
3
|
-
Official plugins for [SnapDOM](https://github.com/zumerlab/snapdom)
|
|
3
|
+
Official plugins for [SnapDOM](https://github.com/zumerlab/snapdom), a capture engine for
|
|
4
|
+
web apps. Core exports images and canvases; this package adds HTML, structured context,
|
|
5
|
+
element maps, PDF, ASCII, visual transforms and live GIF/video recording.
|
|
6
|
+
|
|
7
|
+
Image, HTML and structured exports use captured state; GIF and video start a sequence of
|
|
8
|
+
live captures when their export method is called.
|
|
4
9
|
|
|
5
10
|
## Install
|
|
6
11
|
|
|
12
|
+
Install the plugins next to a matching core:
|
|
13
|
+
|
|
7
14
|
```bash
|
|
8
|
-
npm install @zumer/snapdom-plugins
|
|
15
|
+
npm install @zumer/snapdom @zumer/snapdom-plugins
|
|
9
16
|
```
|
|
10
17
|
|
|
18
|
+
Plugins 3.x need a 3.x core; npm refuses to install them next to an older core.
|
|
19
|
+
|
|
11
20
|
## Usage
|
|
12
21
|
|
|
13
22
|
Import plugins individually (recommended for tree-shaking):
|
|
@@ -23,7 +32,7 @@ const result = await snapdom(element, {
|
|
|
23
32
|
const png = await result.toPng();
|
|
24
33
|
```
|
|
25
34
|
|
|
26
|
-
Or
|
|
35
|
+
Or use the package entry point:
|
|
27
36
|
|
|
28
37
|
```js
|
|
29
38
|
import { filter, timestampOverlay, replaceText } from '@zumer/snapdom-plugins';
|
|
@@ -42,7 +51,8 @@ import { filter } from 'https://esm.sh/@zumer/snapdom-plugins/filter';
|
|
|
42
51
|
|
|
43
52
|
### `filter`
|
|
44
53
|
|
|
45
|
-
Applies CSS filter effects to the
|
|
54
|
+
Applies CSS filter effects to the clone, overriding authored filters. `preset` takes
|
|
55
|
+
precedence over `filter`; an unknown preset logs a warning and `filter` applies.
|
|
46
56
|
|
|
47
57
|
```js
|
|
48
58
|
import { filter } from '@zumer/snapdom-plugins/filter';
|
|
@@ -59,7 +69,7 @@ snapdom(el, { plugins: [filter({ preset: 'grayscale' })] });
|
|
|
59
69
|
|
|
60
70
|
### `timestamp-overlay`
|
|
61
71
|
|
|
62
|
-
Adds a
|
|
72
|
+
Adds a timestamp label. The clock or custom formatter runs for every capture.
|
|
63
73
|
|
|
64
74
|
```js
|
|
65
75
|
import { timestampOverlay } from '@zumer/snapdom-plugins/timestamp-overlay';
|
|
@@ -79,7 +89,8 @@ snapdom(el, { plugins: [timestampOverlay({ position: 'top-right', format: 'date'
|
|
|
79
89
|
|
|
80
90
|
### `replace-text`
|
|
81
91
|
|
|
82
|
-
|
|
92
|
+
Replaces DOM text in the clone using strings or regular expressions. Stylesheet and script
|
|
93
|
+
text are left unchanged.
|
|
83
94
|
|
|
84
95
|
```js
|
|
85
96
|
import { replaceText } from '@zumer/snapdom-plugins/replace-text';
|
|
@@ -100,9 +111,85 @@ snapdom(el, {
|
|
|
100
111
|
|
|
101
112
|
---
|
|
102
113
|
|
|
114
|
+
### `redact-inputs`
|
|
115
|
+
|
|
116
|
+
Masks input and textarea values, excludes selected blocks, and removes named attributes
|
|
117
|
+
from captured outputs. Core already masks password fields to match their visible bullets;
|
|
118
|
+
other field values remain visible unless you redact them. The live page stays unchanged.
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
import { redactInputs } from '@zumer/snapdom-plugins/redact-inputs';
|
|
122
|
+
|
|
123
|
+
// Defaults: email, tel, and the cc-* / current-password / new-password / one-time-code
|
|
124
|
+
// autocomplete tokens.
|
|
125
|
+
snapdom(el, { plugins: [redactInputs()] });
|
|
126
|
+
|
|
127
|
+
// Everything, including textareas:
|
|
128
|
+
snapdom(el, { plugins: [redactInputs({ all: true })] });
|
|
129
|
+
|
|
130
|
+
// App-specific fields, blanked rather than bulleted:
|
|
131
|
+
snapdom(el, { plugins: [redactInputs({ selector: '[data-private]', mask: () => '' })] });
|
|
132
|
+
|
|
133
|
+
// Exclude whole blocks and remove metadata attributes:
|
|
134
|
+
const redaction = redactInputs({
|
|
135
|
+
blocks: ['.private-panel', '[data-private-block]'],
|
|
136
|
+
attributes: [
|
|
137
|
+
{ selector: '[data-token]', names: ['data-token'] },
|
|
138
|
+
{ selector: '.customer', names: ['title', 'aria-label'] }
|
|
139
|
+
]
|
|
140
|
+
});
|
|
141
|
+
snapdom(el, { plugins: [redaction] });
|
|
142
|
+
|
|
143
|
+
// Remove those blocks from the layout as well:
|
|
144
|
+
snapdom(el, { excludeMode: 'remove', plugins: [redaction] });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The default mask keeps the string's length. Glyph widths can differ, so wrapping and truncation may change.
|
|
148
|
+
A `<select>` is not masked (its visible option label stays); hide it with a `blocks` rule instead.
|
|
149
|
+
|
|
150
|
+
| Option | Type | Default | Description |
|
|
151
|
+
|--------|------|---------|-------------|
|
|
152
|
+
| `types` | `string[]` | `['email', 'tel']` | Input `type` attribute values to redact |
|
|
153
|
+
| `autocomplete` | `string[]` | `['cc-*', 'current-password', 'new-password', 'one-time-code']` | Autocomplete tokens; a trailing `*` matches by prefix |
|
|
154
|
+
| `selector` | `string` | `''` | Extra CSS selector for inputs and textareas |
|
|
155
|
+
| `all` | `boolean` | `false` | Redact every input and textarea, regardless of `types`, `autocomplete` or `selector` |
|
|
156
|
+
| `mask` | `(value, el) => string` | same-length bullets | Custom masker |
|
|
157
|
+
| `blocks` | `string \| string[]` | `[]` | CSS selectors for whole subtrees to exclude; uses the capture's `excludeMode` |
|
|
158
|
+
| `attributes` | `Array<{ selector: string, names: string[] }>` | `[]` | Remove exact named attributes from clones of matching source elements and their semantic projections; names are not wildcard patterns |
|
|
159
|
+
|
|
160
|
+
`blocks` keeps an invisible space by default (`excludeMode: 'hide'`); `'remove'` drops the
|
|
161
|
+
subtree from the captured layout. It combines with the capture's existing `exclude` rules.
|
|
162
|
+
If the capture root itself belongs to a blocked subtree, `'remove'` throws; use `'hide'`
|
|
163
|
+
for that capture. Selectors match source elements, including nodes in open shadow trees.
|
|
164
|
+
|
|
165
|
+
`attributes` removes the listed DOM attributes and their projections in `agent-map` and
|
|
166
|
+
`context-export`, such as accessible names or structured state. It does not erase copies
|
|
167
|
+
of those values already materialized as visible text, CSS `content`, or bitmap pixels.
|
|
168
|
+
Use `blocks` to hide the subtree that paints visible content. A `value` attribute rule on
|
|
169
|
+
an input or textarea clears its displayed value and omits its structured `state.value`.
|
|
170
|
+
Existing default field masks still apply when you add block or attribute rules.
|
|
171
|
+
|
|
172
|
+
HTML exports use the sanitized clone. `agent-map` and `context-export` apply the same
|
|
173
|
+
block, attribute and field rules to their source-derived data, regardless of official
|
|
174
|
+
plugin order. The rules do not search arbitrary text or images for sensitive content.
|
|
175
|
+
|
|
176
|
+
The built-in mask without custom selectors permits reuse of unchanged captures. Custom
|
|
177
|
+
masks and nonempty `selector`, `blocks` or `attributes` rules run on every capture. Changed
|
|
178
|
+
content runs the full pipeline so no field skips redaction.
|
|
179
|
+
|
|
180
|
+
Nonempty `blocks` or `attributes` rules add a final `beforeRender` pass. In builds with the
|
|
181
|
+
experimental `engine: 'html-in-canvas'` enabled, that pass makes the capture use the SVG
|
|
182
|
+
renderer. Field-only redaction does not add this pass.
|
|
183
|
+
|
|
184
|
+
`agent-map` and `context-export` omit sensitive values and mask other typed values in their
|
|
185
|
+
structured state fields. Ordinary page text and labels remain. An attached image still
|
|
186
|
+
follows core's capture policy; use this plugin to mask its visible fields too.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
103
190
|
### `color-tint`
|
|
104
191
|
|
|
105
|
-
Tints
|
|
192
|
+
Tints an HTML capture with a `mix-blend-mode` overlay. Opacity `0` leaves it unchanged.
|
|
106
193
|
|
|
107
194
|
```js
|
|
108
195
|
import { colorTint } from '@zumer/snapdom-plugins/color-tint';
|
|
@@ -135,11 +222,14 @@ console.log(art);
|
|
|
135
222
|
| `charset` | `string` | `' .:-=+*#%@'` | Characters from lightest to darkest |
|
|
136
223
|
| `invert` | `boolean` | `false` | Invert luminance mapping |
|
|
137
224
|
|
|
225
|
+
`toAscii({ width, charset, invert })` overrides any of the three per call.
|
|
226
|
+
|
|
138
227
|
---
|
|
139
228
|
|
|
140
229
|
### `pdf-image`
|
|
141
230
|
|
|
142
|
-
|
|
231
|
+
Adds `toPdfImage()`, which downloads a single-page A4 PDF containing a JPEG of the capture.
|
|
232
|
+
Text is part of the image. The method returns a temporary object URL, revoked after five seconds.
|
|
143
233
|
|
|
144
234
|
```js
|
|
145
235
|
import { pdfImage } from '@zumer/snapdom-plugins/pdf-image';
|
|
@@ -156,23 +246,10 @@ await result.toPdfImage(); // triggers download
|
|
|
156
246
|
|
|
157
247
|
---
|
|
158
248
|
|
|
159
|
-
### `html-in-canvas`
|
|
160
|
-
|
|
161
|
-
Uses the experimental [WICG `drawElementImage`](https://github.com/WICG/canvas-place-element) API for direct DOM-to-canvas rendering where supported. Falls back gracefully.
|
|
162
|
-
|
|
163
|
-
```js
|
|
164
|
-
import { htmlInCanvas } from '@zumer/snapdom-plugins/html-in-canvas';
|
|
165
|
-
|
|
166
|
-
snapdom(el, { plugins: [htmlInCanvas()] });
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
> This plugin uses an experimental browser API and may not work in all environments.
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
249
|
### `agent-map`
|
|
174
250
|
|
|
175
|
-
|
|
251
|
+
Adds `toAgentMap()`: an image with numbered badges on interactive elements and a JSON map
|
|
252
|
+
of their names, roles, bounds and state. This format is often called Set-of-Mark.
|
|
176
253
|
|
|
177
254
|
```js
|
|
178
255
|
import { agentMap } from '@zumer/snapdom-plugins/agent-map';
|
|
@@ -181,7 +258,7 @@ const result = await snapdom(el, { plugins: [agentMap()] });
|
|
|
181
258
|
const { image, map, dimensions } = await result.toAgentMap();
|
|
182
259
|
|
|
183
260
|
// image: data URL of the screenshot with numbered red badges overlaid
|
|
184
|
-
// map: [{ i, n, r, b, s? }, …]
|
|
261
|
+
// map: [{ i, n, r, b, s? }, …]: index, name, role, bbox, state
|
|
185
262
|
// Agent says "click element 2" → map[2].b gives [x, y, w, h]
|
|
186
263
|
```
|
|
187
264
|
|
|
@@ -190,16 +267,17 @@ Map entry shape (default `fields: 'minimal'`):
|
|
|
190
267
|
| Key | Type | Description |
|
|
191
268
|
|-----|------|-------------|
|
|
192
269
|
| `i` | `number` | Index matching the badge drawn on the image |
|
|
193
|
-
| `n` | `string` | Accessible name
|
|
194
|
-
| `r` | `string` |
|
|
195
|
-
| `b` | `[x, y, w, h]` | Bounding box in pixels, scaled
|
|
196
|
-
| `s` | `object?` |
|
|
270
|
+
| `n` | `string` | Accessible name: aria-label → labelledby → alt → title → labels → textContent. The textContent fallback is capped at 60 characters |
|
|
271
|
+
| `r` | `string` | Explicit `role` or an implicit role such as `button`, `link`, `checkbox`, `radio`, `textbox`, `combobox`, `slider` or `heading` |
|
|
272
|
+
| `b` | `[x, y, w, h]` | Bounding box in output pixels, scaled with the image and `maxImageWidth` |
|
|
273
|
+
| `s` | `object?` | Meaningful state: `checked`, `disabled`, `focus`, `expanded`, `pressed`, `selected`, `value`, `hasValue`, `open`, `selectedText`, `covered` |
|
|
274
|
+
| `isSemanticOnly` | `true?` | Structural entry added by `semantic: true`; no badge is drawn for it |
|
|
197
275
|
|
|
198
276
|
Example map for a checkout form:
|
|
199
277
|
|
|
200
278
|
```js
|
|
201
279
|
[
|
|
202
|
-
{ i: 0, n: 'Email', r: 'textbox', b: [28, 80, 280, 34]
|
|
280
|
+
{ i: 0, n: 'Email', r: 'textbox', b: [28, 80, 280, 34] },
|
|
203
281
|
{ i: 1, n: 'Send product updates', r: 'checkbox', b: [28, 134, 13, 13], s: { checked: true } },
|
|
204
282
|
{ i: 2, n: 'Apply coupon', r: 'button', b: [28, 176, 114, 38], s: { expanded: false } },
|
|
205
283
|
{ i: 3, n: 'Remove coupon', r: 'button', b: [150, 176, 140, 38], s: { disabled: true } },
|
|
@@ -211,36 +289,87 @@ Example map for a checkout form:
|
|
|
211
289
|
|
|
212
290
|
| Option | Type | Default | Description |
|
|
213
291
|
|--------|------|---------|-------------|
|
|
214
|
-
| `image` | `'annotated' \| 'raw' \| false` | `'annotated'` |
|
|
292
|
+
| `image` | `'annotated' \| 'raw' \| false` | `'annotated'` | Annotated image, raw image or map only. `false` skips this export's image generation; see `needs` to skip capture rendering too |
|
|
215
293
|
| `fields` | `'minimal' \| 'full'` | `'minimal'` | `'full'` adds `t` (raw text content) and `a` (meaningful attributes) per entry. |
|
|
216
|
-
| `semantic` | `boolean` | `false` | Include
|
|
294
|
+
| `semantic` | `boolean` | `false` | Include structural elements such as headings, paragraphs and landmarks |
|
|
217
295
|
| `maxImageWidth` | `number` | `1024` | Downscale target for the image; bboxes rescale to match. |
|
|
218
296
|
| `imageFormat` | `'png' \| 'jpg' \| 'webp'` | `'png'` | Image format (only used when image is rendered). |
|
|
219
297
|
| `imageQuality` | `number` | `0.8` | Quality for lossy formats. |
|
|
220
298
|
| `interactiveSelector` | `string` | see below | CSS selector for interactive elements. |
|
|
221
299
|
| `semanticSelector` | `string` | see below | CSS selector for semantic elements (used when `semantic: true`). |
|
|
222
300
|
| `labelStyle` | `object` | `{}` | Override badge styles. |
|
|
301
|
+
| `needs` | `'clone' \| 'render'` | `'render'` | Use `'clone'` with `image: false` to skip rendering; only valid on a per-capture plugin |
|
|
223
302
|
|
|
224
303
|
Defaults:
|
|
225
|
-
- **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"]`
|
|
226
|
-
- **semantic**: `h1–h6, nav, main, article, section, header, footer, figcaption, blockquote, legend, p`
|
|
227
304
|
|
|
228
|
-
|
|
305
|
+
- Interactive elements: links with `href`, buttons, inputs, selects, textareas, summaries,
|
|
306
|
+
`[contenteditable="true"]`, and elements with `tabindex` other than `-1`. Also matches
|
|
307
|
+
roles `button`, `link`, `tab`, `menuitem`, `checkbox`, `radio`, `switch`, `slider`, `combobox`, `textbox`.
|
|
308
|
+
- Structural elements: `h1`–`h6`, `nav`, `main`, `article`, `section`, `header`, `footer`,
|
|
309
|
+
`figcaption`, `blockquote`, `legend`, `p`.
|
|
310
|
+
|
|
311
|
+
Per-call overrides: `image`, `imageFormat`, `imageQuality`, `maxImageWidth`, plus image sizing
|
|
312
|
+
(`width`, `height`, `scale`, `dpr`) and `backgroundColor`. Selectors, fields, semantics and badge
|
|
313
|
+
styles are frozen during capture. For a map without rendering:
|
|
314
|
+
|
|
315
|
+
```js
|
|
316
|
+
const result = await snapdom(el, { plugins: [agentMap({ image: false, needs: 'clone' })] });
|
|
317
|
+
const { map, dimensions } = await result.toAgentMap();
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Such a result has no image; `result.url` and image exports throw. The map includes open shadow
|
|
321
|
+
roots and slotted content and respects core's exclusion policy. Sensitive input values are
|
|
322
|
+
omitted; other input/textarea values become up to 12 bullets. This does not redact the image.
|
|
323
|
+
|
|
324
|
+
Badges are applied only to `toAgentMap({ image: 'annotated' })`; raw images and other exports
|
|
325
|
+
keep the captured page. Bounding boxes are not exact with perspective, rotated ancestors,
|
|
326
|
+
or a root rotation stripped by `outerTransforms: false`.
|
|
229
327
|
|
|
230
328
|
#### When to use
|
|
231
329
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
330
|
+
Use it for visual agents that need element coordinates, visual QA, or image/map datasets.
|
|
331
|
+
It runs inside the web app, including browser extensions and Electron pages, without
|
|
332
|
+
requiring an external browser automation process.
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
### `context-export`
|
|
337
|
+
|
|
338
|
+
Adds `toContext()`: the captured UI as a text outline or JSON tree containing structure,
|
|
339
|
+
roles, visible text, state and optional bounds. It skips hidden content and collapses
|
|
340
|
+
wrappers that add no information.
|
|
341
|
+
|
|
342
|
+
```js
|
|
343
|
+
import { contextExport } from '@zumer/snapdom-plugins/context-export';
|
|
344
|
+
|
|
345
|
+
const result = await snapdom(el, { plugins: [contextExport()] });
|
|
346
|
+
const outline = await result.toContext();
|
|
347
|
+
const { root, truncated, nodes } = await result.toContext({ format: 'json' });
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
| Option | Type | Default | Description |
|
|
351
|
+
|--------|------|---------|-------------|
|
|
352
|
+
| `format` | `'outline' \| 'json'` | `'outline'` | Text outline or `{ root, truncated, nodes }` |
|
|
353
|
+
| `maxTextLength` | `number` | `120` | Per-node text limit; longer text ends with an ellipsis |
|
|
354
|
+
| `maxNodes` | `number` | `800` | Node limit; output reports truncation |
|
|
355
|
+
| `geometry` | `boolean` | `true` | Include `[x, y, width, height]` relative to the capture root |
|
|
356
|
+
| `needs` | `'clone' \| 'render'` | `'render'` | `'clone'` skips rendering; only valid on a per-capture plugin |
|
|
357
|
+
|
|
358
|
+
The tree is frozen during capture. Per-call options can change `format`, `maxTextLength`,
|
|
359
|
+
or omit captured geometry. They cannot increase `maxNodes` or restore geometry that was
|
|
360
|
+
not captured. Open shadow roots and slots follow core's exclusion policy. Sensitive input
|
|
361
|
+
values are omitted; other input/textarea values are masked. Ordinary page text and attributes
|
|
362
|
+
remain in the output.
|
|
236
363
|
|
|
237
|
-
|
|
364
|
+
Use `contextExport({ needs: 'clone' })` when you only need structured output. That result
|
|
365
|
+
has no image, so `result.url` and image exports throw.
|
|
238
366
|
|
|
239
367
|
---
|
|
240
368
|
|
|
241
369
|
### `html-export`
|
|
242
370
|
|
|
243
|
-
Adds
|
|
371
|
+
Adds `toHtml()`, which returns the frozen clone with its captured styles and fonts as an
|
|
372
|
+
HTML document. You can reopen the captured layout without exporting it as a bitmap.
|
|
244
373
|
|
|
245
374
|
```js
|
|
246
375
|
import { htmlExport } from '@zumer/snapdom-plugins/html-export';
|
|
@@ -257,13 +386,17 @@ await result.toHtml({ download: 'snapshot.html' });
|
|
|
257
386
|
| `fullDocument` | `boolean` | `true` | Wrap output in `<!DOCTYPE html>…`; if `false`, return just `<style>` + the fragment |
|
|
258
387
|
| `filename` | `string` | `'capture.html'` | Download filename when `opts.download` is `true` |
|
|
259
388
|
|
|
260
|
-
Per-call `opts
|
|
389
|
+
Per-call `opts` accepts `fullDocument`, `filename` and `download`. Set `download: true` to
|
|
390
|
+
download, or pass a filename string. The method returns the HTML string either way.
|
|
391
|
+
|
|
392
|
+
The document retains source markup, including event-handler attributes; it is not sanitized.
|
|
261
393
|
|
|
262
394
|
---
|
|
263
395
|
|
|
264
396
|
### `gif-export`
|
|
265
397
|
|
|
266
|
-
Adds
|
|
398
|
+
Adds `toGif()`, which records the live element and returns an `image/gif` Blob. Its built-in
|
|
399
|
+
GIF89a encoder uses median-cut quantization and LZW, with no encoding dependency.
|
|
267
400
|
|
|
268
401
|
```js
|
|
269
402
|
import { gifExport } from '@zumer/snapdom-plugins/gif-export';
|
|
@@ -282,17 +415,20 @@ await result.toGif({ download: 'animation.gif' });
|
|
|
282
415
|
| `frames` | `number` | — | Explicit frame count (overrides `duration`) |
|
|
283
416
|
| `maxColors` | `number` | `256` | Palette size per frame (2–256) |
|
|
284
417
|
| `background` | `string` | `'#ffffff'` | Color composited under transparent pixels |
|
|
285
|
-
| `scale` | `number` |
|
|
418
|
+
| `scale` | `number` | capture's scale | Capture scale |
|
|
286
419
|
| `repeat` | `number` | `0` | Loop count (`0` = forever, `-1` = play once) |
|
|
287
420
|
| `filename` | `string` | `'capture.gif'` | Download filename |
|
|
288
421
|
|
|
289
|
-
Per-call `opts` override
|
|
422
|
+
Per-call `opts` override constructor options and accept `download` (`boolean \| string`).
|
|
423
|
+
Recording starts when `toGif()` runs, including its first frame. It preserves the original
|
|
424
|
+
capture policy, including exclusions and plugins. Frame capture speed limits the sampling rate.
|
|
290
425
|
|
|
291
426
|
---
|
|
292
427
|
|
|
293
428
|
### `video-export`
|
|
294
429
|
|
|
295
|
-
Adds
|
|
430
|
+
Adds `toMp4()`, which records the live element through `MediaRecorder`. The returned Blob's
|
|
431
|
+
type identifies the recorded container.
|
|
296
432
|
|
|
297
433
|
```js
|
|
298
434
|
import { videoExport } from '@zumer/snapdom-plugins/video-export';
|
|
@@ -304,7 +440,9 @@ const blob = await result.toMp4();
|
|
|
304
440
|
await result.toMp4({ download: true });
|
|
305
441
|
```
|
|
306
442
|
|
|
307
|
-
|
|
443
|
+
The plugin tries supported MP4 codecs first, then WebM. A WebM fallback logs a warning;
|
|
444
|
+
the default download filename matches the recorded `.mp4` or `.webm` container. Explicit
|
|
445
|
+
filenames and `download` strings are used as supplied.
|
|
308
446
|
|
|
309
447
|
| Option | Type | Default | Description |
|
|
310
448
|
|--------|------|---------|-------------|
|
|
@@ -312,11 +450,14 @@ await result.toMp4({ download: true });
|
|
|
312
450
|
| `duration` | `number` | `2000` | Total duration in ms (ignored if `frames` is set) |
|
|
313
451
|
| `frames` | `number` | — | Explicit frame count (overrides `duration`) |
|
|
314
452
|
| `background` | `string` | `'#ffffff'` | Color composited under transparent pixels |
|
|
315
|
-
| `scale` | `number` |
|
|
453
|
+
| `scale` | `number` | capture's scale | Capture scale |
|
|
316
454
|
| `bitrate` | `number` | — | `videoBitsPerSecond` passed to `MediaRecorder` |
|
|
317
|
-
| `filename` | `string` | — | Download filename
|
|
455
|
+
| `filename` | `string` | — | Download filename; the default follows the recorded `.mp4` / `.webm` container |
|
|
318
456
|
|
|
319
|
-
Requires `MediaRecorder` (
|
|
457
|
+
Requires `MediaRecorder` and `canvas.captureStream()`, which may be unavailable in headless
|
|
458
|
+
environments. Per-call `opts` override constructor options and accept `download`
|
|
459
|
+
(`boolean \| string`). Recording starts at the call and preserves capture options as GIF
|
|
460
|
+
recording does. MediaRecorder uses a real-time clock, so slow captures can stretch duration.
|
|
320
461
|
|
|
321
462
|
---
|
|
322
463
|
|
|
@@ -345,10 +486,11 @@ Per-capture plugins run before global ones. Duplicate plugin names are skipped a
|
|
|
345
486
|
|
|
346
487
|
## Build your own plugin
|
|
347
488
|
|
|
348
|
-
|
|
489
|
+
Start from the plugin template:
|
|
349
490
|
|
|
350
491
|
```bash
|
|
351
492
|
npx degit zumerlab/snapdom/packages/plugin-template my-plugin
|
|
493
|
+
cd my-plugin && npm install --save-dev @zumer/snapdom
|
|
352
494
|
```
|
|
353
495
|
|
|
354
496
|
See [PLUGIN_SPEC.md](../../PLUGIN_SPEC.md) for the full hook specification and [CONTRIBUTING_PLUGINS.md](../../CONTRIBUTING_PLUGINS.md) to get your plugin listed on the community page.
|
package/agent-map.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for `@zumer/snapdom-plugins/agent-map`.
|
|
3
|
+
*
|
|
4
|
+
* The plugin instance type comes from the core package: one definition of what a plugin IS,
|
|
5
|
+
* so a plugin that stops matching the host's contract fails to typecheck instead of failing
|
|
6
|
+
* at capture time.
|
|
7
|
+
*/
|
|
8
|
+
import type { SnapdomPlugin } from '@zumer/snapdom';
|
|
9
|
+
|
|
10
|
+
export interface AgentMapOptions {
|
|
11
|
+
/** Image output; 'annotated' overlays numbered badges on interactive elements. */
|
|
12
|
+
image?: 'annotated' | 'raw' | false;
|
|
13
|
+
/** Per-entry shape. */
|
|
14
|
+
fields?: 'minimal' | 'full';
|
|
15
|
+
/** Also include non-interactive semantic elements. */
|
|
16
|
+
semantic?: boolean;
|
|
17
|
+
maxImageWidth?: number;
|
|
18
|
+
imageFormat?: 'png' | 'jpg' | 'webp';
|
|
19
|
+
imageQuality?: number;
|
|
20
|
+
interactiveSelector?: string;
|
|
21
|
+
semanticSelector?: string;
|
|
22
|
+
labelStyle?: Record<string, string>;
|
|
23
|
+
/** How far the capture runs. */
|
|
24
|
+
needs?: 'clone' | 'render';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Adds `result.toAgentMap()` for visual agents: an annotated screenshot plus a structured map
|
|
29
|
+
* of the interactive elements and their bounding boxes. Published as `prompt-export` until 2.12.0 renamed it.
|
|
30
|
+
*/
|
|
31
|
+
export declare function agentMap(options?: AgentMapOptions): SnapdomPlugin;
|