@pixldocs/canvas-renderer 0.5.491 → 0.5.492

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.
@@ -12079,7 +12079,29 @@ const PageCanvas = react.forwardRef(
12079
12079
  const [rotationLabel, setRotationLabel] = react.useState(null);
12080
12080
  const [sizeLabel, setSizeLabel] = react.useState(null);
12081
12081
  const [ready, setReady] = react.useState(false);
12082
+ const onReadyRef = react.useRef(onReady);
12083
+ const pendingInitialImageLoadsRef = react.useRef(0);
12082
12084
  const [unlockRequestId, setUnlockRequestId] = react.useState(0);
12085
+ react.useEffect(() => {
12086
+ onReadyRef.current = onReady;
12087
+ }, [onReady]);
12088
+ const trackInitialImageLoad = react.useCallback((promise) => {
12089
+ if (hasNotifiedReadyForPageRef.current === pageId) {
12090
+ return;
12091
+ }
12092
+ pendingInitialImageLoadsRef.current += 1;
12093
+ let settled = false;
12094
+ const finish = () => {
12095
+ if (settled) return;
12096
+ settled = true;
12097
+ pendingInitialImageLoadsRef.current = Math.max(0, pendingInitialImageLoadsRef.current - 1);
12098
+ };
12099
+ const timeout = window.setTimeout(finish, 12e3);
12100
+ void promise.finally(() => {
12101
+ window.clearTimeout(timeout);
12102
+ finish();
12103
+ });
12104
+ }, [pageId]);
12083
12105
  const applyLogicalGroupSelectionVisualState = react.useCallback((selection, groupId) => {
12084
12106
  var _a2;
12085
12107
  selection.__pixldocsGroupSelection = groupId;
@@ -18099,7 +18121,7 @@ const PageCanvas = react.forwardRef(
18099
18121
  placeholder.dirty = true;
18100
18122
  fc.requestRenderAll();
18101
18123
  if (imageUrl) {
18102
- loadImageAsync2(elementForImage, placeholder, fc);
18124
+ trackInitialImageLoad(loadImageAsync2(elementForImage, placeholder, fc));
18103
18125
  }
18104
18126
  } else {
18105
18127
  const resolvedSizeCreate = pageTree.length > 0 ? getNodeBounds(element, pageTree) : { width: typeof element.width === "number" ? element.width : 200, height: typeof element.height === "number" ? element.height : 50 };
@@ -18362,12 +18384,35 @@ const PageCanvas = react.forwardRef(
18362
18384
  }, [allowEditing, isActive, pageId]);
18363
18385
  react.useEffect(() => {
18364
18386
  if (!ready || hasRunPostReadyReflowForPageRef.current === pageId) return;
18365
- hasRunPostReadyReflowForPageRef.current = pageId;
18366
18387
  let cancelled = false;
18388
+ let retryTimer = null;
18389
+ const readyWaitStartedAt = performance.now();
18390
+ const maxAssetWaitMs = 2500;
18391
+ let notifyRaf1 = null;
18392
+ let notifyRaf2 = null;
18367
18393
  const notifyReady = () => {
18368
18394
  if (cancelled || hasNotifiedReadyForPageRef.current === pageId) return;
18369
- hasNotifiedReadyForPageRef.current = pageId;
18370
- onReady == null ? void 0 : onReady();
18395
+ const finishWhenAssetsSettle = () => {
18396
+ if (cancelled || hasNotifiedReadyForPageRef.current === pageId) return;
18397
+ if (pendingInitialImageLoadsRef.current > 0 && performance.now() - readyWaitStartedAt < maxAssetWaitMs) {
18398
+ retryTimer = window.setTimeout(finishWhenAssetsSettle, 50);
18399
+ return;
18400
+ }
18401
+ notifyRaf1 = requestAnimationFrame(() => {
18402
+ notifyRaf2 = requestAnimationFrame(() => {
18403
+ var _a2;
18404
+ if (cancelled || hasNotifiedReadyForPageRef.current === pageId) return;
18405
+ if (pendingInitialImageLoadsRef.current > 0 && performance.now() - readyWaitStartedAt < maxAssetWaitMs) {
18406
+ retryTimer = window.setTimeout(finishWhenAssetsSettle, 50);
18407
+ return;
18408
+ }
18409
+ hasRunPostReadyReflowForPageRef.current = pageId;
18410
+ hasNotifiedReadyForPageRef.current = pageId;
18411
+ (_a2 = onReadyRef.current) == null ? void 0 : _a2.call(onReadyRef);
18412
+ });
18413
+ });
18414
+ };
18415
+ finishWhenAssetsSettle();
18371
18416
  };
18372
18417
  const runReflowAndPersist = () => {
18373
18418
  var _a2;
@@ -18375,37 +18420,42 @@ const PageCanvas = react.forwardRef(
18375
18420
  if (!fc || cancelled) return;
18376
18421
  const state = useEditorStore.getState();
18377
18422
  const repositionTree = isPreviewMode && (pageChildren == null ? void 0 : pageChildren.length) ? pageChildren ?? [] : ((_a2 = state.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _a2.children) ?? [];
18378
- runFontReloadAndReflow({
18379
- canvas: fc,
18380
- pageTree: repositionTree,
18381
- pageBoundsOptions,
18382
- preserveGlobalFontCache,
18383
- // Persist measured Textbox sizes back to the editor store. The
18384
- // headless path passes no callback (it has no store to persist into).
18385
- persistTextboxSize: (id, measured, el) => {
18386
- const storeW = el.width ?? 0;
18387
- const storeH = el.height ?? 0;
18388
- const shouldKeepFixedSize = el.overflowPolicy === "auto-shrink";
18389
- const updates = {};
18390
- if (!shouldKeepFixedSize && measured.width > 0 && typeof storeW === "number" && Math.abs(measured.width - storeW) > 0.1) {
18391
- updates.width = measured.width;
18392
- }
18393
- if (shouldKeepFixedSize) {
18394
- if (measured.height > 0 && typeof storeH === "number" && measured.height < storeH - 0.5) updates.height = measured.height;
18395
- } else {
18396
- if (measured.height > 0 && (typeof storeH !== "number" || Math.abs(measured.height - storeH) > 0.1)) updates.height = measured.height;
18397
- }
18398
- if (Object.keys(updates).length > 0) {
18399
- state.updateElement(id, updates, { recordHistory: false, skipLayoutRecalc: true });
18400
- }
18401
- },
18402
- // Rebuild section/background groups before resnapping top-level objects.
18403
- preResnapSync: (isPreviewMode || isExportMode) && doSyncRef.current ? () => {
18404
- var _a3;
18405
- return (_a3 = doSyncRef.current) == null ? void 0 : _a3.call(doSyncRef);
18406
- } : void 0
18407
- });
18423
+ try {
18424
+ runFontReloadAndReflow({
18425
+ canvas: fc,
18426
+ pageTree: repositionTree,
18427
+ pageBoundsOptions,
18428
+ preserveGlobalFontCache,
18429
+ // Persist measured Textbox sizes back to the editor store. The
18430
+ // headless path passes no callback (it has no store to persist into).
18431
+ persistTextboxSize: (id, measured, el) => {
18432
+ const storeW = el.width ?? 0;
18433
+ const storeH = el.height ?? 0;
18434
+ const shouldKeepFixedSize = el.overflowPolicy === "auto-shrink";
18435
+ const updates = {};
18436
+ if (!shouldKeepFixedSize && measured.width > 0 && typeof storeW === "number" && Math.abs(measured.width - storeW) > 0.1) {
18437
+ updates.width = measured.width;
18438
+ }
18439
+ if (shouldKeepFixedSize) {
18440
+ if (measured.height > 0 && typeof storeH === "number" && measured.height < storeH - 0.5) updates.height = measured.height;
18441
+ } else {
18442
+ if (measured.height > 0 && (typeof storeH !== "number" || Math.abs(measured.height - storeH) > 0.1)) updates.height = measured.height;
18443
+ }
18444
+ if (Object.keys(updates).length > 0) {
18445
+ state.updateElement(id, updates, { recordHistory: false, skipLayoutRecalc: true });
18446
+ }
18447
+ },
18448
+ // Rebuild section/background groups before resnapping top-level objects.
18449
+ preResnapSync: (isPreviewMode || isExportMode) && doSyncRef.current ? () => {
18450
+ var _a3;
18451
+ return (_a3 = doSyncRef.current) == null ? void 0 : _a3.call(doSyncRef);
18452
+ } : void 0
18453
+ });
18454
+ } catch (err) {
18455
+ console.warn("[PageCanvas] post-ready reflow failed; continuing canvas ready state", err);
18456
+ }
18408
18457
  };
18458
+ const fallbackTimer = window.setTimeout(notifyReady, 3500);
18409
18459
  const raf1 = requestAnimationFrame(() => {
18410
18460
  requestAnimationFrame(() => {
18411
18461
  if (cancelled) return;
@@ -18421,9 +18471,13 @@ const PageCanvas = react.forwardRef(
18421
18471
  });
18422
18472
  return () => {
18423
18473
  cancelled = true;
18474
+ window.clearTimeout(fallbackTimer);
18424
18475
  cancelAnimationFrame(raf1);
18476
+ if (retryTimer !== null) window.clearTimeout(retryTimer);
18477
+ if (notifyRaf1 !== null) cancelAnimationFrame(notifyRaf1);
18478
+ if (notifyRaf2 !== null) cancelAnimationFrame(notifyRaf2);
18425
18479
  };
18426
- }, [ready, pageId, onReady]);
18480
+ }, [ready, pageId]);
18427
18481
  react.useEffect(() => {
18428
18482
  var _a2;
18429
18483
  const fc = fabricRef.current;
@@ -22006,7 +22060,6 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
22006
22060
  return void 0;
22007
22061
  }
22008
22062
  const applyValue = (elementId, targetProperty, value, fieldKey, svgColorKey) => {
22009
- var _a3;
22010
22063
  const isTextLike = targetProperty === "text" || targetProperty === "content";
22011
22064
  const isImageLike = targetProperty === "src" || targetProperty === "imageUrl";
22012
22065
  if (value === void 0 || value === null) {
@@ -22032,19 +22085,32 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
22032
22085
  }
22033
22086
  }
22034
22087
  if (elementId === PAGE_BACKGROUND_ELEMENT_ID && targetProperty === "backgroundColor") {
22035
- if ((_a3 = pages[0]) == null ? void 0 : _a3.settings) {
22036
- const settings = pages[0].settings;
22088
+ let applied = false;
22089
+ const hasExplicitBg = (settings) => {
22090
+ const raw = settings.backgroundColor;
22091
+ if (typeof raw !== "string") return false;
22092
+ const trimmed = raw.trim().toLowerCase();
22093
+ if (!trimmed) return false;
22094
+ if (trimmed === "transparent" || trimmed === "none") return false;
22095
+ return true;
22096
+ };
22097
+ for (const page of pages) {
22098
+ if (!page.settings) continue;
22099
+ const settings = page.settings;
22100
+ if (hasExplicitBg(settings) && !("backgroundGradient" in settings)) continue;
22037
22101
  if (isGradientConfig(effectiveValue)) {
22038
22102
  settings.backgroundGradient = effectiveValue;
22039
22103
  settings.backgroundColor = "";
22040
- return true;
22104
+ applied = true;
22105
+ continue;
22041
22106
  }
22042
22107
  if (typeof effectiveValue === "string") {
22043
22108
  if ("backgroundGradient" in settings) delete settings.backgroundGradient;
22044
22109
  settings.backgroundColor = effectiveValue;
22045
- return true;
22110
+ applied = true;
22046
22111
  }
22047
22112
  }
22113
+ if (applied) return true;
22048
22114
  }
22049
22115
  for (const page of pages) {
22050
22116
  if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue, svgColorKey)) return true;
@@ -25759,7 +25825,19 @@ function PixldocsPreview(props) {
25759
25825
  isResolveMode ? props.themeId : void 0
25760
25826
  ]);
25761
25827
  const config = isResolveMode ? resolvedConfig : props.config;
25762
- const previewKey = react.useMemo(() => `${pageIndex}`, [pageIndex]);
25828
+ const previewKey = react.useMemo(() => {
25829
+ var _a3, _b3;
25830
+ const page = (_a3 = config == null ? void 0 : config.pages) == null ? void 0 : _a3[pageIndex];
25831
+ const settings = (page == null ? void 0 : page.settings) ?? {};
25832
+ const gradientSignature = settings.backgroundGradient ? JSON.stringify(settings.backgroundGradient) : "";
25833
+ return [
25834
+ pageIndex,
25835
+ (page == null ? void 0 : page.id) ?? "page",
25836
+ settings.backgroundColor ?? "",
25837
+ gradientSignature,
25838
+ ((_b3 = page == null ? void 0 : page.children) == null ? void 0 : _b3.length) ?? 0
25839
+ ].join("|");
25840
+ }, [config, pageIndex]);
25763
25841
  const fontSignature = react.useMemo(() => computeFontSignature(config), [config]);
25764
25842
  const imageSignature = react.useMemo(() => computeImageSignature(config), [config]);
25765
25843
  const [imagesReady, setImagesReady] = react.useState(true);
@@ -26442,9 +26520,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26442
26520
  }
26443
26521
  return svgString;
26444
26522
  }
26445
- const resolvedPackageVersion = "0.5.491";
26523
+ const resolvedPackageVersion = "0.5.492";
26446
26524
  const PACKAGE_VERSION = resolvedPackageVersion;
26447
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.491";
26525
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.492";
26448
26526
  const roundParityValue = (value) => {
26449
26527
  if (typeof value !== "number") return value;
26450
26528
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -27258,7 +27336,7 @@ class PixldocsRenderer {
27258
27336
  await this.waitForCanvasScene(container, cloned, i);
27259
27337
  }
27260
27338
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
27261
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-BWUlBEaK.cjs"));
27339
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-PMQQ8BkR.cjs"));
27262
27340
  const prepared = preparePagesForExport(
27263
27341
  cloned.pages,
27264
27342
  canvasWidth,
@@ -29578,7 +29656,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29578
29656
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29579
29657
  sanitizeSvgTreeForPdf(svgToDraw);
29580
29658
  try {
29581
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-BWUlBEaK.cjs"));
29659
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-PMQQ8BkR.cjs"));
29582
29660
  try {
29583
29661
  await logTextMeasurementDiagnostic(svgToDraw);
29584
29662
  } catch {
@@ -29892,4 +29970,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
29892
29970
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
29893
29971
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
29894
29972
  exports.warmTemplateFromForm = warmTemplateFromForm;
29895
- //# sourceMappingURL=index-sfUNqu61.cjs.map
29973
+ //# sourceMappingURL=index-CoJw0tIl.cjs.map