@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.
@@ -21092,6 +21092,13 @@ function setInTree(nodes, elementId, targetProperty, value) {
21092
21092
  delete node.cropPanY;
21093
21093
  delete node.cropZoom;
21094
21094
  }
21095
+ if (nextSrc === "") {
21096
+ node.visible = false;
21097
+ node.opacity = 0;
21098
+ } else {
21099
+ node.visible = true;
21100
+ if (node.opacity === 0) delete node.opacity;
21101
+ }
21095
21102
  } else {
21096
21103
  const gradSibling = gradientSiblingForTarget(targetProperty);
21097
21104
  if (gradSibling) {
@@ -25211,6 +25218,89 @@ const previewBlur = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
25211
25218
  injectPreviewBlur,
25212
25219
  resolveBlurElementExactIdsFromFlatFormKeys
25213
25220
  }, Symbol.toStringTag, { value: "Module" }));
25221
+ function collectImageUrls(config) {
25222
+ const urls = [];
25223
+ const walk = (nodes) => {
25224
+ for (const node of nodes) {
25225
+ if (!node || node.visible === false) continue;
25226
+ const src = typeof node.src === "string" ? node.src.trim() : "";
25227
+ const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
25228
+ if (node.type === "image") {
25229
+ const url = src || imageUrl;
25230
+ if (url) urls.push(url);
25231
+ }
25232
+ if (Array.isArray(node.children) && node.children.length > 0) {
25233
+ walk(node.children);
25234
+ }
25235
+ }
25236
+ };
25237
+ for (const page of config.pages || []) {
25238
+ walk(page.children || []);
25239
+ }
25240
+ return urls;
25241
+ }
25242
+ function normalizeAssetUrl(rawUrl, imageProxyUrl) {
25243
+ if (!rawUrl) return null;
25244
+ if (rawUrl.startsWith("data:") || rawUrl.startsWith("blob:")) return null;
25245
+ if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
25246
+ if (typeof window !== "undefined") return new URL(rawUrl, window.location.origin).toString();
25247
+ return null;
25248
+ }
25249
+ try {
25250
+ const h = new URL(rawUrl).hostname.toLowerCase();
25251
+ if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0" || h.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(h)) {
25252
+ if (typeof window !== "undefined" && new URL(rawUrl).origin === window.location.origin) {
25253
+ return rawUrl;
25254
+ }
25255
+ return null;
25256
+ }
25257
+ } catch {
25258
+ return null;
25259
+ }
25260
+ const supabaseUrl = typeof globalThis.__VITE_SUPABASE_URL === "string" ? globalThis.__VITE_SUPABASE_URL : "";
25261
+ if (supabaseUrl && rawUrl.includes(supabaseUrl)) {
25262
+ const signedMatch = rawUrl.match(/\/storage\/v1\/object\/sign\/([^?]+)/);
25263
+ if (signedMatch) return `${supabaseUrl}/storage/v1/object/public/${signedMatch[1]}`;
25264
+ if (rawUrl.includes("/storage/v1/object/public/")) return rawUrl;
25265
+ }
25266
+ const proxyBase = imageProxyUrl ? imageProxyUrl.replace(/\/image-proxy(?:\?.*)?$/, "") : API_URL;
25267
+ if (proxyBase) {
25268
+ return `${proxyBase}/image-proxy?url=${encodeURIComponent(rawUrl)}`;
25269
+ }
25270
+ return rawUrl;
25271
+ }
25272
+ const CONCURRENCY = 6;
25273
+ async function prefetchUrls(urls, signal) {
25274
+ const unique = [...new Set(urls)];
25275
+ if (unique.length === 0) return;
25276
+ let i = 0;
25277
+ const next = async () => {
25278
+ while (i < unique.length) {
25279
+ if (signal == null ? void 0 : signal.aborted) return;
25280
+ const url = unique[i++];
25281
+ try {
25282
+ await fetch(url, { signal, mode: "cors", credentials: "omit" });
25283
+ } catch {
25284
+ }
25285
+ }
25286
+ };
25287
+ const workers = Array.from({ length: Math.min(CONCURRENCY, unique.length) }, () => next());
25288
+ await Promise.all(workers);
25289
+ }
25290
+ async function warmResolvedTemplateForPreview(config, options) {
25291
+ const { signal, imageProxyUrl } = options ?? {};
25292
+ await ensureFontsForResolvedConfig(config);
25293
+ if (signal == null ? void 0 : signal.aborted) return;
25294
+ const rawUrls = collectImageUrls(config);
25295
+ const resolvedUrls = rawUrls.map((u) => normalizeAssetUrl(u, imageProxyUrl)).filter((u) => u !== null);
25296
+ await prefetchUrls(resolvedUrls, signal);
25297
+ }
25298
+ async function warmTemplateFromForm(options) {
25299
+ const { signal, imageProxyUrl, ...resolveOpts } = options;
25300
+ const resolved = await resolveFromForm(resolveOpts);
25301
+ if (signal == null ? void 0 : signal.aborted) return;
25302
+ await warmResolvedTemplateForPreview(resolved.config, { signal, imageProxyUrl });
25303
+ }
25214
25304
  const PREVIEW_DEBUG_PREFIX = "[canvas-renderer][preview-debug]";
