@primestyleai/tryon 5.9.0 → 5.9.1

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.
@@ -1488,12 +1488,22 @@ const STYLES = `
1488
1488
  }
1489
1489
  .ps-tryon-v2-processing-label {
1490
1490
  position: absolute; bottom: 1vw; left: 50%; transform: translateX(-50%);
1491
- z-index: 5; font-size: 0.65vw; font-weight: 500;
1492
- color: var(--ps-accent); letter-spacing: 0.06em;
1493
- background: rgba(0,0,0,0.6); backdrop-filter: blur(8px);
1494
- padding: 0.25vw 0.8vw; border-radius: 2vw;
1491
+ z-index: 5; font-size: 0.7vw; font-weight: 600;
1492
+ color: #fff; letter-spacing: 0.05em;
1493
+ background: rgba(0,0,0,0.72); backdrop-filter: blur(10px);
1494
+ padding: 0.6vw 0.9vw; border-radius: 0.6vw;
1495
+ display: flex; flex-direction: column; align-items: center; gap: 0.5vw;
1496
+ min-width: 14vw;
1497
+ box-shadow: 0 0.4vw 1.5vw rgba(0,0,0,0.35);
1498
+ }
1499
+ .ps-tryon-v2-processing-label > span:first-child {
1495
1500
  animation: ps-loading-pulse 2s ease-in-out infinite;
1496
1501
  }
1502
+ .ps-tryon-v2-processing-label .ps-tryon-progress-ring-track { stroke: rgba(255,255,255,0.18); }
1503
+ .ps-tryon-v2-processing-label .ps-tryon-progress-ring-fill { stroke: var(--ps-accent-light); }
1504
+ .ps-tryon-v2-processing-label .ps-tryon-progress-eta { color: #fff; }
1505
+ .ps-tryon-v2-processing-label .ps-tryon-progress-bar-wrap { background: rgba(255,255,255,0.18); }
1506
+ .ps-tryon-v2-processing-label .ps-tryon-progress-pct { color: var(--ps-accent-light); }
1497
1507
 
1498
1508
  /* "I don't know" link */
1499
1509
  .ps-tryon-v2-dontknow {
@@ -2086,6 +2096,26 @@ const STYLES = `
2086
2096
  .ps-tryon-progress-section {
2087
2097
  display: flex; align-items: center; gap: 0.63vw; width: 100%; max-width: 18vw; margin-bottom: 0.83vw;
2088
2098
  }
2099
+ /* Shared progress layout used inside StageCycler (desktop) and
2100
+ MobileScanningView — row of ring + bar + percent, same tokens. */
2101
+ .ps-tryon-progress-wrap {
2102
+ display: flex; align-items: center; gap: 10px;
2103
+ width: 100%; max-width: 320px; margin-top: 16px;
2104
+ }
2105
+ .ps-tryon-progress-wrap .ps-tryon-progress-bar-wrap {
2106
+ flex: 1; height: 4px; border-radius: 3px; overflow: hidden;
2107
+ position: relative; background: var(--ps-border-color);
2108
+ }
2109
+ .ps-tryon-progress-wrap .ps-tryon-progress-bar-fill {
2110
+ height: 100%; width: 0%;
2111
+ background: linear-gradient(90deg, var(--ps-accent), var(--ps-accent-light));
2112
+ border-radius: 3px; transition: width 0.3s ease;
2113
+ }
2114
+ .ps-tryon-progress-wrap .ps-tryon-progress-pct {
2115
+ font-size: 11px; font-weight: 700; color: var(--ps-accent);
2116
+ min-width: 30px; text-align: right;
2117
+ font-variant-numeric: tabular-nums;
2118
+ }
2089
2119
  .ps-tryon-progress-bar-wrap {
2090
2120
  flex: 1; height: 0.31vw; background: var(--ps-border-color); border-radius: 3px; overflow: hidden;
2091
2121
  position: relative;
@@ -7451,17 +7481,71 @@ function MobileSkeleton({ landmarks, w, h }) {
7451
7481
  }
7452
7482
  );
7453
7483
  }
