@primestyleai/tryon 5.8.58 → 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.
@@ -9296,9 +9296,10 @@ class ApiClient {
9296
9296
  Authorization: `Bearer ${this.apiKey}`
9297
9297
  };
9298
9298
  }
9299
- async submitTryOn(modelImage, garmentImage, fitInfo) {
9299
+ async submitTryOn(modelImage, garmentImage, fitInfo, category) {
9300
9300
  const body = { modelImage, garmentImage };
9301
9301
  if (fitInfo && fitInfo.length > 0) body.fitInfo = fitInfo;
9302
+ if (category && category !== "apparel") body.category = category;
9302
9303
  const res = await fetch(`${this.baseUrl}/api/v1/tryon`, {
9303
9304
  method: "POST",
9304
9305
  headers: this.headers,
@@ -10912,12 +10913,22 @@ const STYLES$1 = `
10912
10913
  }
10913
10914
  .ps-tryon-v2-processing-label {
10914
10915
  position: absolute; bottom: 1vw; left: 50%; transform: translateX(-50%);
10915
- z-index: 5; font-size: 0.65vw; font-weight: 500;
10916
- color: var(--ps-accent); letter-spacing: 0.06em;
10917
- background: rgba(0,0,0,0.6); backdrop-filter: blur(8px);
10918
- padding: 0.25vw 0.8vw; border-radius: 2vw;
10916
+ z-index: 5; font-size: 0.7vw; font-weight: 600;
10917
+ color: #fff; letter-spacing: 0.05em;
10918
+ background: rgba(0,0,0,0.72); backdrop-filter: blur(10px);
10919
+ padding: 0.6vw 0.9vw; border-radius: 0.6vw;
10920
+ display: flex; flex-direction: column; align-items: center; gap: 0.5vw;
10921
+ min-width: 14vw;
10922
+ box-shadow: 0 0.4vw 1.5vw rgba(0,0,0,0.35);
10923
+ }
10924
+ .ps-tryon-v2-processing-label > span:first-child {
10919
10925
  animation: ps-loading-pulse 2s ease-in-out infinite;
10920
10926
  }
10927
+ .ps-tryon-v2-processing-label .ps-tryon-progress-ring-track { stroke: rgba(255,255,255,0.18); }
10928
+ .ps-tryon-v2-processing-label .ps-tryon-progress-ring-fill { stroke: var(--ps-accent-light); }
10929
+ .ps-tryon-v2-processing-label .ps-tryon-progress-eta { color: #fff; }
10930
+ .ps-tryon-v2-processing-label .ps-tryon-progress-bar-wrap { background: rgba(255,255,255,0.18); }
10931
+ .ps-tryon-v2-processing-label .ps-tryon-progress-pct { color: var(--ps-accent-light); }
10921
10932
 
10922
10933
  /* "I don't know" link */
10923
10934
  .ps-tryon-v2-dontknow {
@@ -11508,22 +11519,81 @@ const STYLES$1 = `
11508
11519
  }
11509
11520
 
11510
11521
  .ps-tryon-progress-section {
11511
- display: flex; align-items: center; gap: 0.63vw; width: 100%; max-width: 15.6vw; margin-bottom: 0.83vw;
11522
+ display: flex; align-items: center; gap: 0.63vw; width: 100%; max-width: 18vw; margin-bottom: 0.83vw;
11523
+ }
11524
+ /* Shared progress layout used inside StageCycler (desktop) and
11525
+ MobileScanningView — row of ring + bar + percent, same tokens. */
11526
+ .ps-tryon-progress-wrap {
11527
+ display: flex; align-items: center; gap: 10px;
11528
+ width: 100%; max-width: 320px; margin-top: 16px;
11529
+ }
11530
+ .ps-tryon-progress-wrap .ps-tryon-progress-bar-wrap {
11531
+ flex: 1; height: 4px; border-radius: 3px; overflow: hidden;
11532
+ position: relative; background: var(--ps-border-color);
11533
+ }
11534
+ .ps-tryon-progress-wrap .ps-tryon-progress-bar-fill {
11535
+ height: 100%; width: 0%;
11536
+ background: linear-gradient(90deg, var(--ps-accent), var(--ps-accent-light));
11537
+ border-radius: 3px; transition: width 0.3s ease;
11538
+ }
11539
+ .ps-tryon-progress-wrap .ps-tryon-progress-pct {
11540
+ font-size: 11px; font-weight: 700; color: var(--ps-accent);
11541
+ min-width: 30px; text-align: right;
11542
+ font-variant-numeric: tabular-nums;
11512
11543
  }
11513
11544
  .ps-tryon-progress-bar-wrap {
11514
11545
  flex: 1; height: 0.31vw; background: var(--ps-border-color); border-radius: 3px; overflow: hidden;
11546
+ position: relative;
11547
+ }
11548
+ .ps-tryon-progress-bar-wrap::after {
11549
+ content: ""; position: absolute; inset: 0;
11550
+ background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.45) 50%, transparent 100%);
11551
+ transform: translateX(-100%);
11552
+ animation: ps-progress-shimmer 2s linear infinite;
11553
+ pointer-events: none;
11554
+ }
11555
+ @keyframes ps-progress-shimmer {
11556
+ 0% { transform: translateX(-100%); }
11557
+ 100% { transform: translateX(100%); }
11515
11558
  }
11516
11559
  .ps-tryon-progress-bar-fill {
11517
11560
  height: 100%; background: linear-gradient(90deg, var(--ps-accent), var(--ps-accent-light));
11518
11561
  border-radius: 3px; transition: width 0.3s ease; width: 0%;
11562
+ position: relative; z-index: 1;
11519
11563
  }
11520
11564
  .ps-tryon-progress-pct {
11521
11565
  font-size: 0.68vw; font-weight: 700; color: var(--ps-accent); min-width: 1.88vw; text-align: right;
11522
11566
  font-variant-numeric: tabular-nums;
11523
11567
  }
11524
11568
 
11569
+ /* Circular ETA ring — 48×48 px SVG with a track + progress circle; ETA
11570
+ text centered. strokeDashoffset is driven by the ticker in
11571
+ PrimeStyleTryonInner, so CSS only styles the appearance. */
11572
+ .ps-tryon-progress-ring {
11573
+ position: relative; width: 48px; height: 48px; flex: 0 0 48px;
11574
+ display: flex; align-items: center; justify-content: center;
11575
+ }
11576
+ .ps-tryon-progress-ring svg { transform: rotate(-90deg); }
11577
+ .ps-tryon-progress-ring-track {
11578
+ fill: none; stroke: var(--ps-border-color); stroke-width: 3.5;
11579
+ }
11580
+ .ps-tryon-progress-ring-fill {
11581
+ fill: none; stroke: var(--ps-accent); stroke-width: 3.5;
11582
+ stroke-linecap: round;
11583
+ transition: stroke-dashoffset 0.3s ease;
11584
+ }
11585
+ .ps-tryon-progress-eta {
11586
+ position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
11587
+ font-size: 10px; font-weight: 700; color: var(--ps-accent);
11588
+ font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
11589
+ pointer-events: none;
11590
+ }
11591
+
11525
11592
  @keyframes ps-scale-in { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
11526
- .ps-tryon-processing-text { font-size: 0.73vw; color: var(--ps-text-primary); margin: 0 0 0.21vw; }
11593
+ .ps-tryon-processing-text {
11594
+ font-size: 0.73vw; color: var(--ps-text-primary); margin: 0 0 0.21vw;
11595
+ opacity: 1; transition: opacity 0.18s ease;
11596
+ }
11527
11597
  .ps-tryon-processing-sub { font-size: 0.63vw; color: var(--ps-text-secondary); margin: 0; }
11528
11598
 
11529
11599
  /* Result — split layout */
@@ -16836,17 +16906,71 @@ function MobileSkeleton({ landmarks, w: w2, h }) {
16836
16906
  }
16837
16907
  );
16838
16908
  }
