@pixldocs/canvas-renderer 0.5.457 → 0.5.458

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.
@@ -25236,6 +25236,89 @@ const previewBlur = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
25236
25236
  injectPreviewBlur,
25237
25237
  resolveBlurElementExactIdsFromFlatFormKeys
25238
25238
  }, Symbol.toStringTag, { value: "Module" }));
25239
+ function collectImageUrls(config) {
25240
+ const urls = [];
25241
+ const walk = (nodes) => {
25242
+ for (const node of nodes) {
25243
+ if (!node || node.visible === false) continue;
25244
+ const src = typeof node.src === "string" ? node.src.trim() : "";
25245
+ const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
25246
+ if (node.type === "image") {
25247
+ const url = src || imageUrl;
25248
+ if (url) urls.push(url);
25249
+ }
25250
+ if (Array.isArray(node.children) && node.children.length > 0) {
25251
+ walk(node.children);
25252
+ }
25253
+ }
25254
+ };
25255
+ for (const page of config.pages || []) {
25256
+ walk(page.children || []);
25257
+ }
25258
+ return urls;
25259
+ }
25260
+ function normalizeAssetUrl(rawUrl, imageProxyUrl) {
25261
+ if (!rawUrl) return null;
25262
+ if (rawUrl.startsWith("data:") || rawUrl.startsWith("blob:")) return null;
25263
+ if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
25264
+ if (typeof window !== "undefined") return new URL(rawUrl, window.location.origin).toString();
25265
+ return null;
25266
+ }
25267
+ try {
25268
+ const h = new URL(rawUrl).hostname.toLowerCase();
25269
+ if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0" || h.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(h)) {
25270
+ if (typeof window !== "undefined" && new URL(rawUrl).origin === window.location.origin) {
25271
+ return rawUrl;
25272
+ }
25273
+ return null;
25274
+ }
25275
+ } catch {
25276
+ return null;
25277
+ }
25278
+ const supabaseUrl = typeof globalThis.__VITE_SUPABASE_URL === "string" ? globalThis.__VITE_SUPABASE_URL : "";
25279
+ if (supabaseUrl && rawUrl.includes(supabaseUrl)) {
25280
+ const signedMatch = rawUrl.match(/\/storage\/v1\/object\/sign\/([^?]+)/);
25281
+ if (signedMatch) return `${supabaseUrl}/storage/v1/object/public/${signedMatch[1]}`;
25282
+ if (rawUrl.includes("/storage/v1/object/public/")) return rawUrl;
25283
+ }
25284
+ const proxyBase = imageProxyUrl ? imageProxyUrl.replace(/\/image-proxy(?:\?.*)?$/, "") : exports.API_URL;
25285
+ if (proxyBase) {
25286
+ return `${proxyBase}/image-proxy?url=${encodeURIComponent(rawUrl)}`;
25287
+ }
25288
+ return rawUrl;
25289
+ }
25290
+ const CONCURRENCY = 6;
25291
+ async function prefetchUrls(urls, signal) {
25292
+ const unique = [...new Set(urls)];
25293
+ if (unique.length === 0) return;
25294
+ let i = 0;
25295
+ const next = async () => {
25296
+ while (i < unique.length) {
25297
+ if (signal == null ? void 0 : signal.aborted) return;
25298
+ const url = unique[i++];
25299
+ try {
25300
+ await fetch(url, { signal, mode: "cors", credentials: "omit" });
25301
+ } catch {
25302
+ }
25303
+ }
25304
+ };
25305
+ const workers = Array.from({ length: Math.min(CONCURRENCY, unique.length) }, () => next());
25306
+ await Promise.all(workers);
25307
+ }
25308
+ async function warmResolvedTemplateForPreview(config, options) {
25309
+ const { signal, imageProxyUrl } = options ?? {};
25310
+ await ensureFontsForResolvedConfig(config);
25311
+ if (signal == null ? void 0 : signal.aborted) return;
25312
+ const rawUrls = collectImageUrls(config);
25313
+ const resolvedUrls = rawUrls.map((u) => normalizeAssetUrl(u, imageProxyUrl)).filter((u) => u !== null);
25314
+ await prefetchUrls(resolvedUrls, signal);
25315
+ }
25316
+ async function warmTemplateFromForm(options) {
25317
+ const { signal, imageProxyUrl, ...resolveOpts } = options;
25318
+ const resolved = await resolveFromForm(resolveOpts);
25319
+ if (signal == null ? void 0 : signal.aborted) return;
25320
+ await warmResolvedTemplateForPreview(resolved.config, { signal, imageProxyUrl });
25321
+ }
25239
25322
  const PREVIEW_DEBUG_PREFIX = "[canvas-renderer][preview-debug]";
