@primestyleai/tryon 5.8.45 → 5.8.47

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.
@@ -238,7 +238,8 @@ function computeMeasurements(lm, imageWidth, imageHeight) {
238
238
  return pxDist * pxToMm;
239
239
  };
240
240
  const pd = mmBetween(lm.leftIrisCenter, lm.rightIrisCenter);
241
- const bridgeWidth = mmBetween(lm.leftInnerEye, lm.rightInnerEye);
241
+ const innerEyeGap = mmBetween(lm.leftInnerEye, lm.rightInnerEye);
242
+ const bridgeWidth = innerEyeGap / 1.6;
242
243
  const faceWidth = mmBetween(lm.leftTragus, lm.rightTragus);
243
244
  const templeLengthLeft = mmBetween(lm.leftTragus, lm.leftOuterEye);
244
245
  const templeLengthRight = mmBetween(lm.rightTragus, lm.rightOuterEye);
@@ -7950,10 +7951,12 @@ const fitLabelFn = (fit, t) => fit === "good" ? t("perfect fit") : fit === "too-
7950
7951
  const lengthFitLabelFn = (fit, t) => fit === "good" ? t("perfect fit") : fit === "too-tight" ? t("too short") : fit === "tight" ? t("short") : fit === "a-bit-tight" ? t("a bit short") : fit === "too-loose" ? t("too long") : fit === "loose" ? t("long") : t("a bit long");
7951
7952
  function convertNum(n, from, to) {
7952
7953
  if (from === to || !n || isNaN(n)) return n;
7954
+ if (from === "mm" || to === "mm") return n;
7953
7955
  return from === "cm" ? Math.round(n / 2.54 * 10) / 10 : Math.round(n * 2.54 * 10) / 10;
7954
7956
  }