16909
+ const MSC_TRYON_TARGET_SECONDS = 22;
16910
+ const MSC_RING_RADIUS = 20;
16911
+ const MSC_RING_CIRC = 2 * Math.PI * MSC_RING_RADIUS;
16912
+ function MscTryOnProgress({ t: t2 }) {
16913
+ const startRef = reactExports.useRef(Date.now());
16914
+ const ringRef = reactExports.useRef(null);
16915
+ const barRef = reactExports.useRef(null);
16916
+ const etaRef = reactExports.useRef(null);
16917
+ const pctRef = reactExports.useRef(null);
16918
+ reactExports.useEffect(() => {
16919
+ startRef.current = Date.now();
16920
+ const id2 = setInterval(() => {
16921
+ const elapsed = (Date.now() - startRef.current) / 1e3;
16922
+ const pct = Math.min(95, elapsed / MSC_TRYON_TARGET_SECONDS * 100);
16923
+ const val = Math.round(pct);
16924
+ if (barRef.current) barRef.current.style.width = `${val}%`;
16925
+ if (pctRef.current) pctRef.current.textContent = `${val}%`;
16926
+ if (ringRef.current) ringRef.current.style.strokeDashoffset = String(MSC_RING_CIRC * (1 - pct / 100));
16927
+ if (etaRef.current) {
16928
+ const remaining = Math.max(0, MSC_TRYON_TARGET_SECONDS - Math.floor(elapsed));
16929
+ etaRef.current.textContent = elapsed >= MSC_TRYON_TARGET_SECONDS ? t2("Finalizing...") : `~${remaining}s`;
16930
+ }
16931
+ }, 200);
16932
+ return () => clearInterval(id2);
16933
+ }, [t2]);
16934
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-wrap", children: [
16935
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
16936
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", "aria-hidden": "true", children: [
16937
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: MSC_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
16938
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
16939
+ "circle",
16940
+ {
16941
+ ref: ringRef,
16942
+ cx: "24",
16943
+ cy: "24",
16944
+ r: MSC_RING_RADIUS,
16945
+ className: "ps-tryon-progress-ring-fill",
16946
+ strokeDasharray: MSC_RING_CIRC,
16947
+ strokeDashoffset: MSC_RING_CIRC
16948
+ }
16949
+ )
16950
+ ] }),
16951
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: etaRef, className: "ps-tryon-progress-eta", children: `~${MSC_TRYON_TARGET_SECONDS}s` })
16952
+ ] }),
16953
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: barRef, className: "ps-tryon-progress-bar-fill" }) }),
16954
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: pctRef, className: "ps-tryon-progress-pct", children: "0%" })
16955
+ ] });
16956
+ }
16839
16957
  function MobileScanningView({
16840
16958
  previewUrl,
16841
16959
  productImage,
16842
16960
  bodyLandmarks,
16843
16961
  sizingDone,
16962
+ tryOnProcessing,
16844
16963
  onSwitchToManual,
16845
16964
  t: t2
16846
16965
  }) {
16847
16966
  const displayImage = previewUrl || productImage || "";
16848
16967
  const isPhotoMode = !!previewUrl;
16849
- const stages = isPhotoMode ? [
16968
+ const stages = tryOnProcessing ? [
16969
+ { title: t2("GENERATING TRY-ON"), desc: t2("Rendering the garment on your photo."), viewfinderText: t2("GENERATING") },
16970
+ { title: t2("REFINING DETAILS"), desc: t2("Fine-tuning fit, drape and shadows."), viewfinderText: t2("REFINING") },
16971
+ { title: t2("ALMOST THERE"), desc: t2("Final compositing in progress."), viewfinderText: t2("COMPOSITING") },
16972
+ { title: t2("FINISHING TOUCHES"), desc: t2("Polishing the result."), viewfinderText: t2("FINISHING") }
16973
+ ] : isPhotoMode ? [
16850
16974
  { title: t2("DETECTING POSE"), desc: t2("Identifying body landmarks from your photo."), viewfinderText: t2("DETECTING POSE") },
16851
16975
  { title: t2("SCANNING FRAME"), desc: t2("Our AI is mapping your proportions to calculate the perfect fit."), viewfinderText: t2("SCANNING FRAME") },
16852
16976
  { title: t2("ANALYZING BODY"), desc: t2("Measuring shoulders, chest, waist and hips."), viewfinderText: t2("ANALYZING") },
@@ -16889,10 +17013,13 @@ function MobileScanningView({
16889
17013
  ),
16890
17014
  isPhotoMode && bodyLandmarks && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-pose-wrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx(MobileSkeleton, { landmarks: bodyLandmarks, w: dims.w, h: dims.h }) })
16891
17015
  ] }),
