@pixldocs/canvas-renderer 0.3.13 → 0.3.15

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
@@ -10388,79 +10388,6 @@ function paintRepeatableSections(config, repeatableSections) {
10388
10388
  }
10389
10389
  }
10390
10390
  }
10391
- function PixldocsPreview(props) {
10392
- const {
10393
- pageIndex = 0,
10394
- zoom = 1,
10395
- absoluteZoom = false,
10396
- imageProxyUrl,
10397
- className,
10398
- style,
10399
- onDynamicFieldClick,
10400
- onReady,
10401
- onError
10402
- } = props;
10403
- react.useEffect(() => {
10404
- setPackageApiUrl(imageProxyUrl);
10405
- }, [imageProxyUrl]);
10406
- const [resolvedConfig, setResolvedConfig] = react.useState(null);
10407
- const [isLoading, setIsLoading] = react.useState(false);
10408
- const isResolveMode = !("config" in props && props.config);
10409
- react.useEffect(() => {
10410
- if (!isResolveMode) {
10411
- setResolvedConfig(null);
10412
- return;
10413
- }
10414
- const p = props;
10415
- if (!p.templateId || !p.formSchemaId || !p.supabaseUrl || !p.supabaseAnonKey) return;
10416
- let cancelled = false;
10417
- setIsLoading(true);
10418
- resolveFromForm({
10419
- templateId: p.templateId,
10420
- formSchemaId: p.formSchemaId,
10421
- sectionState: p.sectionState,
10422
- themeId: p.themeId,
10423
- supabaseUrl: p.supabaseUrl,
10424
- supabaseAnonKey: p.supabaseAnonKey
10425
- }).then((resolved) => {
10426
- if (!cancelled) {
10427
- setResolvedConfig(resolved.config);
10428
- setIsLoading(false);
10429
- }
10430
- }).catch((err) => {
10431
- if (!cancelled) {
10432
- setIsLoading(false);
10433
- onError == null ? void 0 : onError(err instanceof Error ? err : new Error(String(err)));
10434
- }
10435
- });
10436
- return () => {
10437
- cancelled = true;
10438
- };
10439
- }, [
10440
- isResolveMode,
10441
- // For resolve mode, re-resolve when these change
10442
- isResolveMode ? props.templateId : void 0,
10443
- isResolveMode ? props.formSchemaId : void 0,
10444
- isResolveMode ? JSON.stringify(props.sectionState) : void 0,
10445
- isResolveMode ? props.themeId : void 0
10446
- ]);
10447
- const config = isResolveMode ? resolvedConfig : props.config;
10448
- if (isLoading) {
10449
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10450
- }
10451
- if (!config) return null;
10452
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children: /* @__PURE__ */ jsxRuntime.jsx(
10453
- PreviewCanvas,
10454
- {
10455
- config,
10456
- pageIndex,
10457
- zoom,
10458
- absoluteZoom,
10459
- onDynamicFieldClick,
10460
- onReady
10461
- }
10462
- ) });
10463
- }
10464
10391
  function normalizeFontFamily(fontStack) {
10465
10392
  const first = fontStack.split(",")[0].trim();
10466
10393
  return first.replace(/^['"]|['"]$/g, "");
@@ -10475,12 +10402,7 @@ async function loadGoogleFontCSS(rawFontFamily) {
10475
10402
  const existing = loadingPromises.get(fontFamily);
10476
10403
  if (existing) return existing;
10477
10404
  const promise = (async () => {
10478
- var _a;
10479
10405
  try {
10480
- if ((_a = document.fonts) == null ? void 0 : _a.check(`16px "${fontFamily}"`)) {
10481
- loadedFonts.add(fontFamily);
10482
- return;
10483
- }
10484
10406
  const encoded = encodeURIComponent(fontFamily);
10485
10407
  const url = `https://fonts.googleapis.com/css?family=${encoded}:300,400,500,600,700&display=swap`;
10486
10408
  const link = document.createElement("link");
@@ -10509,6 +10431,7 @@ function collectFontsFromConfig(config) {
10509
10431
  var _a;
10510
10432
  const fonts = /* @__PURE__ */ new Set();
10511
10433
  fonts.add("Open Sans");
10434
+ fonts.add("Hind");
10512
10435
  function walk(nodes) {
10513
10436
  var _a2;
10514
10437
  if (!nodes) return;
@@ -10561,12 +10484,18 @@ function collectFontDescriptorsFromConfig(config) {
10561
10484
  for (const node of nodes) {
10562
10485
  if (node.fontFamily) {
10563
10486
  add(node.fontFamily, node.fontWeight, node.fontStyle);
10487
+ if (node.type === "text") {
10488
+ for (const w of [300, 400, 500, 600, 700]) {
10489
+ add(node.fontFamily, w, node.fontStyle);
10490
+ }
10491
+ }
10564
10492
  }
10565
10493
  if ((_a2 = node.smartProps) == null ? void 0 : _a2.fontFamily) {
10566
10494
  add(node.smartProps.fontFamily, node.smartProps.fontWeight, node.smartProps.fontStyle);
10567
10495
  }
10568
- if (node.styles && Array.isArray(node.styles)) {
10569
- for (const lineStyle of node.styles) {
10496
+ if (node.styles) {
10497
+ const styleEntries = Array.isArray(node.styles) ? node.styles : Object.values(node.styles);
10498
+ for (const lineStyle of styleEntries) {
10570
10499
  if (lineStyle && typeof lineStyle === "object") {
10571
10500
  for (const charStyle of Object.values(lineStyle)) {
10572
10501
  if (charStyle == null ? void 0 : charStyle.fontFamily) {
@@ -10580,6 +10509,8 @@ function collectFontDescriptorsFromConfig(config) {
10580
10509
  }
10581
10510
  }
10582
10511
  add("Open Sans", 400, "normal");
10512
+ add("Hind", 400, "normal");
10513
+ add("Hind", 700, "normal");
10583
10514
  for (const page of config.pages || []) {
10584
10515
  walk(page.children || []);
10585
10516
  }
@@ -10611,6 +10542,95 @@ async function ensureFontsForResolvedConfig(config) {
10611
10542
  await document.fonts.ready;
10612
10543
  }
10613
10544
  }
10545
+ function PixldocsPreview(props) {
10546
+ const {
10547
+ pageIndex = 0,
10548
+ zoom = 1,
10549
+ absoluteZoom = false,
10550
+ imageProxyUrl,
10551
+ className,
10552
+ style,
10553
+ onDynamicFieldClick,
10554
+ onReady,
10555
+ onError
10556
+ } = props;
10557
+ react.useEffect(() => {
10558
+ setPackageApiUrl(imageProxyUrl);
10559
+ }, [imageProxyUrl]);
10560
+ const [resolvedConfig, setResolvedConfig] = react.useState(null);
10561
+ const [isLoading, setIsLoading] = react.useState(false);
10562
+ const [fontsReady, setFontsReady] = react.useState(false);
10563
+ const isResolveMode = !("config" in props && props.config);
10564
+ react.useEffect(() => {
10565
+ if (!isResolveMode) {
10566
+ setResolvedConfig(null);
10567
+ return;
10568
+ }
10569
+ const p = props;
10570
+ if (!p.templateId || !p.formSchemaId || !p.supabaseUrl || !p.supabaseAnonKey) return;
10571
+ let cancelled = false;
10572
+ setIsLoading(true);
10573
+ resolveFromForm({
10574
+ templateId: p.templateId,
10575
+ formSchemaId: p.formSchemaId,
10576
+ sectionState: p.sectionState,
10577
+ themeId: p.themeId,
10578
+ supabaseUrl: p.supabaseUrl,
10579
+ supabaseAnonKey: p.supabaseAnonKey
10580
+ }).then((resolved) => {
10581
+ if (!cancelled) {
10582
+ setResolvedConfig(resolved.config);
10583
+ ensureFontsForResolvedConfig(resolved.config).then(() => {
10584
+ if (!cancelled) {
10585
+ setFontsReady(true);
10586
+ setIsLoading(false);
10587
+ }
10588
+ }).catch(() => {
10589
+ if (!cancelled) {
10590
+ setFontsReady(true);
10591
+ setIsLoading(false);
10592
+ }
10593
+ });
10594
+ }
10595
+ }).catch((err) => {
10596
+ if (!cancelled) {
10597
+ setIsLoading(false);
10598
+ onError == null ? void 0 : onError(err instanceof Error ? err : new Error(String(err)));
10599
+ }
10600
+ });
10601
+ return () => {
10602
+ cancelled = true;
10603
+ };
10604
+ }, [
10605
+ isResolveMode,
10606
+ // For resolve mode, re-resolve when these change
10607
+ isResolveMode ? props.templateId : void 0,
10608
+ isResolveMode ? props.formSchemaId : void 0,
10609
+ isResolveMode ? JSON.stringify(props.sectionState) : void 0,
10610
+ isResolveMode ? props.themeId : void 0
10611
+ ]);
10612
+ const config = isResolveMode ? resolvedConfig : props.config;
10613
+ react.useEffect(() => {
10614
+ if (isResolveMode || !config) return;
10615
+ setFontsReady(false);
10616
+ ensureFontsForResolvedConfig(config).then(() => setFontsReady(true)).catch(() => setFontsReady(true));
10617
+ }, [isResolveMode, config]);
10618
+ if (isLoading) {
10619
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style: { ...style, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) });
10620
+ }
10621
+ if (!config) return null;
10622
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className, style, children: /* @__PURE__ */ jsxRuntime.jsx(
10623
+ PreviewCanvas,
10624
+ {
10625
+ config,
10626
+ pageIndex,
10627
+ zoom,
10628
+ absoluteZoom,
10629
+ onDynamicFieldClick,
10630
+ onReady
10631
+ }
10632
+ ) });
10633
+ }
10614
10634
  class PixldocsRenderer {
10615
10635
  constructor(config) {
10616
10636
  __publicField(this, "config");
@@ -10690,7 +10710,8 @@ class PixldocsRenderer {
10690
10710
  // ─── Internal: render a page using the full PreviewCanvas engine ───
10691
10711
  /**
10692
10712
  * Wait until every image on the Fabric canvas inside the container has loaded.
10693
- * Polls img elements since we can't hook into Fabric's internal load events.
10713
+ * Checks both DOM <img> elements AND Fabric canvas image objects to ensure
10714
+ * all async assets are fully painted before capture.
10694
10715
  */
10695
10716
  waitForCanvasImages(container, maxWaitMs = 15e3, pollMs = 200) {
10696
10717
  return new Promise((resolve) => {
@@ -10701,6 +10722,29 @@ class PixldocsRenderer {
10701
10722
  images.forEach((img) => {
10702
10723
  if (!img.complete) allLoaded = false;
10703
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
+ }
10746
+ }
10747
+ }
10704
10748
  if (allLoaded || Date.now() - start > maxWaitMs) {
10705
10749
  requestAnimationFrame(() => setTimeout(resolve, 400));
10706
10750
  return;