@primestyleai/tryon 5.8.46 → 5.8.48
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/index.js +229 -39
- package/dist/storefront/primestyle-tryon.js +229 -39
- package/package.json +4 -1
package/dist/react/index.js
CHANGED
|
@@ -7759,6 +7759,37 @@ function FaceOverlay({
|
|
|
7759
7759
|
] }, key))
|
|
7760
7760
|
] });
|
|
7761
7761
|
}
|
|
7762
|
+
function AccessoryStageCycler({
|
|
7763
|
+
category,
|
|
7764
|
+
sizingDone,
|
|
7765
|
+
t
|
|
7766
|
+
}) {
|
|
7767
|
+
const stages = category === "head" ? [
|
|
7768
|
+
{ title: t("DETECTING HEAD"), desc: t("Reading head landmarks from your photo.") },
|
|
7769
|
+
{ title: t("MAPPING CIRCUMFERENCE"), desc: t("Estimating head width and depth.") },
|
|
7770
|
+
{ title: t("MATCHING HAT SIZE"), desc: t("Comparing your circumference to the size chart.") },
|
|
7771
|
+
{ title: t("FINALIZING RESULT"), desc: t("Almost done — preparing your recommendation.") }
|
|
7772
|
+
] : [
|
|
7773
|
+
{ title: t("DETECTING FACE"), desc: t("Identifying 478 face landmarks in your photo.") },
|
|
7774
|
+
{ title: t("CALIBRATING SCALE"), desc: t("Using iris size as the pixel-to-mm anchor.") },
|
|
7775
|
+
{ title: t("MEASURING FRAME"), desc: t("Mapping bridge, lens width and temple length.") },
|
|
7776
|
+
{ title: t("MATCHING FRAME SIZE"), desc: t("Comparing your measurements to the size chart.") },
|
|
7777
|
+
{ title: t("FINALIZING RESULT"), desc: t("Almost done — preparing your recommendation.") }
|
|
7778
|
+
];
|
|
7779
|
+
const [idx, setIdx] = useState(0);
|
|
7780
|
+
useEffect(() => {
|
|
7781
|
+
if (sizingDone) return;
|
|
7782
|
+
const id = setInterval(() => {
|
|
7783
|
+
setIdx((i) => Math.min(i + 1, stages.length - 1));
|
|
7784
|
+
}, 900);
|
|
7785
|
+
return () => clearInterval(id);
|
|
7786
|
+
}, [sizingDone, stages.length]);
|
|
7787
|
+
const current = stages[idx] ?? stages[0];
|
|
7788
|
+
return /* @__PURE__ */ jsx("div", { className: "ps-msc-stage", style: { alignSelf: "center", marginTop: "auto", marginBottom: "auto" }, children: /* @__PURE__ */ jsxs("div", { className: "ps-msc-stage-slot", children: [
|
|
7789
|
+
/* @__PURE__ */ jsx("div", { className: "ps-msc-stage-title", children: current.title }),
|
|
7790
|
+
/* @__PURE__ */ jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
|
|
7791
|
+
] }, idx) });
|
|
7792
|
+
}
|
|
7762
7793
|
function SkeletonOverlay({ landmarks, imgWidth, imgHeight }) {
|
|
7763
7794
|
const W = imgWidth;
|
|
7764
7795
|
const H = imgHeight;
|
|
@@ -8922,6 +8953,9 @@ function SizeResultView({
|
|
|
8922
8953
|
const isFaceCategory = measurementType === "face" || measurementType === "head";
|
|
8923
8954
|
const detectionDone = isFaceCategory ? !!faceLandmarks : !!bodyLandmarks;
|
|
8924
8955
|
const detectLabel = isFaceCategory ? measurementType === "head" ? t("Detecting head") : t("Detecting face") : t("Detecting body pose");
|
|
8956
|
+
if (isFaceCategory) {
|
|
8957
|
+
return /* @__PURE__ */ jsx("div", { className: "ps-tryon-sr-right-col", style: { display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx(AccessoryStageCycler, { category: measurementType, sizingDone, t }) });
|
|
8958
|
+
}
|
|
8925
8959
|
return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sr-right-col ps-tryon-snap-steps", children: [
|
|
8926
8960
|
/* @__PURE__ */ jsxs("div", { className: `ps-tryon-snap-step${detectionDone ? " ps-done" : " ps-active"}`, children: [
|
|
8927
8961
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-snap-step-icon", children: detectionDone ? /* @__PURE__ */ jsx("span", { className: "ps-tryon-snap-check", children: "✓" }) : /* @__PURE__ */ jsx("div", { className: "ps-tryon-size-loading-spinner", style: { width: "1vw", height: "1vw", borderWidth: "1.5px" } }) }),
|
|
@@ -12490,6 +12524,7 @@ function AccessorySizeView({
|
|
|
12490
12524
|
const [photoPreview, setPhotoPreview] = useState(null);
|
|
12491
12525
|
const [photoFile, setPhotoFile] = useState(null);
|
|
12492
12526
|
const [photoBase64, setPhotoBase64] = useState(null);
|
|
12527
|
+
const [ageConfirmed, setAgeConfirmed] = useState(null);
|
|
12493
12528
|
const handlePhotoSelect = useCallback(async (e) => {
|
|
12494
12529
|
const file = e.target.files?.[0];
|
|
12495
12530
|
if (!file) return;
|
|
@@ -12531,6 +12566,10 @@ function AccessorySizeView({
|
|
|
12531
12566
|
submitSizing("exact");
|
|
12532
12567
|
};
|
|
12533
12568
|
const handlePhotoSubmit = () => {
|
|
12569
|
+
if (ageConfirmed !== true) {
|
|
12570
|
+
setError(t("Please confirm that the person in the photo is 18 or older before uploading."));
|
|
12571
|
+
return;
|
|
12572
|
+
}
|
|
12534
12573
|
if (!photoFile || !photoBase64) {
|
|
12535
12574
|
setError(t("Please upload a photo"));
|
|
12536
12575
|
return;
|
|
@@ -12585,7 +12624,7 @@ function AccessorySizeView({
|
|
|
12585
12624
|
className: "ps-bpm-value-input",
|
|
12586
12625
|
value: values[f.key],
|
|
12587
12626
|
onChange: (e) => updateValue(f.key, e.target.value),
|
|
12588
|
-
placeholder: sizingUnit
|
|
12627
|
+
placeholder: f.placeholder[sizingUnit] || f.placeholder.cm || f.placeholder.in || "",
|
|
12589
12628
|
step: f.step ?? 0.5,
|
|
12590
12629
|
min: f.min,
|
|
12591
12630
|
max: f.max
|
|
@@ -12630,38 +12669,180 @@ function AccessorySizeView({
|
|
|
12630
12669
|
/* ── Photo step — identical to BodyProfileView photo step ── */
|
|
12631
12670
|
/* @__PURE__ */ jsxs("div", { className: "ps-bp-wrapper", style: { display: "flex", flexDirection: "column", width: "100%", height: "100%" }, children: [
|
|
12632
12671
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "1.2vw", padding: "1.5vw", width: "100%", flex: 1, minHeight: "20vw", alignItems: "stretch" }, children: [
|
|
12633
|
-
/* @__PURE__ */
|
|
12634
|
-
|
|
12635
|
-
|
|
12636
|
-
|
|
12637
|
-
|
|
12638
|
-
|
|
12639
|
-
|
|
12640
|
-
|
|
12641
|
-
|
|
12642
|
-
|
|
12643
|
-
|
|
12644
|
-
|
|
12645
|
-
|
|
12646
|
-
|
|
12647
|
-
|
|
12648
|
-
|
|
12649
|
-
|
|
12650
|
-
|
|
12651
|
-
|
|
12652
|
-
|
|
12653
|
-
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
12657
|
-
|
|
12658
|
-
|
|
12659
|
-
|
|
12660
|
-
|
|
12661
|
-
|
|
12662
|
-
|
|
12663
|
-
|
|
12664
|
-
|
|
12672
|
+
/* @__PURE__ */ jsxs("div", { style: { flex: 1, position: "relative", display: "flex" }, children: [
|
|
12673
|
+
/* @__PURE__ */ jsx(
|
|
12674
|
+
"div",
|
|
12675
|
+
{
|
|
12676
|
+
onClick: () => {
|
|
12677
|
+
if (!photoPreview && ageConfirmed === true) fileInputRef.current?.click();
|
|
12678
|
+
},
|
|
12679
|
+
style: {
|
|
12680
|
+
flex: 1,
|
|
12681
|
+
display: "flex",
|
|
12682
|
+
flexDirection: "column",
|
|
12683
|
+
alignItems: "center",
|
|
12684
|
+
justifyContent: "center",
|
|
12685
|
+
border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
|
|
12686
|
+
borderRadius: "0.5vw",
|
|
12687
|
+
cursor: photoPreview ? "default" : ageConfirmed === true ? "pointer" : "not-allowed",
|
|
12688
|
+
position: "relative",
|
|
12689
|
+
background: "var(--ps-bg-secondary)",
|
|
12690
|
+
transition: "border-color 0.2s",
|
|
12691
|
+
overflow: "hidden",
|
|
12692
|
+
filter: !photoPreview && ageConfirmed !== true ? "blur(6px) saturate(0.7)" : void 0,
|
|
12693
|
+
pointerEvents: !photoPreview && ageConfirmed !== true ? "none" : void 0
|
|
12694
|
+
},
|
|
12695
|
+
children: photoPreview ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12696
|
+
/* @__PURE__ */ jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
|
|
12697
|
+
/* @__PURE__ */ jsx("button", { onClick: (e) => {
|
|
12698
|
+
e.stopPropagation();
|
|
12699
|
+
handleRemovePhoto();
|
|
12700
|
+
}, style: { position: "absolute", top: "0.5vw", right: "0.5vw", width: "1.4vw", height: "1.4vw", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "none", color: "#fff", fontSize: "0.7vw", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }, children: "×" })
|
|
12701
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
12702
|
+
/* @__PURE__ */ jsx(UploadIcon, { size: 32 }),
|
|
12703
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.85vw", fontWeight: 600, color: "var(--ps-text-primary)", marginTop: "0.5vw" }, children: isCloseUp ? t("Upload close-up photo") : t("Upload your photo") }),
|
|
12704
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: isCloseUp ? t("Click or drag a close-up face photo") : t("Click or drag a full-body photo") }),
|
|
12705
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
|
|
12706
|
+
] })
|
|
12707
|
+
}
|
|
12708
|
+
),
|
|
12709
|
+
!photoPreview && ageConfirmed === null && /* @__PURE__ */ jsx(
|
|
12710
|
+
"div",
|
|
12711
|
+
{
|
|
12712
|
+
role: "dialog",
|
|
12713
|
+
style: {
|
|
12714
|
+
position: "absolute",
|
|
12715
|
+
inset: 0,
|
|
12716
|
+
display: "flex",
|
|
12717
|
+
alignItems: "center",
|
|
12718
|
+
justifyContent: "center",
|
|
12719
|
+
padding: "1vw",
|
|
12720
|
+
background: "rgba(255,255,255,0.55)",
|
|
12721
|
+
backdropFilter: "blur(8px)",
|
|
12722
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
12723
|
+
borderRadius: "0.5vw",
|
|
12724
|
+
zIndex: 2
|
|
12725
|
+
},
|
|
12726
|
+
children: /* @__PURE__ */ jsxs("div", { style: {
|
|
12727
|
+
width: "100%",
|
|
12728
|
+
maxWidth: "min(420px, 22vw)",
|
|
12729
|
+
padding: "1.4vw 1.6vw",
|
|
12730
|
+
background: "#FFFFFF",
|
|
12731
|
+
border: "1px solid var(--ps-border-subtle)",
|
|
12732
|
+
borderRadius: "0.9vw",
|
|
12733
|
+
boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25), 0 8px 16px -8px rgba(17,24,39,0.15)",
|
|
12734
|
+
display: "flex",
|
|
12735
|
+
flexDirection: "column",
|
|
12736
|
+
alignItems: "center",
|
|
12737
|
+
textAlign: "center",
|
|
12738
|
+
gap: "0.75vw"
|
|
12739
|
+
}, children: [
|
|
12740
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--ps-accent)" }, children: t("AGE VERIFICATION") }),
|
|
12741
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.95vw", fontWeight: 600, lineHeight: 1.35, color: "var(--ps-text-primary)" }, children: t("Is the person in this photo 18 years or older?") }),
|
|
12742
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.65vw", width: "100%", marginTop: "0.4vw" }, children: [
|
|
12743
|
+
/* @__PURE__ */ jsx(
|
|
12744
|
+
"button",
|
|
12745
|
+
{
|
|
12746
|
+
type: "button",
|
|
12747
|
+
onClick: () => {
|
|
12748
|
+
setAgeConfirmed(true);
|
|
12749
|
+
setError("");
|
|
12750
|
+
},
|
|
12751
|
+
style: {
|
|
12752
|
+
flex: 1,
|
|
12753
|
+
padding: "0.75vw 1vw",
|
|
12754
|
+
borderRadius: "999px",
|
|
12755
|
+
background: "var(--ps-accent)",
|
|
12756
|
+
color: "#FFFFFF",
|
|
12757
|
+
border: "1.5px solid var(--ps-accent)",
|
|
12758
|
+
fontFamily: "inherit",
|
|
12759
|
+
fontSize: "0.78vw",
|
|
12760
|
+
fontWeight: 700,
|
|
12761
|
+
cursor: "pointer"
|
|
12762
|
+
},
|
|
12763
|
+
children: t("Yes")
|
|
12764
|
+
}
|
|
12765
|
+
),
|
|
12766
|
+
/* @__PURE__ */ jsx(
|
|
12767
|
+
"button",
|
|
12768
|
+
{
|
|
12769
|
+
type: "button",
|
|
12770
|
+
onClick: () => setAgeConfirmed(false),
|
|
12771
|
+
style: {
|
|
12772
|
+
flex: 1,
|
|
12773
|
+
padding: "0.75vw 1vw",
|
|
12774
|
+
borderRadius: "999px",
|
|
12775
|
+
background: "transparent",
|
|
12776
|
+
color: "var(--ps-text-primary)",
|
|
12777
|
+
border: "1.5px solid var(--ps-border-color)",
|
|
12778
|
+
fontFamily: "inherit",
|
|
12779
|
+
fontSize: "0.78vw",
|
|
12780
|
+
fontWeight: 700,
|
|
12781
|
+
cursor: "pointer"
|
|
12782
|
+
},
|
|
12783
|
+
children: t("No")
|
|
12784
|
+
}
|
|
12785
|
+
)
|
|
12786
|
+
] })
|
|
12787
|
+
] })
|
|
12788
|
+
}
|
|
12789
|
+
),
|
|
12790
|
+
!photoPreview && ageConfirmed === false && /* @__PURE__ */ jsx(
|
|
12791
|
+
"div",
|
|
12792
|
+
{
|
|
12793
|
+
role: "alert",
|
|
12794
|
+
style: {
|
|
12795
|
+
position: "absolute",
|
|
12796
|
+
inset: 0,
|
|
12797
|
+
display: "flex",
|
|
12798
|
+
alignItems: "center",
|
|
12799
|
+
justifyContent: "center",
|
|
12800
|
+
padding: "1vw",
|
|
12801
|
+
background: "rgba(255,255,255,0.55)",
|
|
12802
|
+
backdropFilter: "blur(8px)",
|
|
12803
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
12804
|
+
borderRadius: "0.5vw",
|
|
12805
|
+
zIndex: 2
|
|
12806
|
+
},
|
|
12807
|
+
children: /* @__PURE__ */ jsxs("div", { style: {
|
|
12808
|
+
width: "100%",
|
|
12809
|
+
maxWidth: "min(420px, 22vw)",
|
|
12810
|
+
padding: "1.4vw 1.6vw",
|
|
12811
|
+
background: "#FFFFFF",
|
|
12812
|
+
border: "1px solid rgba(192,38,38,0.35)",
|
|
12813
|
+
borderRadius: "0.9vw",
|
|
12814
|
+
boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25)",
|
|
12815
|
+
display: "flex",
|
|
12816
|
+
flexDirection: "column",
|
|
12817
|
+
alignItems: "center",
|
|
12818
|
+
textAlign: "center",
|
|
12819
|
+
gap: "0.75vw"
|
|
12820
|
+
}, children: [
|
|
12821
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "#C02626" }, children: t("UPLOAD NOT ALLOWED") }),
|
|
12822
|
+
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.95vw", fontWeight: 600, lineHeight: 1.35, color: "var(--ps-text-primary)" }, children: t("For your safety, we cannot process photos of people under 18.") }),
|
|
12823
|
+
/* @__PURE__ */ jsx(
|
|
12824
|
+
"button",
|
|
12825
|
+
{
|
|
12826
|
+
type: "button",
|
|
12827
|
+
onClick: () => setAgeConfirmed(null),
|
|
12828
|
+
style: {
|
|
12829
|
+
padding: "0.75vw 1.4vw",
|
|
12830
|
+
borderRadius: "999px",
|
|
12831
|
+
background: "transparent",
|
|
12832
|
+
color: "var(--ps-text-primary)",
|
|
12833
|
+
border: "1.5px solid var(--ps-border-color)",
|
|
12834
|
+
fontFamily: "inherit",
|
|
12835
|
+
fontSize: "0.78vw",
|
|
12836
|
+
fontWeight: 700,
|
|
12837
|
+
cursor: "pointer"
|
|
12838
|
+
},
|
|
12839
|
+
children: t("Go back")
|
|
12840
|
+
}
|
|
12841
|
+
)
|
|
12842
|
+
] })
|
|
12843
|
+
}
|
|
12844
|
+
)
|
|
12845
|
+
] }),
|
|
12665
12846
|
/* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", gap: "0.6vw", justifyContent: "center" }, children: [
|
|
12666
12847
|
/* @__PURE__ */ jsx("div", { style: { fontSize: "0.85vw", fontWeight: 700, color: "var(--ps-text-primary)", marginBottom: "0.3vw" }, children: t("How to take the best photo") }),
|
|
12667
12848
|
/* @__PURE__ */ jsxs("div", { style: { background: "#ddfbe7", borderRadius: "0.5vw", padding: "0.6vw 0.8vw" }, children: [
|
|
@@ -12766,7 +12947,7 @@ function AccessorySizeView({
|
|
|
12766
12947
|
className: "ps-bp-inline-input",
|
|
12767
12948
|
value: values[f.key],
|
|
12768
12949
|
onChange: (e) => updateValue(f.key, e.target.value),
|
|
12769
|
-
placeholder: sizingUnit
|
|
12950
|
+
placeholder: f.placeholder[sizingUnit] || f.placeholder.cm || f.placeholder.in || "",
|
|
12770
12951
|
step: f.step ?? 0.5,
|
|
12771
12952
|
min: f.min,
|
|
12772
12953
|
max: f.max
|
|
@@ -12871,7 +13052,8 @@ const FALLBACK_FACE_FIELDS = [
|
|
|
12871
13052
|
{
|
|
12872
13053
|
key: "lensWidth",
|
|
12873
13054
|
label: "Lens Width",
|
|
12874
|
-
placeholder
|
|
13055
|
+
// `cm` key = mm placeholder (default eyewear unit); `in` key = cm placeholder.
|
|
13056
|
+
placeholder: { mm: "e.g. 52", cm: "e.g. 5.2" },
|
|
12875
13057
|
hint: "Width of one lens",
|
|
12876
13058
|
min: 0,
|
|
12877
13059
|
step: 1
|
|
@@ -12879,7 +13061,7 @@ const FALLBACK_FACE_FIELDS = [
|
|
|
12879
13061
|
{
|
|
12880
13062
|
key: "bridgeWidth",
|
|
12881
13063
|
label: "Bridge",
|
|
12882
|
-
placeholder: {
|
|
13064
|
+
placeholder: { mm: "e.g. 18", cm: "e.g. 1.8" },
|
|
12883
13065
|
hint: "Distance over the nose between lenses",
|
|
12884
13066
|
min: 0,
|
|
12885
13067
|
step: 1
|
|
@@ -12887,15 +13069,15 @@ const FALLBACK_FACE_FIELDS = [
|
|
|
12887
13069
|
{
|
|
12888
13070
|
key: "templeLength",
|
|
12889
13071
|
label: "Arm Length",
|
|
12890
|
-
placeholder: {
|
|
13072
|
+
placeholder: { mm: "e.g. 140", cm: "e.g. 14" },
|
|
12891
13073
|
hint: "Length of the arm from hinge to tip",
|
|
12892
13074
|
min: 0,
|
|
12893
13075
|
step: 1
|
|
12894
13076
|
}
|
|
12895
13077
|
];
|
|
12896
13078
|
const EYEWEAR_UNIT_OPTIONS = [
|
|
12897
|
-
{ label: "
|
|
12898
|
-
{ label: "
|
|
13079
|
+
{ label: "Millimetre", value: "mm" },
|
|
13080
|
+
{ label: "Centimetre", value: "cm" }
|
|
12899
13081
|
];
|
|
12900
13082
|
function buildFieldsFromSizeGuide(sizeGuide) {
|
|
12901
13083
|
const req = sizeGuide?.requiredFields;
|
|
@@ -13463,6 +13645,7 @@ function PrimeStyleTryonInner({
|
|
|
13463
13645
|
category: measurementType,
|
|
13464
13646
|
...Object.keys(faceMm).length > 0 && { faceMeasurementsMm: faceMm, irisConfidence: 1 }
|
|
13465
13647
|
};
|
|
13648
|
+
const minVisible = new Promise((r) => setTimeout(r, 3200));
|
|
13466
13649
|
try {
|
|
13467
13650
|
const resp = await fetch(`${baseUrl}/api/v1/sizing/face-recommend`, {
|
|
13468
13651
|
method: "POST",
|
|
@@ -13471,17 +13654,20 @@ function PrimeStyleTryonInner({
|
|
|
13471
13654
|
});
|
|
13472
13655
|
if (resp.ok) {
|
|
13473
13656
|
const data = await resp.json();
|
|
13657
|
+
await minVisible;
|
|
13474
13658
|
setSizingResult(data);
|
|
13475
13659
|
onComplete?.(data);
|
|
13476
13660
|
} else {
|
|
13477
13661
|
const body = await resp.text().catch(() => "");
|
|
13478
13662
|
console.error("[PS-SDK] face-recommend failed:", resp.status, body);
|
|
13663
|
+
await minVisible;
|
|
13479
13664
|
setErrorMessage(t("Unable to get size recommendation. Please try again."));
|
|
13480
13665
|
setView("error");
|
|
13481
13666
|
setEstimationDone(true);
|
|
13482
13667
|
}
|
|
13483
13668
|
} catch (err) {
|
|
13484
13669
|
console.error("[PS-SDK] face-recommend network error:", err);
|
|
13670
|
+
await minVisible;
|
|
13485
13671
|
setErrorMessage(t("Unable to connect to sizing service. Please try again."));
|
|
13486
13672
|
setView("error");
|
|
13487
13673
|
setEstimationDone(true);
|
|
@@ -13676,6 +13862,7 @@ function PrimeStyleTryonInner({
|
|
|
13676
13862
|
const measurementType = detectMeasurementType(productTitle);
|
|
13677
13863
|
if (measurementType === "face" || measurementType === "head") {
|
|
13678
13864
|
setFaceLandmarks(null);
|
|
13865
|
+
const minVisible = new Promise((r) => setTimeout(r, 3200));
|
|
13679
13866
|
try {
|
|
13680
13867
|
const faceResult = await detectFaceMeasurements(objUrl);
|
|
13681
13868
|
if (faceResult) setFaceLandmarks(faceResult.landmarks);
|
|
@@ -13698,6 +13885,7 @@ function PrimeStyleTryonInner({
|
|
|
13698
13885
|
});
|
|
13699
13886
|
if (recRes.ok) {
|
|
13700
13887
|
const recData = await recRes.json();
|
|
13888
|
+
await minVisible;
|
|
13701
13889
|
setSizingResult(recData);
|
|
13702
13890
|
onComplete?.(recData);
|
|
13703
13891
|
persistResultToProfile(
|
|
@@ -13713,10 +13901,12 @@ function PrimeStyleTryonInner({
|
|
|
13713
13901
|
recData
|
|
13714
13902
|
);
|
|
13715
13903
|
} else {
|
|
13904
|
+
await minVisible;
|
|
13716
13905
|
setEstimationDone(true);
|
|
13717
13906
|
}
|
|
13718
13907
|
} catch (err) {
|
|
13719
13908
|
console.error("[ps-sdk] face-recommend failed:", err);
|
|
13909
|
+
await minVisible;
|
|
13720
13910
|
setEstimationDone(true);
|
|
13721
13911
|
}
|
|
13722
13912
|
setSizingLoading(false);
|
|
@@ -17183,6 +17183,37 @@ function FaceOverlay({
|
|
|
17183
17183
|
] }, key))
|
|
17184
17184
|
] });
|
|
17185
17185
|
}
|
|
17186
|
+
function AccessoryStageCycler({
|
|
17187
|
+
category,
|
|
17188
|
+
sizingDone,
|
|
17189
|
+
t: t2
|
|
17190
|
+
}) {
|
|
17191
|
+
const stages = category === "head" ? [
|
|
17192
|
+
{ title: t2("DETECTING HEAD"), desc: t2("Reading head landmarks from your photo.") },
|
|
17193
|
+
{ title: t2("MAPPING CIRCUMFERENCE"), desc: t2("Estimating head width and depth.") },
|
|
17194
|
+
{ title: t2("MATCHING HAT SIZE"), desc: t2("Comparing your circumference to the size chart.") },
|
|
17195
|
+
{ title: t2("FINALIZING RESULT"), desc: t2("Almost done — preparing your recommendation.") }
|
|
17196
|
+
] : [
|
|
17197
|
+
{ title: t2("DETECTING FACE"), desc: t2("Identifying 478 face landmarks in your photo.") },
|
|
17198
|
+
{ title: t2("CALIBRATING SCALE"), desc: t2("Using iris size as the pixel-to-mm anchor.") },
|
|
17199
|
+
{ title: t2("MEASURING FRAME"), desc: t2("Mapping bridge, lens width and temple length.") },
|
|
17200
|
+
{ title: t2("MATCHING FRAME SIZE"), desc: t2("Comparing your measurements to the size chart.") },
|
|
17201
|
+
{ title: t2("FINALIZING RESULT"), desc: t2("Almost done — preparing your recommendation.") }
|
|
17202
|
+
];
|
|
17203
|
+
const [idx, setIdx] = reactExports.useState(0);
|
|
17204
|
+
reactExports.useEffect(() => {
|
|
17205
|
+
if (sizingDone) return;
|
|
17206
|
+
const id2 = setInterval(() => {
|
|
17207
|
+
setIdx((i) => Math.min(i + 1, stages.length - 1));
|
|
17208
|
+
}, 900);
|
|
17209
|
+
return () => clearInterval(id2);
|
|
17210
|
+
}, [sizingDone, stages.length]);
|
|
17211
|
+
const current = stages[idx] ?? stages[0];
|
|
17212
|
+
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: [
|
|
17213
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-title", children: current.title }),
|
|
17214
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-msc-stage-desc", children: current.desc })
|
|
17215
|
+
] }, idx) });
|
|
17216
|
+
}
|
|
17186
17217
|
function SkeletonOverlay({ landmarks, imgWidth, imgHeight }) {
|
|
17187
17218
|
const W2 = imgWidth;
|
|
17188
17219
|
const H2 = imgHeight;
|
|
@@ -18346,6 +18377,9 @@ function SizeResultView({
|
|
|
18346
18377
|
const isFaceCategory = measurementType === "face" || measurementType === "head";
|
|
18347
18378
|
const detectionDone = isFaceCategory ? !!faceLandmarks : !!bodyLandmarks;
|
|
18348
18379
|
const detectLabel = isFaceCategory ? measurementType === "head" ? t2("Detecting head") : t2("Detecting face") : t2("Detecting body pose");
|
|
18380
|
+
if (isFaceCategory) {
|
|
18381
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-sr-right-col", style: { display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(AccessoryStageCycler, { category: measurementType, sizingDone, t: t2 }) });
|
|
18382
|
+
}
|
|
18349
18383
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-tryon-sr-right-col ps-tryon-snap-steps", children: [
|
|
18350
18384
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `ps-tryon-snap-step${detectionDone ? " ps-done" : " ps-active"}`, children: [
|
|
18351
18385
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-snap-step-icon", children: detectionDone ? /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "ps-tryon-snap-check", children: "✓" }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ps-tryon-size-loading-spinner", style: { width: "1vw", height: "1vw", borderWidth: "1.5px" } }) }),
|
|
@@ -21914,6 +21948,7 @@ function AccessorySizeView({
|
|
|
21914
21948
|
const [photoPreview, setPhotoPreview] = reactExports.useState(null);
|
|
21915
21949
|
const [photoFile, setPhotoFile] = reactExports.useState(null);
|
|
21916
21950
|
const [photoBase64, setPhotoBase64] = reactExports.useState(null);
|
|
21951
|
+
const [ageConfirmed, setAgeConfirmed] = reactExports.useState(null);
|
|
21917
21952
|
const handlePhotoSelect = reactExports.useCallback(async (e) => {
|
|
21918
21953
|
const file = e.target.files?.[0];
|
|
21919
21954
|
if (!file) return;
|
|
@@ -21955,6 +21990,10 @@ function AccessorySizeView({
|
|
|
21955
21990
|
submitSizing("exact");
|
|
21956
21991
|
};
|
|
21957
21992
|
const handlePhotoSubmit = () => {
|
|
21993
|
+
if (ageConfirmed !== true) {
|
|
21994
|
+
setError(t2("Please confirm that the person in the photo is 18 or older before uploading."));
|
|
21995
|
+
return;
|
|
21996
|
+
}
|
|
21958
21997
|
if (!photoFile || !photoBase64) {
|
|
21959
21998
|
setError(t2("Please upload a photo"));
|
|
21960
21999
|
return;
|
|
@@ -22009,7 +22048,7 @@ function AccessorySizeView({
|
|
|
22009
22048
|
className: "ps-bpm-value-input",
|
|
22010
22049
|
value: values[f2.key],
|
|
22011
22050
|
onChange: (e) => updateValue(f2.key, e.target.value),
|
|
22012
|
-
placeholder: sizingUnit
|
|
22051
|
+
placeholder: f2.placeholder[sizingUnit] || f2.placeholder.cm || f2.placeholder.in || "",
|
|
22013
22052
|
step: f2.step ?? 0.5,
|
|
22014
22053
|
min: f2.min,
|
|
22015
22054
|
max: f2.max
|
|
@@ -22054,38 +22093,180 @@ function AccessorySizeView({
|
|
|
22054
22093
|
/* ── Photo step — identical to BodyProfileView photo step ── */
|
|
22055
22094
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-wrapper", style: { display: "flex", flexDirection: "column", width: "100%", height: "100%" }, children: [
|
|
22056
22095
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: "1.2vw", padding: "1.5vw", width: "100%", flex: 1, minHeight: "20vw", alignItems: "stretch" }, children: [
|
|
22057
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
22058
|
-
|
|
22059
|
-
|
|
22060
|
-
|
|
22061
|
-
|
|
22062
|
-
|
|
22063
|
-
|
|
22064
|
-
|
|
22065
|
-
|
|
22066
|
-
|
|
22067
|
-
|
|
22068
|
-
|
|
22069
|
-
|
|
22070
|
-
|
|
22071
|
-
|
|
22072
|
-
|
|
22073
|
-
|
|
22074
|
-
|
|
22075
|
-
|
|
22076
|
-
|
|
22077
|
-
|
|
22078
|
-
|
|
22079
|
-
|
|
22080
|
-
|
|
22081
|
-
|
|
22082
|
-
|
|
22083
|
-
|
|
22084
|
-
|
|
22085
|
-
|
|
22086
|
-
|
|
22087
|
-
|
|
22088
|
-
|
|
22096
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { flex: 1, position: "relative", display: "flex" }, children: [
|
|
22097
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22098
|
+
"div",
|
|
22099
|
+
{
|
|
22100
|
+
onClick: () => {
|
|
22101
|
+
if (!photoPreview && ageConfirmed === true) fileInputRef.current?.click();
|
|
22102
|
+
},
|
|
22103
|
+
style: {
|
|
22104
|
+
flex: 1,
|
|
22105
|
+
display: "flex",
|
|
22106
|
+
flexDirection: "column",
|
|
22107
|
+
alignItems: "center",
|
|
22108
|
+
justifyContent: "center",
|
|
22109
|
+
border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
|
|
22110
|
+
borderRadius: "0.5vw",
|
|
22111
|
+
cursor: photoPreview ? "default" : ageConfirmed === true ? "pointer" : "not-allowed",
|
|
22112
|
+
position: "relative",
|
|
22113
|
+
background: "var(--ps-bg-secondary)",
|
|
22114
|
+
transition: "border-color 0.2s",
|
|
22115
|
+
overflow: "hidden",
|
|
22116
|
+
filter: !photoPreview && ageConfirmed !== true ? "blur(6px) saturate(0.7)" : void 0,
|
|
22117
|
+
pointerEvents: !photoPreview && ageConfirmed !== true ? "none" : void 0
|
|
22118
|
+
},
|
|
22119
|
+
children: photoPreview ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
22120
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
|
|
22121
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: (e) => {
|
|
22122
|
+
e.stopPropagation();
|
|
22123
|
+
handleRemovePhoto();
|
|
22124
|
+
}, style: { position: "absolute", top: "0.5vw", right: "0.5vw", width: "1.4vw", height: "1.4vw", borderRadius: "50%", background: "rgba(0,0,0,0.6)", border: "none", color: "#fff", fontSize: "0.7vw", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center" }, children: "×" })
|
|
22125
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
22126
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(UploadIcon, { size: 32 }),
|
|
22127
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.85vw", fontWeight: 600, color: "var(--ps-text-primary)", marginTop: "0.5vw" }, children: isCloseUp ? t2("Upload close-up photo") : t2("Upload your photo") }),
|
|
22128
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.6vw", color: "var(--ps-text-muted)", marginTop: "0.2vw" }, children: isCloseUp ? t2("Click or drag a close-up face photo") : t2("Click or drag a full-body photo") }),
|
|
22129
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
|
|
22130
|
+
] })
|
|
22131
|
+
}
|
|
22132
|
+
),
|
|
22133
|
+
!photoPreview && ageConfirmed === null && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22134
|
+
"div",
|
|
22135
|
+
{
|
|
22136
|
+
role: "dialog",
|
|
22137
|
+
style: {
|
|
22138
|
+
position: "absolute",
|
|
22139
|
+
inset: 0,
|
|
22140
|
+
display: "flex",
|
|
22141
|
+
alignItems: "center",
|
|
22142
|
+
justifyContent: "center",
|
|
22143
|
+
padding: "1vw",
|
|
22144
|
+
background: "rgba(255,255,255,0.55)",
|
|
22145
|
+
backdropFilter: "blur(8px)",
|
|
22146
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
22147
|
+
borderRadius: "0.5vw",
|
|
22148
|
+
zIndex: 2
|
|
22149
|
+
},
|
|
22150
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: {
|
|
22151
|
+
width: "100%",
|
|
22152
|
+
maxWidth: "min(420px, 22vw)",
|
|
22153
|
+
padding: "1.4vw 1.6vw",
|
|
22154
|
+
background: "#FFFFFF",
|
|
22155
|
+
border: "1px solid var(--ps-border-subtle)",
|
|
22156
|
+
borderRadius: "0.9vw",
|
|
22157
|
+
boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25), 0 8px 16px -8px rgba(17,24,39,0.15)",
|
|
22158
|
+
display: "flex",
|
|
22159
|
+
flexDirection: "column",
|
|
22160
|
+
alignItems: "center",
|
|
22161
|
+
textAlign: "center",
|
|
22162
|
+
gap: "0.75vw"
|
|
22163
|
+
}, children: [
|
|
22164
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--ps-accent)" }, children: t2("AGE VERIFICATION") }),
|
|
22165
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.95vw", fontWeight: 600, lineHeight: 1.35, color: "var(--ps-text-primary)" }, children: t2("Is the person in this photo 18 years or older?") }),
|
|
22166
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: "0.65vw", width: "100%", marginTop: "0.4vw" }, children: [
|
|
22167
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22168
|
+
"button",
|
|
22169
|
+
{
|
|
22170
|
+
type: "button",
|
|
22171
|
+
onClick: () => {
|
|
22172
|
+
setAgeConfirmed(true);
|
|
22173
|
+
setError("");
|
|
22174
|
+
},
|
|
22175
|
+
style: {
|
|
22176
|
+
flex: 1,
|
|
22177
|
+
padding: "0.75vw 1vw",
|
|
22178
|
+
borderRadius: "999px",
|
|
22179
|
+
background: "var(--ps-accent)",
|
|
22180
|
+
color: "#FFFFFF",
|
|
22181
|
+
border: "1.5px solid var(--ps-accent)",
|
|
22182
|
+
fontFamily: "inherit",
|
|
22183
|
+
fontSize: "0.78vw",
|
|
22184
|
+
fontWeight: 700,
|
|
22185
|
+
cursor: "pointer"
|
|
22186
|
+
},
|
|
22187
|
+
children: t2("Yes")
|
|
22188
|
+
}
|
|
22189
|
+
),
|
|
22190
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22191
|
+
"button",
|
|
22192
|
+
{
|
|
22193
|
+
type: "button",
|
|
22194
|
+
onClick: () => setAgeConfirmed(false),
|
|
22195
|
+
style: {
|
|
22196
|
+
flex: 1,
|
|
22197
|
+
padding: "0.75vw 1vw",
|
|
22198
|
+
borderRadius: "999px",
|
|
22199
|
+
background: "transparent",
|
|
22200
|
+
color: "var(--ps-text-primary)",
|
|
22201
|
+
border: "1.5px solid var(--ps-border-color)",
|
|
22202
|
+
fontFamily: "inherit",
|
|
22203
|
+
fontSize: "0.78vw",
|
|
22204
|
+
fontWeight: 700,
|
|
22205
|
+
cursor: "pointer"
|
|
22206
|
+
},
|
|
22207
|
+
children: t2("No")
|
|
22208
|
+
}
|
|
22209
|
+
)
|
|
22210
|
+
] })
|
|
22211
|
+
] })
|
|
22212
|
+
}
|
|
22213
|
+
),
|
|
22214
|
+
!photoPreview && ageConfirmed === false && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22215
|
+
"div",
|
|
22216
|
+
{
|
|
22217
|
+
role: "alert",
|
|
22218
|
+
style: {
|
|
22219
|
+
position: "absolute",
|
|
22220
|
+
inset: 0,
|
|
22221
|
+
display: "flex",
|
|
22222
|
+
alignItems: "center",
|
|
22223
|
+
justifyContent: "center",
|
|
22224
|
+
padding: "1vw",
|
|
22225
|
+
background: "rgba(255,255,255,0.55)",
|
|
22226
|
+
backdropFilter: "blur(8px)",
|
|
22227
|
+
WebkitBackdropFilter: "blur(8px)",
|
|
22228
|
+
borderRadius: "0.5vw",
|
|
22229
|
+
zIndex: 2
|
|
22230
|
+
},
|
|
22231
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: {
|
|
22232
|
+
width: "100%",
|
|
22233
|
+
maxWidth: "min(420px, 22vw)",
|
|
22234
|
+
padding: "1.4vw 1.6vw",
|
|
22235
|
+
background: "#FFFFFF",
|
|
22236
|
+
border: "1px solid rgba(192,38,38,0.35)",
|
|
22237
|
+
borderRadius: "0.9vw",
|
|
22238
|
+
boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25)",
|
|
22239
|
+
display: "flex",
|
|
22240
|
+
flexDirection: "column",
|
|
22241
|
+
alignItems: "center",
|
|
22242
|
+
textAlign: "center",
|
|
22243
|
+
gap: "0.75vw"
|
|
22244
|
+
}, children: [
|
|
22245
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "#C02626" }, children: t2("UPLOAD NOT ALLOWED") }),
|
|
22246
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.95vw", fontWeight: 600, lineHeight: 1.35, color: "var(--ps-text-primary)" }, children: t2("For your safety, we cannot process photos of people under 18.") }),
|
|
22247
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
22248
|
+
"button",
|
|
22249
|
+
{
|
|
22250
|
+
type: "button",
|
|
22251
|
+
onClick: () => setAgeConfirmed(null),
|
|
22252
|
+
style: {
|
|
22253
|
+
padding: "0.75vw 1.4vw",
|
|
22254
|
+
borderRadius: "999px",
|
|
22255
|
+
background: "transparent",
|
|
22256
|
+
color: "var(--ps-text-primary)",
|
|
22257
|
+
border: "1.5px solid var(--ps-border-color)",
|
|
22258
|
+
fontFamily: "inherit",
|
|
22259
|
+
fontSize: "0.78vw",
|
|
22260
|
+
fontWeight: 700,
|
|
22261
|
+
cursor: "pointer"
|
|
22262
|
+
},
|
|
22263
|
+
children: t2("Go back")
|
|
22264
|
+
}
|
|
22265
|
+
)
|
|
22266
|
+
] })
|
|
22267
|
+
}
|
|
22268
|
+
)
|
|
22269
|
+
] }),
|
|
22089
22270
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", gap: "0.6vw", justifyContent: "center" }, children: [
|
|
22090
22271
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.85vw", fontWeight: 700, color: "var(--ps-text-primary)", marginBottom: "0.3vw" }, children: t2("How to take the best photo") }),
|
|
22091
22272
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { background: "#ddfbe7", borderRadius: "0.5vw", padding: "0.6vw 0.8vw" }, children: [
|
|
@@ -22190,7 +22371,7 @@ function AccessorySizeView({
|
|
|
22190
22371
|
className: "ps-bp-inline-input",
|
|
22191
22372
|
value: values[f2.key],
|
|
22192
22373
|
onChange: (e) => updateValue(f2.key, e.target.value),
|
|
22193
|
-
placeholder: sizingUnit
|
|
22374
|
+
placeholder: f2.placeholder[sizingUnit] || f2.placeholder.cm || f2.placeholder.in || "",
|
|
22194
22375
|
step: f2.step ?? 0.5,
|
|
22195
22376
|
min: f2.min,
|
|
22196
22377
|
max: f2.max
|
|
@@ -22295,7 +22476,8 @@ const FALLBACK_FACE_FIELDS = [
|
|
|
22295
22476
|
{
|
|
22296
22477
|
key: "lensWidth",
|
|
22297
22478
|
label: "Lens Width",
|
|
22298
|
-
placeholder
|
|
22479
|
+
// `cm` key = mm placeholder (default eyewear unit); `in` key = cm placeholder.
|
|
22480
|
+
placeholder: { mm: "e.g. 52", cm: "e.g. 5.2" },
|
|
22299
22481
|
hint: "Width of one lens",
|
|
22300
22482
|
min: 0,
|
|
22301
22483
|
step: 1
|
|
@@ -22303,7 +22485,7 @@ const FALLBACK_FACE_FIELDS = [
|
|
|
22303
22485
|
{
|
|
22304
22486
|
key: "bridgeWidth",
|
|
22305
22487
|
label: "Bridge",
|
|
22306
|
-
placeholder: {
|
|
22488
|
+
placeholder: { mm: "e.g. 18", cm: "e.g. 1.8" },
|
|
22307
22489
|
hint: "Distance over the nose between lenses",
|
|
22308
22490
|
min: 0,
|
|
22309
22491
|
step: 1
|
|
@@ -22311,15 +22493,15 @@ const FALLBACK_FACE_FIELDS = [
|
|
|
22311
22493
|
{
|
|
22312
22494
|
key: "templeLength",
|
|
22313
22495
|
label: "Arm Length",
|
|
22314
|
-
placeholder: {
|
|
22496
|
+
placeholder: { mm: "e.g. 140", cm: "e.g. 14" },
|
|
22315
22497
|
hint: "Length of the arm from hinge to tip",
|
|
22316
22498
|
min: 0,
|
|
22317
22499
|
step: 1
|
|
22318
22500
|
}
|
|
22319
22501
|
];
|
|
22320
22502
|
const EYEWEAR_UNIT_OPTIONS = [
|
|
22321
|
-
{ label: "
|
|
22322
|
-
{ label: "
|
|
22503
|
+
{ label: "Millimetre", value: "mm" },
|
|
22504
|
+
{ label: "Centimetre", value: "cm" }
|
|
22323
22505
|
];
|
|
22324
22506
|
function buildFieldsFromSizeGuide(sizeGuide) {
|
|
22325
22507
|
const req = sizeGuide?.requiredFields;
|
|
@@ -22887,6 +23069,7 @@ function PrimeStyleTryonInner({
|
|
|
22887
23069
|
category: measurementType,
|
|
22888
23070
|
...Object.keys(faceMm).length > 0 && { faceMeasurementsMm: faceMm, irisConfidence: 1 }
|
|
22889
23071
|
};
|
|
23072
|
+
const minVisible = new Promise((r2) => setTimeout(r2, 3200));
|
|
22890
23073
|
try {
|
|
22891
23074
|
const resp = await fetch(`${baseUrl}/api/v1/sizing/face-recommend`, {
|
|
22892
23075
|
method: "POST",
|
|
@@ -22895,17 +23078,20 @@ function PrimeStyleTryonInner({
|
|
|
22895
23078
|
});
|
|
22896
23079
|
if (resp.ok) {
|
|
22897
23080
|
const data = await resp.json();
|
|
23081
|
+
await minVisible;
|
|
22898
23082
|
setSizingResult(data);
|
|
22899
23083
|
onComplete?.(data);
|
|
22900
23084
|
} else {
|
|
22901
23085
|
const body = await resp.text().catch(() => "");
|
|
22902
23086
|
console.error("[PS-SDK] face-recommend failed:", resp.status, body);
|
|
23087
|
+
await minVisible;
|
|
22903
23088
|
setErrorMessage(t2("Unable to get size recommendation. Please try again."));
|
|
22904
23089
|
setView("error");
|
|
22905
23090
|
setEstimationDone(true);
|
|
22906
23091
|
}
|
|
22907
23092
|
} catch (err) {
|
|
22908
23093
|
console.error("[PS-SDK] face-recommend network error:", err);
|
|
23094
|
+
await minVisible;
|
|
22909
23095
|
setErrorMessage(t2("Unable to connect to sizing service. Please try again."));
|
|
22910
23096
|
setView("error");
|
|
22911
23097
|
setEstimationDone(true);
|
|
@@ -23100,6 +23286,7 @@ function PrimeStyleTryonInner({
|
|
|
23100
23286
|
const measurementType = detectMeasurementType(productTitle);
|
|
23101
23287
|
if (measurementType === "face" || measurementType === "head") {
|
|
23102
23288
|
setFaceLandmarks(null);
|
|
23289
|
+
const minVisible = new Promise((r2) => setTimeout(r2, 3200));
|
|
23103
23290
|
try {
|
|
23104
23291
|
const faceResult = await detectFaceMeasurements(objUrl);
|
|
23105
23292
|
if (faceResult) setFaceLandmarks(faceResult.landmarks);
|
|
@@ -23122,6 +23309,7 @@ function PrimeStyleTryonInner({
|
|
|
23122
23309
|
});
|
|
23123
23310
|
if (recRes.ok) {
|
|
23124
23311
|
const recData = await recRes.json();
|
|
23312
|
+
await minVisible;
|
|
23125
23313
|
setSizingResult(recData);
|
|
23126
23314
|
onComplete?.(recData);
|
|
23127
23315
|
persistResultToProfile(
|
|
@@ -23137,10 +23325,12 @@ function PrimeStyleTryonInner({
|
|
|
23137
23325
|
recData
|
|
23138
23326
|
);
|
|
23139
23327
|
} else {
|
|
23328
|
+
await minVisible;
|
|
23140
23329
|
setEstimationDone(true);
|
|
23141
23330
|
}
|
|
23142
23331
|
} catch (err) {
|
|
23143
23332
|
console.error("[ps-sdk] face-recommend failed:", err);
|
|
23333
|
+
await minVisible;
|
|
23144
23334
|
setEstimationDone(true);
|
|
23145
23335
|
}
|
|
23146
23336
|
setSizingLoading(false);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primestyleai/tryon",
|
|
3
|
-
"version": "5.8.
|
|
3
|
+
"version": "5.8.48",
|
|
4
4
|
"description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/primestyle-tryon.js",
|
|
@@ -60,5 +60,8 @@
|
|
|
60
60
|
"terser": "^5.31.0",
|
|
61
61
|
"typescript": "^5.5.0",
|
|
62
62
|
"vite": "^5.4.0"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"@primestyleai/tryon": "^5.8.46"
|
|
63
66
|
}
|
|
64
67
|
}
|