@pixldocs/canvas-renderer 0.5.456 → 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.
@@ -21110,6 +21110,13 @@ function setInTree(nodes, elementId, targetProperty, value) {
21110
21110
  delete node.cropPanY;
21111
21111
  delete node.cropZoom;
21112
21112
  }
21113
+ if (nextSrc === "") {
21114
+ node.visible = false;
21115
+ node.opacity = 0;
21116
+ } else {
21117
+ node.visible = true;
21118
+ if (node.opacity === 0) delete node.opacity;
21119
+ }
21113
21120
  } else {
21114
21121
  const gradSibling = gradientSiblingForTarget(targetProperty);
21115
21122
  if (gradSibling) {
@@ -25229,6 +25236,89 @@ const previewBlur = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
25229
25236
  injectPreviewBlur,
25230
25237
  resolveBlurElementExactIdsFromFlatFormKeys
25231
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
+ }
25232
25322
  const PREVIEW_DEBUG_PREFIX = "[canvas-renderer][preview-debug]";
25233
25323
  function computeFontSignature(config) {
25234
25324
  var _a2;
@@ -25244,6 +25334,39 @@ function computeFontSignature(config) {
25244
25334
  for (const page of config.pages) walk(page.children || []);
25245
25335
  return Array.from(fams).sort().join("|");
25246
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
+ }
25247
25370
  function countUnderlinedNodes(config) {
25248
25371
  var _a2;
25249
25372
  if (!((_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2.length)) return 0;
@@ -25406,6 +25529,38 @@ function PixldocsPreview(props) {
25406
25529
  const config = isResolveMode ? resolvedConfig : props.config;
25407
25530
  const previewKey = react.useMemo(() => `${pageIndex}`, [pageIndex]);
25408
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]);
25409
25564
  react.useEffect(() => {
25410
25565
  if (isResolveMode) return;
25411
25566
  if (!config) {
@@ -25488,7 +25643,7 @@ function PixldocsPreview(props) {
25488
25643
  /* @__PURE__ */ jsxRuntime.jsxs(
25489
25644
  "div",
25490
25645
  {
25491
- 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" },
25492
25647
  children: [
25493
25648
  /* @__PURE__ */ jsxRuntime.jsx(
25494
25649
  PreviewCanvas,
@@ -25534,7 +25689,7 @@ function PixldocsPreview(props) {
25534
25689
  ]
25535
25690
  }
25536
25691
  ),
25537
- !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..." }) })
25538
25693
  ] });
25539
25694
  }
25540
25695
  function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
@@ -26055,9 +26210,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26055
26210
  }
26056
26211
  return svgString;
26057
26212
  }
26058
- const resolvedPackageVersion = "0.5.456";
26213
+ const resolvedPackageVersion = "0.5.458";
26059
26214
  const PACKAGE_VERSION = resolvedPackageVersion;
26060
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.456";
26215
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26061
26216
  const roundParityValue = (value) => {
26062
26217
  if (typeof value !== "number") return value;
26063
26218
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26871,7 +27026,7 @@ class PixldocsRenderer {
26871
27026
  await this.waitForCanvasScene(container, cloned, i);
26872
27027
  }
26873
27028
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
26874
- const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-Cyok7Rsu.cjs"));
27029
+ const { exportMultiPagePdf, preparePagesForExport } = await Promise.resolve().then(() => require("./vectorPdfExport-DySc4jWC.cjs"));
26875
27030
  const prepared = preparePagesForExport(
26876
27031
  cloned.pages,
26877
27032
  canvasWidth,
@@ -29191,7 +29346,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29191
29346
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29192
29347
  sanitizeSvgTreeForPdf(svgToDraw);
29193
29348
  try {
29194
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-Cyok7Rsu.cjs"));
29349
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await Promise.resolve().then(() => require("./vectorPdfExport-DySc4jWC.cjs"));
29195
29350
  try {
29196
29351
  await logTextMeasurementDiagnostic(svgToDraw);
29197
29352
  } catch {
@@ -29439,89 +29594,6 @@ async function getPublishedTemplate(options) {
29439
29594
  const rows = await res.json();
29440
29595
  return rows[0] ?? null;
29441
29596
  }
29442
- function collectImageUrls(config) {
29443
- const urls = [];
29444
- const walk = (nodes) => {
29445
- for (const node of nodes) {
29446
- if (!node || node.visible === false) continue;
29447
- const src = typeof node.src === "string" ? node.src.trim() : "";
29448
- const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
29449
- if (node.type === "image") {
29450
- const url = src || imageUrl;
29451
- if (url) urls.push(url);
29452
- }
29453
- if (Array.isArray(node.children) && node.children.length > 0) {
29454
- walk(node.children);
29455
- }
29456
- }
29457
- };
29458
- for (const page of config.pages || []) {
29459
- walk(page.children || []);
29460
- }
29461
- return urls;
29462
- }
29463
- function normalizeAssetUrl(rawUrl, imageProxyUrl) {
29464
- if (!rawUrl) return null;
29465
- if (rawUrl.startsWith("data:") || rawUrl.startsWith("blob:")) return null;
29466
- if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
29467
- if (typeof window !== "undefined") return new URL(rawUrl, window.location.origin).toString();
29468
- return null;
29469
- }
29470
- try {
29471
- const h = new URL(rawUrl).hostname.toLowerCase();
29472
- if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0" || h.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(h)) {
29473
- if (typeof window !== "undefined" && new URL(rawUrl).origin === window.location.origin) {
29474
- return rawUrl;
29475
- }
29476
- return null;
29477
- }
29478
- } catch {
29479
- return null;
29480
- }
29481
- const supabaseUrl = typeof globalThis.__VITE_SUPABASE_URL === "string" ? globalThis.__VITE_SUPABASE_URL : "";
29482
- if (supabaseUrl && rawUrl.includes(supabaseUrl)) {
29483
- const signedMatch = rawUrl.match(/\/storage\/v1\/object\/sign\/([^?]+)/);
29484
- if (signedMatch) return `${supabaseUrl}/storage/v1/object/public/${signedMatch[1]}`;
29485
- if (rawUrl.includes("/storage/v1/object/public/")) return rawUrl;
29486
- }
29487
- const proxyBase = imageProxyUrl ? imageProxyUrl.replace(/\/image-proxy(?:\?.*)?$/, "") : exports.API_URL;
29488
- if (proxyBase) {
29489
- return `${proxyBase}/image-proxy?url=${encodeURIComponent(rawUrl)}`;
29490
- }
29491
- return rawUrl;
29492
- }
29493
- const CONCURRENCY = 6;
29494
- async function prefetchUrls(urls, signal) {
29495
- const unique = [...new Set(urls)];
29496
- if (unique.length === 0) return;
29497
- let i = 0;
29498
- const next = async () => {
29499
- while (i < unique.length) {
29500
- if (signal == null ? void 0 : signal.aborted) return;
29501
- const url = unique[i++];
29502
- try {
29503
- await fetch(url, { signal, mode: "cors", credentials: "omit" });
29504
- } catch {
29505
- }
29506
- }
29507
- };
29508
- const workers = Array.from({ length: Math.min(CONCURRENCY, unique.length) }, () => next());
29509
- await Promise.all(workers);
29510
- }
29511
- async function warmResolvedTemplateForPreview(config, options) {
29512
- const { signal, imageProxyUrl } = options ?? {};
29513
- await ensureFontsForResolvedConfig(config);
29514
- if (signal == null ? void 0 : signal.aborted) return;
29515
- const rawUrls = collectImageUrls(config);
29516
- const resolvedUrls = rawUrls.map((u) => normalizeAssetUrl(u, imageProxyUrl)).filter((u) => u !== null);
29517
- await prefetchUrls(resolvedUrls, signal);
29518
- }
29519
- async function warmTemplateFromForm(options) {
29520
- const { signal, imageProxyUrl, ...resolveOpts } = options;
29521
- const resolved = await resolveFromForm(resolveOpts);
29522
- if (signal == null ? void 0 : signal.aborted) return;
29523
- await warmResolvedTemplateForPreview(resolved.config, { signal, imageProxyUrl });
29524
- }
29525
29597
  function setAutoShrinkDebug(enabled) {
29526
29598
  if (typeof window !== "undefined") {
29527
29599
  window.__pixldocsDebugAutoShrink = !!enabled;
@@ -29588,4 +29660,4 @@ exports.setAutoShrinkDebug = setAutoShrinkDebug;
29588
29660
  exports.setBundledAssetPrefixes = setBundledAssetPrefixes;
29589
29661
  exports.warmResolvedTemplateForPreview = warmResolvedTemplateForPreview;
29590
29662
  exports.warmTemplateFromForm = warmTemplateFromForm;
29591
- //# sourceMappingURL=index-D7XRqqRF.cjs.map
29663
+ //# sourceMappingURL=index-CScn1p9A.cjs.map