@primestyleai/tryon 5.9.1 → 5.10.0
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/react/components/ConfirmMeasurementsModal.d.ts +22 -0
- package/dist/react/index.js +199 -54
- package/dist/react/recommendForProduct.d.ts +4 -0
- package/dist/react/styles.d.ts +1 -1
- package/dist/react/utils/units.d.ts +6 -0
- package/dist/storefront/primestyle-tryon.js +199 -54
- package/dist/types.d.ts +7 -0
- package/package.json +1 -1
|
@@ -4,3 +4,9 @@ export declare function kgToLbs(kg: number): number;
|
|
|
4
4
|
export declare function lbsToKg(lbs: number): number;
|
|
5
5
|
export declare function ftInToCm(ft: number, inch: number): number;
|
|
6
6
|
export declare function isImperial(locale: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Human-readable label for a unit token. Used to append a system label to
|
|
9
|
+
* primary CTAs ("Find My Best Fit (Metric)") so the user knows whether their
|
|
10
|
+
* values will be interpreted as cm/kg or in/lbs before they submit.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getUnitLabel(unit: string | undefined | null): string;
|
|
@@ -10080,7 +10080,8 @@ async function recommendForProduct(input) {
|
|
|
10080
10080
|
sections: sectionsMap,
|
|
10081
10081
|
profileId: profile.id,
|
|
10082
10082
|
fromCache: false,
|
|
10083
|
-
raw: result
|
|
10083
|
+
raw: result,
|
|
10084
|
+
found: result.found
|
|
10084
10085
|
};
|
|
10085
10086
|
}
|
|
10086
10087
|
async function estimateFullMeasurements(args) {
|
|
@@ -10139,6 +10140,12 @@ async function estimateFullMeasurements(args) {
|
|
|
10139
10140
|
function isImperial(locale) {
|
|
10140
10141
|
return ["US", "UK", "AU"].includes(locale);
|
|
10141
10142
|
}
|
|
10143
|
+
function getUnitLabel(unit) {
|
|
10144
|
+
if (unit === "in" || unit === "inches" || unit === "lbs") return "Imperial";
|
|
10145
|
+
if (unit === "cm" || unit === "kg") return "Metric";
|
|
10146
|
+
if (unit === "mm") return "mm";
|
|
10147
|
+
return "";
|
|
10148
|
+
}
|
|
10142
10149
|
function cx(base, override) {
|
|
10143
10150
|
return override ? `${base} ${override}` : base;
|
|
10144
10151
|
}
|
|
@@ -11524,21 +11531,21 @@ const STYLES$1 = `
|
|
|
11524
11531
|
/* Shared progress layout used inside StageCycler (desktop) and
|
|
11525
11532
|
MobileScanningView — row of ring + bar + percent, same tokens. */
|
|
11526
11533
|
.ps-tryon-progress-wrap {
|
|
11527
|
-
display: flex; align-items: center; gap:
|
|
11528
|
-
width: 100%; max-width:
|
|
11534
|
+
display: flex; align-items: center; gap: 12px;
|
|
11535
|
+
width: 100%; max-width: 360px; margin-top: 18px;
|
|
11529
11536
|
}
|
|
11530
11537
|
.ps-tryon-progress-wrap .ps-tryon-progress-bar-wrap {
|
|
11531
|
-
flex: 1; height:
|
|
11538
|
+
flex: 1; height: 6px; border-radius: 4px; overflow: hidden;
|
|
11532
11539
|
position: relative; background: var(--ps-border-color);
|
|
11533
11540
|
}
|
|
11534
11541
|
.ps-tryon-progress-wrap .ps-tryon-progress-bar-fill {
|
|
11535
11542
|
height: 100%; width: 0%;
|
|
11536
11543
|
background: linear-gradient(90deg, var(--ps-accent), var(--ps-accent-light));
|
|
11537
|
-
border-radius:
|
|
11544
|
+
border-radius: 4px; transition: width 0.3s ease;
|
|
11538
11545
|
}
|
|
11539
11546
|
.ps-tryon-progress-wrap .ps-tryon-progress-pct {
|
|
11540
|
-
font-size:
|
|
11541
|
-
min-width:
|
|
11547
|
+
font-size: 13px; font-weight: 700; color: var(--ps-accent);
|
|
11548
|
+
min-width: 36px; text-align: right;
|
|
11542
11549
|
font-variant-numeric: tabular-nums;
|
|
11543
11550
|
}
|
|
11544
11551
|
.ps-tryon-progress-bar-wrap {
|
|
@@ -11566,25 +11573,25 @@ const STYLES$1 = `
|
|
|
11566
11573
|
font-variant-numeric: tabular-nums;
|
|
11567
11574
|
}
|
|
11568
11575
|
|
|
11569
|
-
/* Circular ETA ring —
|
|
11576
|
+
/* Circular ETA ring — 64×64 px SVG with a track + progress circle; ETA
|
|
11570
11577
|
text centered. strokeDashoffset is driven by the ticker in
|
|
11571
11578
|
PrimeStyleTryonInner, so CSS only styles the appearance. */
|
|
11572
11579
|
.ps-tryon-progress-ring {
|
|
11573
|
-
position: relative; width:
|
|
11580
|
+
position: relative; width: 64px; height: 64px; flex: 0 0 64px;
|
|
11574
11581
|
display: flex; align-items: center; justify-content: center;
|
|
11575
11582
|
}
|
|
11576
11583
|
.ps-tryon-progress-ring svg { transform: rotate(-90deg); }
|
|
11577
11584
|
.ps-tryon-progress-ring-track {
|
|
11578
|
-
fill: none; stroke: var(--ps-border-color); stroke-width:
|
|
11585
|
+
fill: none; stroke: var(--ps-border-color); stroke-width: 5;
|
|
11579
11586
|
}
|
|
11580
11587
|
.ps-tryon-progress-ring-fill {
|
|
11581
|
-
fill: none; stroke: var(--ps-accent); stroke-width:
|
|
11588
|
+
fill: none; stroke: var(--ps-accent); stroke-width: 5;
|
|
11582
11589
|
stroke-linecap: round;
|
|
11583
11590
|
transition: stroke-dashoffset 0.3s ease;
|
|
11584
11591
|
}
|
|
11585
11592
|
.ps-tryon-progress-eta {
|
|
11586
11593
|
position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
|
|
11587
|
-
font-size:
|
|
11594
|
+
font-size: 13px; font-weight: 700; color: var(--ps-accent);
|
|
11588
11595
|
font-variant-numeric: tabular-nums; letter-spacing: 0.01em;
|
|
11589
11596
|
pointer-events: none;
|
|
11590
11597
|
}
|
|
@@ -16671,6 +16678,92 @@ function ProfileDetailModal({
|
|
|
16671
16678
|
document.body
|
|
16672
16679
|
);
|
|
16673
16680
|
}
|
|
16681
|
+
function ConfirmMeasurementsModal({
|
|
16682
|
+
profile,
|
|
16683
|
+
onProceed,
|
|
16684
|
+
onEdit,
|
|
16685
|
+
t: t2
|
|
16686
|
+
}) {
|
|
16687
|
+
const heightUnit = profile.heightUnit === "in" || profile.heightUnit === "ft" ? "in" : "cm";
|
|
16688
|
+
const weightUnit = profile.weightUnit === "lbs" ? "lbs" : "kg";
|
|
16689
|
+
const systemLabel = getUnitLabel(heightUnit);
|
|
16690
|
+
const formatHeight = (h) => {
|
|
16691
|
+
if (!h) return "—";
|
|
16692
|
+
if (heightUnit === "in") {
|
|
16693
|
+
const ft = Math.floor(h / 12);
|
|
16694
|
+
const inches = Math.round(h % 12);
|
|
16695
|
+
return `${ft}'${inches}"`;
|
|
16696
|
+
}
|
|
16697
|
+
return `${Math.round(h)} cm`;
|
|
16698
|
+
};
|
|
16699
|
+
const formatWeight = (w2) => {
|
|
16700
|
+
if (!w2) return "—";
|
|
16701
|
+
return `${Math.round(w2)} ${weightUnit}`;
|
|
16702
|
+
};
|
|
16703
|
+
const height = profile.height ?? profile.heightCm;
|
|
16704
|
+
const weight = profile.weight ?? profile.weightKg;
|
|
16705
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-confirm-overlay", onClick: onEdit, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-confirm-modal", onClick: (e) => e.stopPropagation(), children: [
|
|
16706
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
16707
|
+
"button",
|
|
16708
|
+
{
|
|
16709
|
+
type: "button",
|
|
16710
|
+
"aria-label": t2("Close"),
|
|
16711
|
+
onClick: onEdit,
|
|
16712
|
+
style: {
|
|
16713
|
+
position: "absolute",
|
|
16714
|
+
top: "0.75vw",
|
|
16715
|
+
right: "0.75vw",
|
|
16716
|
+
width: "1.8vw",
|
|
16717
|
+
height: "1.8vw",
|
|
16718
|
+
borderRadius: "50%",
|
|
16719
|
+
background: "transparent",
|
|
16720
|
+
border: "none",
|
|
16721
|
+
cursor: "pointer",
|
|
16722
|
+
display: "flex",
|
|
16723
|
+
alignItems: "center",
|
|
16724
|
+
justifyContent: "center",
|
|
16725
|
+
color: "var(--ps-text-muted)"
|
|
16726
|
+
},
|
|
16727
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", width: "16", height: "16", children: [
|
|
16728
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
16729
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
16730
|
+
] })
|
|
16731
|
+
}
|
|
16732
|
+
),
|
|
16733
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { style: { fontWeight: 700, marginBottom: "0.4vw" }, children: t2("Confirm your measurements") }),
|
|
16734
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("small", { style: { color: "var(--ps-text-muted)" }, children: systemLabel ? t2("You chose") + " " + systemLabel + ". " + t2("Review before continuing.") : t2("Review before continuing.") }),
|
|
16735
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { style: {
|
|
16736
|
+
listStyle: "none",
|
|
16737
|
+
padding: 0,
|
|
16738
|
+
margin: "0.8vw 0 0.2vw 0",
|
|
16739
|
+
width: "100%",
|
|
16740
|
+
textAlign: "left",
|
|
16741
|
+
fontSize: "0.78vw",
|
|
16742
|
+
lineHeight: 1.6
|
|
16743
|
+
}, children: [
|
|
16744
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
|
|
16745
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Height") }),
|
|
16746
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: formatHeight(height) })
|
|
16747
|
+
] }),
|
|
16748
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
|
|
16749
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Weight") }),
|
|
16750
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: formatWeight(weight) })
|
|
16751
|
+
] }),
|
|
16752
|
+
profile.age ? /* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
|
|
16753
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Age") }),
|
|
16754
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: profile.age })
|
|
16755
|
+
] }) : null,
|
|
16756
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("li", { style: { display: "flex", justifyContent: "space-between", gap: "1vw" }, children: [
|
|
16757
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "var(--ps-text-muted)" }, children: t2("Gender") }),
|
|
16758
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontWeight: 600 }, children: profile.gender === "female" ? t2("Female") : t2("Male") })
|
|
16759
|
+
] })
|
|
16760
|
+
] }),
|
|
16761
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-confirm-actions", children: [
|
|
16762
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-confirm-cancel", onClick: onEdit, children: t2("Edit") }),
|
|
16763
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: "ps-confirm-delete", onClick: onProceed, children: t2("Proceed") })
|
|
16764
|
+
] })
|
|
16765
|
+
] }) });
|
|
16766
|
+
}
|
|
16674
16767
|
function WelcomeView({
|
|
16675
16768
|
productImage,
|
|
16676
16769
|
setView,
|
|
@@ -16907,7 +17000,7 @@ function MobileSkeleton({ landmarks, w: w2, h }) {
|
|
|
16907
17000
|
);
|
|
16908
17001
|
}
|
|
16909
17002
|
const MSC_TRYON_TARGET_SECONDS = 22;
|
|
16910
|
-
const MSC_RING_RADIUS =
|
|
17003
|
+
const MSC_RING_RADIUS = 27;
|
|
16911
17004
|
const MSC_RING_CIRC = 2 * Math.PI * MSC_RING_RADIUS;
|
|
16912
17005
|
function MscTryOnProgress({ t: t2 }) {
|
|
16913
17006
|
const startRef = reactExports.useRef(Date.now());
|
|
@@ -16930,17 +17023,17 @@ function MscTryOnProgress({ t: t2 }) {
|
|
|
16930
17023
|
}
|
|
16931
17024
|
}, 200);
|
|
16932
17025
|
return () => clearInterval(id2);
|
|
16933
|
-
}, [
|
|
17026
|
+
}, []);
|
|
16934
17027
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-wrap", children: [
|
|
16935
17028
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
|
|
16936
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "
|
|
16937
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "
|
|
17029
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "64", height: "64", viewBox: "0 0 64 64", "aria-hidden": "true", children: [
|
|
17030
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "32", cy: "32", r: MSC_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
|
|
16938
17031
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
16939
17032
|
"circle",
|
|
16940
17033
|
{
|
|
16941
17034
|
ref: ringRef,
|
|
16942
|
-
cx: "
|
|
16943
|
-
cy: "
|
|
17035
|
+
cx: "32",
|
|
17036
|
+
cy: "32",
|
|
16944
17037
|
r: MSC_RING_RADIUS,
|
|
16945
17038
|
className: "ps-tryon-progress-ring-fill",
|
|
16946
17039
|
strokeDasharray: MSC_RING_CIRC,
|
|
@@ -16989,11 +17082,12 @@ function MobileScanningView({
|
|
|
16989
17082
|
};
|
|
16990
17083
|
const [stageIdx, setStageIdx] = reactExports.useState(0);
|
|
16991
17084
|
reactExports.useEffect(() => {
|
|
17085
|
+
const intervalMs = tryOnProcessing ? 2200 : 1500;
|
|
16992
17086
|
const id2 = setInterval(() => {
|
|
16993
17087
|
setStageIdx((i) => (i + 1) % stages.length);
|
|
16994
|
-
},
|
|
17088
|
+
}, intervalMs);
|
|
16995
17089
|
return () => clearInterval(id2);
|
|
16996
|
-
}, [stages.length]);
|
|
17090
|
+
}, [stages.length, tryOnProcessing]);
|
|
16997
17091
|
reactExports.useEffect(() => {
|
|
16998
17092
|
if (isPhotoMode && bodyLandmarks && stageIdx === 0) {
|
|
16999
17093
|
setStageIdx(1);
|
|
@@ -17150,7 +17244,7 @@ function MultiSectionMobile({
|
|
|
17150
17244
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msr-sections", children: sectionEntries.map(({ name, secResult }) => {
|
|
17151
17245
|
const cleanName = name.replace(/\s*[—–-]\s*.*/g, "");
|
|
17152
17246
|
const sec = secResult;
|
|
17153
|
-
const sizeValue = sec.size || secResult.recommendedSize;
|
|
17247
|
+
const sizeValue = sec.found === false ? t2("No fit") : sec.size || secResult.recommendedSize;
|
|
17154
17248
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
17155
17249
|
"button",
|
|
17156
17250
|
{
|
|
@@ -17185,7 +17279,19 @@ function MultiSectionMobile({
|
|
|
17185
17279
|
children: t2("Continue Shopping")
|
|
17186
17280
|
}
|
|
17187
17281
|
)
|
|
17188
|
-
] }) :
|
|
17282
|
+
] }) : sizingResult?.found === false ? (
|
|
17283
|
+
// Backend couldn't find a size that fits — Try-On is meaningless
|
|
17284
|
+
// without a recommendation, so surface a clear terminal action.
|
|
17285
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
17286
|
+
"button",
|
|
17287
|
+
{
|
|
17288
|
+
type: "button",
|
|
17289
|
+
className: "ps-msr-tryon-cta",
|
|
17290
|
+
onClick: onClose,
|
|
17291
|
+
children: t2("Continue Shopping")
|
|
17292
|
+
}
|
|
17293
|
+
)
|
|
17294
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
17189
17295
|
"button",
|
|
17190
17296
|
{
|
|
17191
17297
|
type: "button",
|
|
@@ -17218,7 +17324,7 @@ const SKELETON_CONNECTIONS = [
|
|
|
17218
17324
|
["rightKnee", "rightAnkle"]
|
|
17219
17325
|
];
|
|
17220
17326
|
const TRYON_TARGET_SECONDS = 22;
|
|
17221
|
-
const TRYON_RING_RADIUS =
|
|
17327
|
+
const TRYON_RING_RADIUS = 27;
|
|
17222
17328
|
const TRYON_RING_CIRC = 2 * Math.PI * TRYON_RING_RADIUS;
|
|
17223
17329
|
function TryOnProgress({ t: t2, isActive }) {
|
|
17224
17330
|
const startRef = reactExports.useRef(null);
|
|
@@ -17248,18 +17354,18 @@ function TryOnProgress({ t: t2, isActive }) {
|
|
|
17248
17354
|
}
|
|
17249
17355
|
}, 200);
|
|
17250
17356
|
return () => clearInterval(id2);
|
|
17251
|
-
}, [isActive
|
|
17357
|
+
}, [isActive]);
|
|
17252
17358
|
if (!isActive) return null;
|
|
17253
17359
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-wrap", children: [
|
|
17254
17360
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
|
|
17255
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "
|
|
17256
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "
|
|
17361
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { width: "64", height: "64", viewBox: "0 0 64 64", "aria-hidden": "true", children: [
|
|
17362
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "32", cy: "32", r: TRYON_RING_RADIUS, className: "ps-tryon-progress-ring-track" }),
|
|
17257
17363
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
17258
17364
|
"circle",
|
|
17259
17365
|
{
|
|
17260
17366
|
ref: ringRef,
|
|
17261
|
-
cx: "
|
|
17262
|
-
cy: "
|
|
17367
|
+
cx: "32",
|
|
17368
|
+
cy: "32",
|
|
17263
17369
|
r: TRYON_RING_RADIUS,
|
|
17264
17370
|
className: "ps-tryon-progress-ring-fill",
|
|
17265
17371
|
strokeDasharray: TRYON_RING_CIRC,
|
|
@@ -17410,11 +17516,12 @@ function StageCycler({
|
|
|
17410
17516
|
}, [tryOnProcessing]);
|
|
17411
17517
|
reactExports.useEffect(() => {
|
|
17412
17518
|
if (isDone) return;
|
|
17519
|
+
const intervalMs = tryOnProcessing ? 2200 : 900;
|
|
17413
17520
|
const id2 = setInterval(() => {
|
|
17414
17521
|
setIdx((i) => Math.min(i + 1, active.length - 1));
|
|
17415
|
-
},
|
|
17522
|
+
}, intervalMs);
|
|
17416
17523
|
return () => clearInterval(id2);
|
|
17417
|
-
}, [isDone, active.length]);
|
|
17524
|
+
}, [isDone, active.length, tryOnProcessing]);
|
|
17418
17525
|
const current = active[idx] ?? active[0];
|
|
17419
17526
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage", style: { alignSelf: "center", marginTop: "auto", marginBottom: "auto" }, children: [
|
|
17420
17527
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-msc-stage-slot", children: [
|
|
@@ -17661,7 +17768,8 @@ function SectionDetailView({
|
|
|
17661
17768
|
backLabel,
|
|
17662
17769
|
internationalSizes,
|
|
17663
17770
|
continueLabel,
|
|
17664
|
-
renderRaw = false
|
|
17771
|
+
renderRaw = false,
|
|
17772
|
+
sectionFound
|
|
17665
17773
|
}) {
|
|
17666
17774
|
const recSize = sectionResult?.recommendedSize || "";
|
|
17667
17775
|
const [selectedSize, setSelectedSize] = reactExports.useState(null);
|
|
@@ -17706,7 +17814,8 @@ function SectionDetailView({
|
|
|
17706
17814
|
const hasBadFit = details.some((d) => BAD_FIT.test(d.fit || ""));
|
|
17707
17815
|
return hasBadFit ? t2("Not Recommended") : t2("Your Selection");
|
|
17708
17816
|
}, [isRecommended, sectionResult, t2]);
|
|
17709
|
-
const
|
|
17817
|
+
const noFitMessage = t2("We couldn't find a size that fits for this product");
|
|
17818
|
+
const displaySizeLabel = sectionFound === false ? noFitMessage : selectedCountry && isRecommended && internationalSizes && internationalSizes[selectedCountry] ? internationalSizes[selectedCountry] : displaySize;
|
|
17710
17819
|
const columnUnits = reactExports.useMemo(() => {
|
|
17711
17820
|
const units = [];
|
|
17712
17821
|
for (let i = 0; i < section.headers.length; i++) {
|
|
@@ -18560,6 +18669,7 @@ function SizeResultView({
|
|
|
18560
18669
|
const allDone = hasPhoto ? sizingDone && tryOnDone : sizingDone;
|
|
18561
18670
|
const isMobile = useIsMobile();
|
|
18562
18671
|
const isAccessory = measurementType === "face" || measurementType === "head";
|
|
18672
|
+
const noFit = sizingResult?.found === false;
|
|
18563
18673
|
const vtoExcluded = measurementType === "foot";
|
|
18564
18674
|
console.log("[PS-SDK] SizeResultView render:", {
|
|
18565
18675
|
hasPhoto,
|
|
@@ -18644,6 +18754,7 @@ function SizeResultView({
|
|
|
18644
18754
|
sectionName: entry.name,
|
|
18645
18755
|
section: entry.section,
|
|
18646
18756
|
sectionResult: entry.secResult,
|
|
18757
|
+
sectionFound: entry.secResult?.found,
|
|
18647
18758
|
userMeasurements: entry.userMeasurements,
|
|
18648
18759
|
unitLbl,
|
|
18649
18760
|
chartUnit: resultUnit,
|
|
@@ -18693,6 +18804,7 @@ function SizeResultView({
|
|
|
18693
18804
|
sectionName: entry.name,
|
|
18694
18805
|
section: entry.section,
|
|
18695
18806
|
sectionResult: entry.secResult,
|
|
18807
|
+
sectionFound: entry.secResult?.found,
|
|
18696
18808
|
userMeasurements: entry.userMeasurements,
|
|
18697
18809
|
unitLbl,
|
|
18698
18810
|
chartUnit: resultUnit,
|
|
@@ -18814,7 +18926,7 @@ function SizeResultView({
|
|
|
18814
18926
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: `ps-tryon-sr-card-v2${isLast ? " ps-full" : ""}`, onClick: () => setActiveSection(name), style: { animationDelay: `${idx * 0.07}s` }, children: [
|
|
18815
18927
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-sr-card-v2-text", children: [
|
|
18816
18928
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-label", children: name.replace(/\s*[—–-]\s*.*/g, "") }),
|
|
18817
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-value", children: sec.size || secResult.recommendedSize }),
|
|
18929
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-value", children: sec.found === false ? t2("No fit") : sec.size || secResult.recommendedSize }),
|
|
18818
18930
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-sr-card-v2-rec", children: t2("recommended") })
|
|
18819
18931
|
] }),
|
|
18820
18932
|
sectionImg && /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: sectionImg, alt: name, className: "ps-tryon-sr-card-v2-img" }),
|
|
@@ -18838,7 +18950,7 @@ function SizeResultView({
|
|
|
18838
18950
|
" →"
|
|
18839
18951
|
]
|
|
18840
18952
|
}
|
|
18841
|
-
) : vtoExcluded ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
18953
|
+
) : vtoExcluded || noFit ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
18842
18954
|
"button",
|
|
18843
18955
|
{
|
|
18844
18956
|
className: "ps-tryon-v2-cta",
|
|
@@ -18891,6 +19003,7 @@ function SizeResultView({
|
|
|
18891
19003
|
sectionName,
|
|
18892
19004
|
section: singleSection,
|
|
18893
19005
|
sectionResult: singleResult,
|
|
19006
|
+
sectionFound: sizingResult?.found,
|
|
18894
19007
|
userMeasurements: singleUserMeasurements,
|
|
18895
19008
|
unitLbl,
|
|
18896
19009
|
chartUnit: resultUnit,
|
|
@@ -18898,7 +19011,7 @@ function SizeResultView({
|
|
|
18898
19011
|
onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
|
|
18899
19012
|
backLabel: t2("Back"),
|
|
18900
19013
|
internationalSizes: sizingResult?.internationalSizes,
|
|
18901
|
-
onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
|
|
19014
|
+
onTryOn: resultImageUrl || vtoExcluded || noFit ? void 0 : handleSingleTryOn,
|
|
18902
19015
|
continueLabel: resultImageUrl ? t2("Continue Shopping") : void 0,
|
|
18903
19016
|
tryOnProcessing,
|
|
18904
19017
|
productImage: resultImageUrl || productImage,
|
|
@@ -18938,6 +19051,7 @@ function SizeResultView({
|
|
|
18938
19051
|
sectionName,
|
|
18939
19052
|
section: singleSection,
|
|
18940
19053
|
sectionResult: singleResult,
|
|
19054
|
+
sectionFound: sizingResult?.found,
|
|
18941
19055
|
userMeasurements: singleUserMeasurements,
|
|
18942
19056
|
unitLbl,
|
|
18943
19057
|
chartUnit: resultUnit,
|
|
@@ -18945,7 +19059,7 @@ function SizeResultView({
|
|
|
18945
19059
|
onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
|
|
18946
19060
|
backLabel: t2("Back"),
|
|
18947
19061
|
internationalSizes: sizingResult?.internationalSizes,
|
|
18948
|
-
onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
|
|
19062
|
+
onTryOn: resultImageUrl || vtoExcluded || noFit ? void 0 : handleSingleTryOn,
|
|
18949
19063
|
continueLabel: resultImageUrl ? t2("Continue Shopping") : void 0,
|
|
18950
19064
|
tryOnProcessing,
|
|
18951
19065
|
t: t2,
|
|
@@ -19321,7 +19435,7 @@ function UploadView({
|
|
|
19321
19435
|
}
|
|
19322
19436
|
) });
|
|
19323
19437
|
}
|
|
19324
|
-
const RING_RADIUS =
|
|
19438
|
+
const RING_RADIUS = 27;
|
|
19325
19439
|
const RING_CIRCUMFERENCE = 2 * Math.PI * RING_RADIUS;
|
|
19326
19440
|
function ProcessingView({
|
|
19327
19441
|
previewUrl,
|
|
@@ -19366,12 +19480,12 @@ function ProcessingView({
|
|
|
19366
19480
|
] }),
|
|
19367
19481
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-section", children: [
|
|
19368
19482
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-progress-ring", children: [
|
|
19369
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0
|
|
19483
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 64 64", width: "64", height: "64", "aria-hidden": "true", children: [
|
|
19370
19484
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
19371
19485
|
"circle",
|
|
19372
19486
|
{
|
|
19373
|
-
cx: "
|
|
19374
|
-
cy: "
|
|
19487
|
+
cx: "32",
|
|
19488
|
+
cy: "32",
|
|
19375
19489
|
r: RING_RADIUS,
|
|
19376
19490
|
className: "ps-tryon-progress-ring-track"
|
|
19377
19491
|
}
|
|
@@ -19380,8 +19494,8 @@ function ProcessingView({
|
|
|
19380
19494
|
"circle",
|
|
19381
19495
|
{
|
|
19382
19496
|
ref: ringCb,
|
|
19383
|
-
cx: "
|
|
19384
|
-
cy: "
|
|
19497
|
+
cx: "32",
|
|
19498
|
+
cy: "32",
|
|
19385
19499
|
r: RING_RADIUS,
|
|
19386
19500
|
className: "ps-tryon-progress-ring-fill",
|
|
19387
19501
|
strokeDasharray: RING_CIRCUMFERENCE,
|
|
@@ -21951,7 +22065,7 @@ function BodyProfileView({
|
|
|
21951
22065
|
hidePhotoOptions: hasActiveProfileWithMeasurements,
|
|
21952
22066
|
onNext: hasActiveProfileWithMeasurements && onUseActiveProfile ? onUseActiveProfile : handleNext,
|
|
21953
22067
|
canProceed: true,
|
|
21954
|
-
fastPathLabel: hasActiveProfileWithMeasurements ? t2("Find My Best Fit") : void 0,
|
|
22068
|
+
fastPathLabel: hasActiveProfileWithMeasurements ? t2("Find My Best Fit") + (getUnitLabel(hUnit) ? ` (${getUnitLabel(hUnit)})` : "") : void 0,
|
|
21955
22069
|
activeProfileName: hasActiveProfileWithMeasurements ? activeProfileName : null,
|
|
21956
22070
|
onStartFresh,
|
|
21957
22071
|
error,
|
|
@@ -22206,7 +22320,8 @@ function BodyProfileView({
|
|
|
22206
22320
|
!(isMobile && step === "basics") && (() => {
|
|
22207
22321
|
const useProfileFast = step === "basics" && hasActiveProfileWithMeasurements && !!onUseActiveProfile;
|
|
22208
22322
|
const handleClick = useProfileFast ? onUseActiveProfile : handleNext;
|
|
22209
|
-
const
|
|
22323
|
+
const unitSuffix = getUnitLabel(hUnit) ? ` (${getUnitLabel(hUnit)})` : "";
|
|
22324
|
+
const label = useProfileFast ? t2("Find My Best Fit") + unitSuffix : isLastStep ? t2("Find My Size") + unitSuffix : t2("Next");
|
|
22210
22325
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-nav", children: [
|
|
22211
22326
|
step !== "basics" ? /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ps-bp-back-btn", onClick: handleBackStep, type: "button", children: [
|
|
22212
22327
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-bp-back-arrow", children: "←" }),
|
|
@@ -22396,6 +22511,7 @@ function AccessorySizeView({
|
|
|
22396
22511
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bpm-bottom", children: [
|
|
22397
22512
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: "ps-bpm-next-btn", onClick: handleManualSubmit, children: [
|
|
22398
22513
|
t2("Find My Size"),
|
|
22514
|
+
getUnitLabel(sizingUnit) ? ` (${getUnitLabel(sizingUnit)})` : "",
|
|
22399
22515
|
" ",
|
|
22400
22516
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRightIcon, {})
|
|
22401
22517
|
] }),
|
|
@@ -22730,6 +22846,7 @@ function AccessorySizeView({
|
|
|
22730
22846
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", {}),
|
|
22731
22847
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("button", { className: "ps-bp-next-btn", onClick: handleManualSubmit, type: "button", children: [
|
|
22732
22848
|
t2("Find My Size"),
|
|
22849
|
+
getUnitLabel(sizingUnit) ? ` (${getUnitLabel(sizingUnit)})` : "",
|
|
22733
22850
|
" ",
|
|
22734
22851
|
/* @__PURE__ */ jsxRuntimeExports.jsx(ArrowRightIcon, {})
|
|
22735
22852
|
] })
|
|
@@ -22963,6 +23080,9 @@ function PrimeStyleTryonInner({
|
|
|
22963
23080
|
const [sizingUnit, setSizingUnit] = reactExports.useState(imperial ? "in" : "cm");
|
|
22964
23081
|
const [heightUnit, setHeightUnit] = reactExports.useState(imperial ? "in" : "cm");
|
|
22965
23082
|
const [weightUnit, setWeightUnit] = reactExports.useState(imperial ? "lbs" : "kg");
|
|
23083
|
+
reactExports.useEffect(() => {
|
|
23084
|
+
if (detectMeasurementType(productTitle) === "foot") setSizingUnit("cm");
|
|
23085
|
+
}, [productTitle]);
|
|
22966
23086
|
const formRef = reactExports.useRef({});
|
|
22967
23087
|
const [formGender, setFormGender] = reactExports.useState("male");
|
|
22968
23088
|
const [formKey, setFormKey] = reactExports.useState(0);
|
|
@@ -23037,7 +23157,7 @@ function PrimeStyleTryonInner({
|
|
|
23037
23157
|
{ at: 75, text: t2("Refining details...") },
|
|
23038
23158
|
{ at: 90, text: t2("Almost there...") }
|
|
23039
23159
|
];
|
|
23040
|
-
const RING_CIRCUMFERENCE2 = 2 * Math.PI *
|
|
23160
|
+
const RING_CIRCUMFERENCE2 = 2 * Math.PI * 27;
|
|
23041
23161
|
progressIntervalRef.current = setInterval(() => {
|
|
23042
23162
|
if (completedRef.current) return;
|
|
23043
23163
|
const startTs = progressStartTsRef.current || Date.now();
|
|
@@ -23215,13 +23335,10 @@ function PrimeStyleTryonInner({
|
|
|
23215
23335
|
[activeProfileId, profiles, apiUrl, productImage, productTitle, effectiveProductId, setActiveProfileId$1]
|
|
23216
23336
|
);
|
|
23217
23337
|
const snapSubmitRef = reactExports.useRef(null);
|
|
23218
|
-
const
|
|
23219
|
-
|
|
23220
|
-
if (!p2) return;
|
|
23338
|
+
const [confirmProfile, setConfirmProfile] = reactExports.useState(null);
|
|
23339
|
+
const runRecommendWithProfile = reactExports.useCallback(async (p2) => {
|
|
23221
23340
|
const profileHeight = p2.height ?? p2.heightCm ?? 0;
|
|
23222
23341
|
const profileWeight = p2.weight ?? p2.weightKg ?? 0;
|
|
23223
|
-
const hasIdentity = profileHeight > 0 && profileWeight > 0;
|
|
23224
|
-
if (!hasIdentity) return;
|
|
23225
23342
|
const hasStored = !!p2.measurements && Object.keys(p2.measurements).length > 0;
|
|
23226
23343
|
const storedPhoto = p2.photoBase64;
|
|
23227
23344
|
if (!hasStored && storedPhoto && profileHeight > 0 && snapSubmitRef.current) {
|
|
@@ -23245,9 +23362,8 @@ function PrimeStyleTryonInner({
|
|
|
23245
23362
|
}
|
|
23246
23363
|
setSizingResult(null);
|
|
23247
23364
|
setSizingLoading(true);
|
|
23248
|
-
|
|
23249
|
-
|
|
23250
|
-
if (hasStoredMeasurements) {
|
|
23365
|
+
setEstimationDone(hasStored);
|
|
23366
|
+
if (hasStored) {
|
|
23251
23367
|
setPreviewUrl(null);
|
|
23252
23368
|
setBodyLandmarks(null);
|
|
23253
23369
|
}
|
|
@@ -23280,7 +23396,27 @@ function PrimeStyleTryonInner({
|
|
|
23280
23396
|
setEstimationDone(true);
|
|
23281
23397
|
}).catch(() => {
|
|
23282
23398
|
}).finally(() => setSizingLoading(false));
|
|
23283
|
-
}, [
|
|
23399
|
+
}, [effectiveProductId, productTitle, productImage, sizeGuideData, apiUrl, previewUrl]);
|
|
23400
|
+
const handleUseActiveProfile = reactExports.useCallback(async () => {
|
|
23401
|
+
const p2 = profiles.find((x2) => x2.id === activeProfileId);
|
|
23402
|
+
if (!p2) return;
|
|
23403
|
+
const profileHeight = p2.height ?? p2.heightCm ?? 0;
|
|
23404
|
+
const profileWeight = p2.weight ?? p2.weightKg ?? 0;
|
|
23405
|
+
const hasIdentity = profileHeight > 0 && profileWeight > 0;
|
|
23406
|
+
if (!hasIdentity) return;
|
|
23407
|
+
setConfirmProfile(p2);
|
|
23408
|
+
}, [profiles, activeProfileId]);
|
|
23409
|
+
const proceedFromConfirmProfile = reactExports.useCallback(() => {
|
|
23410
|
+
if (!confirmProfile) return;
|
|
23411
|
+
const p2 = confirmProfile;
|
|
23412
|
+
setConfirmProfile(null);
|
|
23413
|
+
void runRecommendWithProfile(p2);
|
|
23414
|
+
}, [confirmProfile, runRecommendWithProfile]);
|
|
23415
|
+
const cancelFromConfirmProfile = reactExports.useCallback(() => {
|
|
23416
|
+
setConfirmProfile(null);
|
|
23417
|
+
setFormKey((k2) => k2 + 1);
|
|
23418
|
+
setView("body-profile");
|
|
23419
|
+
}, []);
|
|
23284
23420
|
const applyProfileRef = reactExports.useRef(() => {
|
|
23285
23421
|
});
|
|
23286
23422
|
const handleOpen = reactExports.useCallback(() => {
|
|
@@ -24655,6 +24791,15 @@ function PrimeStyleTryonInner({
|
|
|
24655
24791
|
onCancel: () => setDeleteConfirmId(null),
|
|
24656
24792
|
t: t2
|
|
24657
24793
|
}
|
|
24794
|
+
),
|
|
24795
|
+
confirmProfile && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
24796
|
+
ConfirmMeasurementsModal,
|
|
24797
|
+
{
|
|
24798
|
+
profile: confirmProfile,
|
|
24799
|
+
onProceed: proceedFromConfirmProfile,
|
|
24800
|
+
onEdit: cancelFromConfirmProfile,
|
|
24801
|
+
t: t2
|
|
24802
|
+
}
|
|
24658
24803
|
)
|
|
24659
24804
|
] }) }),
|
|
24660
24805
|
document.body
|
package/dist/types.d.ts
CHANGED
|
@@ -198,6 +198,8 @@ export interface MatchDetail {
|
|
|
198
198
|
export interface SectionRecommendation {
|
|
199
199
|
recommendedSize: string;
|
|
200
200
|
matchDetails: MatchDetail[];
|
|
201
|
+
/** True when the user's measurements fit inside this section's chart. */
|
|
202
|
+
found?: boolean;
|
|
201
203
|
}
|
|
202
204
|
/** Full sizing recommendation result */
|
|
203
205
|
export interface SizingResult {
|
|
@@ -213,6 +215,11 @@ export interface SizingResult {
|
|
|
213
215
|
sections?: Record<string, SectionRecommendation>;
|
|
214
216
|
/** Unit the matchDetails values (and chart columns used for matching) are in. */
|
|
215
217
|
unit?: "cm" | "in";
|
|
218
|
+
/** True when the backend found a size whose measurements actually fit the
|
|
219
|
+
* user. False when the user's body is outside every row in the chart —
|
|
220
|
+
* the SDK renders a "no size" message and disables Try-On. Optional for
|
|
221
|
+
* backwards compatibility with older backends (treat `undefined` as true). */
|
|
222
|
+
found?: boolean;
|
|
216
223
|
}
|
|
217
224
|
/** Fit info for a body area — tells Gemini how the garment should render at this region */
|
|
218
225
|
export interface FitAreaInfo {
|