@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.
- package/dist/api-client.d.ts +1 -1
- package/dist/{index-J6U0-q_3.js → index-CuIieeOy.js} +2 -1
- package/dist/primestyle-tryon.js +2 -2
- package/dist/react/index.js +392 -61
- package/dist/react/styles.d.ts +1 -1
- package/dist/react/views/MobileScanningView.d.ts +4 -1
- package/dist/react/views/PhotoStepMobile.d.ts +3 -1
- package/dist/react/views/ProcessingView.d.ts +3 -1
- package/dist/storefront/primestyle-tryon.js +393 -61
- package/package.json +1 -4
package/dist/react/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES, a as compressImage, c as createT, A as ApiClient, S as SseClient, i as isValidImageFile } from "../index-
|
|
2
|
+
import { P as PrimeStyleError, L as LOCALE_LABELS, b as SUPPORTED_LOCALES, a as compressImage, c as createT, A as ApiClient, S as SseClient, i as isValidImageFile } from "../index-CuIieeOy.js";
|
|
3
3
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
4
4
|
import { useState, useRef, useCallback, useEffect, useMemo } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
@@ -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.
|
|
1492
|
-
color:
|
|
1493
|
-
background: rgba(0,0,0,0.
|
|
1494
|
-
padding: 0.
|
|
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 {
|
|
@@ -2084,22 +2094,81 @@ const STYLES = `
|
|
|
2084
2094
|
}
|
|
2085
2095
|
|
|
2086
2096
|
.ps-tryon-progress-section {
|
|
2087
|
-
display: flex; align-items: center; gap: 0.63vw; width: 100%; max-width:
|
|
2097
|
+
display: flex; align-items: center; gap: 0.63vw; width: 100%; max-width: 18vw; margin-bottom: 0.83vw;
|
|
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;
|
|
2088
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;
|
|
2121
|
+
position: relative;
|
|
2122
|
+
}
|
|
2123
|
+
.ps-tryon-progress-bar-wrap::after {
|
|
2124
|
+
content: ""; position: absolute; inset: 0;
|
|
2125
|
+
background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.45) 50%, transparent 100%);
|
|
2126
|
+
transform: translateX(-100%);
|
|
2127
|
+
animation: ps-progress-shimmer 2s linear infinite;
|
|
2128
|
+
pointer-events: none;
|
|
2129
|
+
}
|
|
2130
|
+
@keyframes ps-progress-shimmer {
|
|
2131
|
+
0% { transform: translateX(-100%); }
|
|
2132
|
+
100% { transform: translateX(100%); }
|
|
2091
2133
|
}
|
|
2092
2134
|
.ps-tryon-progress-bar-fill {
|
|
2093
2135
|
height: 100%; background: linear-gradient(90deg, var(--ps-accent), var(--ps-accent-light));
|
|
2094
2136
|
border-radius: 3px; transition: width 0.3s ease; width: 0%;
|
|
2137
|
+
position: relative; z-index: 1;
|
|
2095
2138
|
}
|
|
2096
2139
|
.ps-tryon-progress-pct {
|
|
2097
2140
|
font-size: 0.68vw; font-weight: 700; color: var(--ps-accent); min-width: 1.88vw; text-align: right;
|
|
2098
2141
|
font-variant-numeric: tabular-nums;
|
|
2099
2142
|
}
|
|
2100
2143
|
|
|
2144
|
+
/* Circular ETA ring — 48×48 px SVG with a track + progress circle; ETA
|
|
2145
|
+
text centered. strokeDashoffset is driven by the ticker in
|
|
2146
|
+
PrimeStyleTryonInner, so CSS only styles the appearance. */
|
|
2147
|
+
.ps-tryon-progress-ring {
|
|
2148
|
+
position: relative; width: 48px; height: 48px; flex: 0 0 48px;
|
|
2149
|
+
display: flex; align-items: center; justify-content: center;
|
|
2150
|
+
}
|
|
2151
|
+
.ps-tryon-progress-ring svg { transform: rotate(-90deg); }
|
|
2152
|
+
.ps-tryon-progress-ring-track {
|
|
2153
|
+
fill: none; stroke: var(--ps-border-color); stroke-width: 3.5;
|
|
2154
|
+
}
|
|
2155
|
+
.ps-tryon-progress-ring-fill {
|
|
2156
|
+
fill: none; stroke: var(--ps-accent); stroke-width: 3.5;
|
|
2157
|
+
stroke-linecap: round;
|
|
2158
|
+
transition: stroke-dashoffset 0.3s ease;
|
|
2159
|
+
}
|
|
2160
|
+
.ps-tryon-progress-eta {
|
|
2161
|
+
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
|
2162
|
+
font-size: 10px; font-weight: 700; color: var(--ps-accent);
|
|
2163
|
+
font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
|
|
2164
|
+
pointer-events: none;
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2101
2167
|
@keyframes ps-scale-in { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
|
|
2102
|
-
.ps-tryon-processing-text {
|
|
2168
|
+
.ps-tryon-processing-text {
|
|
2169
|
+
font-size: 0.73vw; color: var(--ps-text-primary); margin: 0 0 0.21vw;
|
|
2170
|
+
opacity: 1; transition: opacity 0.18s ease;
|
|
2171
|
+
}
|
|
2103
2172
|
.ps-tryon-processing-sub { font-size: 0.63vw; color: var(--ps-text-secondary); margin: 0; }
|
|
2104
2173
|
|
|
2105
2174
|
/* Result — split layout */
|
|
@@ -7412,17 +7481,71 @@ function MobileSkeleton({ landmarks, w, h }) {
|
|
|
7412
7481
|
}
|
|
7413
7482
|
);
|
|
7414
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
|
+
}
|
|
7415
7532
|
function MobileScanningView({
|
|
7416
7533
|
previewUrl,
|
|
7417
7534
|
productImage,
|
|
7418
7535
|
bodyLandmarks,
|
|
7419
7536
|
sizingDone,
|
|
7537
|
+
tryOnProcessing,
|
|
7420
7538
|
onSwitchToManual,
|
|
7421
7539
|
t
|
|
7422
7540
|
}) {
|
|
7423
7541
|
const displayImage = previewUrl || productImage || "";
|
|
7424
7542
|
const isPhotoMode = !!previewUrl;
|
|
7425
|
-
const stages =
|
|
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 ? [
|
|
7426
7549
|
{ title: t("DETECTING POSE"), desc: t("Identifying body landmarks from your photo."), viewfinderText: t("DETECTING POSE") },
|
|
7427
7550
|
{ title: t("SCANNING FRAME"), desc: t("Our AI is mapping your proportions to calculate the perfect fit."), viewfinderText: t("SCANNING FRAME") },
|
|
7428
7551
|
{ title: t("ANALYZING BODY"), desc: t("Measuring shoulders, chest, waist and hips."), viewfinderText: t("ANALYZING") },
|
|
@@ -7465,10 +7588,13 @@ function MobileScanningView({
|
|
|
7465
7588
|
),
|
|
7466
7589
|
isPhotoMode && bodyLandmarks && /* @__PURE__ */ jsx("div", { className: "ps-msc-pose-wrap", children: /* @__PURE__ */ jsx(MobileSkeleton, { landmarks: bodyLandmarks, w: dims.w, h: dims.h }) })
|
|
7467
7590
|
] }),
|
|
7468
|
-
/* @__PURE__ */
|
|
7469
|
-
/* @__PURE__ */
|
|
7470
|
-
|
|
7471
|
-
|
|
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
|
+
] }),
|
|
7472
7598
|
/* @__PURE__ */ jsx("div", { className: "ps-bpm-spacer" }),
|
|
7473
7599
|
/* @__PURE__ */ jsx("div", { className: "ps-bpm-bottom", children: /* @__PURE__ */ jsx(
|
|
7474
7600
|
MobileBottomTabs,
|
|
@@ -7666,6 +7792,62 @@ const SKELETON_CONNECTIONS = [
|
|
|
7666
7792
|
["rightHip", "rightKnee"],
|
|
7667
7793
|
["rightKnee", "rightAnkle"]
|
|
7668
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
|
+
}
|
|
7669
7851
|
function FaceOverlay({
|
|
7670
7852
|
landmarks,
|
|
7671
7853
|
imgWidth,
|
|
@@ -7809,10 +7991,13 @@ function StageCycler({
|
|
|
7809
7991
|
return () => clearInterval(id);
|
|
7810
7992
|
}, [isDone, active.length]);
|
|
7811
7993
|
const current = active[idx] ?? active[0];
|
|
7812
|
-
return /* @__PURE__ */
|
|
7813
|
-
/* @__PURE__ */
|
|
7814
|
-
|
|
7815
|
-
|
|
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
|
+
] });
|
|
7816
8001
|
}
|
|
7817
8002
|
function SkeletonOverlay({ landmarks, imgWidth, imgHeight }) {
|
|
7818
8003
|
const W = imgWidth;
|
|
@@ -8950,6 +9135,7 @@ function SizeResultView({
|
|
|
8950
9135
|
const allDone = hasPhoto ? sizingDone && tryOnDone : sizingDone;
|
|
8951
9136
|
const isMobile = useIsMobile();
|
|
8952
9137
|
const isAccessory = measurementType === "face" || measurementType === "head";
|
|
9138
|
+
const vtoExcluded = measurementType === "foot";
|
|
8953
9139
|
console.log("[PS-SDK] SizeResultView render:", {
|
|
8954
9140
|
hasPhoto,
|
|
8955
9141
|
isSnapProcessing,
|
|
@@ -8987,6 +9173,7 @@ function SizeResultView({
|
|
|
8987
9173
|
previewUrl,
|
|
8988
9174
|
bodyLandmarks: bodyLandmarks ?? null,
|
|
8989
9175
|
sizingDone,
|
|
9176
|
+
tryOnProcessing,
|
|
8990
9177
|
onSwitchToManual: () => setView("body-profile"),
|
|
8991
9178
|
t
|
|
8992
9179
|
}
|
|
@@ -9043,7 +9230,7 @@ function SizeResultView({
|
|
|
9043
9230
|
isMobile: true,
|
|
9044
9231
|
isTryOnImage: !!resultImageUrl,
|
|
9045
9232
|
showLines,
|
|
9046
|
-
onToggleLines: () => setShowLines(!showLines),
|
|
9233
|
+
onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
|
|
9047
9234
|
onImageLoad: handleImgLoad,
|
|
9048
9235
|
overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsx(
|
|
9049
9236
|
MeasurementOverlay,
|
|
@@ -9117,7 +9304,7 @@ function SizeResultView({
|
|
|
9117
9304
|
},
|
|
9118
9305
|
onClose,
|
|
9119
9306
|
showLines,
|
|
9120
|
-
onToggleLines: () => setShowLines(!showLines),
|
|
9307
|
+
onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
|
|
9121
9308
|
onImageLoad: handleImgLoad,
|
|
9122
9309
|
overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsx(
|
|
9123
9310
|
MeasurementOverlay,
|
|
@@ -9152,7 +9339,10 @@ function SizeResultView({
|
|
|
9152
9339
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-v2-bg", style: { position: "relative" }, children: [
|
|
9153
9340
|
/* @__PURE__ */ jsx("img", { src: tryOnProcessing && previewUrl ? previewUrl : resultImageUrl || productImage, alt: productTitle, className: "ps-tryon-v2-bg-img", onLoad: handleImgLoad }),
|
|
9154
9341
|
tryOnProcessing && bodyLandmarks && /* @__PURE__ */ jsx(SkeletonOverlay, { landmarks: bodyLandmarks, imgWidth: imgDims.w, imgHeight: imgDims.h }),
|
|
9155
|
-
tryOnProcessing && /* @__PURE__ */
|
|
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
|
+
] }),
|
|
9156
9346
|
resultImageUrl && !tryOnProcessing && poseReady && poseLines && /* @__PURE__ */ jsx(MeasurementOverlay, { lines: poseLines, fitRows: (() => {
|
|
9157
9347
|
const all = [...sizingResult?.matchDetails || []];
|
|
9158
9348
|
if (sizingResult?.sections) {
|
|
@@ -9169,7 +9359,7 @@ function SizeResultView({
|
|
|
9169
9359
|
}).map((m) => ({ area: m.measurement, userNum: parseFloat(m.userValue) || 0, chartLabel: m.chartRange || "", fit: m.fit }));
|
|
9170
9360
|
})(), show: showLines, imgWidth: imgDims.w, imgHeight: imgDims.h }),
|
|
9171
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: [
|
|
9172
|
-
/* @__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: [
|
|
9173
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: [
|
|
9174
9364
|
/* @__PURE__ */ jsx("line", { x1: "4", y1: "9", x2: "20", y2: "9" }),
|
|
9175
9365
|
/* @__PURE__ */ jsx("line", { x1: "4", y1: "15", x2: "20", y2: "15" }),
|
|
@@ -9223,7 +9413,7 @@ function SizeResultView({
|
|
|
9223
9413
|
" →"
|
|
9224
9414
|
]
|
|
9225
9415
|
}
|
|
9226
|
-
) :
|
|
9416
|
+
) : vtoExcluded ? /* @__PURE__ */ jsxs(
|
|
9227
9417
|
"button",
|
|
9228
9418
|
{
|
|
9229
9419
|
className: "ps-tryon-v2-cta",
|
|
@@ -9283,7 +9473,7 @@ function SizeResultView({
|
|
|
9283
9473
|
onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
|
|
9284
9474
|
backLabel: t("Back"),
|
|
9285
9475
|
internationalSizes: sizingResult?.internationalSizes,
|
|
9286
|
-
onTryOn: resultImageUrl ||
|
|
9476
|
+
onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
|
|
9287
9477
|
continueLabel: resultImageUrl ? t("Continue Shopping") : void 0,
|
|
9288
9478
|
tryOnProcessing,
|
|
9289
9479
|
productImage: resultImageUrl || productImage,
|
|
@@ -9292,7 +9482,7 @@ function SizeResultView({
|
|
|
9292
9482
|
renderRaw: isAccessory,
|
|
9293
9483
|
isTryOnImage: !!resultImageUrl,
|
|
9294
9484
|
showLines,
|
|
9295
|
-
onToggleLines: () => setShowLines(!showLines),
|
|
9485
|
+
onToggleLines: isAccessory ? void 0 : () => setShowLines(!showLines),
|
|
9296
9486
|
onImageLoad: handleImgLoad,
|
|
9297
9487
|
overlayNode: resultImageUrl && poseReady && poseLines ? /* @__PURE__ */ jsx(
|
|
9298
9488
|
MeasurementOverlay,
|
|
@@ -9313,7 +9503,7 @@ function SizeResultView({
|
|
|
9313
9503
|
/* @__PURE__ */ jsx("img", { src: resultImageUrl || productImage, alt: productTitle, className: "ps-tryon-v2-bg-img", onLoad: handleImgLoad }),
|
|
9314
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 }),
|
|
9315
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: [
|
|
9316
|
-
/* @__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") }),
|
|
9317
9507
|
/* @__PURE__ */ jsx("button", { className: "ps-tryon-sr-glass-btn", onClick: handleDownload, children: t("Download") })
|
|
9318
9508
|
] })
|
|
9319
9509
|
] }),
|
|
@@ -9330,7 +9520,7 @@ function SizeResultView({
|
|
|
9330
9520
|
onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
|
|
9331
9521
|
backLabel: t("Back"),
|
|
9332
9522
|
internationalSizes: sizingResult?.internationalSizes,
|
|
9333
|
-
onTryOn: resultImageUrl ||
|
|
9523
|
+
onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
|
|
9334
9524
|
continueLabel: resultImageUrl ? t("Continue Shopping") : void 0,
|
|
9335
9525
|
tryOnProcessing,
|
|
9336
9526
|
t,
|
|
@@ -9340,14 +9530,14 @@ function SizeResultView({
|
|
|
9340
9530
|
] });
|
|
9341
9531
|
})()
|
|
9342
9532
|
),
|
|
9343
|
-
showPhotoGuide && !
|
|
9533
|
+
showPhotoGuide && !vtoExcluded && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-chart-overlay", children: isMobile ? (
|
|
9344
9534
|
/* ── Mobile: same layout as the AI-sizing photo step
|
|
9345
9535
|
(PhotoStepMobile) — title + subtitle, large preview,
|
|
9346
9536
|
checklist card, primary CTA + RETAKE secondary. ── */
|
|
9347
9537
|
/* @__PURE__ */ jsx("div", { className: "ps-bp-wrapper", style: { padding: "16px 16px 0", background: "var(--ps-bg-primary)" }, children: /* @__PURE__ */ jsxs("div", { className: "ps-pm-root", children: [
|
|
9348
9538
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-header", children: [
|
|
9349
9539
|
/* @__PURE__ */ jsx("h2", { className: "ps-pm-title", children: t("Review your photo") }),
|
|
9350
|
-
/* @__PURE__ */ jsx("p", { className: "ps-pm-subtitle", children: t("Ensure your full body is visible for the most accurate virtual try-on.") })
|
|
9540
|
+
/* @__PURE__ */ jsx("p", { className: "ps-pm-subtitle", children: measurementType === "face" ? t("A clear, front-facing face photo — no glasses on — gives us the most accurate try-on.") : measurementType === "head" ? t("Face the camera with your head and shoulders in frame, leaving space above your head.") : t("Ensure your full body is visible for the most accurate virtual try-on.") })
|
|
9351
9541
|
] }),
|
|
9352
9542
|
/* @__PURE__ */ jsx(
|
|
9353
9543
|
"input",
|
|
@@ -9398,11 +9588,19 @@ function SizeResultView({
|
|
|
9398
9588
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-checklist-icon", children: /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "currentColor", width: "14", height: "14", children: /* @__PURE__ */ 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" }) }) }),
|
|
9399
9589
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-checklist-body", children: [
|
|
9400
9590
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-checklist-title", children: t("Checklist for accuracy") }),
|
|
9401
|
-
/* @__PURE__ */
|
|
9591
|
+
/* @__PURE__ */ jsx("ul", { className: "ps-pm-checklist-items", children: measurementType === "face" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9592
|
+
/* @__PURE__ */ jsx("li", { children: t("Face the camera directly at eye level") }),
|
|
9593
|
+
/* @__PURE__ */ jsx("li", { children: t("Remove any glasses you're wearing") }),
|
|
9594
|
+
/* @__PURE__ */ jsx("li", { children: t("Good lighting, plain background") })
|
|
9595
|
+
] }) : measurementType === "head" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9596
|
+
/* @__PURE__ */ jsx("li", { children: t("Head and shoulders in frame") }),
|
|
9597
|
+
/* @__PURE__ */ jsx("li", { children: t("Leave space above your head") }),
|
|
9598
|
+
/* @__PURE__ */ jsx("li", { children: t("Good lighting, plain background") })
|
|
9599
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9402
9600
|
/* @__PURE__ */ jsx("li", { children: t("Form-fitting clothing is recommended") }),
|
|
9403
9601
|
/* @__PURE__ */ jsx("li", { children: t("Standing 2-3 meters from camera") }),
|
|
9404
9602
|
/* @__PURE__ */ jsx("li", { children: t("Neutral background with good lighting") })
|
|
9405
|
-
] })
|
|
9603
|
+
] }) })
|
|
9406
9604
|
] })
|
|
9407
9605
|
] }),
|
|
9408
9606
|
/* @__PURE__ */ jsx("div", { className: "ps-bpm-spacer" }),
|
|
@@ -9482,7 +9680,7 @@ function SizeResultView({
|
|
|
9482
9680
|
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9483
9681
|
/* @__PURE__ */ jsx(UploadIcon, { size: 32 }),
|
|
9484
9682
|
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.85vw", fontWeight: 600, color: "var(--ps-text-primary)", marginTop: "0.5vw" }, children: t("Upload your photo") }),
|
|
9485
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: t("Click or drag a full-body photo") })
|
|
9683
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: measurementType === "face" ? t("Click or drag a close-up face photo") : measurementType === "head" ? t("Click or drag a head-and-shoulders photo") : t("Click or drag a full-body photo") })
|
|
9486
9684
|
] }),
|
|
9487
9685
|
/* @__PURE__ */ jsx(
|
|
9488
9686
|
"input",
|
|
@@ -9507,7 +9705,23 @@ function SizeResultView({
|
|
|
9507
9705
|
/* @__PURE__ */ jsx("span", { style: { color: "#1c9d4c", fontSize: "0.75vw", fontWeight: 700 }, children: "✓" }),
|
|
9508
9706
|
/* @__PURE__ */ jsx("span", { style: { color: "#1c9d4c", fontSize: "0.65vw", fontWeight: 600 }, children: t("Do") })
|
|
9509
9707
|
] }),
|
|
9510
|
-
/* @__PURE__ */
|
|
9708
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: measurementType === "face" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9709
|
+
t("Face the camera directly, centered in frame"),
|
|
9710
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9711
|
+
t("Use natural, even lighting (e.g. near a window)"),
|
|
9712
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9713
|
+
t("Keep hair away from your face and ears"),
|
|
9714
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9715
|
+
t("Choose a plain, light background")
|
|
9716
|
+
] }) : measurementType === "head" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9717
|
+
t("Face the camera with head and shoulders in frame"),
|
|
9718
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9719
|
+
t("Leave some space above your head"),
|
|
9720
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9721
|
+
t("Use natural, even lighting"),
|
|
9722
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9723
|
+
t("Choose a plain, light background")
|
|
9724
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9511
9725
|
t("Stand facing the camera with your full body in frame"),
|
|
9512
9726
|
/* @__PURE__ */ jsx("br", {}),
|
|
9513
9727
|
t("Use natural or even lighting"),
|
|
@@ -9515,14 +9729,30 @@ function SizeResultView({
|
|
|
9515
9729
|
t("Wear fitted, simple clothing"),
|
|
9516
9730
|
/* @__PURE__ */ jsx("br", {}),
|
|
9517
9731
|
t("Stand straight and still, arms relaxed")
|
|
9518
|
-
] })
|
|
9732
|
+
] }) })
|
|
9519
9733
|
] }),
|
|
9520
9734
|
/* @__PURE__ */ jsxs("div", { style: { background: "#ffe2e2", borderRadius: "0.5vw", padding: "0.6vw 0.8vw" }, children: [
|
|
9521
9735
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.3vw", marginBottom: "0.3vw" }, children: [
|
|
9522
9736
|
/* @__PURE__ */ jsx("span", { style: { color: "#e7000b", fontSize: "0.75vw", fontWeight: 700 }, children: "✗" }),
|
|
9523
9737
|
/* @__PURE__ */ jsx("span", { style: { color: "#e7000b", fontSize: "0.65vw", fontWeight: 600 }, children: t("Don't") })
|
|
9524
9738
|
] }),
|
|
9525
|
-
/* @__PURE__ */
|
|
9739
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.8 }, children: measurementType === "face" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9740
|
+
t("Don't wear sunglasses or a hat in the photo"),
|
|
9741
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9742
|
+
t("Don't tilt or turn your head"),
|
|
9743
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9744
|
+
t("Don't use strong backlighting or flash"),
|
|
9745
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9746
|
+
t("Don't apply filters or edits")
|
|
9747
|
+
] }) : measurementType === "head" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9748
|
+
t("Don't wear a hat in the photo"),
|
|
9749
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9750
|
+
t("Don't crop out the top of your head"),
|
|
9751
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9752
|
+
t("Don't use strong backlighting or flash"),
|
|
9753
|
+
/* @__PURE__ */ jsx("br", {}),
|
|
9754
|
+
t("Don't apply filters or edits")
|
|
9755
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9526
9756
|
t("Don't wear loose or baggy clothing"),
|
|
9527
9757
|
/* @__PURE__ */ jsx("br", {}),
|
|
9528
9758
|
t("Don't sit, pose, or bend"),
|
|
@@ -9530,14 +9760,14 @@ function SizeResultView({
|
|
|
9530
9760
|
t("Don't take mirror photos or selfies"),
|
|
9531
9761
|
/* @__PURE__ */ jsx("br", {}),
|
|
9532
9762
|
t("Don't apply filters or edits")
|
|
9533
|
-
] })
|
|
9763
|
+
] }) })
|
|
9534
9764
|
] }),
|
|
9535
9765
|
/* @__PURE__ */ 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: [
|
|
9536
9766
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.3vw", marginBottom: "0.2vw" }, children: [
|
|
9537
9767
|
/* @__PURE__ */ jsx(SparkleIcon, { size: 12 }),
|
|
9538
9768
|
/* @__PURE__ */ jsx("span", { style: { color: "var(--ps-accent)", fontSize: "0.65vw", fontWeight: 700 }, children: t("Pro Tip") })
|
|
9539
9769
|
] }),
|
|
9540
|
-
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.55vw", color: "var(--ps-text-secondary)", lineHeight: 1.7 }, children: t("Our AI works best with front-facing, full-body photos in fitted clothing. Better photos = more accurate virtual try-on!") })
|
|
9770
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.55vw", color: "var(--ps-text-secondary)", lineHeight: 1.7 }, children: measurementType === "face" ? t("A clear, well-lit face photo gives the most accurate eyewear try-on.") : measurementType === "head" ? t("A clear head-and-shoulders photo with space above your head gives the most accurate headwear try-on.") : t("Our AI works best with front-facing, full-body photos in fitted clothing. Better photos = more accurate virtual try-on!") })
|
|
9541
9771
|
] })
|
|
9542
9772
|
] })
|
|
9543
9773
|
] }),
|
|
@@ -9666,12 +9896,16 @@ function UploadView({
|
|
|
9666
9896
|
}
|
|
9667
9897
|
) });
|
|
9668
9898
|
}
|
|
9899
|
+
const RING_RADIUS = 20;
|
|
9900
|
+
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
|
|
9669
9901
|
function ProcessingView({
|
|
9670
9902
|
previewUrl,
|
|
9671
9903
|
progressRef,
|
|
9672
9904
|
progressBarRef,
|
|
9673
9905
|
progressTextRef,
|
|
9674
9906
|
progressStatusRef,
|
|
9907
|
+
progressEtaRef,
|
|
9908
|
+
progressRingRef,
|
|
9675
9909
|
cn,
|
|
9676
9910
|
t
|
|
9677
9911
|
}) {
|
|
@@ -9686,6 +9920,16 @@ function ProcessingView({
|
|
|
9686
9920
|
const statusCb = useCallback((el) => {
|
|
9687
9921
|
progressStatusRef.current = el;
|
|
9688
9922
|
}, []);
|
|
9923
|
+
const etaCb = useCallback((el) => {
|
|
9924
|
+
progressEtaRef.current = el;
|
|
9925
|
+
}, []);
|
|
9926
|
+
const ringCb = useCallback((el) => {
|
|
9927
|
+
progressRingRef.current = el;
|
|
9928
|
+
if (el) {
|
|
9929
|
+
const offset = RING_CIRCUMFERENCE * (1 - Math.round(progressRef.current) / 100);
|
|
9930
|
+
el.style.strokeDashoffset = String(offset);
|
|
9931
|
+
}
|
|
9932
|
+
}, []);
|
|
9689
9933
|
return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-processing", children: [
|
|
9690
9934
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-processing-image-wrap", children: [
|
|
9691
9935
|
previewUrl && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -9696,6 +9940,32 @@ function ProcessingView({
|
|
|
9696
9940
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-scan-overlay" })
|
|
9697
9941
|
] }),
|
|
9698
9942
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-section", children: [
|
|
9943
|
+
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-ring", children: [
|
|
9944
|
+
/* @__PURE__ */ jsxs("svg", { viewBox: "0 0 48 48", width: "48", height: "48", "aria-hidden": "true", children: [
|
|
9945
|
+
/* @__PURE__ */ jsx(
|
|
9946
|
+
"circle",
|
|
9947
|
+
{
|
|
9948
|
+
cx: "24",
|
|
9949
|
+
cy: "24",
|
|
9950
|
+
r: RING_RADIUS,
|
|
9951
|
+
className: "ps-tryon-progress-ring-track"
|
|
9952
|
+
}
|
|
9953
|
+
),
|
|
9954
|
+
/* @__PURE__ */ jsx(
|
|
9955
|
+
"circle",
|
|
9956
|
+
{
|
|
9957
|
+
ref: ringCb,
|
|
9958
|
+
cx: "24",
|
|
9959
|
+
cy: "24",
|
|
9960
|
+
r: RING_RADIUS,
|
|
9961
|
+
className: "ps-tryon-progress-ring-fill",
|
|
9962
|
+
strokeDasharray: RING_CIRCUMFERENCE,
|
|
9963
|
+
strokeDashoffset: RING_CIRCUMFERENCE
|
|
9964
|
+
}
|
|
9965
|
+
)
|
|
9966
|
+
] }),
|
|
9967
|
+
/* @__PURE__ */ jsx("span", { ref: etaCb, className: "ps-tryon-progress-eta", children: `~22s` })
|
|
9968
|
+
] }),
|
|
9699
9969
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsx("div", { ref: barCb, className: "ps-tryon-progress-bar-fill" }) }),
|
|
9700
9970
|
/* @__PURE__ */ jsx("span", { ref: pctCb, className: "ps-tryon-progress-pct", children: "0%" })
|
|
9701
9971
|
] }),
|
|
@@ -11361,12 +11631,13 @@ function PhotoStepMobile({
|
|
|
11361
11631
|
photoVariant = "full-body",
|
|
11362
11632
|
photoStepHeight,
|
|
11363
11633
|
onPhotoStepHeightChange,
|
|
11634
|
+
ageConfirmed,
|
|
11635
|
+
onAgeConfirmedChange,
|
|
11364
11636
|
t
|
|
11365
11637
|
}) {
|
|
11366
11638
|
const isCloseUp = photoVariant === "close-up";
|
|
11367
11639
|
const fileRef = useRef(null);
|
|
11368
11640
|
const hasPhoto = !!photoPreview;
|
|
11369
|
-
const [ageConfirmed, setAgeConfirmed] = useState(null);
|
|
11370
11641
|
const gated = !hasPhoto && ageConfirmed !== true;
|
|
11371
11642
|
return /* @__PURE__ */ jsxs("div", { className: "ps-pm-root", children: [
|
|
11372
11643
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-header", children: [
|
|
@@ -11418,14 +11689,14 @@ function PhotoStepMobile({
|
|
|
11418
11689
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-eyebrow", children: t("AGE VERIFICATION") }),
|
|
11419
11690
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-question", children: t("Is the person in this photo 18 years or older?") }),
|
|
11420
11691
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-age-gate-actions", children: [
|
|
11421
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-primary", onClick: () =>
|
|
11422
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () =>
|
|
11692
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-primary", onClick: () => onAgeConfirmedChange(true), children: t("Yes") }),
|
|
11693
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => onAgeConfirmedChange(false), children: t("No") })
|
|
11423
11694
|
] })
|
|
11424
11695
|
] }) }),
|
|
11425
11696
|
ageConfirmed === false && /* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate", role: "alert", children: /* @__PURE__ */ jsxs("div", { className: "ps-pm-age-gate-card", children: [
|
|
11426
11697
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-eyebrow ps-pm-age-gate-eyebrow-blocked", children: t("UPLOAD NOT ALLOWED") }),
|
|
11427
11698
|
/* @__PURE__ */ jsx("div", { className: "ps-pm-age-gate-question", children: t("For your safety, we cannot process photos of people under 18.") }),
|
|
11428
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () =>
|
|
11699
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "ps-pm-age-gate-btn ps-pm-age-gate-btn-secondary", onClick: () => onAgeConfirmedChange(null), children: t("Go back") })
|
|
11429
11700
|
] }) })
|
|
11430
11701
|
] }) }),
|
|
11431
11702
|
/* @__PURE__ */ jsxs("div", { className: "ps-pm-legal-notice", children: [
|
|
@@ -11893,6 +12164,11 @@ function BodyProfileView({
|
|
|
11893
12164
|
error,
|
|
11894
12165
|
photoStepHeight,
|
|
11895
12166
|
onPhotoStepHeightChange: setPhotoStepHeight,
|
|
12167
|
+
ageConfirmed,
|
|
12168
|
+
onAgeConfirmedChange: (v) => {
|
|
12169
|
+
setAgeConfirmed(v);
|
|
12170
|
+
if (v === true) setError("");
|
|
12171
|
+
},
|
|
11896
12172
|
t
|
|
11897
12173
|
}
|
|
11898
12174
|
) });
|
|
@@ -12639,6 +12915,11 @@ function AccessorySizeView({
|
|
|
12639
12915
|
onSwitchToManual: () => setStep("manual"),
|
|
12640
12916
|
error,
|
|
12641
12917
|
photoVariant,
|
|
12918
|
+
ageConfirmed,
|
|
12919
|
+
onAgeConfirmedChange: (v) => {
|
|
12920
|
+
setAgeConfirmed(v);
|
|
12921
|
+
if (v === true) setError("");
|
|
12922
|
+
},
|
|
12642
12923
|
t
|
|
12643
12924
|
}
|
|
12644
12925
|
) });
|
|
@@ -13196,6 +13477,12 @@ function detectMeasurementType(title) {
|
|
|
13196
13477
|
if (/\b(sunglass|sunglasses|eyewear|eyeglasses|glasses|spectacles|optical|goggles|frames|aviator|wayfarer|lens)\b/.test(t)) return "face";
|
|
13197
13478
|
return "body";
|
|
13198
13479
|
}
|
|
13480
|
+
function measurementTypeToVtoCategory(type) {
|
|
13481
|
+
if (type === "face") return "sunglasses";
|
|
13482
|
+
if (type === "head") return "hat";
|
|
13483
|
+
if (type === "body") return "apparel";
|
|
13484
|
+
return null;
|
|
13485
|
+
}
|
|
13199
13486
|
function PrimeStyleTryonInner({
|
|
13200
13487
|
productImage,
|
|
13201
13488
|
productTitle = "Product",
|
|
@@ -13301,15 +13588,22 @@ function PrimeStyleTryonInner({
|
|
|
13301
13588
|
if (pollingRef.current) clearInterval(pollingRef.current);
|
|
13302
13589
|
};
|
|
13303
13590
|
}, [apiUrl]);
|
|
13591
|
+
const TARGET_SECONDS = 22;
|
|
13304
13592
|
const progressRef = useRef(0);
|
|
13305
13593
|
const progressBarRef = useRef(null);
|
|
13306
13594
|
const progressTextRef = useRef(null);
|
|
13307
13595
|
const progressStatusRef = useRef(null);
|
|
13596
|
+
const progressEtaRef = useRef(null);
|
|
13597
|
+
const progressRingRef = useRef(null);
|
|
13598
|
+
const progressStartTsRef = useRef(null);
|
|
13599
|
+
const progressLastStageRef = useRef("");
|
|
13308
13600
|
const progressIntervalRef = useRef(null);
|
|
13309
13601
|
useEffect(() => {
|
|
13310
13602
|
if (view === "processing") {
|
|
13311
13603
|
if (progressIntervalRef.current) return;
|
|
13312
13604
|
progressRef.current = 0;
|
|
13605
|
+
progressStartTsRef.current = Date.now();
|
|
13606
|
+
progressLastStageRef.current = "";
|
|
13313
13607
|
const statuses = [
|
|
13314
13608
|
{ at: 0, text: t("Preparing your image...") },
|
|
13315
13609
|
{ at: 15, text: t("Analyzing body proportions...") },
|
|
@@ -13318,17 +13612,35 @@ function PrimeStyleTryonInner({
|
|
|
13318
13612
|
{ at: 75, text: t("Refining details...") },
|
|
13319
13613
|
{ at: 90, text: t("Almost there...") }
|
|
13320
13614
|
];
|
|
13615
|
+
const RING_CIRCUMFERENCE2 = 2 * Math.PI * 20;
|
|
13321
13616
|
progressIntervalRef.current = setInterval(() => {
|
|
13322
|
-
|
|
13323
|
-
|
|
13324
|
-
const
|
|
13325
|
-
|
|
13326
|
-
|
|
13617
|
+
if (completedRef.current) return;
|
|
13618
|
+
const startTs = progressStartTsRef.current || Date.now();
|
|
13619
|
+
const elapsed = (Date.now() - startTs) / 1e3;
|
|
13620
|
+
const target = Math.min(95, elapsed / TARGET_SECONDS * 100);
|
|
13621
|
+
progressRef.current = target;
|
|
13622
|
+
const val = Math.round(target);
|
|
13327
13623
|
if (progressBarRef.current) progressBarRef.current.style.width = `${val}%`;
|
|
13328
13624
|
if (progressTextRef.current) progressTextRef.current.textContent = `${val}%`;
|
|
13625
|
+
if (progressRingRef.current) {
|
|
13626
|
+
const offset = RING_CIRCUMFERENCE2 * (1 - target / 100);
|
|
13627
|
+
progressRingRef.current.style.strokeDashoffset = String(offset);
|
|
13628
|
+
}
|
|
13629
|
+
if (progressEtaRef.current) {
|
|
13630
|
+
const remaining = Math.max(0, TARGET_SECONDS - Math.floor(elapsed));
|
|
13631
|
+
progressEtaRef.current.textContent = elapsed >= TARGET_SECONDS ? t("Finalizing...") : `~${remaining}s`;
|
|
13632
|
+
}
|
|
13329
13633
|
if (progressStatusRef.current) {
|
|
13330
13634
|
const status = [...statuses].reverse().find((s) => val >= s.at);
|
|
13331
|
-
if (status
|
|
13635
|
+
if (status && status.text !== progressLastStageRef.current) {
|
|
13636
|
+
const el = progressStatusRef.current;
|
|
13637
|
+
el.style.opacity = "0";
|
|
13638
|
+
setTimeout(() => {
|
|
13639
|
+
el.textContent = status.text;
|
|
13640
|
+
el.style.opacity = "1";
|
|
13641
|
+
}, 180);
|
|
13642
|
+
progressLastStageRef.current = status.text;
|
|
13643
|
+
}
|
|
13332
13644
|
}
|
|
13333
13645
|
}, 200);
|
|
13334
13646
|
return () => {
|
|
@@ -13340,8 +13652,9 @@ function PrimeStyleTryonInner({
|
|
|
13340
13652
|
clearInterval(progressIntervalRef.current);
|
|
13341
13653
|
progressIntervalRef.current = null;
|
|
13342
13654
|
}
|
|
13655
|
+
progressStartTsRef.current = null;
|
|
13343
13656
|
}
|
|
13344
|
-
}, [view]);
|
|
13657
|
+
}, [view, t]);
|
|
13345
13658
|
useEffect(() => {
|
|
13346
13659
|
return () => {
|
|
13347
13660
|
if (previewUrl) URL.revokeObjectURL(previewUrl);
|
|
@@ -13668,7 +13981,16 @@ function PrimeStyleTryonInner({
|
|
|
13668
13981
|
progressRef.current = 100;
|
|
13669
13982
|
if (progressBarRef.current) progressBarRef.current.style.width = "100%";
|
|
13670
13983
|
if (progressTextRef.current) progressTextRef.current.textContent = "100%";
|
|
13671
|
-
if (
|
|
13984
|
+
if (progressRingRef.current) progressRingRef.current.style.strokeDashoffset = "0";
|
|
13985
|
+
if (progressEtaRef.current) progressEtaRef.current.textContent = t("Done");
|
|
13986
|
+
if (progressStatusRef.current) {
|
|
13987
|
+
const el = progressStatusRef.current;
|
|
13988
|
+
el.style.opacity = "0";
|
|
13989
|
+
setTimeout(() => {
|
|
13990
|
+
el.textContent = t("Complete!");
|
|
13991
|
+
el.style.opacity = "1";
|
|
13992
|
+
}, 180);
|
|
13993
|
+
}
|
|
13672
13994
|
cleanupJob();
|
|
13673
13995
|
setTryOnProcessing(false);
|
|
13674
13996
|
onComplete?.({ jobId: update.galleryId, imageUrl: update.imageUrl });
|
|
@@ -14077,25 +14399,29 @@ function PrimeStyleTryonInner({
|
|
|
14077
14399
|
}
|
|
14078
14400
|
completedRef.current = false;
|
|
14079
14401
|
setTryOnProcessing(true);
|
|
14402
|
+
const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
|
|
14403
|
+
const isApparel = vtoCategory === "apparel";
|
|
14080
14404
|
const previewObjUrl = (overrideFile ? null : previewUrl) || URL.createObjectURL(file);
|
|
14081
14405
|
if (overrideFile || !previewUrl) setPreviewUrl(previewObjUrl);
|
|
14082
14406
|
modelPoseRef.current = null;
|
|
14083
14407
|
setBodyLandmarks(null);
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14408
|
+
if (isApparel) {
|
|
14409
|
+
detectMeasurementLines(previewObjUrl).then((lines) => {
|
|
14410
|
+
modelPoseRef.current = lines;
|
|
14411
|
+
}).catch(() => {
|
|
14412
|
+
});
|
|
14413
|
+
detectBodyLandmarks(previewObjUrl).then((lm) => {
|
|
14414
|
+
setBodyLandmarks(lm);
|
|
14415
|
+
}).catch(() => {
|
|
14416
|
+
});
|
|
14417
|
+
}
|
|
14092
14418
|
try {
|
|
14093
14419
|
const modelImage = await compressImage(file);
|
|
14094
14420
|
let fitInfo;
|
|
14095
|
-
if (sizingResult?.matchDetails?.length) {
|
|
14421
|
+
if (isApparel && sizingResult?.matchDetails?.length) {
|
|
14096
14422
|
fitInfo = buildFitInfo(sizingResult.matchDetails, modelPoseRef.current);
|
|
14097
14423
|
}
|
|
14098
|
-
const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo);
|
|
14424
|
+
const response = await apiRef.current.submitTryOn(modelImage, productImage, fitInfo, vtoCategory ?? "apparel");
|
|
14099
14425
|
onProcessing?.(response.jobId);
|
|
14100
14426
|
unsubRef.current = sseRef.current.onJob(response.jobId, handleVtoUpdate);
|
|
14101
14427
|
let attempts = 0;
|
|
@@ -14126,11 +14452,13 @@ function PrimeStyleTryonInner({
|
|
|
14126
14452
|
setView("error");
|
|
14127
14453
|
onError?.({ message, code });
|
|
14128
14454
|
}
|
|
14129
|
-
}, [selectedFile, productImage, sizingResult, onProcessing, onError, handleVtoUpdate]);
|
|
14455
|
+
}, [selectedFile, productImage, productTitle, sizingResult, onProcessing, onError, handleVtoUpdate]);
|
|
14130
14456
|
const handleRetryWithFit = useCallback(async (fitInfo) => {
|
|
14131
14457
|
if (!selectedFile || !apiRef.current || !sseRef.current) return;
|
|
14132
14458
|
setRetryLoading(true);
|
|
14133
|
-
|
|
14459
|
+
const vtoCategory = measurementTypeToVtoCategory(detectMeasurementType(productTitle));
|
|
14460
|
+
const isApparel = vtoCategory === "apparel";
|
|
14461
|
+
if (isApparel && modelPoseRef.current) {
|
|
14134
14462
|
const AREA_MAP = {
|
|
14135
14463
|
chest: "chest",
|
|
14136
14464
|
bust: "chest",
|
|
@@ -14156,7 +14484,8 @@ function PrimeStyleTryonInner({
|
|
|
14156
14484
|
pollingRef.current = null;
|
|
14157
14485
|
}
|
|
14158
14486
|
const modelImage = await compressImage(selectedFile);
|
|
14159
|
-
const
|
|
14487
|
+
const outboundFitInfo = isApparel ? fitInfo : void 0;
|
|
14488
|
+
const response = await apiRef.current.submitTryOn(modelImage, productImage, outboundFitInfo, vtoCategory ?? "apparel");
|
|
14160
14489
|
unsubRef.current = sseRef.current.onJob(response.jobId, (update) => {
|
|
14161
14490
|
if (update.status === "completed" && update.imageUrl) {
|
|
14162
14491
|
setResultImageUrl(update.imageUrl);
|
|
@@ -14221,7 +14550,7 @@ function PrimeStyleTryonInner({
|
|
|
14221
14550
|
} catch {
|
|
14222
14551
|
setRetryLoading(false);
|
|
14223
14552
|
}
|
|
14224
|
-
}, [selectedFile, productImage]);
|
|
14553
|
+
}, [selectedFile, productImage, productTitle]);
|
|
14225
14554
|
const handleDownload = useCallback(() => {
|
|
14226
14555
|
if (!resultImageUrl) return;
|
|
14227
14556
|
if (resultImageUrl.startsWith("data:")) {
|
|
@@ -14663,6 +14992,8 @@ function PrimeStyleTryonInner({
|
|
|
14663
14992
|
progressBarRef,
|
|
14664
14993
|
progressTextRef,
|
|
14665
14994
|
progressStatusRef,
|
|
14995
|
+
progressEtaRef,
|
|
14996
|
+
progressRingRef,
|
|
14666
14997
|
cn,
|
|
14667
14998
|
t
|
|
14668
14999
|
}
|