@pixldocs/canvas-renderer 0.5.185 → 0.5.187

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
@@ -447,3 +447,112 @@ const previewConfig = injectPreviewBlur(paginatedConfig, {
447
447
  This is exactly how the Biodata preset on pixldocs.com wires its per-field
448
448
  🔒 checkboxes — no template authoring required, the user picks at runtime
449
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.
485
+
486
+ ### Biodata teaser previews — blur by flat form key (v0.5.187+)
487
+
488
+ Form-driven apps (BioMaker, the pixldocs.com Use page, etc.) already
489
+ speak the **flat form-key** language produced by `applyFormDataToConfig`
490
+ — e.g.
491
+
492
+ ```
493
+ field_<treeNodeId>_<entryIdx1>_<fieldKey>
494
+ field_<parentTree>_<pi>_field_<childTree>_<ci>_<fieldKey> // nested
495
+ ```
496
+
497
+ Instead of reverse-engineering `config.__cloneIdMap` keys or the
498
+ `__cN` / `_eN` clone suffixes on canvas element ids, you can pass those
499
+ flat keys straight to `<PixldocsPreview>` and the package resolves them:
500
+
501
+ ```tsx
502
+ import { PixldocsPreview } from '@pixldocs/canvas-renderer';
503
+
504
+ // e.g. for a biodata teaser: blur every detail-row VALUE after row 3.
505
+ const blurFlatFormKeys = buildTeaserBlurFlatKeys(sectionState, schema, {
506
+ afterRow: 3,
507
+ bindings: 'value',
508
+ });
509
+
510
+ <PixldocsPreview
511
+ config={displayConfig}
512
+ frostedBlur
513
+ blurFlatFormKeys={blurFlatFormKeys}
514
+ // Optional — defaults to { bindings: 'value' } so only `*_value` keys
515
+ // are honoured. Pass 'all' to blur labels / titles too.
516
+ blurFlatFormKeyOptions={{ bindings: 'value' }}
517
+ />
518
+ ```
519
+
520
+ Notes:
521
+
522
+ - Matching is done on `config.__cloneIdMap` and handles all alias
523
+ variants (`grp-…_4_value`, `…_4_field_detail_section_N_field_N_value`,
524
+ with or without the wrapper `field_` prefix). Unknown keys are
525
+ silently ignored — the package never blurs an element it can't
526
+ positively identify, so a stale key won't accidentally blur the full
527
+ name, photo, or section titles.
528
+ - Defaults to `bindings: 'value'`, i.e. only flat keys whose last
529
+ segment is `value` resolve. This is exactly the biodata teaser
530
+ pattern: blur row values, leave labels / section titles / full-name /
531
+ photo sharp. Switch to `'all'` if you want labels blurred too.
532
+ - This only drives the live preview overlay. For the watermarked
533
+ PNG/PDF download path, resolve the same keys to exact ids and bake
534
+ them in (see below).
535
+
536
+ #### Watermarked downloads — same resolver + `injectPreviewBlur`
537
+
538
+ ```ts
539
+ import {
540
+ injectPreviewBlur,
541
+ resolveBlurElementExactIdsFromFlatFormKeys,
542
+ } from '@pixldocs/canvas-renderer';
543
+
544
+ const exactIds = resolveBlurElementExactIdsFromFlatFormKeys(
545
+ resolvedConfig,
546
+ blurFlatFormKeys,
547
+ // { bindings: 'value' } by default
548
+ );
549
+
550
+ const exportConfig = injectPreviewBlur(resolvedConfig, {
551
+ extraElementExactIds: new Set(exactIds),
552
+ });
553
+
554
+ // Hand `exportConfig` to renderer.renderAllPages / renderPdf / …
555
+ ```
556
+
557
+ Same resolution path as the live preview — so the on-screen overlay and
558
+ the downloaded PNG/PDF blur exactly the same elements.
@@ -16115,6 +16115,44 @@ async function getTemplateForm(options) {
16115
16115
  initialSectionState
16116
16116
  };
16117
16117
  }
16118
+ function stripFieldPrefix(s) {
16119
+ return s.startsWith("field_") ? s.slice("field_".length) : s;
16120
+ }
16121
+ function addAll(val, out) {
16122
+ if (!val) return false;
16123
+ if (Array.isArray(val)) {
16124
+ for (const v of val) out.add(v);
16125
+ return val.length > 0;
16126
+ }
16127
+ out.add(val);
16128
+ return true;
16129
+ }
16130
+ function resolveBlurElementExactIdsFromFlatFormKeys(config, flatFormKeys, options) {
16131
+ const cloneIdMap = (config == null ? void 0 : config.__cloneIdMap) || {};
16132
+ if (!cloneIdMap || typeof cloneIdMap !== "object") return [];
16133
+ const bindings = (options == null ? void 0 : options.bindings) ?? "value";
16134
+ const out = /* @__PURE__ */ new Set();
16135
+ for (const rawId of flatFormKeys) {
16136
+ if (typeof rawId !== "string" || !rawId) continue;
16137
+ if (bindings === "value" && !rawId.endsWith("_value")) continue;
16138
+ const candidates = [rawId];
16139
+ if (rawId.startsWith("field_")) {
16140
+ const topStripped = stripFieldPrefix(rawId);
16141
+ candidates.push(topStripped);
16142
+ const nestedIdx = topStripped.indexOf("_field_");
16143
+ if (nestedIdx >= 0) {
16144
+ const nestedStripped = topStripped.slice(0, nestedIdx + 1) + topStripped.slice(nestedIdx + 1 + "field_".length);
16145
+ candidates.push(nestedStripped);
16146
+ }
16147
+ }
16148
+ for (const k of candidates) {
16149
+ if (k in cloneIdMap) {
16150
+ if (addAll(cloneIdMap[k], out)) break;
16151
+ }
16152
+ }
16153
+ }
16154
+ return Array.from(out);
16155
+ }
16118
16156
  const PREVIEW_DEBUG_PREFIX = "[canvas-renderer][preview-debug]";
16119
16157
  function computeFontSignature(config) {
16120
16158
  var _a;
@@ -16147,15 +16185,18 @@ function countUnderlinedNodes(config) {
16147
16185
  function getNum(v, fallback = 0) {
16148
16186
  return typeof v === "number" && Number.isFinite(v) ? v : fallback;
16149
16187
  }
16150
- function collectBlurBounds(node, parentLeft, parentTop, inheritedBlur, out) {
16188
+ function collectBlurBounds(node, parentLeft, parentTop, inheritedBlur, extraBaseIds, extraExactIds, out) {
16151
16189
  if (!node || typeof node !== "object") return;
16152
- const isBlurred = inheritedBlur || node.previewBlur === true;
16190
+ const matchesBase = !!(extraBaseIds && typeof node.id === "string" && extraBaseIds.has(baseId(node.id)));
16191
+ const matchesExact = !!(extraExactIds && typeof node.id === "string" && extraExactIds.has(node.id));
16192
+ const isBlurred = inheritedBlur || node.previewBlur === true || matchesBase || matchesExact;
16153
16193
  const absLeft = parentLeft + getNum(node.left, 0);
16154
16194
  const absTop = parentTop + getNum(node.top, 0);
16155
16195
  if (node.type === "group") {
16156
16196
  const children = node.children || node.elements;
16157
16197
  if (Array.isArray(children)) {
16158
- for (const c of children) collectBlurBounds(c, absLeft, absTop, isBlurred, out);
16198
+ for (const c of children)
16199
+ collectBlurBounds(c, absLeft, absTop, isBlurred, extraBaseIds, extraExactIds, out);
16159
16200
  }
16160
16201
  return;
16161
16202
  }
@@ -16166,13 +16207,14 @@ function collectBlurBounds(node, parentLeft, parentTop, inheritedBlur, out) {
16166
16207
  const h = Math.max(4, getNum(node.height, 0) * sy);
16167
16208
  out.push({ left: absLeft, top: absTop, width: w, height: h });
16168
16209
  }
16169
- function computeFrostedBoundsForPage(config, pageIndex) {
16210
+ function computeFrostedBoundsForPage(config, pageIndex, extraBaseIds, extraExactIds) {
16170
16211
  var _a;
16171
16212
  const page = (_a = config == null ? void 0 : config.pages) == null ? void 0 : _a[pageIndex];
16172
16213
  const children = (page == null ? void 0 : page.children) || (page == null ? void 0 : page.elements);
16173
16214
  if (!Array.isArray(children)) return [];
16174
16215
  const out = [];
16175
- for (const c of children) collectBlurBounds(c, 0, 0, false, out);
16216
+ for (const c of children)
16217
+ collectBlurBounds(c, 0, 0, false, extraBaseIds, extraExactIds, out);
16176
16218
  return out;
16177
16219
  }
16178
16220
  function PixldocsPreview(props) {
@@ -16202,7 +16244,11 @@ function PixldocsPreview(props) {
16202
16244
  // while the on-screen preview wasn't.
16203
16245
  skipFontReadyWait = false,
16204
16246
  frostedBlur = true,
16205
- frostedBlurOptions
16247
+ frostedBlurOptions,
16248
+ blurFieldIds,
16249
+ blurElementExactIds,
16250
+ blurFlatFormKeys,
16251
+ blurFlatFormKeyOptions
16206
16252
  } = props;
16207
16253
  useEffect(() => {
16208
16254
  setPackageApiUrl(imageProxyUrl);
@@ -16318,9 +16364,35 @@ function PixldocsPreview(props) {
16318
16364
  setCanvasSettled(true);
16319
16365
  onReady == null ? void 0 : onReady();
16320
16366
  }, [onReady, pageIndex]);
16367
+ const blurBaseSet = useMemo(
16368
+ () => blurFieldIds && blurFieldIds.length ? new Set(blurFieldIds) : void 0,
16369
+ [blurFieldIds ? blurFieldIds.join("|") : ""]
16370
+ );
16371
+ const blurExactSet = useMemo(
16372
+ () => {
16373
+ const merged = /* @__PURE__ */ new Set();
16374
+ if (blurElementExactIds) for (const id of blurElementExactIds) merged.add(id);
16375
+ if (config && blurFlatFormKeys && blurFlatFormKeys.length) {
16376
+ for (const id of resolveBlurElementExactIdsFromFlatFormKeys(
16377
+ config,
16378
+ blurFlatFormKeys,
16379
+ blurFlatFormKeyOptions
16380
+ )) {
16381
+ merged.add(id);
16382
+ }
16383
+ }
16384
+ return merged.size > 0 ? merged : void 0;
16385
+ },
16386
+ [
16387
+ blurElementExactIds ? blurElementExactIds.join("|") : "",
16388
+ blurFlatFormKeys ? blurFlatFormKeys.join("|") : "",
16389
+ blurFlatFormKeyOptions == null ? void 0 : blurFlatFormKeyOptions.bindings,
16390
+ config
16391
+ ]
16392
+ );
16321
16393
  const frostedBounds = useMemo(
16322
- () => frostedBlur ? computeFrostedBoundsForPage(config, pageIndex) : [],
16323
- [frostedBlur, config, pageIndex]
16394
+ () => frostedBlur ? computeFrostedBoundsForPage(config, pageIndex, blurBaseSet, blurExactSet) : [],
16395
+ [frostedBlur, config, pageIndex, blurBaseSet, blurExactSet]
16324
16396
  );
16325
16397
  const blurPx = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.blurPx) ?? 5;
16326
16398
  const satPct = (frostedBlurOptions == null ? void 0 : frostedBlurOptions.saturatePct) ?? 130;
@@ -16561,9 +16633,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
16561
16633
  }
16562
16634
  return svgString;
16563
16635
  }
16564
- const resolvedPackageVersion = "0.5.185";
16636
+ const resolvedPackageVersion = "0.5.187";
16565
16637
  const PACKAGE_VERSION = resolvedPackageVersion;
16566
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.185";
16638
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.187";
16567
16639
  const roundParityValue = (value) => {
16568
16640
  if (typeof value !== "number") return value;
16569
16641
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -16862,8 +16934,8 @@ class PixldocsRenderer {
16862
16934
  if (shouldWatermark) {
16863
16935
  const { injectWatermark } = await import("./canvasWatermark-pkhacGge.js");
16864
16936
  configToRender = injectWatermark(configToRender, watermarkOptions);
16865
- const { injectPreviewBlur } = await import("./previewBlur-B5SlBl4a.js");
16866
- configToRender = injectPreviewBlur(configToRender);
16937
+ const { injectPreviewBlur: injectPreviewBlur2 } = await Promise.resolve().then(() => previewBlur);
16938
+ configToRender = injectPreviewBlur2(configToRender);
16867
16939
  }
16868
16940
  return this.renderAllPages(configToRender, renderOpts);
16869
16941
  }
@@ -16921,8 +16993,8 @@ class PixldocsRenderer {
16921
16993
  if (shouldWatermark) {
16922
16994
  const { injectWatermark } = await import("./canvasWatermark-pkhacGge.js");
16923
16995
  configToRender = injectWatermark(configToRender, watermarkOptions);
16924
- const { injectPreviewBlur } = await import("./previewBlur-B5SlBl4a.js");
16925
- configToRender = injectPreviewBlur(configToRender);
16996
+ const { injectPreviewBlur: injectPreviewBlur2 } = await Promise.resolve().then(() => previewBlur);
16997
+ configToRender = injectPreviewBlur2(configToRender);
16926
16998
  }
16927
16999
  return this.renderAllPageSvgs(configToRender);
16928
17000
  }
@@ -16965,8 +17037,8 @@ class PixldocsRenderer {
16965
17037
  if (shouldWatermark) {
16966
17038
  const { injectWatermark } = await import("./canvasWatermark-pkhacGge.js");
16967
17039
  configToRender = injectWatermark(configToRender, watermarkOptions);
16968
- const { injectPreviewBlur } = await import("./previewBlur-B5SlBl4a.js");
16969
- configToRender = injectPreviewBlur(configToRender);
17040
+ const { injectPreviewBlur: injectPreviewBlur2 } = await Promise.resolve().then(() => previewBlur);
17041
+ configToRender = injectPreviewBlur2(configToRender);
16970
17042
  }
16971
17043
  return this.renderPdfViaClientExport(configToRender, {
16972
17044
  title: title ?? resolved.config.name,
@@ -17071,7 +17143,7 @@ class PixldocsRenderer {
17071
17143
  await this.waitForCanvasScene(container, cloned, i);
17072
17144
  }
17073
17145
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
17074
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CfmlwA1-.js");
17146
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-BkERGCkM.js");
17075
17147
  const prepared = preparePagesForExport(
17076
17148
  cloned.pages,
17077
17149
  canvasWidth,
@@ -17913,7 +17985,7 @@ function collectFontSpecsFromMarkup(markup) {
17913
17985
  }
17914
17986
  return Array.from(specs);
17915
17987
  }
17916
- function parseColor(color) {
17988
+ function parseColor$1(color) {
17917
17989
  if (!color) return null;
17918
17990
  const raw = color.trim().toLowerCase();
17919
17991
  if (!raw || raw === "transparent" || raw === "none") return null;
@@ -18466,7 +18538,7 @@ function stripSuspiciousFullPageOverlayNodes(svg) {
18466
18538
  if (!(pageWidth > 0 && pageHeight > 0)) return;
18467
18539
  const isNear = (a, b, tolerance = 0.75) => Math.abs(a - b) <= tolerance;
18468
18540
  const isDarkPaint = (value) => {
18469
- const rgb = value ? parseColor(value) : null;
18541
+ const rgb = value ? parseColor$1(value) : null;
18470
18542
  return rgb ? rgb.r <= 32 && rgb.g <= 32 && rgb.b <= 32 : false;
18471
18543
  };
18472
18544
  const removeIfSuspicious = (el) => {
@@ -18579,13 +18651,13 @@ function inlineComputedStyles(svg) {
18579
18651
  const fill = cs.fill;
18580
18652
  const stroke = cs.stroke;
18581
18653
  if (fill && fill !== "none" && fill !== "rgba(0, 0, 0, 0)") {
18582
- const parsed = parseColor(fill);
18654
+ const parsed = parseColor$1(fill);
18583
18655
  if (parsed) el.setAttribute("fill", rgbToHex(parsed.r, parsed.g, parsed.b));
18584
18656
  } else if (fill === "rgba(0, 0, 0, 0)" || fill === "transparent") {
18585
18657
  el.setAttribute("fill", "none");
18586
18658
  }
18587
18659
  if (stroke && stroke !== "none" && stroke !== "rgba(0, 0, 0, 0)") {
18588
- const parsed = parseColor(stroke);
18660
+ const parsed = parseColor$1(stroke);
18589
18661
  if (parsed) el.setAttribute("stroke", rgbToHex(parsed.r, parsed.g, parsed.b));
18590
18662
  }
18591
18663
  }
@@ -18733,7 +18805,7 @@ function setPdfColorFromSvg(pdf, svg, _elementId) {
18733
18805
  const { fill, stroke } = getFirstExplicitColorFromSvg(svg);
18734
18806
  const setColor = (hex, setter) => {
18735
18807
  if (!hex) return;
18736
- const c = parseColor(hex);
18808
+ const c = parseColor$1(hex);
18737
18809
  if (c) pdf[setter](c.r, c.g, c.b);
18738
18810
  };
18739
18811
  setColor(fill, "setFillColor");
@@ -19216,7 +19288,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
19216
19288
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
19217
19289
  sanitizeSvgTreeForPdf(svgToDraw);
19218
19290
  try {
19219
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CfmlwA1-.js");
19291
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-BkERGCkM.js");
19220
19292
  try {
19221
19293
  await logTextMeasurementDiagnostic(svgToDraw);
19222
19294
  } catch {
@@ -19236,7 +19308,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
19236
19308
  if (backgroundGradient && ((_a = backgroundGradient.stops) == null ? void 0 : _a.length) >= 2) {
19237
19309
  const grad = backgroundGradient;
19238
19310
  const colorStops = grad.stops.map((s) => {
19239
- const c = parseColor(s.color);
19311
+ const c = parseColor$1(s.color);
19240
19312
  return {
19241
19313
  offset: Math.max(0, Math.min(1, Number(s.offset))),
19242
19314
  color: c ? [c.r, c.g, c.b] : [0, 0, 0]
@@ -19292,7 +19364,7 @@ function drawPageBackground(pdf, pageIndex, pageWidth, pageHeight, backgroundCol
19292
19364
  pdf.rect(0, 0, pageWidth, pageHeight, "F");
19293
19365
  }
19294
19366
  } else {
19295
- const bgColor = parseColor(backgroundColor && backgroundColor !== "transparent" ? backgroundColor : "#ffffff");
19367
+ const bgColor = parseColor$1(backgroundColor && backgroundColor !== "transparent" ? backgroundColor : "#ffffff");
19296
19368
  if (bgColor) {
19297
19369
  pdf.setFillColor(bgColor.r, bgColor.g, bgColor.b);
19298
19370
  pdf.rect(0, 0, pageWidth, pageHeight, "F");
@@ -19415,6 +19487,123 @@ async function assemblePdfFromSvgs(svgResults, options = {}) {
19415
19487
  pages: svgResults.map((p) => ({ width: p.width, height: p.height }))
19416
19488
  };
19417
19489
  }
19490
+ const OVERLAY_ID_PREFIX = "__pb_";
19491
+ function getNumber(v, fallback = 0) {
19492
+ return typeof v === "number" && Number.isFinite(v) ? v : fallback;
19493
+ }
19494
+ function resolveSize(node, key) {
19495
+ const v = node == null ? void 0 : node[key];
19496
+ if (typeof v === "number" && Number.isFinite(v)) return v;
19497
+ return 0;
19498
+ }
19499
+ function collectOverlays(node, parentLeft, parentTop, inheritedBlur, extraBaseIds, extraExactIds, out) {
19500
+ if (!node || typeof node !== "object") return;
19501
+ const matchesBase = !!(extraBaseIds && typeof node.id === "string" && extraBaseIds.has(baseId(node.id)));
19502
+ const matchesExact = !!(extraExactIds && typeof node.id === "string" && extraExactIds.has(node.id));
19503
+ const isBlurred = inheritedBlur || node.previewBlur === true || matchesBase || matchesExact;
19504
+ const absLeft = parentLeft + getNumber(node.left, 0);
19505
+ const absTop = parentTop + getNumber(node.top, 0);
19506
+ if (node.type === "group") {
19507
+ const children = node.children || node.elements;
19508
+ if (Array.isArray(children)) {
19509
+ for (const child of children) {
19510
+ collectOverlays(child, absLeft, absTop, isBlurred, extraBaseIds, extraExactIds, out);
19511
+ }
19512
+ }
19513
+ return;
19514
+ }
19515
+ if (!isBlurred) return;
19516
+ const scaleX = getNumber(node.scaleX, 1) || 1;
19517
+ const scaleY = getNumber(node.scaleY, 1) || 1;
19518
+ const w = Math.max(4, resolveSize(node, "width") * scaleX);
19519
+ const h = Math.max(4, resolveSize(node, "height") * scaleY);
19520
+ out.push({
19521
+ left: absLeft,
19522
+ top: absTop,
19523
+ width: w,
19524
+ height: h,
19525
+ hintFill: typeof node.fill === "string" ? node.fill : void 0
19526
+ });
19527
+ }
19528
+ function parseColor(c) {
19529
+ if (!c) return null;
19530
+ const s = c.trim();
19531
+ const hex = s.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/i);
19532
+ if (hex) {
19533
+ let h = hex[1];
19534
+ if (h.length === 3) h = h.split("").map((x) => x + x).join("");
19535
+ return {
19536
+ r: parseInt(h.slice(0, 2), 16),
19537
+ g: parseInt(h.slice(2, 4), 16),
19538
+ b: parseInt(h.slice(4, 6), 16)
19539
+ };
19540
+ }
19541
+ const rgb = s.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
19542
+ if (rgb) return { r: +rgb[1], g: +rgb[2], b: +rgb[3] };
19543
+ return null;
19544
+ }
19545
+ function buildOverlay(b, idx) {
19546
+ const c = parseColor(b.hintFill);
19547
+ const lum = c ? (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) / 255 : 0.2;
19548
+ const isLightGlass = lum < 0.5;
19549
+ const baseFill = isLightGlass ? "rgba(245,245,245,0.68)" : "rgba(30,30,30,0.62)";
19550
+ return {
19551
+ id: `${OVERLAY_ID_PREFIX}${idx}`,
19552
+ type: "shape",
19553
+ shapeType: "rounded-rect",
19554
+ left: b.left,
19555
+ top: b.top,
19556
+ width: b.width,
19557
+ height: b.height,
19558
+ cornerRadius: 0,
19559
+ fill: baseFill,
19560
+ stroke: "transparent",
19561
+ strokeWidth: 0,
19562
+ opacity: 1,
19563
+ selectable: false,
19564
+ locked: true,
19565
+ visible: true,
19566
+ scaleX: 1,
19567
+ scaleY: 1
19568
+ };
19569
+ }
19570
+ function injectPreviewBlur(config, options) {
19571
+ const cloned = typeof structuredClone === "function" ? structuredClone(config) : JSON.parse(JSON.stringify(config));
19572
+ const extraBase = (options == null ? void 0 : options.extraElementBaseIds) && options.extraElementBaseIds.size > 0 ? options.extraElementBaseIds : void 0;
19573
+ const extraExact = (options == null ? void 0 : options.extraElementExactIds) && options.extraElementExactIds.size > 0 ? options.extraElementExactIds : void 0;
19574
+ let idx = 0;
19575
+ for (const page of cloned.pages || []) {
19576
+ const children = page.children || page.elements;
19577
+ if (!Array.isArray(children)) continue;
19578
+ const overlays = [];
19579
+ for (const child of children) {
19580
+ collectOverlays(child, 0, 0, false, extraBase, extraExact, overlays);
19581
+ }
19582
+ if (overlays.length === 0) continue;
19583
+ for (const b of overlays) {
19584
+ children.push(buildOverlay(b, idx++));
19585
+ }
19586
+ }
19587
+ return cloned;
19588
+ }
19589
+ function hasAnyPreviewBlur(config) {
19590
+ function walk(node) {
19591
+ if (!node || typeof node !== "object") return false;
19592
+ if (node.previewBlur === true) return true;
19593
+ const children = node.children || node.elements;
19594
+ if (Array.isArray(children)) {
19595
+ for (const c of children) if (walk(c)) return true;
19596
+ }
19597
+ return false;
19598
+ }
19599
+ for (const page of config.pages || []) if (walk(page)) return true;
19600
+ return false;
19601
+ }
19602
+ const previewBlur = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
19603
+ __proto__: null,
19604
+ hasAnyPreviewBlur,
19605
+ injectPreviewBlur
19606
+ }, Symbol.toStringTag, { value: "Module" }));
19418
19607
  const SELECT_COLUMNS = "id,name,description,category,thumbnail_url,preview_images,price,download_count,workspace_id,sort_order,created_at,updated_at";
19419
19608
  async function listPublishedTemplates(options) {
19420
19609
  const { workspaceId, supabaseUrl, supabaseAnonKey, category, limit = 200, offset = 0 } = options;
@@ -19554,63 +19743,65 @@ function setAutoShrinkDebug(enabled) {
19554
19743
  }
19555
19744
  }
19556
19745
  export {
19557
- setAutoShrinkDebug as $,
19746
+ resolveTemplateData as $,
19558
19747
  API_URL as A,
19559
- collectFontsFromConfig as B,
19560
- collectImageUrls as C,
19748
+ collectImageUrls as B,
19749
+ configHasAutoShrinkText$1 as C,
19561
19750
  DEPLOYMENT_VERSION_MARKER as D,
19562
- configHasAutoShrinkText$1 as E,
19751
+ dumpSvgTextDiagnostics as E,
19563
19752
  FONT_FALLBACK_DEVANAGARI as F,
19564
- dumpSvgTextDiagnostics as G,
19565
- embedFont as H,
19566
- embedFontsForConfig as I,
19567
- embedFontsInPdf as J,
19568
- ensureFontsForResolvedConfig as K,
19569
- extractFontFamiliesFromSvgs as L,
19570
- getEmbeddedJsPDFFontName as M,
19571
- getPublishedTemplate as N,
19572
- getTemplateForm as O,
19753
+ embedFont as G,
19754
+ embedFontsForConfig as H,
19755
+ embedFontsInPdf as I,
19756
+ ensureFontsForResolvedConfig as J,
19757
+ extractFontFamiliesFromSvgs as K,
19758
+ getEmbeddedJsPDFFontName as L,
19759
+ getPublishedTemplate as M,
19760
+ getTemplateForm as N,
19761
+ hasAnyPreviewBlur as O,
19573
19762
  PACKAGE_VERSION as P,
19574
- isBundledAssetUrl as Q,
19575
- isFontAvailable as R,
19576
- isPrivateUrl as S,
19763
+ injectPreviewBlur as Q,
19764
+ isBundledAssetUrl as R,
19765
+ isFontAvailable as S,
19577
19766
  TRIANGLE_STROKE_MITER_LIMIT as T,
19578
- listPublishedTemplates as U,
19579
- loadGoogleFontCSS as V,
19580
- normalizeFontFamily as W,
19581
- resolveFontWeight as X,
19582
- resolveFromForm as Y,
19583
- resolveTemplateData as Z,
19584
- rewriteSvgFontsForJsPDF as _,
19767
+ isPrivateUrl as U,
19768
+ listPublishedTemplates as V,
19769
+ loadGoogleFontCSS as W,
19770
+ normalizeFontFamily as X,
19771
+ resolveBlurElementExactIdsFromFlatFormKeys as Y,
19772
+ resolveFontWeight as Z,
19773
+ resolveFromForm as _,
19585
19774
  getAbsoluteBounds as a,
19586
- setBundledAssetPrefixes as a0,
19587
- warmResolvedTemplateForPreview as a1,
19588
- warmTemplateFromForm as a2,
19589
- canvasImageLoader as a3,
19590
- baseId as b,
19775
+ rewriteSvgFontsForJsPDF as a0,
19776
+ setAutoShrinkDebug as a1,
19777
+ setBundledAssetPrefixes as a2,
19778
+ warmResolvedTemplateForPreview as a3,
19779
+ warmTemplateFromForm as a4,
19780
+ canvasImageLoader as a5,
19781
+ getProxiedImageUrl as b,
19591
19782
  captureFabricCanvasSvgForPdf as c,
19592
- getProxiedImageUrl as d,
19593
- bakeEdgeFade as e,
19783
+ bakeEdgeFade as d,
19784
+ isGroup as e,
19594
19785
  findNodeById as f,
19595
19786
  getCanvasForPage as g,
19596
19787
  hasEdgeFade as h,
19597
19788
  isElement as i,
19598
- isGroup as j,
19599
- buildRoundedTrianglePath as k,
19600
- getImageProxyFetchOptions as l,
19601
- getRoundedRectRadii as m,
19789
+ buildRoundedTrianglePath as j,
19790
+ getImageProxyFetchOptions as k,
19791
+ getRoundedRectRadii as l,
19792
+ getTrianglePoints as m,
19602
19793
  normalizeShapeType as n,
19603
- getTrianglePoints as o,
19794
+ FONT_FALLBACK_MATH as o,
19604
19795
  parseTextMarkdown as p,
19605
- FONT_FALLBACK_MATH as q,
19796
+ FONT_FALLBACK_SYMBOLS as q,
19606
19797
  renderSmartElementToSvg as r,
19607
- FONT_FALLBACK_SYMBOLS as s,
19608
- FONT_FILES as t,
19609
- PixldocsPreview as u,
19610
- PixldocsRenderer as v,
19611
- applyThemeToConfig as w,
19612
- assemblePdfFromSvgs as x,
19613
- awaitFontsForConfig as y,
19614
- collectFontDescriptorsFromConfig as z
19798
+ FONT_FILES as s,
19799
+ PixldocsPreview as t,
19800
+ PixldocsRenderer as u,
19801
+ applyThemeToConfig as v,
19802
+ assemblePdfFromSvgs as w,
19803
+ awaitFontsForConfig as x,
19804
+ collectFontDescriptorsFromConfig as y,
19805
+ collectFontsFromConfig as z
19615
19806
  };
19616
- //# sourceMappingURL=index-NWuzIM_r.js.map
19807
+ //# sourceMappingURL=index-C9H38u7P.js.map