@pixldocs/canvas-renderer 0.5.184 → 0.5.186

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 CHANGED
@@ -406,3 +406,79 @@ rectangle (the closest pure-vector approximation), unchanged.
406
406
 
407
407
  No API change is required on the consumer side — the same `renderFromForm`,
408
408
  `renderPdfFromForm`, etc. respect the flag automatically.
409
+
410
+ ### Example: per-field blur for a biodata form (use page)
411
+
412
+ On pixldocs.com's Use page for the Biodata preset, every form field renders
413
+ a small **🔒 Blur in preview** checkbox next to it. Toggling it redacts only
414
+ that field's text in the watermarked preview — perfect for hiding reference
415
+ rows, phone numbers, or addresses behind the paywall while leaving the rest
416
+ of the biodata visible. Paid / clean downloads always render the original
417
+ content.
418
+
419
+ Under the hood the flow is:
420
+
421
+ 1. Keep a `Set<string>` of "blurred field ids" in component state — each id
422
+ encodes the field's path (single field, repeatable entry, or nested
423
+ repeatable entry), e.g.
424
+ `__bioPreviewBlur__:nested:family:1:siblings:2:name`.
425
+ 2. Resolve each blurred field id to the **exact cloned element ids** in the
426
+ paginated config (a single repeatable field may map to N cloned elements,
427
+ one per entry).
428
+ 3. Pass that set to `injectPreviewBlur` as `extraElementExactIds` (or
429
+ `extraElementBaseIds` if you only know the source/base id), then render
430
+ the returned config like any other.
431
+
432
+ ```tsx
433
+ import { injectPreviewBlur } from '@pixldocs/canvas-renderer';
434
+
435
+ // `blurredElementIds` = the resolved Set<string> of exact cloned element ids
436
+ // you built from the user's checkbox state.
437
+ const previewConfig = injectPreviewBlur(paginatedConfig, {
438
+ extraElementExactIds: blurredElementIds,
439
+ });
440
+
441
+ // Then render `previewConfig` with <PixldocsPreview config={previewConfig} />
442
+ // or `renderer.renderAllPages(previewConfig)`. The live React preview will
443
+ // additionally upgrade the static rectangles to real CSS
444
+ // `backdrop-filter: blur()` overlays automatically.
445
+ ```
446
+
447
+ This is exactly how the Biodata preset on pixldocs.com wires its per-field
448
+ 🔒 checkboxes — no template authoring required, the user picks at runtime
449
+ which fields stay visible in the watermarked preview.
450
+
451
+ ### Shortcut: pass blur field ids straight to `<PixldocsPreview>` (v0.5.186+)
452
+
453
+ If you're using the React `<PixldocsPreview>` component (rather than the
454
+ imperative renderer), you don't need to call `injectPreviewBlur` yourself.
455
+ Just pass the field ids directly as a prop:
456
+
457
+ ```tsx
458
+ import { PixldocsPreview } from '@pixldocs/canvas-renderer';
459
+
460
+ // Source / base ids straight from your form schema.
461
+ // Every cloned instance across pages + repeatable entries is auto-matched.
462
+ const blurFieldIds = ['text-reference-name', 'text-phone', 'text-address'];
463
+
464
+ <PixldocsPreview
465
+ templateId={templateId}
466
+ formSchemaId={formSchemaId}
467
+ sectionState={sectionState}
468
+ supabaseUrl={SUPABASE_URL}
469
+ supabaseAnonKey={SUPABASE_ANON_KEY}
470
+ blurFieldIds={blurFieldIds}
471
+ />
472
+ ```
473
+
474
+ - `blurFieldIds`: matched by **base id** — cloned suffixes (`_1`, `_2_3`, …)
475
+ are stripped before comparison, so one entry blurs every clone of that
476
+ element across all pages / repeatable entries.
477
+ - `blurElementExactIds`: optional companion prop for targeting a single
478
+ specific clone (no base-id stripping).
479
+ - The preview still respects any `previewBlur: true` flags already baked
480
+ into the resolved config — `blurFieldIds` is purely additive.
481
+ - This only affects the live React preview overlay. For watermarked
482
+ **downloads** (PNG/PDF) you still want to run `injectPreviewBlur(...)` on
483
+ the resolved config before handing it to the imperative renderer, so the
484
+ blur is baked into the exported pixels.
@@ -16147,15 +16147,18 @@ function countUnderlinedNodes(config) {
16147
16147
  function getNum(v, fallback = 0) {
16148
16148
  return typeof v === "number" && Number.isFinite(v) ? v : fallback;
16149
16149
  }
16150
- function collectBlurBounds(node, parentLeft, parentTop, inheritedBlur, out) {
16150
+ function collectBlurBounds(node, parentLeft, parentTop, inheritedBlur, extraBaseIds, extraExactIds, out) {
16151
16151
  if (!node || typeof node !== "object") return;
16152
- const isBlurred = inheritedBlur || node.previewBlur === true;
16152
+ const matchesBase = !!(extraBaseIds && typeof node.id === "string" && extraBaseIds.has(baseId(node.id)));
16153
+ const matchesExact = !!(extraExactIds && typeof node.id === "string" && extraExactIds.has(node.id));
16154
+ const isBlurred = inheritedBlur || node.previewBlur === true || matchesBase || matchesExact;
16153
16155
  const absLeft = parentLeft + getNum(node.left, 0);
16154
16156
  const absTop = parentTop + getNum(node.top, 0);
16155
16157
  if (node.type === "group") {
16156
16158
  const children = node.children || node.elements;
16157
16159
  if (Array.isArray(children)) {
16158
- for (const c of children) collectBlurBounds(c, absLeft, absTop, isBlurred, out);
16160
+ for (const c of children)
16161
+ collectBlurBounds(c, absLeft, absTop, isBlurred, extraBaseIds, extraExactIds, out);
16159
16162
  }
16160
16163
  return;
16161
16164
  }
@@ -16166,13 +16169,14 @@ function collectBlurBounds(node, parentLeft, parentTop, inheritedBlur, out) {
16166
16169
  const h = Math.max(4, getNum(node.height, 0) * sy);
16167
16170
  out.push({ left: absLeft, top: absTop, width: w, height: h });
16168
16171
  }
16169
- function computeFrostedBoundsForPage(config, pageIndex) {
16172
+ function computeFrostedBoundsForPage(config, pageIndex, extraBaseIds, extraExactIds) {
16170
16173
  var _a;
16171
16174
  const page = (_a = config == null ? void 0 : config.pages) == null ? void 0 : _a[pageIndex];
16172
16175
  const children = (page == null ? void 0 : page.children) || (page == null ? void 0 : page.elements);
16173
16176
  if (!Array.isArray(children)) return [];
16174
16177
  const out = [];
16175
- for (const c of children) collectBlurBounds(c, 0, 0, false, out);
16178
+ for (const c of children)
16179
+ collectBlurBounds(c, 0, 0, false, extraBaseIds, extraExactIds, out);
16176
16180
  return out;
16177
16181
  }
16178
16182
  function PixldocsPreview(props) {
@@ -16202,7 +16206,9 @@ function PixldocsPreview(props) {
16202
16206
  // while the on-screen preview wasn't.
16203
16207
  skipFontReadyWait = false,
16204
16208
  frostedBlur = true,
16205
- frostedBlurOptions
16209
+ frostedBlurOptions,
16210
+ blurFieldIds,
16211
+ blurElementExactIds
16206
16212
  } = props;
16207
16213
  useEffect(() => {
16208
16214
  setPackageApiUrl(imageProxyUrl);
@@ -16318,9 +16324,17 @@ function PixldocsPreview(props) {
16318
16324
  setCanvasSettled(true);
16319
16325
  onReady == null ? void 0 : onReady();
16320
16326
  }, [onReady, pageIndex]);
16327
+ const blurBaseSet = useMemo(
16328
+ () => blurFieldIds && blurFieldIds.length ? new Set(blurFieldIds) : void 0,
16329
+ [blurFieldIds ? blurFieldIds.join("|") : ""]
16330
+ );
16331
+ const blurExactSet = useMemo(
16332
+ () => blurElementExactIds && blurElementExactIds.length ? new Set(blurElementExactIds) : void 0,
16333
+ [blurElementExactIds ? blurElementExactIds.join("|") : ""]
16334
+ );
16321
16335
  const frostedBounds = useMemo(
16322
- () => frostedBlur ? computeFrostedBoundsForPage(config, pageIndex) : [],
16323
- [frostedBlur, config, pageIndex]
16336
+ () => frostedBlur ? computeFrostedBoundsForPage(config, pageIndex, blurBaseSet, blurExactSet) : [],
16337
+ [frostedBlur, config, pageIndex, blurBaseSet, blurExactSet]
16324
16338
  );
16325
16339
  const blurPx = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.blurPx) ?? 5;
16326
16340
  const satPct = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.saturatePct) ?? 130;
@@ -16561,9 +16575,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16561
16575
  }
16562
16576
  return svgString;
16563
16577
  }
16564
- const resolvedPackageVersion = "0.5.184";
16578
+ const resolvedPackageVersion = "0.5.186";
16565
16579
  const PACKAGE_VERSION = resolvedPackageVersion;
16566
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.184";
16580
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.186";
16567
16581
  const roundParityValue = (value) => {
16568
16582
  if (typeof value !== "number") return value;
16569
16583
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16862,7 +16876,7 @@ class PixldocsRenderer {
16862
16876
  if (shouldWatermark) {
16863
16877
  const { injectWatermark } = await import("./canvasWatermark-pkhacGge.js");
16864
16878
  configToRender = injectWatermark(configToRender, watermarkOptions);
16865
- const { injectPreviewBlur } = await import("./previewBlur-Dwbjk5sM.js");
16879
+ const { injectPreviewBlur } = await import("./previewBlur-CDBjIIjX.js");
16866
16880
  configToRender = injectPreviewBlur(configToRender);
16867
16881
  }
16868
16882
  return this.renderAllPages(configToRender, renderOpts);
@@ -16921,7 +16935,7 @@ class PixldocsRenderer {
16921
16935
  if (shouldWatermark) {
16922
16936
  const { injectWatermark } = await import("./canvasWatermark-pkhacGge.js");
16923
16937
  configToRender = injectWatermark(configToRender, watermarkOptions);
16924
- const { injectPreviewBlur } = await import("./previewBlur-Dwbjk5sM.js");
16938
+ const { injectPreviewBlur } = await import("./previewBlur-CDBjIIjX.js");
16925
16939
  configToRender = injectPreviewBlur(configToRender);
16926
16940
  }
16927
16941
  return this.renderAllPageSvgs(configToRender);
@@ -16965,7 +16979,7 @@ class PixldocsRenderer {
16965
16979
  if (shouldWatermark) {
16966
16980
  const { injectWatermark } = await import("./canvasWatermark-pkhacGge.js");
16967
16981
  configToRender = injectWatermark(configToRender, watermarkOptions);
16968
- const { injectPreviewBlur } = await import("./previewBlur-Dwbjk5sM.js");
16982
+ const { injectPreviewBlur } = await import("./previewBlur-CDBjIIjX.js");
16969
16983
  configToRender = injectPreviewBlur(configToRender);
16970
16984
  }
16971
16985
  return this.renderPdfViaClientExport(configToRender, {
@@ -17071,7 +17085,7 @@ class PixldocsRenderer {
17071
17085
  await this.waitForCanvasScene(container, cloned, i);
17072
17086
  }
17073
17087
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
17074
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CBUTexHq.js");
17088
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-v3mr9nks.js");
17075
17089
  const prepared = preparePagesForExport(
17076
17090
  cloned.pages,
17077
17091
  canvasWidth,
@@ -19216,7 +19230,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19216
19230
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19217
19231
  sanitizeSvgTreeForPdf(svgToDraw);
19218
19232
  try {
19219
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CBUTexHq.js");
19233
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-v3mr9nks.js");
19220
19234
  try {
19221
19235
  await logTextMeasurementDiagnostic(svgToDraw);
19222
19236
  } catch {
@@ -19613,4 +19627,4 @@ export {
19613
19627
  awaitFontsForConfig as y,
19614
19628
  collectFontDescriptorsFromConfig as z
19615
19629
  };
19616
- //# sourceMappingURL=index-D2deevHj.js.map
19630
+ //# sourceMappingURL=index-Xg0sHfrw.js.map