@zumer/snapdom 2.24.18 → 3.0.0-beta.1
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/README.md +219 -257
- package/dist/snapdom.js +35 -10
- package/dist/snapdom.mjs +35 -10
- package/package.json +20 -14
- package/types/snapdom.d.ts +339 -165
- package/dist/plugins.mjs +0 -7
- package/dist/preCache.mjs +0 -10
package/types/snapdom.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* TypeScript definitions (
|
|
2
|
+
* SnapDOM — browser capture engine for web interfaces
|
|
3
|
+
* TypeScript definitions (v3)
|
|
4
4
|
*
|
|
5
5
|
* Notes:
|
|
6
6
|
* - Style compression is internal (no public option).
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
7
|
+
* - Recognized icon fonts are rendered as images; `embedFonts` controls text fonts.
|
|
8
|
+
* - These are the v3 public declarations; see the migration guide for removed options.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/* =========================
|
|
@@ -16,14 +16,43 @@ export type RasterMime = "png" | "jpg" | "jpeg" | "webp";
|
|
|
16
16
|
export type BlobType = "svg" | RasterMime;
|
|
17
17
|
|
|
18
18
|
export type IconFontMatcher = string | RegExp;
|
|
19
|
-
export type CachePolicy = "disabled" | "full" | "auto" | "soft";
|
|
20
19
|
|
|
21
|
-
/**
|
|
20
|
+
/**
|
|
21
|
+
* How far a capture runs: the frozen clone, or the rendered image. Plugins declare it as
|
|
22
|
+
* `needs`; the result reports it as `needs`. A shallower "dom" stage existed and was
|
|
23
|
+
* removed: a capture that takes no clone does no capturing.
|
|
24
|
+
*/
|
|
25
|
+
export type CaptureStage = "clone" | "render";
|
|
26
|
+
/**
|
|
27
|
+
* Persistent resource/style cache policy. 'soft' is the v3 default; 'disabled' (or
|
|
28
|
+
* `cache: false`) empties and bypasses those caches for debugging. v2's 'auto' and 'full'
|
|
29
|
+
* are no longer part of the type: at runtime both map to 'soft' (see the README migration
|
|
30
|
+
* table), so a stored v2 value keeps working while new code gets the v3 vocabulary.
|
|
31
|
+
*/
|
|
32
|
+
export type CachePolicy = "soft" | "disabled";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Stable codes of the degradations a capture records in `result.warnings`. Every literal
|
|
36
|
+
* here is pushed by the runtime (grep `sessionWarn(` in src); the trailing `string & {}`
|
|
37
|
+
* keeps the union open without widening the literals away.
|
|
38
|
+
*/
|
|
39
|
+
export type WarningCode =
|
|
40
|
+
| "image-fallback"
|
|
41
|
+
| "raster-clamp"
|
|
42
|
+
| "canvas-clamp"
|
|
43
|
+
| "safari-png-fallback"
|
|
44
|
+
| "reconcile-risk"
|
|
45
|
+
| "backdrop-filter-failed"
|
|
46
|
+
| "selection-compose-failed"
|
|
47
|
+
| "shrink-failed"
|
|
48
|
+
| (string & {});
|
|
49
|
+
|
|
50
|
+
/** Geometry of the rendered capture, expressed in viewBox-style CSS pixels. */
|
|
22
51
|
export interface CaptureMeta {
|
|
23
52
|
/** Logical capture-box size (the clip-window size when clip is active). */
|
|
24
53
|
readonly w0: number;
|
|
25
54
|
readonly h0: number;
|
|
26
|
-
/**
|
|
55
|
+
/** Render viewBox size, including bleed/padding. */
|
|
27
56
|
readonly vbW: number;
|
|
28
57
|
readonly vbH: number;
|
|
29
58
|
/** Requested output basis before scale/dpr rasterization. */
|
|
@@ -36,7 +65,7 @@ export interface CaptureMeta {
|
|
|
36
65
|
readonly clip: Readonly<{ x: number; y: number; width: number; height: number }> | null;
|
|
37
66
|
}
|
|
38
67
|
|
|
39
|
-
/** A window in
|
|
68
|
+
/** A window in capture viewBox coordinates (see `CaptureMeta`). */
|
|
40
69
|
export interface CanvasCrop {
|
|
41
70
|
x: number;
|
|
42
71
|
y: number;
|
|
@@ -46,18 +75,6 @@ export interface CanvasCrop {
|
|
|
46
75
|
|
|
47
76
|
export type CanvasExportOptions = Partial<SnapdomOptions> & { crop?: CanvasCrop };
|
|
48
77
|
|
|
49
|
-
/** Silent core exporters exposed only while a plugin defines custom exports. */
|
|
50
|
-
export interface PluginExportFacade {
|
|
51
|
-
img(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
52
|
-
svg(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
53
|
-
canvas(options?: CanvasExportOptions): Promise<HTMLCanvasElement>;
|
|
54
|
-
blob(options?: BlobOptions & Partial<SnapdomOptions>): Promise<Blob>;
|
|
55
|
-
png(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
56
|
-
jpeg(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
57
|
-
jpg(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
58
|
-
webp(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
78
|
/* =========================
|
|
62
79
|
* Font & proxy declarations
|
|
63
80
|
* ========================= */
|
|
@@ -85,9 +102,8 @@ export interface ExcludeFonts {
|
|
|
85
102
|
* ========================= */
|
|
86
103
|
|
|
87
104
|
export interface SnapdomOptions {
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
/** Output scale multiplier. Takes precedence over width/height. */
|
|
105
|
+
/** Output scale multiplier. Applies only when neither width nor height is set —
|
|
106
|
+
* width/height are the absolute output size and win (one rule across all exporters). */
|
|
91
107
|
scale?: number;
|
|
92
108
|
/** Device pixel ratio to use for rasterization (defaults to `devicePixelRatio`). */
|
|
93
109
|
dpr?: number;
|
|
@@ -97,29 +113,36 @@ export interface SnapdomOptions {
|
|
|
97
113
|
height?: number;
|
|
98
114
|
|
|
99
115
|
/** Background fallback color. Default: `"#ffffff"` for JPEG/WebP, `null` (transparent) otherwise. */
|
|
100
|
-
backgroundColor?: string;
|
|
116
|
+
backgroundColor?: string | null;
|
|
101
117
|
/** Quality for JPEG/WebP (0..1). Default 0.92. */
|
|
102
118
|
quality?: number;
|
|
103
|
-
/**
|
|
119
|
+
/** Default for format-selecting exports; named helpers choose their own codec. Default "png". */
|
|
104
120
|
format?: BlobType;
|
|
105
121
|
|
|
106
122
|
/** Cross-origin proxy prefix (used as a fallback when CORS blocks). */
|
|
107
123
|
useProxy?: string;
|
|
108
124
|
|
|
109
|
-
/**
|
|
125
|
+
/** @deprecated Legacy alias kept synchronized with canonical `format`; either key is honored. */
|
|
110
126
|
type?: BlobType;
|
|
111
127
|
|
|
112
|
-
/**
|
|
113
|
-
|
|
114
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Nodes to leave out of the capture: a CSS selector, a predicate returning true to
|
|
130
|
+
* EXCLUDE the element, or any mix of both in an array.
|
|
131
|
+
* Predicates are synchronous and run on each new capture; result memoization is bypassed
|
|
132
|
+
* when a predicate is present so it can read current application state.
|
|
133
|
+
*/
|
|
134
|
+
exclude?: string | ((el: Element) => boolean) | Array<string | ((el: Element) => boolean)>;
|
|
135
|
+
/** How excluded nodes leave ("hide" keeps layout via an invisible spacer; "remove" drops them). Default "hide". */
|
|
115
136
|
excludeMode?: "hide" | "remove";
|
|
116
137
|
|
|
117
138
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
139
|
+
* Independent keep predicate, evaluated after exclude. Truthy keeps the node; falsy
|
|
140
|
+
* omits it using filterMode. An exclusion takes precedence and skips this predicate
|
|
141
|
+
* for that node. Synchronous callbacks run again for each capture so application-state
|
|
142
|
+
* changes do not reuse an earlier filtering decision.
|
|
120
143
|
*/
|
|
121
144
|
filter?: (el: Element) => boolean;
|
|
122
|
-
/**
|
|
145
|
+
/** Mode for nodes omitted only by filter; independent of excludeMode. Default "hide". */
|
|
123
146
|
filterMode?: "hide" | "remove";
|
|
124
147
|
|
|
125
148
|
/**
|
|
@@ -130,30 +153,30 @@ export interface SnapdomOptions {
|
|
|
130
153
|
reconcile?: boolean;
|
|
131
154
|
|
|
132
155
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* snapdom warns once (console.warn) if the same element is captured 3+ times within 2s.
|
|
137
|
-
* Default false.
|
|
138
|
-
*/
|
|
139
|
-
burst?: boolean;
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* With `burst: true`, forces a fresh capture on this call for changes the automatic
|
|
143
|
-
* MutationObserver tracking can't see: canvas pixel draws, programmatic CSSOM edits
|
|
156
|
+
* Forces one fresh capture that is not served from the existing memo, and clears style
|
|
157
|
+
* snapshots after application changes for which the browser exposes no signal, notably
|
|
158
|
+
* programmatic CSSOM edits
|
|
144
159
|
* (stylesheet.insertRule/deleteRule, cssRule.style.* on a rule rather than an element).
|
|
145
|
-
*
|
|
160
|
+
* Frame-driven canvas/video/iframe trees already capture fresh automatically.
|
|
161
|
+
* The fresh stable result may become the new memo for later calls. Works with the engine's
|
|
162
|
+
* automatic memoization — no other option required. Default false.
|
|
146
163
|
*/
|
|
147
164
|
invalidate?: boolean;
|
|
148
165
|
|
|
149
|
-
/**
|
|
166
|
+
/** Root translation is normalized in both modes. true (default) keeps rotation;
|
|
167
|
+
* false removes rotation, keeps scale/skew and recomputes the bounding box. */
|
|
150
168
|
outerTransforms?: boolean;
|
|
151
169
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
170
|
+
* Control root shadow/outline bleed. Default false strips root box/text shadows,
|
|
171
|
+
* outline and drop-shadow(); blur remains visible and its bleed is included unless an
|
|
172
|
+
* explicit `clip` fixes the capture edges.
|
|
173
|
+
* true keeps and bounds all root effects.
|
|
174
|
+
*
|
|
175
|
+
* `'subtree'` additionally widens the capture for the outer-shadow ink DESCENDANTS
|
|
176
|
+
* paint past the root's box — a card whose own ring is a box-shadow, captured from a
|
|
177
|
+
* wrapper around it — measured per side and bounded by any ancestor that clips.
|
|
155
178
|
*/
|
|
156
|
-
outerShadows?: boolean;
|
|
179
|
+
outerShadows?: boolean | 'subtree';
|
|
157
180
|
|
|
158
181
|
/**
|
|
159
182
|
* Capture only a region instead of the full element: `'viewport'` (what the user
|
|
@@ -163,14 +186,12 @@ export interface SnapdomOptions {
|
|
|
163
186
|
clip?: "viewport" | { x: number; y: number; width: number; height: number } | null;
|
|
164
187
|
|
|
165
188
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
189
|
+
* Inline non-icon fonts actually used within the subtree. Default 'auto': embeds only
|
|
190
|
+
* when the element uses families the document declares as webfonts (system-font pages
|
|
191
|
+
* skip the phase entirely). true forces the embed pass; false disables it (webfont text
|
|
192
|
+
* will rasterize with fallback metrics).
|
|
169
193
|
*/
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
/** Inline non-icon fonts actually used within the subtree. */
|
|
173
|
-
embedFonts?: boolean;
|
|
194
|
+
embedFonts?: boolean | 'auto';
|
|
174
195
|
/** Provide fonts explicitly to avoid remote discovery. */
|
|
175
196
|
localFonts?: LocalFont[];
|
|
176
197
|
/** Additional matchers for icon font families (strings or regex). */
|
|
@@ -183,44 +204,71 @@ export interface SnapdomOptions {
|
|
|
183
204
|
/**
|
|
184
205
|
* Skip style properties when snapshotting computed styles (e.g. `/^--/` to exclude
|
|
185
206
|
* CSS variables on pages with thousands of custom props).
|
|
207
|
+
* A function is reevaluated on each new capture, without reusing a prior style snapshot.
|
|
186
208
|
*/
|
|
187
209
|
excludeStyleProps?: RegExp | ((prop: string) => boolean);
|
|
188
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Renderer: 'svg' by default. The second engine, 'html-in-canvas', is EXPERIMENTAL and
|
|
213
|
+
* requires a build with SNAPDOM_CANVAS_ENGINE=1. It renders through the WICG html-in-canvas
|
|
214
|
+
* API (ctx.drawElementImage — Chrome 148+ origin trial / chrome://flags/#canvas-draw-element)
|
|
215
|
+
* when available, using the browser's own painter (native form controls, no svg-as-image
|
|
216
|
+
* quirks). Falls back silently to the svg pipeline whenever unsupported/not applicable,
|
|
217
|
+
* including captures with beforeRender/afterRender plugin hooks. Successful native-engine
|
|
218
|
+
* results expose raster exports; reading `.url` or calling `toRaw()` synchronously
|
|
219
|
+
* materializes and memoizes a PNG data URL, while `toSvg()`/`toImg()` return an image
|
|
220
|
+
* backed by a PNG encoding of the bitmap. There is no serialized SVG.
|
|
221
|
+
*/
|
|
222
|
+
engine?: 'svg' | 'html-in-canvas';
|
|
223
|
+
|
|
189
224
|
/** Verbose diagnostics via console.warn. Default false. */
|
|
190
225
|
debug?: boolean;
|
|
191
226
|
|
|
192
|
-
/**
|
|
227
|
+
/**
|
|
228
|
+
* Filename for download(). The extension is appended from the export format when the name
|
|
229
|
+
* does not already carry one, so `'card'` saves as `card.png`. Default `"snapDOM"`.
|
|
230
|
+
*/
|
|
193
231
|
filename?: string;
|
|
194
232
|
|
|
195
233
|
/**
|
|
196
|
-
* Fallback image when <img> fails to
|
|
197
|
-
*
|
|
234
|
+
* Fallback image when an <img> fails to inline. A fixed URL, or a callback that receives
|
|
235
|
+
* the measured box, the failing `src` and the source `<img>` and returns the URL to fetch
|
|
236
|
+
* instead (a Promise is awaited). A falsy return, a throw or a failed fallback fetch falls
|
|
237
|
+
* through to the placeholder. With a callback, each new capture runs the pipeline again
|
|
238
|
+
* and calls it when a fallback is needed; an earlier capture result keeps its image.
|
|
198
239
|
*/
|
|
199
240
|
fallbackURL?:
|
|
200
241
|
| string
|
|
201
|
-
| ((
|
|
242
|
+
| ((info: { width?: number; height?: number; src?: string; element?: HTMLImageElement }) => string | Promise<string>);
|
|
202
243
|
|
|
203
|
-
/**
|
|
204
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Persistent resource/style cache behavior. 'disabled' (or false) empties and bypasses
|
|
246
|
+
* those persistent caches whenever the capture pipeline runs, a debug/testing escape
|
|
247
|
+
* hatch. 'soft' is the default. v2's 'auto'/'full' map to 'soft' at runtime and are gone
|
|
248
|
+
* from the type. Automatic repeat-capture memoization and differential recapture are separate.
|
|
249
|
+
*/
|
|
250
|
+
cache?: CachePolicy | false;
|
|
205
251
|
|
|
206
252
|
/** Show placeholders when resources are missing. Default true. */
|
|
207
253
|
placeholders?: boolean;
|
|
208
254
|
|
|
209
255
|
/**
|
|
210
|
-
*
|
|
211
|
-
*
|
|
256
|
+
* Render the user's live text selection into the capture. A selection is paint without DOM
|
|
257
|
+
* — the browser draws the highlight and restyles the selected glyphs at paint time — so a
|
|
258
|
+
* structural clone loses it. Selected runs are wrapped in the styles the browser painted
|
|
259
|
+
* them with: the authored `::selection` background, color, text-shadow and text-decoration
|
|
260
|
+
* where a rule matches, the UA highlight colour where none does, and nothing at all over
|
|
261
|
+
* `user-select: none`. A focused field's selection is painted as background layers behind
|
|
262
|
+
* its value. Default false.
|
|
212
263
|
*/
|
|
213
|
-
|
|
214
|
-
/** Fine-tune the built-in picture/lazy resolver (timeout, concurrency, etc.). */
|
|
215
|
-
pictureResolver?: {
|
|
216
|
-
timeout?: number;
|
|
217
|
-
concurrency?: number;
|
|
218
|
-
resolveLazySrc?: boolean;
|
|
219
|
-
silent?: boolean;
|
|
220
|
-
};
|
|
264
|
+
captureSelection?: boolean;
|
|
221
265
|
|
|
222
266
|
/** Arbitrary plugin configuration at call-site (see PluginUse). */
|
|
223
267
|
plugins?: PluginUse[];
|
|
268
|
+
|
|
269
|
+
/** Reuse an existing canvas as the render target instead of allocating one
|
|
270
|
+
* (`toCanvas` and everything built on it). */
|
|
271
|
+
canvas?: HTMLCanvasElement;
|
|
224
272
|
}
|
|
225
273
|
|
|
226
274
|
/* =========================
|
|
@@ -228,59 +276,95 @@ export interface SnapdomOptions {
|
|
|
228
276
|
* ========================= */
|
|
229
277
|
|
|
230
278
|
/**
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
* - Export-phase hooks (beforeExport, afterExport, defineExports) get a spread of the normalized
|
|
238
|
-
* options plus `element`, `meta`, `export` and (in defineExports) `exports`. There is no
|
|
239
|
-
* `clone` or `nodeMap` at that point: the clone is already serialized.
|
|
240
|
-
*
|
|
241
|
-
* Consequence: data stashed as `ctx.__x` in afterClone is invisible in defineExports. Mirror it
|
|
242
|
-
* on `ctx.options.__x`, which is the object the export context is spread from.
|
|
279
|
+
* The single object every hook receives: the normalized capture options at the TOP level
|
|
280
|
+
* (`ctx.scale`, `ctx.backgroundColor`, …) plus the per-stage fields as they are produced.
|
|
281
|
+
* It is the very bag the pipeline reads, so changing an option in `beforeSnap` takes effect
|
|
282
|
+
* — except for the options resolved before the first hook (`plugins`, `needs`,
|
|
283
|
+
* `invalidate`, `cache`). `format` is canonical; deprecated `type` stays synchronized with
|
|
284
|
+
* it, and changing either supported name in `beforeSnap` is honored.
|
|
243
285
|
*/
|
|
244
|
-
export interface CaptureContext extends SnapdomOptions {
|
|
245
|
-
/** Input element being captured.
|
|
286
|
+
export interface CaptureContext extends Omit<SnapdomOptions, "exclude" | "filter"> {
|
|
287
|
+
/** Input element being captured. */
|
|
246
288
|
element: Element;
|
|
247
289
|
|
|
248
|
-
/**
|
|
249
|
-
|
|
290
|
+
/** Exclusion selectors only, always an array here: createContext splits the option's
|
|
291
|
+
* predicates off into `excludePredicates`. A plugin deciding whether a node stays should
|
|
292
|
+
* call `shouldExclude(el)` rather than re-run either list. */
|
|
293
|
+
exclude: readonly string[];
|
|
294
|
+
|
|
295
|
+
/** The predicates passed through `exclude`, or null when there were none. */
|
|
296
|
+
readonly excludePredicates: ReadonlyArray<(el: Element) => boolean> | null;
|
|
297
|
+
|
|
298
|
+
/** The keep predicate, or null when the option was not set. */
|
|
299
|
+
filter: ((el: Element) => boolean) | null;
|
|
250
300
|
|
|
251
|
-
/**
|
|
301
|
+
/** Canonical normalized capture format. */
|
|
302
|
+
format: BlobType;
|
|
303
|
+
|
|
304
|
+
/** @deprecated Synchronized legacy alias for `format`. */
|
|
305
|
+
type: BlobType;
|
|
306
|
+
|
|
307
|
+
/** Self-reference to this same context, for plugins written against `ctx.options`. */
|
|
308
|
+
readonly options: CaptureContext;
|
|
309
|
+
|
|
310
|
+
/** Combined omission policy: true for data-capture="exclude", exclusion selectors or
|
|
311
|
+
* predicates, and nodes rejected by filter. Available at every hook stage. */
|
|
312
|
+
readonly shouldExclude: (el: Element) => boolean;
|
|
313
|
+
|
|
314
|
+
/** How far this capture runs (the deepest `needs` of its plugins). A plugin that
|
|
315
|
+
* defines exports can read it to know whether there will be an image at all. */
|
|
316
|
+
needs: CaptureStage;
|
|
317
|
+
|
|
318
|
+
/** Cloned root (detached), from `afterClone` through the render hooks. Released (null)
|
|
319
|
+
* once `afterRender` has run, so a live result never retains the whole tree. */
|
|
252
320
|
clone?: HTMLElement | SVGElement | null;
|
|
253
321
|
|
|
254
|
-
/** Internal style/class caches (opaque to user).
|
|
322
|
+
/** Internal style/class caches (opaque to user). */
|
|
255
323
|
classCSS?: string;
|
|
256
324
|
styleCache?: unknown;
|
|
325
|
+
nodeMap?: unknown;
|
|
257
326
|
fontsCSS?: string;
|
|
258
327
|
baseCSS?: string;
|
|
259
|
-
scrollbarCSS?: string;
|
|
260
|
-
/** Clone node → source node map for this capture. Clone-phase hooks only. */
|
|
261
|
-
nodeMap?: Map<Node, Node>;
|
|
262
328
|
|
|
263
|
-
/** Serialized artifacts, available
|
|
264
|
-
svgString
|
|
329
|
+
/** Serialized artifacts, available in `afterRender`. `svgString` is released as soon as
|
|
330
|
+
* that hook returns (read it lazily from `export.svgString` in an export hook). */
|
|
331
|
+
svgString?: string | null;
|
|
265
332
|
dataURL?: string;
|
|
266
333
|
|
|
267
|
-
/** Authoritative geometry
|
|
334
|
+
/** Authoritative render geometry, frozen and pinned once the render stage runs.
|
|
335
|
+
* Absent on a capture that stopped at 'clone' (no viewBox exists). */
|
|
268
336
|
readonly meta?: Readonly<CaptureMeta>;
|
|
269
337
|
|
|
270
|
-
/**
|
|
271
|
-
|
|
338
|
+
/** Render artifacts handed to `defineExports` so exporters never reverse-parse the data
|
|
339
|
+
* URL for CSS the pipeline already holds. `svgString` stays out on purpose (it would
|
|
340
|
+
* double retained memory) — read it lazily from `export.svgString`. */
|
|
341
|
+
artifacts?: {
|
|
342
|
+
classCSS: string;
|
|
343
|
+
fontsCSS: string;
|
|
344
|
+
baseCSS: string;
|
|
345
|
+
scrollbarCSS: string;
|
|
346
|
+
} | null;
|
|
272
347
|
|
|
273
348
|
/** Current export info during beforeExport/afterExport. */
|
|
274
349
|
export?: {
|
|
275
|
-
/** Export key (e.g., "png", "jpeg", "svg", or any custom key). */
|
|
350
|
+
/** Export key (e.g., "png", "jpeg", "svg", or any custom key). Absent inside `defineExports`. */
|
|
276
351
|
type?: string;
|
|
277
352
|
/** Capture defaults merged with the options passed to the exporter. */
|
|
278
353
|
options?: any;
|
|
279
|
-
/** Exact own options supplied to this export call
|
|
354
|
+
/** Exact own options supplied to this export call, frozen when `toXxx()` was called;
|
|
355
|
+
* omitted keys stay omitted, so a plugin can tell an explicit value from a default. */
|
|
280
356
|
requestedOptions?: Readonly<Record<string, unknown>>;
|
|
281
|
-
/**
|
|
357
|
+
/** Capture data URL: SVG for the default engine; lazily materialized PNG for a
|
|
358
|
+
* successful `html-in-canvas` capture. */
|
|
282
359
|
url: string;
|
|
360
|
+
/** Lazily decodes the default engine's SVG source from `url` (kept a thunk so a live
|
|
361
|
+
* result doesn't retain a second copy). Throws for a raster `html-in-canvas` capture. */
|
|
362
|
+
svgString?: () => string;
|
|
283
363
|
};
|
|
364
|
+
|
|
365
|
+
/** Core export facade for `defineExports`: the built-in exporters without their hooks,
|
|
366
|
+
* so a plugin format can build on `png`/`canvas`/… without re-entering the pipeline. */
|
|
367
|
+
exports?: Record<string, (opts?: any) => Promise<any>>;
|
|
284
368
|
}
|
|
285
369
|
|
|
286
370
|
/* =========================
|
|
@@ -289,9 +373,22 @@ export interface CaptureContext extends SnapdomOptions {
|
|
|
289
373
|
|
|
290
374
|
export type Exporter = (ctx: CaptureContext, opts?: any) => Promise<any>;
|
|
291
375
|
|
|
292
|
-
/** Map returned by `defineExports`:
|
|
376
|
+
/** Map returned by `defineExports`: each key is exposed on the result as a `to<Key>()`
|
|
377
|
+
* helper (`pdf` → `result.toPdf()`) and by name through `result.to('pdf')`. */
|
|
293
378
|
export type ExportMap = Record<string, Exporter>;
|
|
294
379
|
|
|
380
|
+
/** Second argument of the export hooks. Their return values are observational: every plugin
|
|
381
|
+
* gets this same payload and hook returns are ignored, while `options` is the SAME object
|
|
382
|
+
* the exporter receives, so mutating it is how a plugin steers the export. For a
|
|
383
|
+
* format-selecting export, `options.format` is canonical and deprecated `options.type`
|
|
384
|
+
* stays synchronized with it; changing either supported name is honored. */
|
|
385
|
+
export interface ExportHookPayload {
|
|
386
|
+
/** Export name: "png", "blob", "download", or any plugin-declared key. */
|
|
387
|
+
format: string;
|
|
388
|
+
/** Capture defaults merged with the options passed to this export call. */
|
|
389
|
+
options: any;
|
|
390
|
+
}
|
|
391
|
+
|
|
295
392
|
/* =========================
|
|
296
393
|
* Plugin system
|
|
297
394
|
* ========================= */
|
|
@@ -300,6 +397,26 @@ export interface SnapdomPlugin {
|
|
|
300
397
|
/** Unique name for de-dupe/overrides. */
|
|
301
398
|
name: string;
|
|
302
399
|
|
|
400
|
+
/**
|
|
401
|
+
* How far the capture has to run for this plugin: 'clone' (frozen tree, no pixels)
|
|
402
|
+
* or 'render' (the whole pipeline). Default 'render'.
|
|
403
|
+
* The capture runs to the deepest stage any attached plugin declares, so one plugin can
|
|
404
|
+
* only lower it when every other agrees. Below 'render' there is no image: `url`,
|
|
405
|
+
* `meta` and core image exports throw. Custom plugin exports still work, and
|
|
406
|
+
* `result.needs` reports what ran.
|
|
407
|
+
*/
|
|
408
|
+
needs?: CaptureStage;
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Declares capture-affecting hooks deterministic and idempotent, opting the plugin back
|
|
412
|
+
* into unchanged-repeat memoization. Pure beforeRender/afterRender hooks may also use
|
|
413
|
+
* differential recapture; clone-construction hooks still force a conservative full
|
|
414
|
+
* recapture after a change because the splice path cannot skip them. Set this only if
|
|
415
|
+
* re-running your hooks on the same input always produces the same output. Default false.
|
|
416
|
+
* Captures stopped at `needs: 'clone'` are never memoized.
|
|
417
|
+
*/
|
|
418
|
+
pure?: boolean;
|
|
419
|
+
|
|
303
420
|
/** Hook order follows registration order. All hooks may be async. */
|
|
304
421
|
beforeSnap?(context: CaptureContext): void | Promise<void>;
|
|
305
422
|
beforeClone?(context: CaptureContext): void | Promise<void>;
|
|
@@ -307,32 +424,25 @@ export interface SnapdomPlugin {
|
|
|
307
424
|
beforeRender?(context: CaptureContext): void | Promise<void>;
|
|
308
425
|
afterRender?(context: CaptureContext): void | Promise<void>;
|
|
309
426
|
|
|
310
|
-
/** Runs before EACH export. */
|
|
311
|
-
beforeExport?(
|
|
312
|
-
context: CaptureContext,
|
|
313
|
-
payload: { format: string; options: any }
|
|
314
|
-
): void | Promise<void>;
|
|
427
|
+
/** Runs before EACH export. Mutate `payload.options` to change what the exporter does. */
|
|
428
|
+
beforeExport?(context: CaptureContext, payload: ExportHookPayload): void | Promise<void>;
|
|
315
429
|
/**
|
|
316
|
-
* Runs after EACH export.
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
* register your own export with `defineExports`.
|
|
430
|
+
* Runs after EACH export. Observational: the return value is ignored and the export's own
|
|
431
|
+
* result is what the caller gets. To produce a different result, declare that format in
|
|
432
|
+
* `defineExports` (it can build on `ctx.exports.png()` and friends).
|
|
320
433
|
*/
|
|
321
|
-
afterExport?(
|
|
322
|
-
context: CaptureContext,
|
|
323
|
-
payload: { format: string; options: any; result: any }
|
|
324
|
-
): any | Promise<any>;
|
|
434
|
+
afterExport?(context: CaptureContext, payload: ExportHookPayload & { result: any }): void | Promise<void>;
|
|
325
435
|
|
|
326
436
|
/**
|
|
327
437
|
* Provide custom exporters (e.g., { pdf: async (ctx, opts) => Blob }).
|
|
328
|
-
* Keys are exposed on the capture result as
|
|
329
|
-
* `to(name)` (result.to('pdf')). Bare index access (result.pdf()) is NOT generated.
|
|
438
|
+
* Keys are exposed on the capture result as helpers (`toPdf()`) and by name (`to('pdf')`).
|
|
330
439
|
*/
|
|
331
440
|
defineExports?(context: CaptureContext): ExportMap | Promise<ExportMap>;
|
|
332
441
|
|
|
333
442
|
/**
|
|
334
|
-
* Per-node hook, called for every element while the clone is built (after
|
|
335
|
-
* before built-in iframe/canvas/video/audio handling). First plugin
|
|
443
|
+
* Per-node hook, called for every element while the clone is built (after the compiled
|
|
444
|
+
* exclusion policy, before built-in iframe/canvas/video/audio handling). First plugin
|
|
445
|
+
* returning a value wins:
|
|
336
446
|
* - Node → used as the finished clone for that node (mapped to source, box styles applied)
|
|
337
447
|
* - null → skip the node entirely
|
|
338
448
|
* - undefined → continue with the normal pipeline
|
|
@@ -356,11 +466,13 @@ export type PluginUse =
|
|
|
356
466
|
* Capture result API
|
|
357
467
|
* ========================= */
|
|
358
468
|
|
|
469
|
+
/** Options of `download()`. A `dpr` passed alongside is ignored: the file is always written
|
|
470
|
+
* at dpr 1, the capture's CSS size in pixels, independent of the monitor it was taken on. */
|
|
359
471
|
export interface DownloadOptions {
|
|
360
472
|
filename?: string;
|
|
361
473
|
/** Output format for the downloaded file. Default "png". */
|
|
362
474
|
format?: BlobType;
|
|
363
|
-
/**
|
|
475
|
+
/** @deprecated Legacy alias kept synchronized with canonical `format`; either key is honored. */
|
|
364
476
|
type?: BlobType;
|
|
365
477
|
/** Quality hint for raster formats. */
|
|
366
478
|
quality?: number;
|
|
@@ -370,6 +482,9 @@ export interface DownloadOptions {
|
|
|
370
482
|
}
|
|
371
483
|
|
|
372
484
|
export interface BlobOptions {
|
|
485
|
+
/** Blob codec. Defaults to SVG for the default engine and PNG for a successful native capture. */
|
|
486
|
+
format?: BlobType;
|
|
487
|
+
/** @deprecated Legacy alias kept synchronized with canonical `format`; either key is honored. */
|
|
373
488
|
type?: BlobType;
|
|
374
489
|
quality?: number;
|
|
375
490
|
width?: number;
|
|
@@ -377,31 +492,63 @@ export interface BlobOptions {
|
|
|
377
492
|
}
|
|
378
493
|
|
|
379
494
|
export interface CaptureResult {
|
|
380
|
-
/**
|
|
495
|
+
/**
|
|
496
|
+
* Canonical data URL of the capture. On the default engine, the serialized SVG. On a
|
|
497
|
+
* successful experimental engine:'html-in-canvas' capture the artifact is a raster bitmap:
|
|
498
|
+
* pixel exports (toCanvas/toPng/toJpg/toWebp and raster toBlob) consume it directly, and reading `url`
|
|
499
|
+
* (or toRaw()) mints a PNG data URL once, lazily — the encode costs 15-22x the direct
|
|
500
|
+
* draw, so it is paid only when a string is actually asked for.
|
|
501
|
+
*
|
|
502
|
+
* THROWS when `needs` is not 'render': that capture produced no image, and it is not
|
|
503
|
+
* re-captured on demand (it would be a different instant).
|
|
504
|
+
*/
|
|
381
505
|
url: string;
|
|
382
506
|
|
|
383
|
-
/**
|
|
507
|
+
/**
|
|
508
|
+
* How far this capture ran — the deepest stage its plugins declared. 'render' unless a
|
|
509
|
+
* plugin lowered it, in which case core image exports throw. Custom plugin exports
|
|
510
|
+
* can still return their own data.
|
|
511
|
+
*/
|
|
512
|
+
needs: CaptureStage;
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Degradation log for this capture — empty in the common case. Entries record the
|
|
516
|
+
* capture's silent fallbacks, keyed by `WarningCode`. Export-time entries (clamps, PNG
|
|
517
|
+
* fallback) append after the corresponding export resolves.
|
|
518
|
+
*/
|
|
519
|
+
warnings: Array<{ code: WarningCode; message: string; detail?: unknown }>;
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Authoritative render viewBox/content geometry — the same frozen record the exporters
|
|
523
|
+
* read, so raster placement cannot diverge from the captured artifact.
|
|
524
|
+
*
|
|
525
|
+
* THROWS when `needs` is not 'render', for the same reason `url` does.
|
|
526
|
+
*/
|
|
384
527
|
readonly meta: Readonly<CaptureMeta>;
|
|
385
528
|
|
|
386
|
-
/** Returns the
|
|
529
|
+
/** Returns the capture data URL (SVG by default; PNG for a successful html-in-canvas capture). */
|
|
387
530
|
toRaw(): string;
|
|
388
531
|
|
|
389
532
|
/** Run any registered export by name (core or plugin), e.g. `to("png")`. */
|
|
390
533
|
to(type: string, options?: any): Promise<any>;
|
|
391
534
|
|
|
392
535
|
/**
|
|
393
|
-
* @deprecated Use `toSvg()` for an <img>
|
|
536
|
+
* @deprecated Use `toSvg()` for an <img> representing the capture.
|
|
394
537
|
* Historical alias kept for compatibility.
|
|
395
538
|
*/
|
|
396
|
-
toImg(): Promise<HTMLImageElement>;
|
|
539
|
+
toImg(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
397
540
|
|
|
398
|
-
/** Returns an HTMLImageElement
|
|
541
|
+
/** Returns an HTMLImageElement representing the capture (SVG-backed by default,
|
|
542
|
+
* PNG-backed after a successful native html-in-canvas capture, SVG-backed on fallback). */
|
|
399
543
|
toSvg(options?: Partial<SnapdomOptions>): Promise<HTMLImageElement>;
|
|
400
544
|
|
|
401
|
-
/** Returns a Canvas with the rasterized snapshot.
|
|
545
|
+
/** Returns a Canvas with the rasterized snapshot. `crop` windows the capture in
|
|
546
|
+
* `meta` viewBox coordinates, so a long capture can be rasterized page by page. */
|
|
402
547
|
toCanvas(options?: CanvasExportOptions): Promise<HTMLCanvasElement>;
|
|
403
548
|
|
|
404
|
-
/** Returns a Blob of the chosen type (svg/png/jpeg/webp).
|
|
549
|
+
/** Returns a Blob of the chosen type (svg/png/jpeg/webp). Defaults to SVG on the default
|
|
550
|
+
* engine and PNG after a successful html-in-canvas capture. An explicit SVG request on
|
|
551
|
+
* that raster path rejects because there is no SVG source. */
|
|
405
552
|
toBlob(options?: BlobOptions & Partial<SnapdomOptions>): Promise<Blob>;
|
|
406
553
|
|
|
407
554
|
/** Convenience raster exports returning an HTMLImageElement. */
|
|
@@ -415,12 +562,9 @@ export interface CaptureResult {
|
|
|
415
562
|
download(options?: DownloadOptions & Partial<SnapdomOptions>): Promise<void>;
|
|
416
563
|
|
|
417
564
|
/**
|
|
418
|
-
* Custom exporters exposed by plugins:
|
|
419
|
-
*
|
|
420
|
-
*
|
|
421
|
-
*
|
|
422
|
-
* The index signature exists because helper names are not known ahead of time; it does
|
|
423
|
-
* not mean result['pdf']() is defined (it is not: only the toX() helper is generated).
|
|
565
|
+
* Custom exporters exposed by plugins: a plugin returning { pdf: (...) => ... } enables
|
|
566
|
+
* `result.toPdf(...)` and `result.to('pdf', ...)`. Those helper names are not known ahead
|
|
567
|
+
* of time, hence the index signature.
|
|
424
568
|
*/
|
|
425
569
|
[key: string]: any;
|
|
426
570
|
}
|
|
@@ -444,15 +588,43 @@ export declare function snapdom(
|
|
|
444
588
|
export declare namespace snapdom {
|
|
445
589
|
function plugins(...defs: PluginUse[]): typeof snapdom;
|
|
446
590
|
|
|
591
|
+
/** The package version this bundle was built from (`'src'` when imported from source). */
|
|
592
|
+
const version: string;
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Capture an HTML string (SSR markup, templates) without wiring a mount: it mounts
|
|
596
|
+
* offscreen in the live document (page CSS/fonts apply), captures, and cleans up.
|
|
597
|
+
*
|
|
598
|
+
* ⚠️ TRUSTED HTML ONLY. The string is assigned to `innerHTML` and attached to the real
|
|
599
|
+
* document, so it is parsed and activated exactly like markup you wrote yourself:
|
|
600
|
+
* `<img onerror>` and friends RUN, in your origin, with your cookies. This is inherent
|
|
601
|
+
* to rendering arbitrary markup — there is no capture without a live layout — so the
|
|
602
|
+
* boundary is the caller's to hold. Never pass user-supplied HTML that has not been
|
|
603
|
+
* sanitized first (DOMPurify or equivalent).
|
|
604
|
+
*/
|
|
605
|
+
function fromString(html: string, options?: SnapdomOptions): Promise<CaptureResult>;
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* The capture that is ready before the click: link prefetch, translated. Arm it once at
|
|
609
|
+
* setup. A memo-eligible capture started in the same event task as a control's press or
|
|
610
|
+
* click event is learned for that control. Later pointer-enter or focus intent repeats it
|
|
611
|
+
* with a shallow copy of the original call's top-level options, so an unchanged click can
|
|
612
|
+
* be served from memory; the first unknown intent event warms the visible viewport once.
|
|
613
|
+
* It reacts to intent events and does no background polling. A programmatic capture needs
|
|
614
|
+
* none of this, because memoization engages on every eligible element's first capture.
|
|
615
|
+
*/
|
|
616
|
+
function preCapture(): void;
|
|
617
|
+
|
|
447
618
|
/** Shortcut helpers that run a one-off capture+export. */
|
|
448
619
|
|
|
449
|
-
/** Returns the
|
|
620
|
+
/** Returns the capture data URL (SVG by default; PNG after a successful native
|
|
621
|
+
* html-in-canvas capture, SVG on fallback). */
|
|
450
622
|
function toRaw(
|
|
451
623
|
element: Element,
|
|
452
624
|
options?: SnapdomOptions
|
|
453
625
|
): Promise<string>;
|
|
454
626
|
|
|
455
|
-
/** @deprecated Returns an
|
|
627
|
+
/** @deprecated Returns an <img> representing the capture; prefer `toSvg`. */
|
|
456
628
|
function toImg(
|
|
457
629
|
element: Element,
|
|
458
630
|
options?: SnapdomOptions
|
|
@@ -495,37 +667,39 @@ export declare namespace snapdom {
|
|
|
495
667
|
}
|
|
496
668
|
|
|
497
669
|
/* =========================
|
|
498
|
-
*
|
|
499
|
-
* =========================
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
/** Try to embed non-icon fonts used under root (see also localFonts). */
|
|
505
|
-
embedFonts?: boolean;
|
|
506
|
-
/** Provide fonts explicitly to avoid remote discovery. */
|
|
507
|
-
localFonts?: LocalFont[];
|
|
508
|
-
/** Additional matchers for icon fonts (strings or regex). */
|
|
509
|
-
iconFonts?: IconFontMatcher | IconFontMatcher[];
|
|
510
|
-
/** Cross-origin proxy prefix (as in SnapdomOptions.useProxy). */
|
|
511
|
-
useProxy?: string;
|
|
512
|
-
/** Cache policy for this preload operation. */
|
|
513
|
-
cache?: CachePolicy;
|
|
514
|
-
|
|
515
|
-
/** Back-compat fields (no-ops if present) */
|
|
516
|
-
/**
|
|
517
|
-
* @deprecated Use `cache` instead.
|
|
518
|
-
*/
|
|
519
|
-
cacheOpt?: CachePolicy;
|
|
520
|
-
}
|
|
670
|
+
* Plugin registry
|
|
671
|
+
* =========================
|
|
672
|
+
* One runtime owns the registry: the `@zumer/snapdom/plugins` subpath resolves to the root
|
|
673
|
+
* module itself (package.json maps it there), so registering through either reaches the
|
|
674
|
+
* same list.
|
|
675
|
+
*/
|
|
521
676
|
|
|
677
|
+
/** Register plugins globally, deduped by name. A global plugin must run to 'render'. */
|
|
678
|
+
export declare function registerPlugins(...defs: PluginUse[]): void;
|
|
679
|
+
/** Drop every globally registered plugin. */
|
|
680
|
+
export declare function clearPlugins(): void;
|
|
681
|
+
/** The globally registered plugin instances, in registration order. */
|
|
682
|
+
export declare function getGlobalPlugins(): SnapdomPlugin[];
|
|
683
|
+
/** Resolve a factory/tuple/instance into a plugin instance. */
|
|
684
|
+
export declare function normalizePlugin(spec: PluginUse): SnapdomPlugin | null;
|
|
685
|
+
/** Ordered capture stages, cheapest first. */
|
|
686
|
+
export declare const STAGES: readonly CaptureStage[];
|
|
687
|
+
/** The stage a plugin gets when it declares nothing. */
|
|
688
|
+
export declare const DEFAULT_STAGE: CaptureStage;
|
|
522
689
|
/**
|
|
523
|
-
*
|
|
524
|
-
*
|
|
690
|
+
* Validate a declared stage, naming `pluginName` in the error. Returns the deepest supported
|
|
691
|
+
* stage when `value` is undefined/null, so a plugin that declares nothing runs the full pipeline.
|
|
525
692
|
*/
|
|
526
|
-
export declare function
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
693
|
+
export declare function assertNeeds(
|
|
694
|
+
pluginName: string,
|
|
695
|
+
value?: unknown,
|
|
696
|
+
supported?: readonly CaptureStage[]
|
|
697
|
+
): CaptureStage;
|
|
698
|
+
|
|
699
|
+
/* The `/plugins` subpath points at THIS file through the `types` condition
|
|
700
|
+
* in package.json's exports map. They used to be declared here as
|
|
701
|
+
* `declare module "@zumer/snapdom/plugins" { … }`. In a file that is already a module those
|
|
702
|
+
* are AUGMENTATIONS of a specifier TypeScript has to resolve first, and it cannot resolve a
|
|
703
|
+
* package into itself, so every consumer without `skipLibCheck: true` got TS2665. */
|
|
530
704
|
|
|
531
705
|
export {};
|