@pixldocs/canvas-renderer 0.5.491 → 0.5.493

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.
@@ -3,7 +3,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  var _a, _b, _c;
5
5
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
6
- import { forwardRef, useRef, useState, useCallback, useMemo, useEffect, useImperativeHandle, createElement } from "react";
6
+ import { forwardRef, useRef, useState, useEffect, useCallback, useMemo, useImperativeHandle, createElement } from "react";
7
7
  import { flushSync } from "react-dom";
8
8
  import { toast } from "sonner";
9
9
  import { create } from "zustand";
@@ -12061,7 +12061,29 @@ const PageCanvas = forwardRef(
12061
12061
  const [rotationLabel, setRotationLabel] = useState(null);
12062
12062
  const [sizeLabel, setSizeLabel] = useState(null);
12063
12063
  const [ready, setReady] = useState(false);
12064
+ const onReadyRef = useRef(onReady);
12065
+ const pendingInitialImageLoadsRef = useRef(0);
12064
12066
  const [unlockRequestId, setUnlockRequestId] = useState(0);
12067
+ useEffect(() => {
12068
+ onReadyRef.current = onReady;
12069
+ }, [onReady]);
12070
+ const trackInitialImageLoad = useCallback((promise) => {
12071
+ if (hasNotifiedReadyForPageRef.current === pageId) {
12072
+ return;
12073
+ }
12074
+ pendingInitialImageLoadsRef.current += 1;
12075
+ let settled = false;
12076
+ const finish = () => {
12077
+ if (settled) return;
12078
+ settled = true;
12079
+ pendingInitialImageLoadsRef.current = Math.max(0, pendingInitialImageLoadsRef.current - 1);
12080
+ };
12081
+ const timeout = window.setTimeout(finish, 12e3);
12082
+ void promise.finally(() => {
12083
+ window.clearTimeout(timeout);
12084
+ finish();
12085
+ });
12086
+ }, [pageId]);
12065
12087
  const applyLogicalGroupSelectionVisualState = useCallback((selection, groupId) => {
12066
12088
  var _a2;
12067
12089
  selection.__pixldocsGroupSelection = groupId;
@@ -12272,7 +12294,23 @@ const PageCanvas = forwardRef(
12272
12294
  }, [selectedIds]);
12273
12295
  useEffect(() => {
12274
12296
  const fc = fabricRef.current;
12275
- if (!fc || !isActive || !currentPage) return;
12297
+ if (!fc || !currentPage) return;
12298
+ if (!isActive) {
12299
+ try {
12300
+ fc.__activeEditingGroupId = null;
12301
+ drilledGroupIdRef.current = null;
12302
+ setDrilledGroupBounds(null);
12303
+ preserveActiveSelectionAfterTransformRef.current = null;
12304
+ recentGroupSelectionRestoreRef.current = null;
12305
+ if (fc.getActiveObject()) {
12306
+ fc.discardActiveObject();
12307
+ }
12308
+ clearCanvasActiveVisuals(fc);
12309
+ fc.requestRenderAll();
12310
+ } catch {
12311
+ }
12312
+ return;
12313
+ }
12276
12314
  const pageChildren2 = currentPage.children ?? [];
12277
12315
  const hasMissingSelection = selectedIds.some((id) => !findNodeById(pageChildren2, id));
12278
12316
  const activeEditingGroupId = fc.__activeEditingGroupId ?? null;
@@ -17098,6 +17136,10 @@ const PageCanvas = forwardRef(
17098
17136
  const innerImg = ct == null ? void 0 : ct._img;
17099
17137
  const innerSrc = ((_a2 = innerImg == null ? void 0 : innerImg.getSrc) == null ? void 0 : _a2.call(innerImg)) || ((_b2 = innerImg == null ? void 0 : innerImg._originalElement) == null ? void 0 : _b2.src) || (innerImg == null ? void 0 : innerImg.src) || "";
17100
17138
  const isPlaceholder = !innerSrc || innerSrc === EMPTY_IMAGE_PLACEHOLDER_DATA_URL;
17139
+ const activatedAt = cropGroupActivatedAtRef.current.get(target) ?? 0;
17140
+ if (Date.now() - activatedAt < 400) {
17141
+ return;
17142
+ }
17101
17143
  const targetId = getObjectId(target);
17102
17144
  const pageChildrenForCrop = ((_c2 = useEditorStore.getState().canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _c2.children) ?? [];
17103
17145
  const parentForCrop = targetId ? findParentGroup(pageChildrenForCrop, targetId) : null;
@@ -17114,10 +17156,6 @@ const PageCanvas = forwardRef(
17114
17156
  }
17115
17157
  return;
17116
17158
  }
17117
- const activatedAt = cropGroupActivatedAtRef.current.get(target) ?? 0;
17118
- if (Date.now() - activatedAt < 400) {
17119
- return;
17120
- }
17121
17159
  }
17122
17160
  if (innerImg && !isPlaceholder && !isCropGroupInCropMode(target)) {
17123
17161
  enterCropMode(target);
@@ -18081,7 +18119,7 @@ const PageCanvas = forwardRef(
18081
18119
  placeholder.dirty = true;
18082
18120
  fc.requestRenderAll();
18083
18121
  if (imageUrl) {
18084
- loadImageAsync2(elementForImage, placeholder, fc);
18122
+ trackInitialImageLoad(loadImageAsync2(elementForImage, placeholder, fc));
18085
18123
  }
18086
18124
  } else {
18087
18125
  const resolvedSizeCreate = pageTree.length > 0 ? getNodeBounds(element, pageTree) : { width: typeof element.width === "number" ? element.width : 200, height: typeof element.height === "number" ? element.height : 50 };
@@ -18344,12 +18382,35 @@ const PageCanvas = forwardRef(
18344
18382
  }, [allowEditing, isActive, pageId]);
18345
18383
  useEffect(() => {
18346
18384
  if (!ready || hasRunPostReadyReflowForPageRef.current === pageId) return;
18347
- hasRunPostReadyReflowForPageRef.current = pageId;
18348
18385
  let cancelled = false;
18386
+ let retryTimer = null;
18387
+ const readyWaitStartedAt = performance.now();
18388
+ const maxAssetWaitMs = 2500;
18389
+ let notifyRaf1 = null;
18390
+ let notifyRaf2 = null;
18349
18391
  const notifyReady = () => {
18350
18392
  if (cancelled || hasNotifiedReadyForPageRef.current === pageId) return;
18351
- hasNotifiedReadyForPageRef.current = pageId;
18352
- onReady == null ? void 0 : onReady();
18393
+ const finishWhenAssetsSettle = () => {
18394
+ if (cancelled || hasNotifiedReadyForPageRef.current === pageId) return;
18395
+ if (pendingInitialImageLoadsRef.current > 0 && performance.now() - readyWaitStartedAt < maxAssetWaitMs) {
18396
+ retryTimer = window.setTimeout(finishWhenAssetsSettle, 50);
18397
+ return;
18398
+ }
18399
+ notifyRaf1 = requestAnimationFrame(() => {
18400
+ notifyRaf2 = requestAnimationFrame(() => {
18401
+ var _a2;
18402
+ if (cancelled || hasNotifiedReadyForPageRef.current === pageId) return;
18403
+ if (pendingInitialImageLoadsRef.current > 0 && performance.now() - readyWaitStartedAt < maxAssetWaitMs) {
18404
+ retryTimer = window.setTimeout(finishWhenAssetsSettle, 50);
18405
+ return;
18406
+ }
18407
+ hasRunPostReadyReflowForPageRef.current = pageId;
18408
+ hasNotifiedReadyForPageRef.current = pageId;
18409
+ (_a2 = onReadyRef.current) == null ? void 0 : _a2.call(onReadyRef);
18410
+ });
18411
+ });
18412
+ };
18413
+ finishWhenAssetsSettle();
18353
18414
  };
18354
18415
  const runReflowAndPersist = () => {
18355
18416
  var _a2;
@@ -18357,37 +18418,42 @@ const PageCanvas = forwardRef(
18357
18418
  if (!fc || cancelled) return;
18358
18419
  const state = useEditorStore.getState();
18359
18420
  const repositionTree = isPreviewMode && (pageChildren == null ? void 0 : pageChildren.length) ? pageChildren ?? [] : ((_a2 = state.canvas.pages.find((p) => p.id === pageId)) == null ? void 0 : _a2.children) ?? [];
18360
- runFontReloadAndReflow({
18361
- canvas: fc,
18362
- pageTree: repositionTree,
18363
- pageBoundsOptions,
18364
- preserveGlobalFontCache,
18365
- // Persist measured Textbox sizes back to the editor store. The
18366
- // headless path passes no callback (it has no store to persist into).
18367
- persistTextboxSize: (id, measured, el) => {
18368
- const storeW = el.width ?? 0;
18369
- const storeH = el.height ?? 0;
18370
- const shouldKeepFixedSize = el.overflowPolicy === "auto-shrink";
18371
- const updates = {};
18372
- if (!shouldKeepFixedSize && measured.width > 0 && typeof storeW === "number" && Math.abs(measured.width - storeW) > 0.1) {
18373
- updates.width = measured.width;
18374
- }
18375
- if (shouldKeepFixedSize) {
18376
- if (measured.height > 0 && typeof storeH === "number" && measured.height < storeH - 0.5) updates.height = measured.height;
18377
- } else {
18378
- if (measured.height > 0 && (typeof storeH !== "number" || Math.abs(measured.height - storeH) > 0.1)) updates.height = measured.height;
18379
- }
18380
- if (Object.keys(updates).length > 0) {
18381
- state.updateElement(id, updates, { recordHistory: false, skipLayoutRecalc: true });
18382
- }
18383
- },
18384
- // Rebuild section/background groups before resnapping top-level objects.
18385
- preResnapSync: (isPreviewMode || isExportMode) && doSyncRef.current ? () => {
18386
- var _a3;
18387
- return (_a3 = doSyncRef.current) == null ? void 0 : _a3.call(doSyncRef);
18388
- } : void 0
18389
- });
18421
+ try {
18422
+ runFontReloadAndReflow({
18423
+ canvas: fc,
18424
+ pageTree: repositionTree,
18425
+ pageBoundsOptions,
18426
+ preserveGlobalFontCache,
18427
+ // Persist measured Textbox sizes back to the editor store. The
18428
+ // headless path passes no callback (it has no store to persist into).
18429
+ persistTextboxSize: (id, measured, el) => {
18430
+ const storeW = el.width ?? 0;
18431
+ const storeH = el.height ?? 0;
18432
+ const shouldKeepFixedSize = el.overflowPolicy === "auto-shrink";
18433
+ const updates = {};
18434
+ if (!shouldKeepFixedSize && measured.width > 0 && typeof storeW === "number" && Math.abs(measured.width - storeW) > 0.1) {
18435
+ updates.width = measured.width;
18436
+ }
18437
+ if (shouldKeepFixedSize) {
18438
+ if (measured.height > 0 && typeof storeH === "number" && measured.height < storeH - 0.5) updates.height = measured.height;
18439
+ } else {
18440
+ if (measured.height > 0 && (typeof storeH !== "number" || Math.abs(measured.height - storeH) > 0.1)) updates.height = measured.height;
18441
+ }
18442
+ if (Object.keys(updates).length > 0) {
18443
+ state.updateElement(id, updates, { recordHistory: false, skipLayoutRecalc: true });
18444
+ }
18445
+ },
18446
+ // Rebuild section/background groups before resnapping top-level objects.
18447
+ preResnapSync: (isPreviewMode || isExportMode) && doSyncRef.current ? () => {
18448
+ var _a3;
18449
+ return (_a3 = doSyncRef.current) == null ? void 0 : _a3.call(doSyncRef);
18450
+ } : void 0
18451
+ });
18452
+ } catch (err) {
18453
+ console.warn("[PageCanvas] post-ready reflow failed; continuing canvas ready state", err);
18454
+ }
18390
18455
  };
18456
+ const fallbackTimer = window.setTimeout(notifyReady, 3500);
18391
18457
  const raf1 = requestAnimationFrame(() => {
18392
18458
  requestAnimationFrame(() => {
18393
18459
  if (cancelled) return;
@@ -18403,9 +18469,13 @@ const PageCanvas = forwardRef(
18403
18469
  });
18404
18470
  return () => {
18405
18471
  cancelled = true;
18472
+ window.clearTimeout(fallbackTimer);
18406
18473
  cancelAnimationFrame(raf1);
18474
+ if (retryTimer !== null) window.clearTimeout(retryTimer);
18475
+ if (notifyRaf1 !== null) cancelAnimationFrame(notifyRaf1);
18476
+ if (notifyRaf2 !== null) cancelAnimationFrame(notifyRaf2);
18407
18477
  };
18408
- }, [ready, pageId, onReady]);
18478
+ }, [ready, pageId]);
18409
18479
  useEffect(() => {
18410
18480
  var _a2;
18411
18481
  const fc = fabricRef.current;
@@ -21988,7 +22058,6 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
21988
22058
  return void 0;
21989
22059
  }
21990
22060
  const applyValue = (elementId, targetProperty, value, fieldKey, svgColorKey) => {
21991
- var _a3;
21992
22061
  const isTextLike = targetProperty === "text" || targetProperty === "content";
21993
22062
  const isImageLike = targetProperty === "src" || targetProperty === "imageUrl";
21994
22063
  if (value === void 0 || value === null) {
@@ -22014,19 +22083,32 @@ function applyFormDataToConfig(config, mappings, formValues, repeatableSectionsF
22014
22083
  }
22015
22084
  }
22016
22085
  if (elementId === PAGE_BACKGROUND_ELEMENT_ID && targetProperty === "backgroundColor") {
22017
- if ((_a3 = pages[0]) == null ? void 0 : _a3.settings) {
22018
- const settings = pages[0].settings;
22086
+ let applied = false;
22087
+ const hasExplicitBg = (settings) => {
22088
+ const raw = settings.backgroundColor;
22089
+ if (typeof raw !== "string") return false;
22090
+ const trimmed = raw.trim().toLowerCase();
22091
+ if (!trimmed) return false;
22092
+ if (trimmed === "transparent" || trimmed === "none") return false;
22093
+ return true;
22094
+ };
22095
+ for (const page of pages) {
22096
+ if (!page.settings) continue;
22097
+ const settings = page.settings;
22098
+ if (hasExplicitBg(settings) && !("backgroundGradient" in settings)) continue;
22019
22099
  if (isGradientConfig(effectiveValue)) {
22020
22100
  settings.backgroundGradient = effectiveValue;
22021
22101
  settings.backgroundColor = "";
22022
- return true;
22102
+ applied = true;
22103
+ continue;
22023
22104
  }
22024
22105
  if (typeof effectiveValue === "string") {
22025
22106
  if ("backgroundGradient" in settings) delete settings.backgroundGradient;
22026
22107
  settings.backgroundColor = effectiveValue;
22027
- return true;
22108
+ applied = true;
22028
22109
  }
22029
22110
  }
22111
+ if (applied) return true;
22030
22112
  }
22031
22113
  for (const page of pages) {
22032
22114
  if (page.children && setInTree(page.children, elementId, targetProperty, effectiveValue, svgColorKey)) return true;
@@ -25741,7 +25823,19 @@ function PixldocsPreview(props) {
25741
25823
  isResolveMode ? props.themeId : void 0
25742
25824
  ]);
25743
25825
  const config = isResolveMode ? resolvedConfig : props.config;
25744
- const previewKey = useMemo(() => `${pageIndex}`, [pageIndex]);
25826
+ const previewKey = useMemo(() => {
25827
+ var _a3, _b3;
25828
+ const page = (_a3 = config == null ? void 0 : config.pages) == null ? void 0 : _a3[pageIndex];
25829
+ const settings = (page == null ? void 0 : page.settings) ?? {};
25830
+ const gradientSignature = settings.backgroundGradient ? JSON.stringify(settings.backgroundGradient) : "";
25831
+ return [
25832
+ pageIndex,
25833
+ (page == null ? void 0 : page.id) ?? "page",
25834
+ settings.backgroundColor ?? "",
25835
+ gradientSignature,
25836
+ ((_b3 = page == null ? void 0 : page.children) == null ? void 0 : _b3.length) ?? 0
25837
+ ].join("|");
25838
+ }, [config, pageIndex]);
25745
25839
  const fontSignature = useMemo(() => computeFontSignature(config), [config]);
25746
25840
  const imageSignature = useMemo(() => computeImageSignature(config), [config]);
25747
25841
  const [imagesReady, setImagesReady] = useState(true);
@@ -26424,9 +26518,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26424
26518
  }
26425
26519
  return svgString;
26426
26520
  }
26427
- const resolvedPackageVersion = "0.5.491";
26521
+ const resolvedPackageVersion = "0.5.493";
26428
26522
  const PACKAGE_VERSION = resolvedPackageVersion;
26429
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.491";
26523
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.493";
26430
26524
  const roundParityValue = (value) => {
26431
26525
  if (typeof value !== "number") return value;
26432
26526
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -27240,7 +27334,7 @@ class PixldocsRenderer {
27240
27334
  await this.waitForCanvasScene(container, cloned, i);
27241
27335
  }
27242
27336
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
27243
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-7V15HkWe.js");
27337
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CouKfyYT.js");
27244
27338
  const prepared = preparePagesForExport(
27245
27339
  cloned.pages,
27246
27340
  canvasWidth,
@@ -29560,7 +29654,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29560
29654
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29561
29655
  sanitizeSvgTreeForPdf(svgToDraw);
29562
29656
  try {
29563
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-7V15HkWe.js");
29657
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CouKfyYT.js");
29564
29658
  try {
29565
29659
  await logTextMeasurementDiagnostic(svgToDraw);
29566
29660
  } catch {
@@ -29877,4 +29971,4 @@ export {
29877
29971
  buildTeaserBlurFlatKeys as y,
29878
29972
  collectFontDescriptorsFromConfig as z
29879
29973
  };
29880
- //# sourceMappingURL=index-Cr96RvlZ.js.map
29974
+ //# sourceMappingURL=index-yWZhNUVq.js.map