16892
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage-slot", children: [
16893
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-title", children: current.title }),
16894
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
16895
- ] }, stageIdx) }),
17016
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage", children: [
17017
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage-slot", children: [
17018
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-title", children: current.title }),
17019
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
17020
+ ] }, stageIdx),
17021
+ tryOnProcessing && /* @__PURE__ */ jsxRuntimeExports.jsx(MscTryOnProgress, { t: t2 })
17022
+ ] }),
16896
17023
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-bpm-spacer" }),
16897
17024
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-bpm-bottom", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
16898
17025
  MobileBottomTabs,
@@ -17090,6 +17217,62 @@ const SKELETON_CONNECTIONS = [
17090
17217
  ["rightHip", "rightKnee"],
17091
17218
  ["rightKnee", "rightAnkle"]
17092
17219
  ];
17220
+ const TRYON_TARGET_SECONDS = 22;
17221
+ const TRYON_RING_RADIUS = 20;
17222
+ const TRYON_RING_CIRC = 2 * Math.PI * TRYON_RING_RADIUS;
17223
+ function TryOnProgress({ t: t2, isActive }) {
17224
+ const startRef = reactExports.useRef(null);
17225
+ const ringRef = reactExports.useRef(null);
17226
+ const barRef = reactExports.useRef(null);
17227
+ const etaRef = reactExports.useRef(null);
17228
+ const pctRef = reactExports.useRef(null);
17229
+ reactExports.useEffect(() => {
17230
+ if (!isActive) {
17231
+ startRef.current = null;
17232
+ return;
17233
+ }
17234
+ startRef.current = Date.now();
17235
+ const id2 = setInterval(() => {
17236
+ const start = startRef.current || Date.now();
17237
+ const elapsed = (Date.now() - start) / 1e3;
17238
+ const pct = Math.min(95, elapsed / TRYON_TARGET_SECONDS * 100);
17239
+ const val = Math.round(pct);
17240
+ if (barRef.current) barRef.current.style.width = `${val}%`;
17241
+ if (pctRef.current) pctRef.current.textContent = `${val}%`;
17242
+ if (ringRef.current) {
17243
+ ringRef.current.style.strokeDashoffset = String(TRYON_RING_CIRC * (1 - pct / 100));
17244
+ }
17245
+ if (etaRef.current) {
17246
+ const remaining = Math.max(0, TRYON_TARGET_SECONDS - Math.floor(elapsed));
17247
+ etaRef.current.textContent = elapsed >= TRYON_TARGET_SECONDS ? t2("Finalizing...") : `~${remaining}s`;
17248
+ }
17249
+ }, 200);
17250
+ return () => clearInterval(id2);
17251
+ }, [isActive, t2]);
17252
+ if (!isActive) return null;
17253
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-wrap", children: [
17254
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
17255
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "48", height: "48", viewBox: "0 0 48 48", "aria-hidden": "true", children: [
17256
+ /* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: TRYON_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
17257
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
17258
+ "circle",
17259
+ {
17260
+ ref: ringRef,
17261
+ cx: "24",
17262
+ cy: "24",
17263
+ r: TRYON_RING_RADIUS,
17264
+ className: "ps-tryon-progress-ring-fill",
17265
+ strokeDasharray: TRYON_RING_CIRC,
17266
+ strokeDashoffset: TRYON_RING_CIRC
17267
+ }
17268
+ )
17269
+ ] }),
17270
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: etaRef, className: "ps-tryon-progress-eta", children: `~${TRYON_TARGET_SECONDS}s` })
17271
+ ] }),
17272
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: barRef, className: "ps-tryon-progress-bar-fill" }) }),
17273
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: pctRef, className: "ps-tryon-progress-pct", children: "0%" })
17274
+ ] });
17275
+ }
17093
17276
  function FaceOverlay({
17094
17277
  landmarks,
17095
17278
  imgWidth,
@@ -17233,10 +17416,13 @@ function StageCycler({
17233
17416
  return () => clearInterval(id2);
17234
17417
  }, [isDone, active.length]);
17235
17418
  const current = active[idx] ?? active[0];
17236
- return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage", style: { alignSelf: "center", marginTop: "auto", marginBottom: "auto" }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage-slot", children: [
17237
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-title", children: current.title }),
17238
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
17239
- ] }, `${tryOnProcessing ? "t" : "s"}-${idx}`) });
17419
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage", style: { alignSelf: "center", marginTop: "auto", marginBottom: "auto" }, children: [
17420
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage-slot", children: [
17421
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-title", children: current.title }),
17422
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
17423
+ ] }, `${tryOnProcessing ? "t" : "s"}-${idx}`),
17424
+ tryOnProcessing && /* @__PURE__ */ jsxRuntimeExports.jsx(TryOnProgress, { t: t2, isActive: !!tryOnProcessing })
17425
+ ] });
17240
17426
  }
