"Learn More" (50,400 150x20) href="/about"
Semantic structure:
[2] "Welcome to Our Platform"
[3]
"Description text..."
```
Hooks: afterClone (extract metadata, add annotations), defineExports (register 'prompt').
---
## CREATING CUSTOM PLUGINS
### Minimal template
```js
export function myPlugin(options = {}) {
const { enabled = true } = options;
return {
name: 'my-plugin',
afterClone(ctx) {
if (!enabled) return;
// modify ctx.clone here
}
};
}
```
### Watermark example
```js
export function watermark(options = {}) {
const { text = 'DRAFT', fontSize = 48, color = 'rgba(0,0,0,0.08)', rotate = -30 } = options;
return {
name: 'watermark',
afterClone(ctx) {
const overlay = document.createElement('div');
overlay.style.cssText = `position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) rotate(${rotate}deg);font-size:${fontSize}px;color:${color};pointer-events:none;z-index:999999;white-space:nowrap;font-weight:700;`;
overlay.textContent = text;
ctx.clone.style.position = 'relative';
ctx.clone.appendChild(overlay);
}
};
}
```
### Custom export format example
```js
export function jsonExport() {
return {
name: 'json-export',
afterClone(ctx) {
ctx.__jsonMeta = {
elementCount: ctx.clone.querySelectorAll('*').length,
text: ctx.clone.textContent?.slice(0, 1000) || '',
capturedAt: new Date().toISOString()
};
},
defineExports(ctx) {
return {
json: async (ctx, opts = {}) => {
const meta = ctx.__jsonMeta || {};
if (opts.includeImage !== false) {
const img = await ctx.exports.png({ scale: opts.scale || 1 });
meta.image = img.src;
}
return JSON.stringify(meta, null, 2);
}
};
}
};
}
```
### Best practices
1. Be opt-in: zero overhead when inactive
2. Use factory pattern: always accept options with defaults
3. Modify ctx.clone, never ctx.element (after afterClone)
4. Undo live DOM changes: if mutating in beforeClone, restore in afterClone
5. Use ctx.__ prefix for custom data on context
6. Handle errors gracefully with try/catch
7. Keep dependencies minimal (ideally zero)
8. Test with scale: 2 (exposes pixel math issues)
### Publishing
Package name: snapdom-plugin-
Plugin name field: kebab-case
Peer dependency: "@zumer/snapdom": ">=2.0.0"
---
## CAPTURE PIPELINE (INTERNAL)
The capture flows through these stages:
1. INIT: createContext(options) → normalize all options with defaults
2. Safari warmup (if needed): pre-capture + canvas draw to prime font pipeline (WebKit #219770)
3. BEFORESNAP hook
4. Picture/lazy resolution: resolve and data-src on live DOM (undo after clone)
5. BEFORECLONE hook + line-clamp handling
6. DEEP CLONE: recursive clone with computed styles, Shadow DOM, pseudo-elements, CSS variables, counters, iframes, SVG