7484
+ const MSC_TRYON_TARGET_SECONDS = 22;
7485
+ const MSC_RING_RADIUS = 20;
7486
+ const MSC_RING_CIRC = 2 * Math.PI * MSC_RING_RADIUS;
7487
+ function MscTryOnProgress({ t }) {
7488
+ const startRef = useRef(Date.now());
7489
+ const ringRef = useRef(null);
7490
+ const barRef = useRef(null);
7491
+ const etaRef = useRef(null);
7492
+ const pctRef = useRef(null);
7493
+ useEffect(() => {
7494
+ startRef.current = Date.now();
7495
+ const id = setInterval(() => {
7496
+ const elapsed = (Date.now() - startRef.current) / 1e3;
7497
+ const pct = Math.min(95, elapsed / MSC_TRYON_TARGET_SECONDS * 100);
7498
+ const val = Math.round(pct);
7499
+ if (barRef.current) barRef.current.style.width = `${val}%`;
7500
+ if (pctRef.current) pctRef.current.textContent = `${val}%`;
7501
+ if (ringRef.current) ringRef.current.style.strokeDashoffset = String(MSC_RING_CIRC * (1 - pct / 100));
7502
+ if (etaRef.current) {
7503
+ const remaining = Math.max(0, MSC_TRYON_TARGET_SECONDS - Math.floor(elapsed));
7504
+ etaRef.current.textContent = elapsed >= MSC_TRYON_TARGET_SECONDS ? t("Finalizing...") : `~${remaining}s`;
7505
+ }
7506
+ }, 200);
7507
+ return () => clearInterval(id);
7508
+ }, [t]);
7509
+ return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-wrap", children: [
7510
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-ring", children: [
7511
+ /* @__PURE__ */ jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", "aria-hidden": "true", children: [
7512
+ /* @__PURE__ */ jsx("circle", { cx: "24", cy: "24", r: MSC_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
7513
+ /* @__PURE__ */ jsx(
7514
+ "circle",
7515
+ {
7516
+ ref: ringRef,
7517
+ cx: "24",
7518
+ cy: "24",
7519
+ r: MSC_RING_RADIUS,
7520
+ className: "ps-tryon-progress-ring-fill",
7521
+ strokeDasharray: MSC_RING_CIRC,
7522
+ strokeDashoffset: MSC_RING_CIRC
7523
+ }
7524
+ )
7525
+ ] }),
7526
+ /* @__PURE__ */ jsx("span", { ref: etaRef, className: "ps-tryon-progress-eta", children: `~${MSC_TRYON_TARGET_SECONDS}s` })
7527
+ ] }),
7528
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsx("div", { ref: barRef, className: "ps-tryon-progress-bar-fill" }) }),
7529
+ /* @__PURE__ */ jsx("span", { ref: pctRef, className: "ps-tryon-progress-pct", children: "0%" })
7530
+ ] });
7531
+ }
7454
7532
  function MobileScanningView({
7455
7533
  previewUrl,
7456
7534
  productImage,
7457
7535
  bodyLandmarks,
7458
7536
  sizingDone,
7537
+ tryOnProcessing,
7459
7538
  onSwitchToManual,
7460
7539
  t
7461
7540
  }) {
7462
7541
  const displayImage = previewUrl || productImage || "";
7463
7542
  const isPhotoMode = !!previewUrl;
7464
- const stages = isPhotoMode ? [
7543
+ const stages = tryOnProcessing ? [
7544
+ { title: t("GENERATING TRY-ON"), desc: t("Rendering the garment on your photo."), viewfinderText: t("GENERATING") },
7545
+ { title: t("REFINING DETAILS"), desc: t("Fine-tuning fit, drape and shadows."), viewfinderText: t("REFINING") },
7546
+ { title: t("ALMOST THERE"), desc: t("Final compositing in progress."), viewfinderText: t("COMPOSITING") },
7547
+ { title: t("FINISHING TOUCHES"), desc: t("Polishing the result."), viewfinderText: t("FINISHING") }
7548
+ ] : isPhotoMode ? [
7465
7549
  { title: t("DETECTING POSE"), desc: t("Identifying body landmarks from your photo."), viewfinderText: t("DETECTING POSE") },
7466
7550
  { title: t("SCANNING FRAME"), desc: t("Our AI is mapping your proportions to calculate the perfect fit."), viewfinderText: t("SCANNING FRAME") },
7467
7551
  { title: t("ANALYZING BODY"), desc: t("Measuring shoulders, chest, waist and hips."), viewfinderText: t("ANALYZING") },
@@ -7504,10 +7588,13 @@ function MobileScanningView({
7504
7588
  ),
7505
7589
  isPhotoMode && bodyLandmarks && /* @__PURE__ */ jsx("div", { className: "ps-msc-pose-wrap", children: /* @__PURE__ */ jsx(MobileSkeleton, { landmarks: bodyLandmarks, w: dims.w, h: dims.h }) })
7506
7590
  ] }),
7507
- /* @__PURE__ */ jsx("div", { className: "ps-msc-stage", children: /* @__PURE__ */ jsxs("div", { className: "ps-msc-stage-slot", children: [
7508
- /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-title", children: current.title }),
7509
- /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
7510
- ] }, stageIdx) }),
7591
+ /* @__PURE__ */ jsxs("div", { className: "ps-msc-stage", children: [
7592
+ /* @__PURE__ */ jsxs("div", { className: "ps-msc-stage-slot", children: [
7593
+ /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-title", children: current.title }),
7594
+ /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
7595
+ ] }, stageIdx),
7596
+ tryOnProcessing && /* @__PURE__ */ jsx(MscTryOnProgress, { t })
7597
+ ] }),
7511
7598
  /* @__PURE__ */ jsx("div", { className: "ps-bpm-spacer" }),
7512
7599
  /* @__PURE__ */ jsx("div", { className: "ps-bpm-bottom", children: /* @__PURE__ */ jsx(
7513
7600
  MobileBottomTabs,
@@ -7705,6 +7792,62 @@ const SKELETON_CONNECTIONS = [
7705
7792
  ["rightHip", "rightKnee"],
7706
7793
  ["rightKnee", "rightAnkle"]
7707
7794
  ];
7795
+ const TRYON_TARGET_SECONDS = 22;
7796
+ const TRYON_RING_RADIUS = 20;
7797
+ const TRYON_RING_CIRC = 2 * Math.PI * TRYON_RING_RADIUS;
7798
+ function TryOnProgress({ t, isActive }) {
7799
+ const startRef = useRef(null);
7800
+ const ringRef = useRef(null);
7801
+ const barRef = useRef(null);
7802
+ const etaRef = useRef(null);
7803
+ const pctRef = useRef(null);
7804
+ useEffect(() => {
7805
+ if (!isActive) {
7806
+ startRef.current = null;
7807
+ return;
7808
+ }
7809
+ startRef.current = Date.now();
7810
+ const id = setInterval(() => {
7811
+ const start = startRef.current || Date.now();
7812
+ const elapsed = (Date.now() - start) / 1e3;
7813
+ const pct = Math.min(95, elapsed / TRYON_TARGET_SECONDS * 100);
7814
+ const val = Math.round(pct);
7815
+ if (barRef.current) barRef.current.style.width = `${val}%`;
7816
+ if (pctRef.current) pctRef.current.textContent = `${val}%`;
7817
+ if (ringRef.current) {
7818
+ ringRef.current.style.strokeDashoffset = String(TRYON_RING_CIRC * (1 - pct / 100));
7819
+ }
7820
+ if (etaRef.current) {
7821
+ const remaining = Math.max(0, TRYON_TARGET_SECONDS - Math.floor(elapsed));
7822
+ etaRef.current.textContent = elapsed >= TRYON_TARGET_SECONDS ? t("Finalizing...") : `~${remaining}s`;
7823
+ }
7824
+ }, 200);
7825
+ return () => clearInterval(id);
7826
+ }, [isActive, t]);
7827
+ if (!isActive) return null;
7828
+ return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-wrap", children: [
7829
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-ring", children: [
7830
+ /* @__PURE__ */ jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", "aria-hidden": "true", children: [
7831
+ /* @__PURE__ */ jsx("circle", { cx: "24", cy: "24", r: TRYON_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
7832
+ /* @__PURE__ */ jsx(
7833
+ "circle",
7834
+ {
7835
+ ref: ringRef,
7836
+ cx: "24",
7837
+ cy: "24",
7838
+ r: TRYON_RING_RADIUS,
7839
+ className: "ps-tryon-progress-ring-fill",
7840
+ strokeDasharray: TRYON_RING_CIRC,
7841
+ strokeDashoffset: TRYON_RING_CIRC
7842
+ }
7843
+ )
7844
+ ] }),
7845
+ /* @__PURE__ */ jsx("span", { ref: etaRef, className: "ps-tryon-progress-eta", children: `~${TRYON_TARGET_SECONDS}s` })
7846
+ ] }),
7847
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsx("div", { ref: barRef, className: "ps-tryon-progress-bar-fill" }) }),
7848
+ /* @__PURE__ */ jsx("span", { ref: pctRef, className: "ps-tryon-progress-pct", children: "0%" })
7849
+ ] });
7850
+ }
7708
7851
  function FaceOverlay({
7709
7852
  landmarks,
7710
7853
  imgWidth,
@@ -7848,10 +7991,13 @@ function StageCycler({
7848
7991
  return () => clearInterval(id);
7849
7992
  }, [isDone, active.length]);
7850
7993
  const current = active[idx] ?? active[0];
7851
- return /* @__PURE__ */ jsx("div", { className: "ps-msc-stage", style: { alignSelf: "center", marginTop: "auto", marginBottom: "auto" }, children: /* @__PURE__ */ jsxs("div", { className: "ps-msc-stage-slot", children: [
7852
- /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-title", children: current.title }),
7853
- /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
7854
- ] }, `${tryOnProcessing ? "t" : "s"}-${idx}`) });
7994
+ return /* @__PURE__ */ jsxs("div", { className: "ps-msc-stage", style: { alignSelf: "center", marginTop: "auto", marginBottom: "auto" }, children: [
7995
+ /* @__PURE__ */ jsxs("div", { className: "ps-msc-stage-slot", children: [
7996
+ /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-title", children: current.title }),
7997
+ /* @__PURE__ */ jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
7998
+ ] }, `${tryOnProcessing ? "t" : "s"}-${idx}`),
7999
+ tryOnProcessing && /* @__PURE__ */ jsx(TryOnProgress, { t, isActive: !!tryOnProcessing })
8000
+ ] });
7855
8001
  }
7856
8002
  function SkeletonOverlay({ landmarks, imgWidth, imgHeight }) {
7857
8003
  const W = imgWidth;
@@ -9027,6 +9173,7 @@ function SizeResultView({
9027
9173
  previewUrl,
9028
9174
  bodyLandmarks: bodyLandmarks ?? null,
9029
9175
  sizingDone,
9176
+ tryOnProcessing,
9030
9177
  onSwitchToManual: () => setView("body-profile"),
9031
9178
  t
9032
9179
  }
@@ -9083,7 +9230,7 @@ function SizeResultView({
9083
9230
  isMobile: true,
9084
9231
  isTryOnImage: !!resultImageUrl,
9085
9232
  showLines,
9086
- onToggleLines: () => setShowLines(!showLines),
9233
+ onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
9087
9234
  onImageLoad: handleImgLoad,
9088
9235
  overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsx(
9089
9236
  MeasurementOverlay,
@@ -9157,7 +9304,7 @@ function SizeResultView({
9157
9304
  },
9158
9305
  onClose,
9159
9306
  showLines,
9160
- onToggleLines: () => setShowLines(!showLines),
9307
+ onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
9161
9308
  onImageLoad: handleImgLoad,
9162
9309
  overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsx(
9163
9310
  MeasurementOverlay,
@@ -9192,7 +9339,10 @@ function SizeResultView({
9192
9339
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-v2-bg", style: { position: "relative" }, children: [
9193
9340
  /* @__PURE__ */ jsx("img", { src: tryOnProcessing && previewUrl ? previewUrl : resultImageUrl || productImage, alt: productTitle, className: "ps-tryon-v2-bg-img", onLoad: handleImgLoad }),
9194
9341
  tryOnProcessing && bodyLandmarks && /* @__PURE__ */ jsx(SkeletonOverlay, { landmarks: bodyLandmarks, imgWidth: imgDims.w, imgHeight: imgDims.h }),
9195
- tryOnProcessing && /* @__PURE__ */ jsx("div", { className: "ps-tryon-v2-processing-label", children: t("Generating try-on...") }),
9342
+ tryOnProcessing && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-v2-processing-label", children: [
9343
+ /* @__PURE__ */ jsx("span", { children: t("Generating try-on...") }),
9344
+ /* @__PURE__ */ jsx(TryOnProgress, { t, isActive: true })
9345
+ ] }),
9196
9346
  resultImageUrl && !tryOnProcessing && poseReady && poseLines && /* @__PURE__ */ jsx(MeasurementOverlay, { lines: poseLines, fitRows: (() => {
9197
9347
  const all = [...sizingResult?.matchDetails || []];
9198
9348
  if (sizingResult?.sections) {
@@ -9209,7 +9359,7 @@ function SizeResultView({
9209
9359
  }).map((m) => ({ area: m.measurement, userNum: parseFloat(m.userValue) || 0, chartLabel: m.chartRange || "", fit: m.fit }));
9210
9360
  })(), show: showLines, imgWidth: imgDims.w, imgHeight: imgDims.h }),
9211
9361
  resultImageUrl && !tryOnProcessing && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", bottom: "0.5vw", left: "0.5vw", zIndex: 3, display: "flex", flexDirection: "column", gap: "0.3vw" }, children: [
9212
- /* @__PURE__ */ jsxs("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: [
9362
+ !isAccessory && /* @__PURE__ */ jsxs("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: [
9213
9363
  /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", style: { marginRight: "0.3vw" }, children: [
9214
9364
  /* @__PURE__ */ jsx("line", { x1: "4", y1: "9", x2: "20", y2: "9" }),
9215
9365
  /* @__PURE__ */ jsx("line", { x1: "4", y1: "15", x2: "20", y2: "15" }),
@@ -9332,7 +9482,7 @@ function SizeResultView({
9332
9482
  renderRaw: isAccessory,
9333
9483
  isTryOnImage: !!resultImageUrl,
9334
9484
  showLines,
9335
- onToggleLines: () => setShowLines(!showLines),
9485
+ onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
9336
9486
  onImageLoad: handleImgLoad,
9337
9487
  overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsx(
9338
9488
  MeasurementOverlay,
@@ -9353,7 +9503,7 @@ function SizeResultView({
9353
9503
  /* @__PURE__ */ jsx("img", { src: resultImageUrl || productImage, alt: productTitle, className: "ps-tryon-v2-bg-img", onLoad: handleImgLoad }),
9354
9504
  resultImageUrl && poseReady && poseLines && /* @__PURE__ */ jsx(MeasurementOverlay, { lines: poseLines, fitRows: (sizingResult?.matchDetails || []).map((m) => ({ area: m.measurement, userNum: parseFloat(m.userValue) || 0, chartLabel: m.chartRange || "", fit: m.fit })), show: showLines, imgWidth: imgDims.w, imgHeight: imgDims.h }),
9355
9505
  resultImageUrl && !tryOnProcessing && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", bottom: "0.5vw", left: "0.5vw", zIndex: 3, display: "flex", flexDirection: "column", gap: "0.3vw" }, children: [
9356
- /* @__PURE__ */ jsx("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: showLines ? t("Hide Fit") : t("Show Fit") }),
9506
+ !isAccessory && /* @__PURE__ */ jsx("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: showLines ? t("Hide Fit") : t("Show Fit") }),
9357
9507
  /* @__PURE__ */ jsx("button", { className: "ps-tryon-sr-glass-btn", onClick: handleDownload, children: t("Download") })
9358
9508
  ] })
9359
9509
  ] }),