17241
17427
  function SkeletonOverlay({ landmarks, imgWidth, imgHeight }) {
17242
17428
  const W2 = imgWidth;
@@ -18374,6 +18560,7 @@ function SizeResultView({
18374
18560
  const allDone = hasPhoto ? sizingDone && tryOnDone : sizingDone;
18375
18561
  const isMobile = useIsMobile();
18376
18562
  const isAccessory = measurementType === "face" || measurementType === "head";
18563
+ const vtoExcluded = measurementType === "foot";
18377
18564
  console.log("[PS-SDK] SizeResultView render:", {
18378
18565
  hasPhoto,
18379
18566
  isSnapProcessing,
@@ -18411,6 +18598,7 @@ function SizeResultView({
18411
18598
  previewUrl,
18412
18599
  bodyLandmarks: bodyLandmarks ?? null,
18413
18600
  sizingDone,
18601
+ tryOnProcessing,
18414
18602
  onSwitchToManual: () => setView("body-profile"),
18415
18603
  t: t2
18416
18604
  }
@@ -18467,7 +18655,7 @@ function SizeResultView({
18467
18655
  isMobile: true,
18468
18656
  isTryOnImage: !!resultImageUrl,
18469
18657
  showLines,
18470
- onToggleLines: () => setShowLines(!showLines),
18658
+ onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
18471
18659
  onImageLoad: handleImgLoad,
18472
18660
  overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsxRuntimeExports.jsx(
18473
18661
  MeasurementOverlay,
@@ -18541,7 +18729,7 @@ function SizeResultView({
18541
18729
  },
18542
18730
  onClose,
18543
18731
  showLines,
18544
- onToggleLines: () => setShowLines(!showLines),
18732
+ onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
18545
18733
  onImageLoad: handleImgLoad,
18546
18734
  overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsxRuntimeExports.jsx(
18547
18735
  MeasurementOverlay,
@@ -18576,7 +18764,10 @@ function SizeResultView({
18576
18764
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-v2-bg", style: { position: "relative" }, children: [
18577
18765
  /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: tryOnProcessing && previewUrl ? previewUrl : resultImageUrl || productImage, alt: productTitle, className: "ps-tryon-v2-bg-img", onLoad: handleImgLoad }),
18578
18766
  tryOnProcessing && bodyLandmarks && /* @__PURE__ */ jsxRuntimeExports.jsx(SkeletonOverlay, { landmarks: bodyLandmarks, imgWidth: imgDims.w, imgHeight: imgDims.h }),
18579
- tryOnProcessing && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-v2-processing-label", children: t2("Generating try-on...") }),
18767
+ tryOnProcessing && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-v2-processing-label", children: [
18768
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: t2("Generating try-on...") }),
18769
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TryOnProgress, { t: t2, isActive: true })
18770
+ ] }),
18580
18771
  resultImageUrl && !tryOnProcessing && poseReady && poseLines && /* @__PURE__ */ jsxRuntimeExports.jsx(MeasurementOverlay, { lines: poseLines, fitRows: (() => {
18581
18772
  const all = [...sizingResult?.matchDetails || []];
18582
18773
  if (sizingResult?.sections) {
@@ -18593,7 +18784,7 @@ function SizeResultView({
18593
18784
  }).map((m2) => ({ area: m2.measurement, userNum: parseFloat(m2.userValue) || 0, chartLabel: m2.chartRange || "", fit: m2.fit }));
18594
18785
  })(), show: showLines, imgWidth: imgDims.w, imgHeight: imgDims.h }),
18595
18786
  resultImageUrl && !tryOnProcessing && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "absolute", bottom: "0.5vw", left: "0.5vw", zIndex: 3, display: "flex", flexDirection: "column", gap: "0.3vw" }, children: [
18596
- /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: [
18787
+ !isAccessory && /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: [
18597
18788
  /* @__PURE__ */ jsxRuntimeExports.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: [
18598
18789
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "4", y1: "9", x2: "20", y2: "9" }),
18599
18790
  /* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "4", y1: "15", x2: "20", y2: "15" }),
@@ -18647,7 +18838,7 @@ function SizeResultView({
18647
18838
  " →"
18648
18839
  ]
18649
18840
  }
18650
- ) : isAccessory ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
18841
+ ) : vtoExcluded ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
18651
18842
  "button",
18652
18843
  {
18653
18844
  className: "ps-tryon-v2-cta",
@@ -18707,7 +18898,7 @@ function SizeResultView({
18707
18898
  onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
18708
18899
  backLabel: t2("Back"),
18709
18900
  internationalSizes: sizingResult?.internationalSizes,
18710
- onTryOn: resultImageUrl || isAccessory ? void 0 : handleSingleTryOn,
18901
+ onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
18711
18902
  continueLabel: resultImageUrl ? t2("Continue Shopping") : void 0,
18712
18903
  tryOnProcessing,
18713
18904
  productImage: resultImageUrl || productImage,
@@ -18716,7 +18907,7 @@ function SizeResultView({
18716
18907
  renderRaw: isAccessory,
18717
18908
  isTryOnImage: !!resultImageUrl,
18718
18909
  showLines,
18719
- onToggleLines: () => setShowLines(!showLines),
18910
+ onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
18720
18911
  onImageLoad: handleImgLoad,
18721
18912
  overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsxRuntimeExports.jsx(
18722
18913
  MeasurementOverlay,
@@ -18737,7 +18928,7 @@ function SizeResultView({
18737
18928
  /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: resultImageUrl || productImage, alt: productTitle, className: "ps-tryon-v2-bg-img", onLoad: handleImgLoad }),
18738
18929
  resultImageUrl && poseReady && poseLines && /* @__PURE__ */ jsxRuntimeExports.jsx(MeasurementOverlay, { lines: poseLines, fitRows: (sizingResult?.matchDetails || []).map((m2) => ({ area: m2.measurement, userNum: parseFloat(m2.userValue) || 0, chartLabel: m2.chartRange || "", fit: m2.fit })), show: showLines, imgWidth: imgDims.w, imgHeight: imgDims.h }),
18739
18930
  resultImageUrl && !tryOnProcessing && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { position: "absolute", bottom: "0.5vw", left: "0.5vw", zIndex: 3, display: "flex", flexDirection: "column", gap: "0.3vw" }, children: [
18740
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: showLines ? t2("Hide Fit") : t2("Show Fit") }),
18931
+ !isAccessory && /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ps-tryon-sr-glass-btn", onClick: () => setShowLines(!showLines), children: showLines ? t2("Hide Fit") : t2("Show Fit") }),
18741
18932
  /* @__PURE__ */ jsxRuntimeExports.jsx("button", { className: "ps-tryon-sr-glass-btn", onClick: handleDownload, children: t2("Download") })
18742
18933
  ] })
18743
18934
  ] }),
@@ -18754,7 +18945,7 @@ function SizeResultView({
18754
18945
  onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
18755
18946
  backLabel: t2("Back"),
18756
18947
  internationalSizes: sizingResult?.internationalSizes,
18757
- onTryOn: resultImageUrl || isAccessory ? void 0 : handleSingleTryOn,
18948
+ onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
18758
18949
  continueLabel: resultImageUrl ? t2("Continue Shopping") : void 0,
18759
18950
  tryOnProcessing,
18760
18951
  t: t2,
@@ -18764,14 +18955,14 @@ function SizeResultView({
18764
18955
  ] });
18765
18956
  })()