25240
25323
  function computeFontSignature(config) {
25241
25324
  var _a2;
@@ -25251,6 +25334,39 @@ function computeFontSignature(config) {
25251
25334
  for (const page of config.pages) walk(page.children || []);
25252
25335
  return Array.from(fams).sort().join("|");
25253
25336
  }
25337
+ function computeImageSignature(config) {
25338
+ var _a2;
25339
+ if (!((_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2.length)) return "";
25340
+ try {
25341
+ const urls = collectImageUrls(config);
25342
+ return urls.length === 0 ? "" : urls.slice().sort().join("|");
25343
+ } catch {
25344
+ return "";
25345
+ }
25346
+ }
25347
+ function preloadImageUrl(url, proxyBase) {
25348
+ return new Promise((resolve) => {
25349
+ if (!url || url.startsWith("data:") || url.startsWith("blob:")) return resolve();
25350
+ let done = false;
25351
+ const finish = () => {
25352
+ if (!done) {
25353
+ done = true;
25354
+ resolve();
25355
+ }
25356
+ };
25357
+ const isHttp = /^https?:/i.test(url);
25358
+ const target = isHttp && proxyBase && !url.includes("/image-proxy?") ? `${proxyBase.replace(/\/+$/, "")}?url=${encodeURIComponent(url)}` : url;
25359
+ const img = new Image();
25360
+ try {
25361
+ img.crossOrigin = "anonymous";
25362
+ } catch {
25363
+ }
25364
+ img.onload = finish;
25365
+ img.onerror = finish;
25366
+ img.src = target;
25367
+ setTimeout(finish, 6e3);
25368
+ });
25369
+ }
25254
25370
  function countUnderlinedNodes(config) {
25255
25371
  var _a2;
25256
25372
  if (!((_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2.length)) return 0;
@@ -25413,6 +25529,38 @@ function PixldocsPreview(props) {
25413
25529
  const config = isResolveMode ? resolvedConfig : props.config;
25414
25530
  const previewKey = react.useMemo(() => `${pageIndex}`, [pageIndex]);
25415
25531
  const fontSignature = react.useMemo(() => computeFontSignature(config), [config]);
25532
+ const imageSignature = react.useMemo(() => computeImageSignature(config), [config]);
25533
+ const [imagesReady, setImagesReady] = react.useState(true);
25534
+ const firstImageRef = react.useMemo(() => ({ first: true }), []);
25535
+ react.useEffect(() => {
25536
+ if (!config) {
25537
+ setImagesReady(true);
25538
+ return;
25539
+ }
25540
+ if (firstImageRef.first) {
25541
+ firstImageRef.first = false;
25542
+ setImagesReady(true);
25543
+ return;
25544
+ }
25545
+ let urls = [];
25546
+ try {
25547
+ urls = collectImageUrls(config);
25548
+ } catch {
25549
+ urls = [];
25550
+ }
25551
+ if (urls.length === 0) {
25552
+ setImagesReady(true);
25553
+ return;
25554
+ }
25555
+ setImagesReady(false);
25556
+ let cancelled = false;
25557
+ Promise.all(urls.map((u) => preloadImageUrl(u, imageProxyUrl))).then(() => {
25558
+ if (!cancelled) setImagesReady(true);
25559
+ });
25560
+ return () => {
25561
+ cancelled = true;
25562
+ };
25563
+ }, [imageSignature, imageProxyUrl]);
25416
25564
  react.useEffect(() => {
25417
25565
  if (isResolveMode) return;
25418
25566
  if (!config) {
@@ -25495,7 +25643,7 @@ function PixldocsPreview(props) {
25495
25643
  /* @__PURE__ */ jsxRuntime.jsxs(
25496
25644
  "div",
25497
25645
  {
25498
- style: hasOverlays ? { visibility: canvasSettled ? "visible" : "hidden", position: "relative", width: canvasW * zoom, height: canvasH * zoom } : { visibility: canvasSettled ? "visible" : "hidden" },
25646
+ style: hasOverlays ? { visibility: canvasSettled && imagesReady ? "visible" : "hidden", position: "relative", width: canvasW * zoom, height: canvasH * zoom } : { visibility: canvasSettled && imagesReady ? "visible" : "hidden" },
25499
25647
  children: [
25500
25648
  /* @__PURE__ */ jsxRuntime.jsx(
25501
25649
  PreviewCanvas,
@@ -25541,7 +25689,7 @@ function PixldocsPreview(props) {
25541
25689
  ]
25542
25690
  }
25543
25691
  ),
25544
- !canvasSettled && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: loadingFallback ?? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
25692
+ (!canvasSettled || !imagesReady) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: loadingFallback ?? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
25545
25693
  ] });
25546
25694
  }
25547
25695
  function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
@@ -26062,9 +26210,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26062
26210
  }
26063
26211
  return svgString;
26064
26212
  }
26065
- const resolvedPackageVersion = "0.5.457";
26213
+ const resolvedPackageVersion = "0.5.458";
26066
26214
  const PACKAGE_VERSION = resolvedPackageVersion;
26067
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.457";
26215
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26068
26216
  const roundParityValue = (value) => {
26069
26217
  if (typeof value !== "number") return value;
26070
26218
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26878,7 +27026,7 @@ class PixldocsRenderer {
26878
27026
  await this.waitForCanvasScene(container, cloned, i);
26879
27027
  }
26880
27028
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
26881
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Qi65Srzj.cjs"));
27029
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-DySc4jWC.cjs"));
26882
27030
  const prepared = preparePagesForExport(
26883
27031
  cloned.pages,
26884
27032
  canvasWidth,
@@ -29198,7 +29346,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29198
29346
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29199
29347
  sanitizeSvgTreeForPdf(svgToDraw);
29200
29348
  try {
29201
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-Qi65Srzj.cjs"));
29349
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-DySc4jWC.cjs"));
29202
29350
  try {
29203
29351
  await logTextMeasurementDiagnostic(svgToDraw);
29204
29352
  } catch {
@@ -29446,89 +29594,6 @@ async function getPublishedTemplate(options) {
29446
29594
  const rows = await res.json();
29447
29595
  return rows[0] ?? null;
29448
29596
  }
29449
- function collectImageUrls(config) {
29450
- const urls = [];
29451
- const walk = (nodes) => {
29452
- for (const node of nodes) {
29453
- if (!node || node.visible === false) continue;
29454
- const src = typeof node.src === "string" ? node.src.trim() : "";
29455
- const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
29456
- if (node.type === "image") {
29457
- const url = src || imageUrl;
29458
- if (url) urls.push(url);
29459
- }
29460
- if (Array.isArray(node.children) && node.children.length > 0) {
29461
- walk(node.children);
29462
- }
29463
- }
29464
- };
29465
- for (const page of config.pages || []) {
29466
- walk(page.children || []);
29467
- }
29468
- return urls;
29469
- }
29470
- function normalizeAssetUrl(rawUrl, imageProxyUrl) {
29471
- if (!rawUrl) return null;
29472
- if (rawUrl.startsWith("data:") || rawUrl.startsWith("blob:")) return null;
29473
- if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
29474
- if (typeof window !== "undefined") return new URL(rawUrl, window.location.origin).toString();
29475
- return null;
29476
- }
29477
- try {
29478
- const h = new URL(rawUrl).hostname.toLowerCase();
29479
- if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0" || h.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(h)) {
29480
- if (typeof window !== "undefined" && new URL(rawUrl).origin === window.location.origin) {
29481
- return rawUrl;
29482
- }
29483
- return null;
29484
- }
29485
- } catch {
29486
- return null;
29487
- }
29488
- const supabaseUrl = typeof globalThis.__VITE_SUPABASE_URL === "string" ? globalThis.__VITE_SUPABASE_URL : "";
29489
- if (supabaseUrl && rawUrl.includes(supabaseUrl)) {
29490
- const signedMatch = rawUrl.match(/\/storage\/v1\/object\/sign\/([^?]+)/);
29491
- if (signedMatch) return `${supabaseUrl}/storage/v1/object/public/${signedMatch[1]}`;
29492
- if (rawUrl.includes("/storage/v1/object/public/")) return rawUrl;
29493
- }
29494
- const proxyBase = imageProxyUrl ? imageProxyUrl.replace(/\/image-proxy(?:\?.*)?$/, "") : exports.API_URL;
29495
- if (proxyBase) {
29496
- return `${proxyBase}/image-proxy?url=${encodeURIComponent(rawUrl)}`;
29497
- }
29498
- return rawUrl;
29499
- }
29500
- const CONCURRENCY = 6;
29501
- async function prefetchUrls(urls, signal) {
29502
- const unique = [...new Set(urls)];
29503
- if (unique.length === 0) return;
29504
- let i = 0;
29505
- const next = async () => {
29506
- while (i < unique.length) {
29507
- if (signal == null ? void 0 : signal.aborted) return;
29508
- const url = unique[i++];
29509
- try {
29510
- await fetch(url, { signal, mode: "cors", credentials: "omit" });
29511
- } catch {
29512
- }
29513
- }
29514
- };
29515
- const workers = Array.from({ length: Math.min(CONCURRENCY, unique.length) }, () => next());
29516
- await Promise.all(workers);
29517
- }
29518
- async function warmResolvedTemplateForPreview(config, options) {
29519
- const { signal, imageProxyUrl } = options ?? {};
29520
- await ensureFontsForResolvedConfig(config);
29521
- if (signal == null ? void 0 : signal.aborted) return;
29522
- const rawUrls = collectImageUrls(config);
29523
- const resolvedUrls = rawUrls.map((u) => normalizeAssetUrl(u, imageProxyUrl)).filter((u) => u !== null);
29524
- await prefetchUrls(resolvedUrls, signal);
29525
- }
29526
- async function warmTemplateFromForm(options) {
29527
- const { signal, imageProxyUrl, ...resolveOpts } = options;
29528
- const resolved = await resolveFromForm(resolveOpts);
29529
- if (signal == null ? void 0 : signal.aborted) return;
29530
- await warmResolvedTemplateForPreview(resolved.config, { signal, imageProxyUrl });
29531
- }
29532
29597
  function setAutoShrinkDebug(enabled) {
29533
29598
  if (typeof window !== "undefined") {
29534
29599
  window.__pixldocsDebugAutoShrink = !!enabled;
@@ -29595,4 +29660,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
29595
29660
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
29596
29661
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
29597
29662
  exports.warmTemplateFromForm = warmTemplateFromForm;
29598
- //# sourceMappingURL=index-C-zHLTyR.cjs.map
29663
+ //# sourceMappingURL=index-CScn1p9A.cjs.map