@pixldocs/canvas-renderer 0.3.14 → 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
@@ -10402,12 +10402,7 @@ async function loadGoogleFontCSS(rawFontFamily) {
10402
10402
  const existing = loadingPromises.get(fontFamily);
10403
10403
  if (existing) return existing;
10404
10404
  const promise = (async () => {
10405
- var _a;
10406
10405
  try {
10407
- if ((_a = document.fonts) == null ? void 0 : _a.check(`16px "${fontFamily}"`)) {
10408
- loadedFonts.add(fontFamily);
10409
- return;
10410
- }
10411
10406
  const encoded = encodeURIComponent(fontFamily);
10412
10407
  const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
10413
10408
  const link = document.createElement("link");
@@ -10713,26 +10708,85 @@ class PixldocsRenderer {
10713
10708
  return this.render(resolved.config, options);
10714
10709
  }
10715
10710
  // ─── Internal: render a page using the full PreviewCanvas engine ───
10716
- /**
10717
- * Wait until every image on the Fabric canvas inside the container has loaded.
10718
- * Polls img elements since we can't hook into Fabric's internal load events.
10719
- */
10720
- 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) {
10721
10730
  return new Promise((resolve) => {
10722
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()));
10723
10762
  const check = () => {
10724
- const images = container.querySelectorAll("img");
10725
- let allLoaded = true;
10726
- images.forEach((img) => {
10727
- if (!img.complete) allLoaded = false;
10728
- });
10729
- if (allLoaded || Date.now() - start > maxWaitMs) {
10730
- requestAnimationFrame(() => setTimeout(resolve, 400));
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;
10779
+ }
10780
+ } else {
10781
+ stableFrames = 0;
10782
+ }
10783
+ if (elapsed >= maxWaitMs) {
10784
+ settle();
10731
10785
  return;
10732
10786
  }
10733
10787
  setTimeout(check, pollMs);
10734
10788
  };
10735
- setTimeout(check, 500);
10789
+ setTimeout(check, 0);
10736
10790
  });
10737
10791
  }
10738
10792
  getNormalizedGradientStops(gradient) {
@@ -10833,7 +10887,8 @@ class PixldocsRenderer {
10833
10887
  container.remove();
10834
10888
  };
10835
10889
  const onReady = () => {
10836
- this.waitForCanvasImages(container).then(() => {
10890
+ const expectedImageCount = this.getExpectedImageCount(config, pageIndex);
10891
+ this.waitForCanvasImages(container, expectedImageCount).then(() => {
10837
10892
  try {
10838
10893
  const fabricCanvas = container.querySelector("canvas.upper-canvas, canvas");
10839
10894
  if (!fabricCanvas) {