25215
25305
  function computeFontSignature(config) {
25216
25306
  var _a2;
@@ -25226,6 +25316,39 @@ function computeFontSignature(config) {
25226
25316
  for (const page of config.pages) walk(page.children || []);
25227
25317
  return Array.from(fams).sort().join("|");
25228
25318
  }
25319
+ function computeImageSignature(config) {
25320
+ var _a2;
25321
+ if (!((_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2.length)) return "";
25322
+ try {
25323
+ const urls = collectImageUrls(config);
25324
+ return urls.length === 0 ? "" : urls.slice().sort().join("|");
25325
+ } catch {
25326
+ return "";
25327
+ }
25328
+ }
25329
+ function preloadImageUrl(url, proxyBase) {
25330
+ return new Promise((resolve) => {
25331
+ if (!url || url.startsWith("data:") || url.startsWith("blob:")) return resolve();
25332
+ let done = false;
25333
+ const finish = () => {
25334
+ if (!done) {
25335
+ done = true;
25336
+ resolve();
25337
+ }
25338
+ };
25339
+ const isHttp = /^https?:/i.test(url);
25340
+ const target = isHttp && proxyBase && !url.includes("/image-proxy?") ? `${proxyBase.replace(/\/+$/, "")}?url=${encodeURIComponent(url)}` : url;
25341
+ const img = new Image();
25342
+ try {
25343
+ img.crossOrigin = "anonymous";
25344
+ } catch {
25345
+ }
25346
+ img.onload = finish;
25347
+ img.onerror = finish;
25348
+ img.src = target;
25349
+ setTimeout(finish, 6e3);
25350
+ });
25351
+ }
25229
25352
  function countUnderlinedNodes(config) {
25230
25353
  var _a2;
25231
25354
  if (!((_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2.length)) return 0;
@@ -25388,6 +25511,38 @@ function PixldocsPreview(props) {
25388
25511
  const config = isResolveMode ? resolvedConfig : props.config;
25389
25512
  const previewKey = useMemo(() => `${pageIndex}`, [pageIndex]);
25390
25513
  const fontSignature = useMemo(() => computeFontSignature(config), [config]);
25514
+ const imageSignature = useMemo(() => computeImageSignature(config), [config]);
25515
+ const [imagesReady, setImagesReady] = useState(true);
25516
+ const firstImageRef = useMemo(() => ({ first: true }), []);
25517
+ useEffect(() => {
25518
+ if (!config) {
25519
+ setImagesReady(true);
25520
+ return;
25521
+ }
25522
+ if (firstImageRef.first) {
25523
+ firstImageRef.first = false;
25524
+ setImagesReady(true);
25525
+ return;
25526
+ }
25527
+ let urls = [];
25528
+ try {
25529
+ urls = collectImageUrls(config);
25530
+ } catch {
25531
+ urls = [];
25532
+ }
25533
+ if (urls.length === 0) {
25534
+ setImagesReady(true);
25535
+ return;
25536
+ }
25537
+ setImagesReady(false);
25538
+ let cancelled = false;
25539
+ Promise.all(urls.map((u) => preloadImageUrl(u, imageProxyUrl))).then(() => {
25540
+ if (!cancelled) setImagesReady(true);
25541
+ });
25542
+ return () => {
25543
+ cancelled = true;
25544
+ };
25545
+ }, [imageSignature, imageProxyUrl]);
25391
25546
  useEffect(() => {
25392
25547
  if (isResolveMode) return;
25393
25548
  if (!config) {
@@ -25470,7 +25625,7 @@ function PixldocsPreview(props) {
25470
25625
  /* @__PURE__ */ jsxs(
25471
25626
  "div",
25472
25627
  {
25473
- style: hasOverlays ? { visibility: canvasSettled ? "visible" : "hidden", position: "relative", width: canvasW * zoom, height: canvasH * zoom } : { visibility: canvasSettled ? "visible" : "hidden" },
25628
+ style: hasOverlays ? { visibility: canvasSettled && imagesReady ? "visible" : "hidden", position: "relative", width: canvasW * zoom, height: canvasH * zoom } : { visibility: canvasSettled && imagesReady ? "visible" : "hidden" },
25474
25629
  children: [
25475
25630
  /* @__PURE__ */ jsx(
25476
25631
  PreviewCanvas,
@@ -25516,7 +25671,7 @@ function PixldocsPreview(props) {
25516
25671
  ]
25517
25672
  }
25518
25673
  ),
25519
- !canvasSettled && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: loadingFallback ?? /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
25674
+ (!canvasSettled || !imagesReady) && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", minHeight: 200 }, children: loadingFallback ?? /* @__PURE__ */ jsx("div", { style: { color: "#888", fontSize: 14 }, children: "Loading preview..." }) })
25520
25675
  ] });
25521
25676
  }
25522
25677
  function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
@@ -26037,9 +26192,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26037
26192
  }
26038
26193
  return svgString;
26039
26194
  }
26040
- const resolvedPackageVersion = "0.5.456";
26195
+ const resolvedPackageVersion = "0.5.458";
26041
26196
  const PACKAGE_VERSION = resolvedPackageVersion;
26042
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.456";
26197
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26043
26198
  const roundParityValue = (value) => {
26044
26199
  if (typeof value !== "number") return value;
26045
26200
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26853,7 +27008,7 @@ class PixldocsRenderer {
26853
27008
  await this.waitForCanvasScene(container, cloned, i);
26854
27009
  }
26855
27010
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
26856
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-CAeuEsjr.js");
27011
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DTC5MXqp.js");
26857
27012
  const prepared = preparePagesForExport(
26858
27013
  cloned.pages,
26859
27014
  canvasWidth,
@@ -29173,7 +29328,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29173
29328
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29174
29329
  sanitizeSvgTreeForPdf(svgToDraw);
29175
29330
  try {
29176
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-CAeuEsjr.js");
29331
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DTC5MXqp.js");
29177
29332
  try {
29178
29333
  await logTextMeasurementDiagnostic(svgToDraw);
29179
29334
  } catch {
@@ -29421,89 +29576,6 @@ async function getPublishedTemplate(options) {
29421
29576
  const rows = await res.json();
29422
29577
  return rows[0] ?? null;
29423
29578
  }
29424
- function collectImageUrls(config) {
29425
- const urls = [];
29426
- const walk = (nodes) => {
29427
- for (const node of nodes) {
29428
- if (!node || node.visible === false) continue;
29429
- const src = typeof node.src === "string" ? node.src.trim() : "";
29430
- const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
29431
- if (node.type === "image") {
29432
- const url = src || imageUrl;
29433
- if (url) urls.push(url);
29434
- }
29435
- if (Array.isArray(node.children) && node.children.length > 0) {
29436
- walk(node.children);
29437
- }
29438
- }
29439
- };
29440
- for (const page of config.pages || []) {
29441
- walk(page.children || []);
29442
- }
29443
- return urls;
29444
- }
29445
- function normalizeAssetUrl(rawUrl, imageProxyUrl) {
29446
- if (!rawUrl) return null;
29447
- if (rawUrl.startsWith("data:") || rawUrl.startsWith("blob:")) return null;
29448
- if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
29449
- if (typeof window !== "undefined") return new URL(rawUrl, window.location.origin).toString();
29450
- return null;
29451
- }
29452
- try {
29453
- const h = new URL(rawUrl).hostname.toLowerCase();
29454
- if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0" || h.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(h)) {
29455
- if (typeof window !== "undefined" && new URL(rawUrl).origin === window.location.origin) {
29456
- return rawUrl;
29457
- }
29458
- return null;
29459
- }
29460
- } catch {
29461
- return null;
29462
- }
29463
- const supabaseUrl = typeof globalThis.__VITE_SUPABASE_URL === "string" ? globalThis.__VITE_SUPABASE_URL : "";
29464
- if (supabaseUrl && rawUrl.includes(supabaseUrl)) {
29465
- const signedMatch = rawUrl.match(/\/storage\/v1\/object\/sign\/([^?]+)/);
29466
- if (signedMatch) return `${supabaseUrl}/storage/v1/object/public/${signedMatch[1]}`;
29467
- if (rawUrl.includes("/storage/v1/object/public/")) return rawUrl;
29468
- }
29469
- const proxyBase = imageProxyUrl ? imageProxyUrl.replace(/\/image-proxy(?:\?.*)?$/, "") : API_URL;
29470
- if (proxyBase) {
29471
- return `${proxyBase}/image-proxy?url=${encodeURIComponent(rawUrl)}`;
29472
- }
29473
- return rawUrl;
29474
- }
29475
- const CONCURRENCY = 6;
29476
- async function prefetchUrls(urls, signal) {
29477
- const unique = [...new Set(urls)];
29478
- if (unique.length === 0) return;
29479
- let i = 0;
29480
- const next = async () => {
29481
- while (i < unique.length) {
29482
- if (signal == null ? void 0 : signal.aborted) return;
29483
- const url = unique[i++];
29484
- try {
29485
- await fetch(url, { signal, mode: "cors", credentials: "omit" });
29486
- } catch {
29487
- }
29488
- }
29489
- };
29490
- const workers = Array.from({ length: Math.min(CONCURRENCY, unique.length) }, () => next());
29491
- await Promise.all(workers);
29492
- }
29493
- async function warmResolvedTemplateForPreview(config, options) {
29494
- const { signal, imageProxyUrl } = options ?? {};
29495
- await ensureFontsForResolvedConfig(config);
29496
- if (signal == null ? void 0 : signal.aborted) return;
29497
- const rawUrls = collectImageUrls(config);
29498
- const resolvedUrls = rawUrls.map((u) => normalizeAssetUrl(u, imageProxyUrl)).filter((u) => u !== null);
29499
- await prefetchUrls(resolvedUrls, signal);
29500
- }
29501
- async function warmTemplateFromForm(options) {
29502
- const { signal, imageProxyUrl, ...resolveOpts } = options;
29503
- const resolved = await resolveFromForm(resolveOpts);
29504
- if (signal == null ? void 0 : signal.aborted) return;
29505
- await warmResolvedTemplateForPreview(resolved.config, { signal, imageProxyUrl });
29506
- }
29507
29579
  function setAutoShrinkDebug(enabled) {
29508
29580
  if (typeof window !== "undefined") {
29509
29581
  window.__pixldocsDebugAutoShrink = !!enabled;
@@ -29573,4 +29645,4 @@ export {
29573
29645
  buildTeaserBlurFlatKeys as y,
29574
29646
  collectFontDescriptorsFromConfig as z
29575
29647
  };
29576
- //# sourceMappingURL=index-DbfhlD78.js.map
29648
+ //# sourceMappingURL=index-DgUgxzD1.js.map