7955
7957
  function convertLabel(label, from, to) {
7956
7958
  if (from === to) return label;
7959
+ if (from === "mm" || to === "mm") return label;
7957
7960
  const conv = (n) => convertNum(n, from, to);
7958
7961
  const rangeMatch = label.match(/^(\d+\.?\d*)\s*[-–]\s*(\d+\.?\d*)$/);
7959
7962
  if (rangeMatch) return `${conv(parseFloat(rangeMatch[1]))}-${conv(parseFloat(rangeMatch[2]))}`;
@@ -7994,7 +7997,8 @@ function SectionDetailView({
7994
7997
  }) {
7995
7998
  const recSize = sectionResult?.recommendedSize || "";
7996
7999
  const [selectedSize, setSelectedSize] = useState(null);
7997
- const displayUnitId = unitLbl.toLowerCase().includes("cm") ? "cm" : "in";
8000
+ const unitLblLower = unitLbl.toLowerCase();
8001
+ const displayUnitId = unitLblLower.includes("mm") ? "mm" : unitLblLower.includes("cm") ? "cm" : "in";
7998
8002
  const fromUnit = chartUnit || displayUnitId;
7999
8003
  const dNum = (n) => convertNum(n, fromUnit, displayUnitId);
8000
8004
  const dLabel = (s) => convertLabel(s, fromUnit, displayUnitId);
@@ -8632,8 +8636,9 @@ function SizeResultView({
8632
8636
  userHeightCm,
8633
8637
  t
8634
8638
  }) {
8635
- const resultUnit = sizingResult?.unit || sizingUnit;
8636
- const unitLbl = sizingUnit === "cm" ? t("cm") : t("in");
8639
+ const resultUnitRaw = (sizingResult?.unit || sizingUnit || "").toString().toLowerCase();
8640
+ const resultUnit = resultUnitRaw === "mm" ? "mm" : resultUnitRaw === "cm" ? "cm" : "in";
8641
+ const unitLbl = resultUnit === "mm" ? t("mm") : sizingUnit === "cm" ? t("cm") : t("in");
8637
8642
  const [editVals, setEditVals] = useState({});
8638
8643
  const pRange = pRangeFn;
8639
8644
  const cellVal = useCallback((row, colIdx, header) => {
@@ -12485,6 +12490,7 @@ function AccessorySizeView({
12485
12490
  const [photoPreview, setPhotoPreview] = useState(null);
12486
12491
  const [photoFile, setPhotoFile] = useState(null);
12487
12492
  const [photoBase64, setPhotoBase64] = useState(null);
12493
+ const [ageConfirmed, setAgeConfirmed] = useState(null);
12488
12494
  const handlePhotoSelect = useCallback(async (e) => {
12489
12495
  const file = e.target.files?.[0];
12490
12496
  if (!file) return;
@@ -12526,6 +12532,10 @@ function AccessorySizeView({
12526
12532
  submitSizing("exact");
12527
12533
  };
12528
12534
  const handlePhotoSubmit = () => {
12535
+ if (ageConfirmed !== true) {
12536
+ setError(t("Please confirm that the person in the photo is 18 or older before uploading."));
12537
+ return;
12538
+ }
12529
12539
  if (!photoFile || !photoBase64) {
12530
12540
  setError(t("Please upload a photo"));
12531
12541
  return;
@@ -12580,7 +12590,7 @@ function AccessorySizeView({
12580
12590
  className: "ps-bpm-value-input",
12581
12591
  value: values[f.key],
12582
12592
  onChange: (e) => updateValue(f.key, e.target.value),
12583
- placeholder: sizingUnit === "in" ? f.placeholder.in : f.placeholder.cm,
12593
+ placeholder: f.placeholder[sizingUnit] || f.placeholder.cm || f.placeholder.in || "",
12584
12594
  step: f.step ?? 0.5,
12585
12595
  min: f.min,
12586
12596
  max: f.max
@@ -12625,38 +12635,180 @@ function AccessorySizeView({
12625
12635
  /* ── Photo step — identical to BodyProfileView photo step ── */
12626
12636
  /* @__PURE__ */ jsxs("div", { className: "ps-bp-wrapper", style: { display: "flex", flexDirection: "column", width: "100%", height: "100%" }, children: [
12627
12637
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "1.2vw", padding: "1.5vw", width: "100%", flex: 1, minHeight: "20vw", alignItems: "stretch" }, children: [
12628
- /* @__PURE__ */ jsx(
12629
- "div",
12630
- {
12631
- onClick: () => !photoPreview && fileInputRef.current?.click(),
12632
- style: {
12633
- flex: 1,
12634
- display: "flex",
12635
- flexDirection: "column",
12636
- alignItems: "center",
12637
- justifyContent: "center",
12638
- border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
12639
- borderRadius: "0.5vw",
12640
- cursor: photoPreview ? "default" : "pointer",
12641
- position: "relative",
12642
- background: "var(--ps-bg-secondary)",
12643
- transition: "border-color 0.2s",
12644
- overflow: "hidden"
12645
- },
12646
- children: photoPreview ? /* @__PURE__ */ jsxs(Fragment, { children: [
12647
- /* @__PURE__ */ jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
12648
- /* @__PURE__ */ jsx("button", { onClick: (e) => {
12649
- e.stopPropagation();
12650
- handleRemovePhoto();
12651
- }, 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: "×" })
12652
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
12653
- /* @__PURE__ */ jsx(UploadIcon, { size: 32 }),
12654
- /* @__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") }),
12655
- /* @__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") }),
12656
- /* @__PURE__ */ jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
12657
- ] })
12658
- }
12659
- ),
12638
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, position: "relative", display: "flex" }, children: [
12639
+ /* @__PURE__ */ jsx(
12640
+ "div",
12641
+ {
12642
+ onClick: () => {
12643
+ if (!photoPreview && ageConfirmed === true) fileInputRef.current?.click();
12644
+ },
12645
+ style: {
12646
+ flex: 1,
12647
+ display: "flex",
12648
+ flexDirection: "column",
12649
+ alignItems: "center",
12650
+ justifyContent: "center",
12651
+ border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
12652
+ borderRadius: "0.5vw",
12653
+ cursor: photoPreview ? "default" : ageConfirmed === true ? "pointer" : "not-allowed",
12654
+ position: "relative",
12655
+ background: "var(--ps-bg-secondary)",
12656
+ transition: "border-color 0.2s",
12657
+ overflow: "hidden",
12658
+ filter: !photoPreview && ageConfirmed !== true ? "blur(6px) saturate(0.7)" : void 0,
12659
+ pointerEvents: !photoPreview && ageConfirmed !== true ? "none" : void 0
12660
+ },
12661
+ children: photoPreview ? /* @__PURE__ */ jsxs(Fragment, { children: [
12662
+ /* @__PURE__ */ jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
12663
+ /* @__PURE__ */ jsx("button", { onClick: (e) => {
12664
+ e.stopPropagation();
12665
+ handleRemovePhoto();
12666
+ }, 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: "×" })
12667
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
12668
+ /* @__PURE__ */ jsx(UploadIcon, { size: 32 }),
12669
+ /* @__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") }),
12670
+ /* @__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") }),
12671
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
12672
+ ] })
12673
+ }
12674
+ ),
12675
+ !photoPreview && ageConfirmed === null && /* @__PURE__ */ jsx(
12676
+ "div",
12677
+ {
12678
+ role: "dialog",
12679
+ style: {
12680
+ position: "absolute",
12681
+ inset: 0,
12682
+ display: "flex",
12683
+ alignItems: "center",
12684
+ justifyContent: "center",
12685
+ padding: "1vw",
12686
+ background: "rgba(255,255,255,0.55)",
12687
+ backdropFilter: "blur(8px)",
12688
+ WebkitBackdropFilter: "blur(8px)",
12689
+ borderRadius: "0.5vw",
12690
+ zIndex: 2
12691
+ },
12692
+ children: /* @__PURE__ */ jsxs("div", { style: {
12693
+ width: "100%",
12694
+ maxWidth: "min(420px, 22vw)",
12695
+ padding: "1.4vw 1.6vw",
12696
+ background: "#FFFFFF",
12697
+ border: "1px solid var(--ps-border-subtle)",
12698
+ borderRadius: "0.9vw",
12699
+ boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25), 0 8px 16px -8px rgba(17,24,39,0.15)",
12700
+ display: "flex",
12701
+ flexDirection: "column",
12702
+ alignItems: "center",
12703
+ textAlign: "center",
12704
+ gap: "0.75vw"
12705
+ }, children: [
12706
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--ps-accent)" }, children: t("AGE VERIFICATION") }),
12707
+ /* @__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?") }),
12708
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.65vw", width: "100%", marginTop: "0.4vw" }, children: [
12709
+ /* @__PURE__ */ jsx(
12710
+ "button",
12711
+ {
12712
+ type: "button",
12713
+ onClick: () => {
12714
+ setAgeConfirmed(true);
12715
+ setError("");
12716
+ },
12717
+ style: {
12718
+ flex: 1,
12719
+ padding: "0.75vw 1vw",
12720
+ borderRadius: "999px",
12721
+ background: "var(--ps-accent)",
12722
+ color: "#FFFFFF",
12723
+ border: "1.5px solid var(--ps-accent)",
12724
+ fontFamily: "inherit",
12725
+ fontSize: "0.78vw",
12726
+ fontWeight: 700,
12727
+ cursor: "pointer"
12728
+ },
12729
+ children: t("Yes")
12730
+ }
12731
+ ),
12732
+ /* @__PURE__ */ jsx(
12733
+ "button",
12734
+ {
12735
+ type: "button",
12736
+ onClick: () => setAgeConfirmed(false),
12737
+ style: {
12738
+ flex: 1,
12739
+ padding: "0.75vw 1vw",
12740
+ borderRadius: "999px",
12741
+ background: "transparent",
12742
+ color: "var(--ps-text-primary)",
12743
+ border: "1.5px solid var(--ps-border-color)",
12744
+ fontFamily: "inherit",
12745
+ fontSize: "0.78vw",
12746
+ fontWeight: 700,
12747
+ cursor: "pointer"
12748
+ },
12749
+ children: t("No")
12750
+ }
12751
+ )
12752
+ ] })
12753
+ ] })
12754
+ }
12755
+ ),
12756
+ !photoPreview && ageConfirmed === false && /* @__PURE__ */ jsx(
12757
+ "div",
12758
+ {
12759
+ role: "alert",
12760
+ style: {
12761
+ position: "absolute",
12762
+ inset: 0,
12763
+ display: "flex",
12764
+ alignItems: "center",
12765
+ justifyContent: "center",
12766
+ padding: "1vw",
12767
+ background: "rgba(255,255,255,0.55)",
12768
+ backdropFilter: "blur(8px)",
12769
+ WebkitBackdropFilter: "blur(8px)",
12770
+ borderRadius: "0.5vw",
12771
+ zIndex: 2
12772
+ },
12773
+ children: /* @__PURE__ */ jsxs("div", { style: {
12774
+ width: "100%",
12775
+ maxWidth: "min(420px, 22vw)",
12776
+ padding: "1.4vw 1.6vw",
12777
+ background: "#FFFFFF",
12778
+ border: "1px solid rgba(192,38,38,0.35)",
12779
+ borderRadius: "0.9vw",
12780
+ boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25)",
12781
+ display: "flex",
12782
+ flexDirection: "column",
12783
+ alignItems: "center",
12784
+ textAlign: "center",
12785
+ gap: "0.75vw"
12786
+ }, children: [
12787
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "#C02626" }, children: t("UPLOAD NOT ALLOWED") }),
12788
+ /* @__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.") }),
12789
+ /* @__PURE__ */ jsx(
12790
+ "button",
12791
+ {
12792
+ type: "button",
12793
+ onClick: () => setAgeConfirmed(null),
12794
+ style: {
12795
+ padding: "0.75vw 1.4vw",
12796
+ borderRadius: "999px",
12797
+ background: "transparent",
12798
+ color: "var(--ps-text-primary)",
12799
+ border: "1.5px solid var(--ps-border-color)",
12800
+ fontFamily: "inherit",
12801
+ fontSize: "0.78vw",
12802
+ fontWeight: 700,
12803
+ cursor: "pointer"
12804
+ },
12805
+ children: t("Go back")
12806
+ }
12807
+ )
12808
+ ] })
12809
+ }
12810
+ )
12811
+ ] }),
12660
12812
  /* @__PURE__ */ jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", gap: "0.6vw", justifyContent: "center" }, children: [
12661
12813
  /* @__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") }),
12662
12814
  /* @__PURE__ */ jsxs("div", { style: { background: "#ddfbe7", borderRadius: "0.5vw", padding: "0.6vw 0.8vw" }, children: [
@@ -12761,7 +12913,7 @@ function AccessorySizeView({
12761
12913
  className: "ps-bp-inline-input",
12762
12914
  value: values[f.key],
12763
12915
  onChange: (e) => updateValue(f.key, e.target.value),
12764
- placeholder: sizingUnit === "in" ? f.placeholder.in : f.placeholder.cm,
12916
+ placeholder: f.placeholder[sizingUnit] || f.placeholder.cm || f.placeholder.in || "",
12765
12917
  step: f.step ?? 0.5,
12766
12918
  min: f.min,
12767
12919
  max: f.max
@@ -12866,7 +13018,8 @@ const FALLBACK_FACE_FIELDS = [
12866
13018
  {
12867
13019
  key: "lensWidth",
12868
13020
  label: "Lens Width",
12869
- placeholder: { cm: "e.g. 52", in: "e.g. 2.0" },
13021
+ // `cm` key = mm placeholder (default eyewear unit); `in` key = cm placeholder.
13022
+ placeholder: { mm: "e.g. 52", cm: "e.g. 5.2" },
12870
13023
  hint: "Width of one lens",
12871
13024
  min: 0,
12872
13025
  step: 1
@@ -12874,7 +13027,7 @@ const FALLBACK_FACE_FIELDS = [
12874
13027
  {
12875
13028
  key: "bridgeWidth",
12876
13029
  label: "Bridge",
12877
- placeholder: { cm: "e.g. 18", in: "e.g. 0.7" },
13030
+ placeholder: { mm: "e.g. 18", cm: "e.g. 1.8" },
12878
13031
  hint: "Distance over the nose between lenses",
12879
13032
  min: 0,
12880
13033
  step: 1
@@ -12882,15 +13035,15 @@ const FALLBACK_FACE_FIELDS = [
12882
13035
  {
12883
13036
  key: "templeLength",
12884
13037
  label: "Arm Length",
12885
- placeholder: { cm: "e.g. 140", in: "e.g. 5.5" },
13038
+ placeholder: { mm: "e.g. 140", cm: "e.g. 14" },
12886
13039
  hint: "Length of the arm from hinge to tip",
12887
13040
  min: 0,
12888
13041
  step: 1
12889
13042
  }
12890
13043
  ];
12891
13044
  const EYEWEAR_UNIT_OPTIONS = [
12892
- { label: "Millimeters", value: "mm" },
12893
- { label: "Inches", value: "in" }
13045
+ { label: "Millimetre", value: "mm" },
13046
+ { label: "Centimetre", value: "cm" }
12894
13047
  ];
12895
13048
  function buildFieldsFromSizeGuide(sizeGuide) {
12896
13049
  const req = sizeGuide?.requiredFields;
@@ -9700,7 +9700,8 @@ function computeMeasurements(lm, imageWidth, imageHeight) {
9700
9700
  return pxDist * pxToMm;
9701
9701
  };
9702
9702
  const pd2 = mmBetween(lm.leftIrisCenter, lm.rightIrisCenter);
9703
- const bridgeWidth = mmBetween(lm.leftInnerEye, lm.rightInnerEye);
9703
+ const innerEyeGap = mmBetween(lm.leftInnerEye, lm.rightInnerEye);
9704
+ const bridgeWidth = innerEyeGap / 1.6;
9704
9705
  const faceWidth = mmBetween(lm.leftTragus, lm.rightTragus);
9705
9706
  const templeLengthLeft = mmBetween(lm.leftTragus, lm.leftOuterEye);
9706
9707
  const templeLengthRight = mmBetween(lm.rightTragus, lm.rightOuterEye);
@@ -17374,10 +17375,12 @@ const fitLabelFn = (fit, t2) => fit === "good" ? t2("perfect fit") : fit === "to
17374
17375
  const lengthFitLabelFn = (fit, t2) => fit === "good" ? t2("perfect fit") : fit === "too-tight" ? t2("too short") : fit === "tight" ? t2("short") : fit === "a-bit-tight" ? t2("a bit short") : fit === "too-loose" ? t2("too long") : fit === "loose" ? t2("long") : t2("a bit long");
17375
17376
  function convertNum(n2, from, to) {
17376
17377
  if (from === to || !n2 || isNaN(n2)) return n2;
17378
+ if (from === "mm" || to === "mm") return n2;
17377
17379
  return from === "cm" ? Math.round(n2 / 2.54 * 10) / 10 : Math.round(n2 * 2.54 * 10) / 10;
17378
17380
  }
17379
17381
  function convertLabel(label, from, to) {
17380
17382
  if (from === to) return label;
17383
+ if (from === "mm" || to === "mm") return label;
17381
17384
  const conv = (n2) => convertNum(n2, from, to);
17382
17385
  const rangeMatch = label.match(/^(\d+\.?\d*)\s*[-–]\s*(\d+\.?\d*)$/);
17383
17386
  if (rangeMatch) return `${conv(parseFloat(rangeMatch[1]))}-${conv(parseFloat(rangeMatch[2]))}`;
@@ -17418,7 +17421,8 @@ function SectionDetailView({
17418
17421
  }) {
17419
17422
  const recSize = sectionResult?.recommendedSize || "";
17420
17423
  const [selectedSize, setSelectedSize] = reactExports.useState(null);
17421
- const displayUnitId = unitLbl.toLowerCase().includes("cm") ? "cm" : "in";
17424
+ const unitLblLower = unitLbl.toLowerCase();
17425
+ const displayUnitId = unitLblLower.includes("mm") ? "mm" : unitLblLower.includes("cm") ? "cm" : "in";
17422
17426
  const fromUnit = chartUnit || displayUnitId;
17423
17427
  const dNum = (n2) => convertNum(n2, fromUnit, displayUnitId);
17424
17428
  const dLabel = (s) => convertLabel(s, fromUnit, displayUnitId);
@@ -18056,8 +18060,9 @@ function SizeResultView({
18056
18060
  userHeightCm,
18057
18061
  t: t2
18058
18062
  }) {
18059
- const resultUnit = sizingResult?.unit || sizingUnit;
18060
- const unitLbl = sizingUnit === "cm" ? t2("cm") : t2("in");
18063
+ const resultUnitRaw = (sizingResult?.unit || sizingUnit || "").toString().toLowerCase();
18064
+ const resultUnit = resultUnitRaw === "mm" ? "mm" : resultUnitRaw === "cm" ? "cm" : "in";
18065
+ const unitLbl = resultUnit === "mm" ? t2("mm") : sizingUnit === "cm" ? t2("cm") : t2("in");
18061
18066
  const [editVals, setEditVals] = reactExports.useState({});
18062
18067
  const pRange = pRangeFn;
18063
18068
  const cellVal = reactExports.useCallback((row, colIdx, header) => {
@@ -21909,6 +21914,7 @@ function AccessorySizeView({
21909
21914
  const [photoPreview, setPhotoPreview] = reactExports.useState(null);
21910
21915
  const [photoFile, setPhotoFile] = reactExports.useState(null);
21911
21916
  const [photoBase64, setPhotoBase64] = reactExports.useState(null);
21917
+ const [ageConfirmed, setAgeConfirmed] = reactExports.useState(null);
21912
21918
  const handlePhotoSelect = reactExports.useCallback(async (e) => {
21913
21919
  const file = e.target.files?.[0];
21914
21920
  if (!file) return;
@@ -21950,6 +21956,10 @@ function AccessorySizeView({
21950
21956
  submitSizing("exact");
21951
21957
  };
21952
21958
  const handlePhotoSubmit = () => {
21959
+ if (ageConfirmed !== true) {
21960
+ setError(t2("Please confirm that the person in the photo is 18 or older before uploading."));
21961
+ return;
21962
+ }
21953
21963
  if (!photoFile || !photoBase64) {
21954
21964
  setError(t2("Please upload a photo"));
21955
21965
  return;
@@ -22004,7 +22014,7 @@ function AccessorySizeView({
22004
22014
  className: "ps-bpm-value-input",
22005
22015
  value: values[f2.key],
22006
22016
  onChange: (e) => updateValue(f2.key, e.target.value),
22007
- placeholder: sizingUnit === "in" ? f2.placeholder.in : f2.placeholder.cm,
22017
+ placeholder: f2.placeholder[sizingUnit] || f2.placeholder.cm || f2.placeholder.in || "",
22008
22018
  step: f2.step ?? 0.5,
22009
22019
  min: f2.min,
22010
22020
  max: f2.max
@@ -22049,38 +22059,180 @@ function AccessorySizeView({
22049
22059
  /* ── Photo step — identical to BodyProfileView photo step ── */
22050
22060
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "ps-bp-wrapper", style: { display: "flex", flexDirection: "column", width: "100%", height: "100%" }, children: [
22051
22061
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: "1.2vw", padding: "1.5vw", width: "100%", flex: 1, minHeight: "20vw", alignItems: "stretch" }, children: [
22052
- /* @__PURE__ */ jsxRuntimeExports.jsx(
22053
- "div",
22054
- {
22055
- onClick: () => !photoPreview && fileInputRef.current?.click(),
22056
- style: {
22057
- flex: 1,
22058
- display: "flex",
22059
- flexDirection: "column",
22060
- alignItems: "center",
22061
- justifyContent: "center",
22062
- border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
22063
- borderRadius: "0.5vw",
22064
- cursor: photoPreview ? "default" : "pointer",
22065
- position: "relative",
22066
- background: "var(--ps-bg-secondary)",
22067
- transition: "border-color 0.2s",
22068
- overflow: "hidden"
22069
- },
22070
- children: photoPreview ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22071
- /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
22072
- /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: (e) => {
22073
- e.stopPropagation();
22074
- handleRemovePhoto();
22075
- }, 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: "×" })
22076
- ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22077
- /* @__PURE__ */ jsxRuntimeExports.jsx(UploadIcon, { size: 32 }),
22078
- /* @__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") }),
22079
- /* @__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") }),
22080
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
22081
- ] })
22082
- }
22083
- ),
22062
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { flex: 1, position: "relative", display: "flex" }, children: [
22063
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
22064
+ "div",
22065
+ {
22066
+ onClick: () => {
22067
+ if (!photoPreview && ageConfirmed === true) fileInputRef.current?.click();
22068
+ },
22069
+ style: {
22070
+ flex: 1,
22071
+ display: "flex",
22072
+ flexDirection: "column",
22073
+ alignItems: "center",
22074
+ justifyContent: "center",
22075
+ border: photoPreview ? "2px solid var(--ps-accent)" : "2px dashed var(--ps-border-color)",
22076
+ borderRadius: "0.5vw",
22077
+ cursor: photoPreview ? "default" : ageConfirmed === true ? "pointer" : "not-allowed",
22078
+ position: "relative",
22079
+ background: "var(--ps-bg-secondary)",
22080
+ transition: "border-color 0.2s",
22081
+ overflow: "hidden",
22082
+ filter: !photoPreview && ageConfirmed !== true ? "blur(6px) saturate(0.7)" : void 0,
22083
+ pointerEvents: !photoPreview && ageConfirmed !== true ? "none" : void 0
22084
+ },
22085
+ children: photoPreview ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22086
+ /* @__PURE__ */ jsxRuntimeExports.jsx("img", { src: photoPreview, alt: "preview", style: { width: "100%", height: "100%", objectFit: "contain" } }),
22087
+ /* @__PURE__ */ jsxRuntimeExports.jsx("button", { onClick: (e) => {
22088
+ e.stopPropagation();
22089
+ handleRemovePhoto();
22090
+ }, 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: "×" })
22091
+ ] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
22092
+ /* @__PURE__ */ jsxRuntimeExports.jsx(UploadIcon, { size: 32 }),
22093
+ /* @__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") }),
22094
+ /* @__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") }),
22095
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { fontSize: "0.5vw", color: "var(--ps-text-dim)", marginTop: "0.4vw" }, children: "JPEG, PNG (max 10MB)" })
22096
+ ] })
22097
+ }
22098
+ ),
22099
+ !photoPreview && ageConfirmed === null && /* @__PURE__ */ jsxRuntimeExports.jsx(
22100
+ "div",
22101
+ {
22102
+ role: "dialog",
22103
+ style: {
22104
+ position: "absolute",
22105
+ inset: 0,
22106
+ display: "flex",
22107
+ alignItems: "center",
22108
+ justifyContent: "center",
22109
+ padding: "1vw",
22110
+ background: "rgba(255,255,255,0.55)",
22111
+ backdropFilter: "blur(8px)",
22112
+ WebkitBackdropFilter: "blur(8px)",
22113
+ borderRadius: "0.5vw",
22114
+ zIndex: 2
22115
+ },
22116
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: {
22117
+ width: "100%",
22118
+ maxWidth: "min(420px, 22vw)",
22119
+ padding: "1.4vw 1.6vw",
22120
+ background: "#FFFFFF",
22121
+ border: "1px solid var(--ps-border-subtle)",
22122
+ borderRadius: "0.9vw",
22123
+ boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25), 0 8px 16px -8px rgba(17,24,39,0.15)",
22124
+ display: "flex",
22125
+ flexDirection: "column",
22126
+ alignItems: "center",
22127
+ textAlign: "center",
22128
+ gap: "0.75vw"
22129
+ }, children: [
22130
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "var(--ps-accent)" }, children: t2("AGE VERIFICATION") }),
22131
+ /* @__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?") }),
22132
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", gap: "0.65vw", width: "100%", marginTop: "0.4vw" }, children: [
22133
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
22134
+ "button",
22135
+ {
22136
+ type: "button",
22137
+ onClick: () => {
22138
+ setAgeConfirmed(true);
22139
+ setError("");
22140
+ },
22141
+ style: {
22142
+ flex: 1,
22143
+ padding: "0.75vw 1vw",
22144
+ borderRadius: "999px",
22145
+ background: "var(--ps-accent)",
22146
+ color: "#FFFFFF",
22147
+ border: "1.5px solid var(--ps-accent)",
22148
+ fontFamily: "inherit",
22149
+ fontSize: "0.78vw",
22150
+ fontWeight: 700,
22151
+ cursor: "pointer"
22152
+ },
22153
+ children: t2("Yes")
22154
+ }
22155
+ ),
22156
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
22157
+ "button",
22158
+ {
22159
+ type: "button",
22160
+ onClick: () => setAgeConfirmed(false),
22161
+ style: {
22162
+ flex: 1,
22163
+ padding: "0.75vw 1vw",
22164
+ borderRadius: "999px",
22165
+ background: "transparent",
22166
+ color: "var(--ps-text-primary)",
22167
+ border: "1.5px solid var(--ps-border-color)",
22168
+ fontFamily: "inherit",
22169
+ fontSize: "0.78vw",
22170
+ fontWeight: 700,
22171
+ cursor: "pointer"
22172
+ },
22173
+ children: t2("No")
22174
+ }
22175
+ )
22176
+ ] })
22177
+ ] })
22178
+ }
22179
+ ),
22180
+ !photoPreview && ageConfirmed === false && /* @__PURE__ */ jsxRuntimeExports.jsx(
22181
+ "div",
22182
+ {
22183
+ role: "alert",
22184
+ style: {
22185
+ position: "absolute",
22186
+ inset: 0,
22187
+ display: "flex",
22188
+ alignItems: "center",
22189
+ justifyContent: "center",
22190
+ padding: "1vw",
22191
+ background: "rgba(255,255,255,0.55)",
22192
+ backdropFilter: "blur(8px)",
22193
+ WebkitBackdropFilter: "blur(8px)",
22194
+ borderRadius: "0.5vw",
22195
+ zIndex: 2
22196
+ },
22197
+ children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: {
22198
+ width: "100%",
22199
+ maxWidth: "min(420px, 22vw)",
22200
+ padding: "1.4vw 1.6vw",
22201
+ background: "#FFFFFF",
22202
+ border: "1px solid rgba(192,38,38,0.35)",
22203
+ borderRadius: "0.9vw",
22204
+ boxShadow: "0 20px 40px -12px rgba(17,24,39,0.25)",
22205
+ display: "flex",
22206
+ flexDirection: "column",
22207
+ alignItems: "center",
22208
+ textAlign: "center",
22209
+ gap: "0.75vw"
22210
+ }, children: [
22211
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.18em", textTransform: "uppercase", color: "#C02626" }, children: t2("UPLOAD NOT ALLOWED") }),
22212
+ /* @__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.") }),
22213
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
22214
+ "button",
22215
+ {
22216
+ type: "button",
22217
+ onClick: () => setAgeConfirmed(null),
22218
+ style: {
22219
+ padding: "0.75vw 1.4vw",
22220
+ borderRadius: "999px",
22221
+ background: "transparent",
22222
+ color: "var(--ps-text-primary)",
22223
+ border: "1.5px solid var(--ps-border-color)",
22224
+ fontFamily: "inherit",
22225
+ fontSize: "0.78vw",
22226
+ fontWeight: 700,
22227
+ cursor: "pointer"
22228
+ },
22229
+ children: t2("Go back")
22230
+ }
22231
+ )
22232
+ ] })
22233
+ }
22234
+ )
22235
+ ] }),
22084
22236
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { flex: 1, display: "flex", flexDirection: "column", gap: "0.6vw", justifyContent: "center" }, children: [
22085
22237
  /* @__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") }),
22086
22238
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { background: "#ddfbe7", borderRadius: "0.5vw", padding: "0.6vw 0.8vw" }, children: [
@@ -22185,7 +22337,7 @@ function AccessorySizeView({
22185
22337
  className: "ps-bp-inline-input",
22186
22338
  value: values[f2.key],
22187
22339
  onChange: (e) => updateValue(f2.key, e.target.value),
22188
- placeholder: sizingUnit === "in" ? f2.placeholder.in : f2.placeholder.cm,
22340
+ placeholder: f2.placeholder[sizingUnit] || f2.placeholder.cm || f2.placeholder.in || "",
22189
22341
  step: f2.step ?? 0.5,
22190
22342
  min: f2.min,
22191
22343
  max: f2.max
@@ -22290,7 +22442,8 @@ const FALLBACK_FACE_FIELDS = [
22290
22442
  {
22291
22443
  key: "lensWidth",
22292
22444
  label: "Lens Width",
22293
- placeholder: { cm: "e.g. 52", in: "e.g. 2.0" },
22445
+ // `cm` key = mm placeholder (default eyewear unit); `in` key = cm placeholder.
22446
+ placeholder: { mm: "e.g. 52", cm: "e.g. 5.2" },
22294
22447
  hint: "Width of one lens",
22295
22448
  min: 0,
22296
22449
  step: 1
@@ -22298,7 +22451,7 @@ const FALLBACK_FACE_FIELDS = [
22298
22451
  {
22299
22452
  key: "bridgeWidth",
22300
22453
  label: "Bridge",
22301
- placeholder: { cm: "e.g. 18", in: "e.g. 0.7" },
22454
+ placeholder: { mm: "e.g. 18", cm: "e.g. 1.8" },
22302
22455
  hint: "Distance over the nose between lenses",
22303
22456
  min: 0,
22304
22457
  step: 1
@@ -22306,15 +22459,15 @@ const FALLBACK_FACE_FIELDS = [
22306
22459
  {
22307
22460
  key: "templeLength",
22308
22461
  label: "Arm Length",
22309
- placeholder: { cm: "e.g. 140", in: "e.g. 5.5" },
22462
+ placeholder: { mm: "e.g. 140", cm: "e.g. 14" },
22310
22463
  hint: "Length of the arm from hinge to tip",
22311
22464
  min: 0,
22312
22465
  step: 1
22313
22466
  }
22314
22467
  ];
22315
22468
  const EYEWEAR_UNIT_OPTIONS = [
22316
- { label: "Millimeters", value: "mm" },
22317
- { label: "Inches", value: "in" }
22469
+ { label: "Millimetre", value: "mm" },
22470
+ { label: "Centimetre", value: "cm" }
22318
22471
  ];
22319
22472
  function buildFieldsFromSizeGuide(sizeGuide) {
22320
22473
  const req = sizeGuide?.requiredFields;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "5.8.45",
3
+ "version": "5.8.47",
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
  }