@pixldocs/canvas-renderer 0.3.15 → 0.3.16

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/dist/index.cjs CHANGED
@@ -10708,50 +10708,85 @@ class PixldocsRenderer {
10708
10708
  return this.render(resolved.config, options);
10709
10709
  }
10710
10710
  // ─── Internal: render a page using the full PreviewCanvas engine ───
10711
- /**
10712
- * Wait until every image on the Fabric canvas inside the container has loaded.
10713
- * Checks both DOM <img> elements AND Fabric canvas image objects to ensure
10714
- * all async assets are fully painted before capture.
10715
- */
10716
- waitForCanvasImages(container, maxWaitMs = 15e3, pollMs = 200) {
10711
+ getExpectedImageCount(config, pageIndex) {
10712
+ const page = config.pages[pageIndex];
10713
+ if (!(page == null ? void 0 : page.children)) return 0;
10714
+ let count = 0;
10715
+ const walk = (nodes) => {
10716
+ for (const node of nodes) {
10717
+ if (!node || node.visible === false) continue;
10718
+ const src = typeof node.src === "string" ? node.src.trim() : "";
10719
+ const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
10720
+ if (node.type === "image" && (src || imageUrl)) count += 1;
10721
+ if (Array.isArray(node.children) && node.children.length > 0) {
10722
+ walk(node.children);
10723
+ }
10724
+ }
10725
+ };
10726
+ walk(page.children);
10727
+ return count;
10728
+ }
10729
+ waitForCanvasImages(container, expectedImageCount, maxWaitMs = 15e3, pollMs = 120) {
10717
10730
  return new Promise((resolve) => {
10718
10731
  const start = Date.now();
10732
+ let stableFrames = 0;
10733
+ const isRenderableImage = (value) => value instanceof HTMLImageElement && value.complete && value.naturalWidth > 0 && value.naturalHeight > 0;
10734
+ const collectRenderableImages = (obj, seen) => {
10735
+ if (!obj || typeof obj !== "object") return;
10736
+ const candidates = [obj._element, obj._originalElement, obj._filteredEl, obj._cacheCanvasEl];
10737
+ for (const candidate of candidates) {
10738
+ if (isRenderableImage(candidate)) {
10739
+ seen.add(candidate);
10740
+ } else if (candidate instanceof HTMLImageElement) {
10741
+ return false;
10742
+ }
10743
+ }
10744
+ const nested = Array.isArray(obj._objects) ? obj._objects : Array.isArray(obj.objects) ? obj.objects : [];
10745
+ for (const child of nested) {
10746
+ if (collectRenderableImages(child, seen) === false) return false;
10747
+ }
10748
+ return true;
10749
+ };
10750
+ const getFabricCanvas = () => {
10751
+ const registry2 = window.__fabricCanvasRegistry;
10752
+ if (registry2 instanceof Map) {
10753
+ for (const entry of registry2.values()) {
10754
+ const canvas = entry == null ? void 0 : entry.canvas;
10755
+ const el = (canvas == null ? void 0 : canvas.lowerCanvasEl) || (canvas == null ? void 0 : canvas.upperCanvasEl);
10756
+ if (el && container.contains(el)) return canvas;
10757
+ }
10758
+ }
10759
+ return null;
10760
+ };
10761
+ const settle = () => requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
10719
10762
  const check = () => {
10720
- const images = container.querySelectorAll("img");
10721
- let allLoaded = true;
10722
- images.forEach((img) => {
10723
- if (!img.complete) allLoaded = false;
10724
- });
10725
- if (allLoaded) {
10726
- const canvasEl = container.querySelector("canvas.lower-canvas, canvas");
10727
- const fabricCanvas = canvasEl && canvasEl.__fabric;
10728
- if (fabricCanvas && typeof fabricCanvas.getObjects === "function") {
10729
- for (const obj of fabricCanvas.getObjects()) {
10730
- const el = obj._element || obj._originalElement;
10731
- if (el instanceof HTMLImageElement && !el.complete) {
10732
- allLoaded = false;
10733
- break;
10734
- }
10735
- if (obj._objects) {
10736
- for (const child of obj._objects) {
10737
- const childEl = child._element || child._originalElement;
10738
- if (childEl instanceof HTMLImageElement && !childEl.complete) {
10739
- allLoaded = false;
10740
- break;
10741
- }
10742
- }
10743
- if (!allLoaded) break;
10744
- }
10745
- }
10763
+ const elapsed = Date.now() - start;
10764
+ const domImages = Array.from(container.querySelectorAll("img"));
10765
+ const allDomLoaded = domImages.every((img) => img.complete && img.naturalWidth > 0 && img.naturalHeight > 0);
10766
+ const fabricCanvas = getFabricCanvas();
10767
+ const fabricObjects = fabricCanvas && typeof fabricCanvas.getObjects === "function" ? fabricCanvas.getObjects() : [];
10768
+ const renderableImages = /* @__PURE__ */ new Set();
10769
+ const fabricReady = fabricObjects.every((obj) => collectRenderableImages(obj, renderableImages) !== false);
10770
+ const actualImageCount = Math.max(domImages.length, renderableImages.size);
10771
+ !!fabricCanvas && !!(fabricCanvas.lowerCanvasEl || fabricCanvas.upperCanvasEl);
10772
+ const hasExpectedAssets = expectedImageCount === 0 ? true : actualImageCount >= expectedImageCount;
10773
+ const ready = allDomLoaded && fabricReady && hasExpectedAssets;
10774
+ if (ready) {
10775
+ stableFrames += 1;
10776
+ if (stableFrames >= 2) {
10777
+ settle();
10778
+ return;
10746
10779
  }
10780
+ } else {
10781
+ stableFrames = 0;
10747
10782
  }
10748
- if (allLoaded || Date.now() - start > maxWaitMs) {
10749
- requestAnimationFrame(() => setTimeout(resolve, 400));
10783
+ if (elapsed >= maxWaitMs) {
10784
+ settle();
10750
10785
  return;
10751
10786
  }
10752
10787
  setTimeout(check, pollMs);
10753
10788
  };
10754
- setTimeout(check, 500);
10789
+ setTimeout(check, 0);
10755
10790
  });
10756
10791
  }
10757
10792
  getNormalizedGradientStops(gradient) {
@@ -10852,7 +10887,8 @@ class PixldocsRenderer {
10852
10887
  container.remove();
10853
10888
  };
10854
10889
  const onReady = () => {
10855
- this.waitForCanvasImages(container).then(() => {
10890
+ const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
10891
+ this.waitForCanvasImages(container, expectedImageCount).then(() => {
10856
10892
  try {
10857
10893
  const fabricCanvas = container.querySelector("canvas.upper-canvas, canvas");
10858
10894
  if (!fabricCanvas) {