18766
18957
  ),
18767
- showPhotoGuide && !isAccessory && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-sr-chart-overlay", children: isMobile ? (
18958
+ showPhotoGuide && !vtoExcluded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-sr-chart-overlay", children: isMobile ? (
18768
18959
  /* ── Mobile: same layout as the AI-sizing photo step
18769
18960
  (PhotoStepMobile) — title + subtitle, large preview,
18770
18961
  checklist card, primary CTA + RETAKE secondary. ── */
18771
18962
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-bp-wrapper", style: { padding: "16px 16px 0", background: "var(--ps-bg-primary)" }, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-root", children: [
18772
18963
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-header", children: [
18773
18964
  /* @__PURE__ */ jsxRuntimeExports.jsx("h2", { className: "ps-pm-title", children: t2("Review your photo") }),
18774
- /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-pm-subtitle", children: t2("Ensure your full body is visible for the most accurate virtual try-on.") })
18965
+ /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "ps-pm-subtitle", children: measurementType === "face" ? t2("A clear, front-facing face photo — no glasses on — gives us the most accurate try-on.") : measurementType === "head" ? t2("Face the camera with your head and shoulders in frame, leaving space above your head.") : t2("Ensure your full body is visible for the most accurate virtual try-on.") })
18775
18966
  ] }),
18776
18967
  /* @__PURE__ */ jsxRuntimeExports.jsx(
18777
18968
  "input",
@@ -18822,11 +19013,19 @@ function SizeResultView({
18822
19013
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-pm-checklist-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "14", height: "14", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z" }) }) }),
18823
19014
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-checklist-body", children: [
18824
19015
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-pm-checklist-title", children: t2("Checklist for accuracy") }),
18825
- /* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "ps-pm-checklist-items", children: [
19016
+ /* @__PURE__ */ jsxRuntimeExports.jsx("ul", { className: "ps-pm-checklist-items", children: measurementType === "face" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19017
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Face the camera directly at eye level") }),
19018
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Remove any glasses you're wearing") }),
19019
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Good lighting, plain background") })
19020
+ ] }) : measurementType === "head" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19021
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Head and shoulders in frame") }),
19022
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Leave space above your head") }),
19023
+ /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Good lighting, plain background") })
19024
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
18826
19025
  /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Form-fitting clothing is recommended") }),
18827
19026
  /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Standing 2-3 meters from camera") }),
18828
19027
  /* @__PURE__ */ jsxRuntimeExports.jsx("li", { children: t2("Neutral background with good lighting") })
18829
- ] })
19028
+ ] }) })
18830
19029
  ] })
18831
19030
  ] }),
18832
19031
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-bpm-spacer" }),
@@ -18906,7 +19105,7 @@ function SizeResultView({
18906
19105
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
18907
19106
  /* @__PURE__ */ jsxRuntimeExports.jsx(UploadIcon, { size: 32 }),
18908
19107
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.85vw", fontWeight: 600, color: "var(--ps-text-primary)", marginTop: "0.5vw" }, children: t2("Upload your photo") }),
18909
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: t2("Click or drag a full-body photo") })
19108
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: measurementType === "face" ? t2("Click or drag a close-up face photo") : measurementType === "head" ? t2("Click or drag a head-and-shoulders photo") : t2("Click or drag a full-body photo") })
18910
19109
  ] }),
18911
19110
  /* @__PURE__ */ jsxRuntimeExports.jsx(
18912
19111
  "input",
@@ -18931,7 +19130,23 @@ function SizeResultView({
18931
19130
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#1c9d4c", fontSize: "0.75vw", fontWeight: 700 }, children: "✓" }),
18932
19131
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#1c9d4c", fontSize: "0.65vw", fontWeight: 600 }, children: t2("Do") })
18933
19132
  ] }),
18934
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: [
19133
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: measurementType === "face" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19134
+ t2("Face the camera directly, centered in frame"),
19135
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19136
+ t2("Use natural, even lighting (e.g. near a window)"),
19137
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19138
+ t2("Keep hair away from your face and ears"),
19139
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19140
+ t2("Choose a plain, light background")
19141
+ ] }) : measurementType === "head" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19142
+ t2("Face the camera with head and shoulders in frame"),
19143
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19144
+ t2("Leave some space above your head"),
19145
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19146
+ t2("Use natural, even lighting"),
19147
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19148
+ t2("Choose a plain, light background")
19149
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
18935
19150
  t2("Stand facing the camera with your full body in frame"),
18936
19151
  /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
18937
19152
  t2("Use natural or even lighting"),
@@ -18939,14 +19154,30 @@ function SizeResultView({
18939
19154
  t2("Wear fitted, simple clothing"),
18940
19155
  /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
18941
19156
  t2("Stand straight and still, arms relaxed")
18942
- ] })
19157
+ ] }) })
18943
19158
  ] }),
18944
19159
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { background: "#ffe2e2", borderRadius: "0.5vw", padding: "0.6vw 0.8vw" }, children: [
18945
19160
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.3vw", marginBottom: "0.3vw" }, children: [
18946
19161
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#e7000b", fontSize: "0.75vw", fontWeight: 700 }, children: "✗" }),
18947
19162
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#e7000b", fontSize: "0.65vw", fontWeight: 600 }, children: t2("Don't") })
18948
19163
  ] }),
18949
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: [
19164
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: measurementType === "face" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19165
+ t2("Don't wear sunglasses or a hat in the photo"),
19166
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19167
+ t2("Don't tilt or turn your head"),
19168
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19169
+ t2("Don't use strong backlighting or flash"),
19170
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19171
+ t2("Don't apply filters or edits")
19172
+ ] }) : measurementType === "head" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
19173
+ t2("Don't wear a hat in the photo"),
19174
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19175
+ t2("Don't crop out the top of your head"),
19176
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19177
+ t2("Don't use strong backlighting or flash"),
19178
+ /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
19179
+ t2("Don't apply filters or edits")
19180
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
18950
19181
  t2("Don't wear loose or baggy clothing"),
