@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.
@@ -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 === "in" ? f.placeholder.in : f.placeholder.cm,
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__ */ jsx(
12634
- "div",
12635
- {
12636
- onClick: () => !photoPreview && fileInputRef.current?.click(),
12637
- style: {
12638
- flex: 1,
12639
- display: "flex",
12640
- flexDirection: "column",
12641
- alignItems: "center",
12642
- justifyContent: "center",
12643
- border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
12644
- borderRadius: "0.5vw",
12645
- cursor: photoPreview ? "default" : "pointer",
12646
- position: "relative",
12647
- background: "var(--ps-bg-secondary)",
12648
- transition: "border-color 0.2s",
12649
- overflow: "hidden"
12650
- },
12651
- children: photoPreview ? /* @__PURE__ */ jsxs(Fragment, { children: [
12652
- /* @__PURE__ */ jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
12653
- /* @__PURE__ */ jsx("button", { onClick: (e) => {
12654
- e.stopPropagation();
12655
- handleRemovePhoto();
12656
- }, 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: "×" })
12657
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
12658
- /* @__PURE__ */ jsx(UploadIcon, { size: 32 }),
12659
- /* @__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") }),
12660
- /* @__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") }),
12661
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
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 === "in" ? f.placeholder.in : f.placeholder.cm,
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: { cm: "e.g. 52", in: "e.g. 2.0" },
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: { cm: "e.g. 18", in: "e.g. 0.7" },
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: { cm: "e.g. 140", in: "e.g. 5.5" },
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: "Millimeters", value: "mm" },
12898
- { label: "Inches", value: "in" }
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 === "in" ? f2.placeholder.in : f2.placeholder.cm,
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.jsx(
22058
- "div",
22059
- {
22060
- onClick: () => !photoPreview && fileInputRef.current?.click(),
22061
- style: {
22062
- flex: 1,
22063
- display: "flex",
22064
- flexDirection: "column",
22065
- alignItems: "center",
22066
- justifyContent: "center",
22067
- border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
22068
- borderRadius: "0.5vw",
22069
- cursor: photoPreview ? "default" : "pointer",
22070
- position: "relative",
22071
- background: "var(--ps-bg-secondary)",
22072
- transition: "border-color 0.2s",
22073
- overflow: "hidden"
22074
- },
22075
- children: photoPreview ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22076
- /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
22077
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: (e) => {
22078
- e.stopPropagation();
22079
- handleRemovePhoto();
22080
- }, 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: "×" })
22081
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22082
- /* @__PURE__ */ jsxRuntimeExports.jsx(UploadIcon, { size: 32 }),
22083
- /* @__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") }),
22084
- /* @__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") }),
22085
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
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 === "in" ? f2.placeholder.in : f2.placeholder.cm,
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: { cm: "e.g. 52", in: "e.g. 2.0" },
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: { cm: "e.g. 18", in: "e.g. 0.7" },
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: { cm: "e.g. 140", in: "e.g. 5.5" },
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: "Millimeters", value: "mm" },
22322
- { label: "Inches", value: "in" }
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.46",
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
  }