mnfst 0.5.73 → 0.5.74

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.
@@ -150,11 +150,33 @@ function initializeExportPlugin() {
150
150
  return `export-${ts}.${ext}`;
151
151
  }
152
152
 
153
+ // Transparent 1×1 PNG, used as a fallback when an inline image fails to fetch.
154
+ // html-to-image would otherwise reject the whole snapshot on a single CORS or 404.
155
+ const TRANSPARENT_PIXEL = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkAAIAAAoAAv/lxKUAAAAASUVORK5CYII=';
156
+
157
+ // html-to-image rejects with the raw `image.onerror` Event when the assembled
158
+ // SVG fails to decode (oversized clones, malformed inline assets, etc.).
159
+ // Translate those into something readable before logging.
160
+ function describeExportError(err) {
161
+ if (err && err.message) return err;
162
+ if (err && typeof Event !== 'undefined' && err instanceof Event) {
163
+ const tag = err.target && err.target.tagName ? err.target.tagName.toLowerCase() : 'image';
164
+ return new Error(
165
+ `failed to render ${tag} during export. ` +
166
+ `Common causes: cross-origin images without CORS headers, ` +
167
+ `an oversized target element, or a no-target snapshot of a complex page. ` +
168
+ `Pass a "target" option to scope the snapshot.`
169
+ );
170
+ }
171
+ return new Error(String(err));
172
+ }
173
+
153
174
  function emitError(format, err) {
154
- console.error('[x-export] export failed:', err);
175
+ const e = describeExportError(err);
176
+ console.error('[x-export] export failed:', e.message);
155
177
  try {
156
178
  window.dispatchEvent(new CustomEvent('manifest:export-error', {
157
- detail: { format, error: String(err && err.message || err) }
179
+ detail: { format, error: e.message }
158
180
  }));
159
181
  } catch { /* ignore */ }
160
182
  }
@@ -194,6 +216,10 @@ function initializeExportPlugin() {
194
216
  const out = {
195
217
  pixelRatio: Number(opts.resolution) > 0 ? Number(opts.resolution) : 2,
196
218
  cacheBust: true,
219
+ // A single cross-origin image without CORS headers would otherwise
220
+ // reject the whole snapshot. Substitute a transparent pixel so the
221
+ // export still produces a usable file.
222
+ imagePlaceholder: TRANSPARENT_PIXEL,
197
223
  filter
198
224
  };
199
225
  if (opts.backgroundColor) out.backgroundColor = opts.backgroundColor;
@@ -202,20 +228,35 @@ function initializeExportPlugin() {
202
228
  return out;
203
229
  }
204
230
 
231
+ // Run an html-to-image call once. If it rejects with a raw Event (the SVG
232
+ // failed to decode — usually a font or oversized clone issue), retry once
233
+ // with safer options before propagating the error. Most real-world failures
234
+ // recover on the second attempt.
235
+ async function snapshotWithFallback(lib, fn, target, so) {
236
+ try {
237
+ return await lib[fn](target, so);
238
+ } catch (err) {
239
+ if (err && err.message) throw err; // a real Error — don't mask it
240
+ const safer = { ...so, skipFonts: true, pixelRatio: Math.min(so.pixelRatio || 2, 1) };
241
+ return await lib[fn](target, safer);
242
+ }
243
+ }
244
+
205
245
  async function exportImage(opts, filename, ext) {
206
246
  const lib = await loadHtmlToImage();
207
247
  const target = resolveTarget(opts);
208
248
  const so = snapshotOptions(opts);
209
249
  let dataUrl;
210
- if (ext === 'png') dataUrl = await lib.toPng(target, so);
250
+ if (ext === 'png') dataUrl = await snapshotWithFallback(lib, 'toPng', target, so);
211
251
  else if (ext === 'jpeg') {
212
252
  if (!so.backgroundColor) so.backgroundColor = effectivePageBackground();
213
253
  so.quality = Number(opts.quality) > 0 && Number(opts.quality) <= 1
214
254
  ? Number(opts.quality)
215
255
  : 0.95;
216
- dataUrl = await lib.toJpeg(target, so);
256
+ dataUrl = await snapshotWithFallback(lib, 'toJpeg', target, so);
217
257
  } else if (ext === 'webp') {
218
- const blob = await lib.toBlob(target, { ...so, type: 'image/webp', quality: opts.quality || 0.95 });
258
+ const webpOpts = { ...so, type: 'image/webp', quality: opts.quality || 0.95 };
259
+ const blob = await snapshotWithFallback(lib, 'toBlob', target, webpOpts);
219
260
  dataUrl = URL.createObjectURL(blob);
220
261
  }
221
262
  triggerDownload(dataUrl, filename);
@@ -226,7 +267,7 @@ function initializeExportPlugin() {
226
267
  const target = resolveTarget(opts);
227
268
  const so = snapshotOptions(opts);
228
269
  if (!so.backgroundColor) so.backgroundColor = effectivePageBackground();
229
- const dataUrl = await imgLib.toPng(target, so);
270
+ const dataUrl = await snapshotWithFallback(imgLib, 'toPng', target, so);
230
271
  const img = await loadImage(dataUrl);
231
272
  const orientation = img.width > img.height ? 'landscape' : 'portrait';
232
273
  const pdf = new jsPDFCtor({ orientation, unit: 'pt', format: opts.pageSize || 'a4' });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mnfst",
3
- "version": "0.5.73",
3
+ "version": "0.5.74",
4
4
  "private": false,
5
5
  "workspaces": [
6
6
  "templates/starter",