18951
19182
  /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
18952
19183
  t2("Don't sit, pose, or bend"),
@@ -18954,14 +19185,14 @@ function SizeResultView({
18954
19185
  t2("Don't take mirror photos or selfies"),
18955
19186
  /* @__PURE__ */ jsxRuntimeExports.jsx("br", {}),
18956
19187
  t2("Don't apply filters or edits")
18957
- ] })
19188
+ ] }) })
18958
19189
  ] }),
18959
19190
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { background: "rgba(59,130,246,0.08)", border: "1px solid rgba(59,130,246,0.2)", borderRadius: "0.5vw", padding: "0.5vw 0.8vw" }, children: [
18960
19191
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.3vw", marginBottom: "0.2vw" }, children: [
18961
19192
  /* @__PURE__ */ jsxRuntimeExports.jsx(SparkleIcon, { size: 12 }),
18962
19193
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-accent)", fontSize: "0.65vw", fontWeight: 700 }, children: t2("Pro Tip") })
18963
19194
  ] }),
18964
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.55vw", color: "var(--ps-text-secondary)", lineHeight: 1.7 }, children: t2("Our AI works best with front-facing, full-body photos in fitted clothing. Better photos = more accurate virtual try-on!") })
19195
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.55vw", color: "var(--ps-text-secondary)", lineHeight: 1.7 }, children: measurementType === "face" ? t2("A clear, well-lit face photo gives the most accurate eyewear try-on.") : measurementType === "head" ? t2("A clear head-and-shoulders photo with space above your head gives the most accurate headwear try-on.") : t2("Our AI works best with front-facing, full-body photos in fitted clothing. Better photos = more accurate virtual try-on!") })
18965
19196
  ] })
18966
19197
  ] })
18967
19198
  ] }),
@@ -19090,12 +19321,16 @@ function UploadView({
19090
19321
  }
19091
19322
  ) });
19092
19323
  }
19324
+ const RING_RADIUS = 20;
19325
+ const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
19093
19326
  function ProcessingView({
19094
19327
  previewUrl,
19095
19328
  progressRef,
19096
19329
  progressBarRef,
19097
19330
  progressTextRef,
19098
19331
  progressStatusRef,
19332
+ progressEtaRef,
19333
+ progressRingRef,
19099
19334
  cn,
19100
19335
  t: t2
19101
19336
  }) {
@@ -19110,6 +19345,16 @@ function ProcessingView({
19110
19345
  const statusCb = reactExports.useCallback((el2) => {
19111
19346
  progressStatusRef.current = el2;
19112
19347
  }, []);
19348
+ const etaCb = reactExports.useCallback((el2) => {
19349
+ progressEtaRef.current = el2;
19350
+ }, []);
19351
+ const ringCb = reactExports.useCallback((el2) => {
19352
+ progressRingRef.current = el2;
19353
+ if (el2) {
19354
+ const offset = RING_CIRCUMFERENCE * (1 - Math.round(progressRef.current) / 100);
19355
+ el2.style.strokeDashoffset = String(offset);
19356
+ }
19357
+ }, []);
19113
19358
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-processing", children: [
19114
19359
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-processing-image-wrap", children: [
19115
19360
  previewUrl && /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
@@ -19120,6 +19365,32 @@ function ProcessingView({
19120
19365
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-scan-overlay" })
19121
19366
  ] }),
19122
19367
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-section", children: [
19368
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
19369
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 48 48", width: "48", height: "48", "aria-hidden": "true", children: [
19370
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
19371
+ "circle",
19372
+ {
19373
+ cx: "24",
19374
+ cy: "24",
19375
+ r: RING_RADIUS,
19376
+ className: "ps-tryon-progress-ring-track"
19377
+ }
19378
+ ),
19379
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
19380
+ "circle",
19381
+ {
19382
+ ref: ringCb,
19383
+ cx: "24",
19384
+ cy: "24",
19385
+ r: RING_RADIUS,
19386
+ className: "ps-tryon-progress-ring-fill",
19387
+ strokeDasharray: RING_CIRCUMFERENCE,
19388
+ strokeDashoffset: RING_CIRCUMFERENCE
19389
+ }
19390
+ )
19391
+ ] }),
19392
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: etaCb, className: "ps-tryon-progress-eta", children: `~22s` })
19393
+ ] }),
19123
19394
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { ref: barCb, className: "ps-tryon-progress-bar-fill" }) }),
19124
19395
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { ref: pctCb, className: "ps-tryon-progress-pct", children: "0%" })
19125
19396
  ] }),
@@ -20785,12 +21056,13 @@ function PhotoStepMobile({
20785
21056
  photoVariant = "full-body",
20786
21057
  photoStepHeight,
20787
21058
  onPhotoStepHeightChange,
21059
+ ageConfirmed,
21060
+ onAgeConfirmedChange,
20788
21061
  t: t2
20789
21062
  }) {
20790
21063
  const isCloseUp = photoVariant === "close-up";
20791
21064
  const fileRef = reactExports.useRef(null);
20792
21065
  const hasPhoto = !!photoPreview;
20793
- const [ageConfirmed, setAgeConfirmed] = reactExports.useState(null);
20794
21066
  const gated = !hasPhoto && ageConfirmed !== true;
20795
21067
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-root", children: [
20796
21068
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-header", children: [
@@ -20842,14 +21114,14 @@ function PhotoStepMobile({
20842
21114
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-pm-age-gate-eyebrow", children: t2("AGE VERIFICATION") }),
20843
21115
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-pm-age-gate-question", children: t2("Is the person in this photo 18 years or older?") }),
20844
21116
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-age-gate-actions", children: [
20845
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-primary", onClick: () => setAgeConfirmed(true), children: t2("Yes") }),
20846
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => setAgeConfirmed(false), children: t2("No") })
21117
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-primary", onClick: () => onAgeConfirmedChange(true), children: t2("Yes") }),
21118
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => onAgeConfirmedChange(false), children: t2("No") })
20847
21119
  ] })
20848
21120
  ] }) }),
