@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.
@@ -25218,6 +25218,89 @@ const previewBlur = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineP
25218
25218
  injectPreviewBlur,
25219
25219
  resolveBlurElementExactIdsFromFlatFormKeys
25220
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
+ }
25221
25304
  const PREVIEW_DEBUG_PREFIX = "[canvas-renderer][preview-debug]";
25222
25305
  function computeFontSignature(config) {
25223
25306
  var _a2;
@@ -25233,6 +25316,39 @@ function computeFontSignature(config) {
25233
25316
  for (const page of config.pages) walk(page.children || []);
25234
25317
  return Array.from(fams).sort().join("|");
25235
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
+ }
25236
25352
  function countUnderlinedNodes(config) {
25237
25353
  var _a2;
25238
25354
  if (!((_a2 = config == null ? void 0 : config.pages) == null ? void 0 : _a2.length)) return 0;
@@ -25395,6 +25511,38 @@ function PixldocsPreview(props) {
25395
25511
  const config = isResolveMode ? resolvedConfig : props.config;
25396
25512
  const previewKey = useMemo(() => `${pageIndex}`, [pageIndex]);
25397
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]);
25398
25546
  useEffect(() => {
25399
25547
  if (isResolveMode) return;
25400
25548
  if (!config) {
@@ -25477,7 +25625,7 @@ function PixldocsPreview(props) {
25477
25625
  /* @__PURE__ */ jsxs(
25478
25626
  "div",
25479
25627
  {
25480
- 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" },
25481
25629
  children: [
25482
25630
  /* @__PURE__ */ jsx(
25483
25631
  PreviewCanvas,
@@ -25523,7 +25671,7 @@ function PixldocsPreview(props) {
25523
25671
  ]
25524
25672
  }
25525
25673
  ),
25526
- !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..." }) })
25527
25675
  ] });
25528
25676
  }
25529
25677
  function normalizeSvgDimensions(svg, targetWidth, targetHeight) {
@@ -26044,9 +26192,9 @@ function captureFabricCanvasSvgForPdf(fabricInstance, canvasWidth, canvasHeight)
26044
26192
  }
26045
26193
  return svgString;
26046
26194
  }
26047
- const resolvedPackageVersion = "0.5.457";
26195
+ const resolvedPackageVersion = "0.5.458";
26048
26196
  const PACKAGE_VERSION = resolvedPackageVersion;
26049
- const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.457";
26197
+ const DEPLOYMENT_VERSION_MARKER = "__PIXLDOCS_CANVAS_RENDERER_VERSION__:0.5.458";
26050
26198
  const roundParityValue = (value) => {
26051
26199
  if (typeof value !== "number") return value;
26052
26200
  return Number.isFinite(value) ? Number(value.toFixed(3)) : value;
@@ -26860,7 +27008,7 @@ class PixldocsRenderer {
26860
27008
  await this.waitForCanvasScene(container, cloned, i);
26861
27009
  }
26862
27010
  console.log(`[canvas-renderer][pdf-unified] mounted ${cloned.pages.length} page(s), handing off to client exportMultiPagePdf`);
26863
- const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DHunvQTF.js");
27011
+ const { exportMultiPagePdf, preparePagesForExport } = await import("./vectorPdfExport-DTC5MXqp.js");
26864
27012
  const prepared = preparePagesForExport(
26865
27013
  cloned.pages,
26866
27014
  canvasWidth,
@@ -29180,7 +29328,7 @@ async function prepareLiveCanvasSvgForPdf(rawSvg, pageWidth, pageHeight, pageKey
29180
29328
  if (options == null ? void 0 : options.stripPageBackground) stripRootPageBackgroundFromSvg(svgToDraw);
29181
29329
  sanitizeSvgTreeForPdf(svgToDraw);
29182
29330
  try {
29183
- const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DHunvQTF.js");
29331
+ const { bakeTextAnchorPositionsFromLiveSvg, logTextMeasurementDiagnostic } = await import("./vectorPdfExport-DTC5MXqp.js");
29184
29332
  try {
29185
29333
  await logTextMeasurementDiagnostic(svgToDraw);
29186
29334
  } catch {
@@ -29428,89 +29576,6 @@ async function getPublishedTemplate(options) {
29428
29576
  const rows = await res.json();
29429
29577
  return rows[0] ?? null;
29430
29578
  }
29431
- function collectImageUrls(config) {
29432
- const urls = [];
29433
- const walk = (nodes) => {
29434
- for (const node of nodes) {
29435
- if (!node || node.visible === false) continue;
29436
- const src = typeof node.src === "string" ? node.src.trim() : "";
29437
- const imageUrl = typeof node.imageUrl === "string" ? node.imageUrl.trim() : "";
29438
- if (node.type === "image") {
29439
- const url = src || imageUrl;
29440
- if (url) urls.push(url);
29441
- }
29442
- if (Array.isArray(node.children) && node.children.length > 0) {
29443
- walk(node.children);
29444
- }
29445
- }
29446
- };
29447
- for (const page of config.pages || []) {
29448
- walk(page.children || []);
29449
- }
29450
- return urls;
29451
- }
29452
- function normalizeAssetUrl(rawUrl, imageProxyUrl) {
29453
- if (!rawUrl) return null;
29454
- if (rawUrl.startsWith("data:") || rawUrl.startsWith("blob:")) return null;
29455
- if (rawUrl.startsWith("/") && !rawUrl.startsWith("//")) {
29456
- if (typeof window !== "undefined") return new URL(rawUrl, window.location.origin).toString();
29457
- return null;
29458
- }
29459
- try {
29460
- const h = new URL(rawUrl).hostname.toLowerCase();
29461
- if (h === "localhost" || h === "127.0.0.1" || h === "0.0.0.0" || h.endsWith(".local") || /^(10\.|192\.168\.|169\.254\.)/.test(h)) {
29462
- if (typeof window !== "undefined" && new URL(rawUrl).origin === window.location.origin) {
29463
- return rawUrl;
29464
- }
29465
- return null;
29466
- }
29467
- } catch {
29468
- return null;
29469
- }
29470
- const supabaseUrl = typeof globalThis.__VITE_SUPABASE_URL === "string" ? globalThis.__VITE_SUPABASE_URL : "";
29471
- if (supabaseUrl && rawUrl.includes(supabaseUrl)) {
29472
- const signedMatch = rawUrl.match(/\/storage\/v1\/object\/sign\/([^?]+)/);
29473
- if (signedMatch) return `${supabaseUrl}/storage/v1/object/public/${signedMatch[1]}`;
29474
- if (rawUrl.includes("/storage/v1/object/public/")) return rawUrl;
29475
- }
29476
- const proxyBase = imageProxyUrl ? imageProxyUrl.replace(/\/image-proxy(?:\?.*)?$/, "") : API_URL;
29477
- if (proxyBase) {
29478
- return `${proxyBase}/image-proxy?url=${encodeURIComponent(rawUrl)}`;
29479
- }
29480
- return rawUrl;
29481
- }
29482
- const CONCURRENCY = 6;
29483
- async function prefetchUrls(urls, signal) {
29484
- const unique = [...new Set(urls)];
29485
- if (unique.length === 0) return;
29486
- let i = 0;
29487
- const next = async () => {
29488
- while (i < unique.length) {
29489
- if (signal == null ? void 0 : signal.aborted) return;
29490
- const url = unique[i++];
29491
- try {
29492
- await fetch(url, { signal, mode: "cors", credentials: "omit" });
29493
- } catch {
29494
- }
29495
- }
29496
- };
29497
- const workers = Array.from({ length: Math.min(CONCURRENCY, unique.length) }, () => next());
29498
- await Promise.all(workers);
29499
- }
29500
- async function warmResolvedTemplateForPreview(config, options) {
29501
- const { signal, imageProxyUrl } = options ?? {};
29502
- await ensureFontsForResolvedConfig(config);
29503
- if (signal == null ? void 0 : signal.aborted) return;
29504
- const rawUrls = collectImageUrls(config);
29505
- const resolvedUrls = rawUrls.map((u) => normalizeAssetUrl(u, imageProxyUrl)).filter((u) => u !== null);
29506
- await prefetchUrls(resolvedUrls, signal);
29507
- }
29508
- async function warmTemplateFromForm(options) {
29509
- const { signal, imageProxyUrl, ...resolveOpts } = options;
29510
- const resolved = await resolveFromForm(resolveOpts);
29511
- if (signal == null ? void 0 : signal.aborted) return;
29512
- await warmResolvedTemplateForPreview(resolved.config, { signal, imageProxyUrl });
29513
- }
29514
29579
  function setAutoShrinkDebug(enabled) {
29515
29580
  if (typeof window !== "undefined") {
29516
29581
  window.__pixldocsDebugAutoShrink = !!enabled;
@@ -29580,4 +29645,4 @@ export {
29580
29645
  buildTeaserBlurFlatKeys as y,
29581
29646
  collectFontDescriptorsFromConfig as z
29582
29647
  };
29583
- //# sourceMappingURL=index-BWYQI5Fp.js.map
29648
+ //# sourceMappingURL=index-DgUgxzD1.js.map