20849
21121
  ageConfirmed === false && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-pm-age-gate", role: "alert", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-age-gate-card", children: [
20850
21122
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-pm-age-gate-eyebrow ps-pm-age-gate-eyebrow-blocked", children: t2("UPLOAD NOT ALLOWED") }),
20851
21123
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-pm-age-gate-question", children: t2("For your safety, we cannot process photos of people under 18.") }),
20852
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => setAgeConfirmed(null), children: t2("Go back") })
21124
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => onAgeConfirmedChange(null), children: t2("Go back") })
20853
21125
  ] }) })
20854
21126
  ] }) }),
20855
21127
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-pm-legal-notice", children: [
@@ -21317,6 +21589,11 @@ function BodyProfileView({
21317
21589
  error,
21318
21590
  photoStepHeight,
21319
21591
  onPhotoStepHeightChange: setPhotoStepHeight,
21592
+ ageConfirmed,
21593
+ onAgeConfirmedChange: (v2) => {
21594
+ setAgeConfirmed(v2);
21595
+ if (v2 === true) setError("");
21596
+ },
21320
21597
  t: t2
21321
21598
  }
21322
21599
  ) });
@@ -22063,6 +22340,11 @@ function AccessorySizeView({
22063
22340
  onSwitchToManual: () => setStep("manual"),
22064
22341
  error,
22065
22342
  photoVariant,
22343
+ ageConfirmed,
22344
+ onAgeConfirmedChange: (v2) => {
22345
+ setAgeConfirmed(v2);
22346
+ if (v2 === true) setError("");
22347
+ },
22066
22348
  t: t2
22067
22349
  }
22068
22350
  ) });
@@ -22620,6 +22902,12 @@ function detectMeasurementType(title) {
22620
22902
  if (/\b(sunglass|sunglasses|eyewear|eyeglasses|glasses|spectacles|optical|goggles|frames|aviator|wayfarer|lens)\b/.test(t2)) return "face";
22621
22903
  return "body";
22622
22904
  }
22905
+ function measurementTypeToVtoCategory(type) {
22906
+ if (type === "face") return "sunglasses";
22907
+ if (type === "head") return "hat";
22908
+ if (type === "body") return "apparel";
22909
+ return null;
22910
+ }
22623
22911
  function PrimeStyleTryonInner({
22624
22912
  productImage,
22625
22913
  productTitle = "Product",
@@ -22725,15 +23013,22 @@ function PrimeStyleTryonInner({
22725
23013
  if (pollingRef.current) clearInterval(pollingRef.current);
22726
23014
  };
22727
23015
  }, [apiUrl]);
23016
+ const TARGET_SECONDS = 22;
22728
23017
  const progressRef = reactExports.useRef(0);
22729
23018
  const progressBarRef = reactExports.useRef(null);
22730
23019
  const progressTextRef = reactExports.useRef(null);
22731
23020
  const progressStatusRef = reactExports.useRef(null);
23021
+ const progressEtaRef = reactExports.useRef(null);
23022
+ const progressRingRef = reactExports.useRef(null);
23023
+ const progressStartTsRef = reactExports.useRef(null);
23024
+ const progressLastStageRef = reactExports.useRef("");
22732
23025
  const progressIntervalRef = reactExports.useRef(null);
22733
23026
  reactExports.useEffect(() => {
22734
23027
  if (view === "processing") {
22735
23028
  if (progressIntervalRef.current) return;
22736
23029
  progressRef.current = 0;
23030
+ progressStartTsRef.current = Date.now();
23031
+ progressLastStageRef.current = "";
22737
23032
  const statuses = [
22738
23033
  { at: 0, text: t2("Preparing your image...") },
22739
23034
  { at: 15, text: t2("Analyzing body proportions...") },
@@ -22742,17 +23037,35 @@ function PrimeStyleTryonInner({
22742
23037
  { at: 75, text: t2("Refining details...") },
22743
23038
  { at: 90, text: t2("Almost there...") }
22744
23039
  ];
23040
+ const RING_CIRCUMFERENCE2 = 2 * Math.PI * 20;
22745
23041
  progressIntervalRef.current = setInterval(() => {
22746
- const p2 = progressRef.current;
22747
- if (p2 >= 100) return;
22748
- const increment = p2 < 30 ? 1.2 : p2 < 60 ? 0.8 : p2 < 80 ? 0.4 : p2 < 95 ? 0.15 : 0;
22749
- progressRef.current = Math.min(p2 + increment, 95);
22750
- const val = Math.round(progressRef.current);
23042
+ if (completedRef.current) return;
23043
+ const startTs = progressStartTsRef.current || Date.now();
23044
+ const elapsed = (Date.now() - startTs) / 1e3;
23045
+ const target = Math.min(95, elapsed / TARGET_SECONDS * 100);
23046
+ progressRef.current = target;
23047
+ const val = Math.round(target);
22751
23048
  if (progressBarRef.current) progressBarRef.current.style.width = `${val}%`;
22752
23049
  if (progressTextRef.current) progressTextRef.current.textContent = `${val}%`;
23050
+ if (progressRingRef.current) {
23051
+ const offset = RING_CIRCUMFERENCE2 * (1 - target / 100);
23052
+ progressRingRef.current.style.strokeDashoffset = String(offset);
23053
+ }
23054
+ if (progressEtaRef.current) {
23055
+ const remaining = Math.max(0, TARGET_SECONDS - Math.floor(elapsed));
23056
+ progressEtaRef.current.textContent = elapsed >= TARGET_SECONDS ? t2("Finalizing...") : `~${remaining}s`;
23057
+ }
22753
23058
  if (progressStatusRef.current) {
22754
23059
  const status = [...statuses].reverse().find((s) => val >= s.at);
22755
- if (status) progressStatusRef.current.textContent = status.text;
23060
+ if (status && status.text !== progressLastStageRef.current) {
23061
+ const el2 = progressStatusRef.current;
23062
+ el2.style.opacity = "0";
23063
+ setTimeout(() => {
23064
+ el2.textContent = status.text;
23065
+ el2.style.opacity = "1";
23066
+ }, 180);
23067
+ progressLastStageRef.current = status.text;
23068
+ }
22756
23069
  }
22757
23070
  }, 200);
22758
23071
  return () => {
@@ -22764,8 +23077,9 @@ function PrimeStyleTryonInner({
22764
23077
  clearInterval(progressIntervalRef.current);
22765
23078
  progressIntervalRef.current = null;
22766
23079
  }
23080
+ progressStartTsRef.current = null;
22767
23081
  }
22768
- }, [view]);
23082
+ }, [view, t2]);
22769
23083
  reactExports.useEffect(() => {
22770
23084
  return () => {
22771
23085
  if (previewUrl) URL.revokeObjectURL(previewUrl);
@@ -23092,7 +23406,16 @@ function PrimeStyleTryonInner({
23092
23406
  progressRef.current = 100;
23093
23407
  if (progressBarRef.current) progressBarRef.current.style.width = "100%";
23094
23408
  if (progressTextRef.current) progressTextRef.current.textContent = "100%";
23095
- if (progressStatusRef.current) progressStatusRef.current.textContent = t2("Complete!");
23409
+ if (progressRingRef.current) progressRingRef.current.style.strokeDashoffset = "0";
23410
+ if (progressEtaRef.current) progressEtaRef.current.textContent = t2("Done");
23411
+ if (progressStatusRef.current) {
23412
+ const el2 = progressStatusRef.current;
23413
+ el2.style.opacity = "0";
23414
+ setTimeout(() => {
23415
+ el2.textContent = t2("Complete!");
23416
+ el2.style.opacity = "1";
23417
+ }, 180);
23418
+ }
23096
23419
  cleanupJob();
23097
23420
  setTryOnProcessing(false);
23098
23421
  onComplete?.({ jobId: update.galleryId, imageUrl: update.imageUrl });
@@ -23501,25 +23824,29 @@ function PrimeStyleTryonInner({
23501
23824
  }
23502
23825
  completedRef.current = false;
23503
23826
  setTryOnProcessing(true);
23827
+ const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
23828
+ const isApparel = vtoCategory === "apparel";
23504
23829
  const previewObjUrl = (overrideFile ? null : previewUrl) || URL.createObjectURL(file);
23505
23830
  if (overrideFile || !previewUrl) setPreviewUrl(previewObjUrl);
23506
23831
  modelPoseRef.current = null;
23507
23832
  setBodyLandmarks(null);
23508
- detectMeasurementLines(previewObjUrl).then((lines) => {
23509
- modelPoseRef.current = lines;
23510
- }).catch(() => {
23511
- });
23512
- detectBodyLandmarks(previewObjUrl).then((lm) => {
23513
- setBodyLandmarks(lm);
23514
- }).catch(() => {
23515
- });
23833
+ if (isApparel) {
23834
+ detectMeasurementLines(previewObjUrl).then((lines) => {
23835
+ modelPoseRef.current = lines;
23836
+ }).catch(() => {
23837
+ });
23838
+ detectBodyLandmarks(previewObjUrl).then((lm) => {
23839
+ setBodyLandmarks(lm);
23840
+ }).catch(() => {
23841
+ });
23842
+ }
23516
23843
  try {
23517
23844
  const modelImage = await compressImage(file);
23518
23845
  let fitInfo;
23519
- if (sizingResult?.matchDetails?.length) {
23846
+ if (isApparel && sizingResult?.matchDetails?.length) {
23520
23847
  fitInfo = buildFitInfo(sizingResult.matchDetails, modelPoseRef.current);
23521
23848
  }
23522
- const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo);
23849
+ const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo, vtoCategory ?? "apparel");
23523
23850
  onProcessing?.(response.jobId);
23524
23851
  unsubRef.current = sseRef.current.onJob(response.jobId, handleVtoUpdate);
23525
23852
  let attempts = 0;
@@ -23550,11 +23877,13 @@ function PrimeStyleTryonInner({
23550
23877
  setView("error");
23551
23878
  onError?.({ message, code });
23552
23879
  }
23553
- }, [selectedFile, productImage, sizingResult, onProcessing, onError, handleVtoUpdate]);
23880
+ }, [selectedFile, productImage, productTitle, sizingResult, onProcessing, onError, handleVtoUpdate]);
23554
23881
  const handleRetryWithFit = reactExports.useCallback(async (fitInfo) => {
23555
23882
  if (!selectedFile || !apiRef.current || !sseRef.current) return;
23556
23883
  setRetryLoading(true);
23557
- if (modelPoseRef.current) {
23884
+ const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
23885
+ const isApparel = vtoCategory === "apparel";
23886
+ if (isApparel && modelPoseRef.current) {
23558
23887
  const AREA_MAP = {
23559
23888
  chest: "chest",
23560
23889
  bust: "chest",
@@ -23580,7 +23909,8 @@ function PrimeStyleTryonInner({
23580
23909
  pollingRef.current = null;
23581
23910
  }
23582
23911
  const modelImage = await compressImage(selectedFile);
23583
- const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo);
23912
+ const outboundFitInfo = isApparel ? fitInfo : void 0;
23913
+ const response = await apiRef.current.submitTryOn(modelImage, productImage, outboundFitInfo, vtoCategory ?? "apparel");
23584
23914
  unsubRef.current = sseRef.current.onJob(response.jobId, (update) => {
23585
23915
  if (update.status === "completed" && update.imageUrl) {
23586
23916
  setResultImageUrl(update.imageUrl);
@@ -23645,7 +23975,7 @@ function PrimeStyleTryonInner({
23645
23975
  } catch {
23646
23976
  setRetryLoading(false);
23647
23977
  }
23648
- }, [selectedFile, productImage]);
23978
+ }, [selectedFile, productImage, productTitle]);
23649
23979
  const handleDownload = reactExports.useCallback(() => {
23650
23980
  if (!resultImageUrl) return;
23651
23981
  if (resultImageUrl.startsWith("data:")) {
@@ -24087,6 +24417,8 @@ function PrimeStyleTryonInner({
24087
24417
  progressBarRef,
24088
24418
  progressTextRef,
24089
24419
  progressStatusRef,
24420
+ progressEtaRef,
24421
+ progressRingRef,
24090
24422
  cn,
24091
24423
  t: t2
24092
24424
  }