@primestyleai/tryon 5.8.47 → 5.8.49

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" } }) }),
@@ -12871,6 +12905,21 @@ function AccessorySizeView({
12871
12905
  /* @__PURE__ */ jsx("span", { style: { color: "#3267c3", fontSize: "0.65vw", fontWeight: 600 }, children: t("Quick Tip") })
12872
12906
  ] }),
12873
12907
  /* @__PURE__ */ jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.6 }, children: isCloseUp ? t("A clear, well-lit face photo gives the most accurate fit for eyewear and headwear.") : t("The simpler your photo is, the more accurate your virtual try-on results will be.") })
12908
+ ] }),
12909
+ /* @__PURE__ */ jsxs("div", { style: {
12910
+ background: "rgba(33, 84, 239, 0.04)",
12911
+ border: "1px solid rgba(33, 84, 239, 0.16)",
12912
+ borderRadius: "0.5vw",
12913
+ padding: "0.6vw 0.8vw",
12914
+ display: "flex",
12915
+ flexDirection: "column",
12916
+ gap: "0.3vw"
12917
+ }, children: [
12918
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.35vw" }, children: [
12919
+ /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", width: "0.75vw", height: "0.75vw", fill: "none", stroke: "#2154ef", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }) }),
12920
+ /* @__PURE__ */ jsx("span", { style: { color: "#2154ef", fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.12em", textTransform: "uppercase" }, children: t("Legal Notice") })
12921
+ ] }),
12922
+ /* @__PURE__ */ jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-secondary)", lineHeight: 1.55 }, children: t("Your image will be used to generate a virtual try-on preview showing how selected items may look and fit. Images are processed securely and are not stored after generation.") })
12874
12923
  ] })
12875
12924
  ] })
12876
12925
  ] }),
@@ -13611,6 +13660,7 @@ function PrimeStyleTryonInner({
13611
13660
  category: measurementType,
13612
13661
  ...Object.keys(faceMm).length > 0 && { faceMeasurementsMm: faceMm, irisConfidence: 1 }
13613
13662
  };
13663
+ const minVisible = new Promise((r) => setTimeout(r, 3200));
13614
13664
  try {
13615
13665
  const resp = await fetch(`${baseUrl}/api/v1/sizing/face-recommend`, {
13616
13666
  method: "POST",
@@ -13619,17 +13669,20 @@ function PrimeStyleTryonInner({
13619
13669
  });
13620
13670
  if (resp.ok) {
13621
13671
  const data = await resp.json();
13672
+ await minVisible;
13622
13673
  setSizingResult(data);
13623
13674
  onComplete?.(data);
13624
13675
  } else {
13625
13676
  const body = await resp.text().catch(() => "");
13626
13677
  console.error("[PS-SDK] face-recommend failed:", resp.status, body);
13678
+ await minVisible;
13627
13679
  setErrorMessage(t("Unable to get size recommendation. Please try again."));
13628
13680
  setView("error");
13629
13681
  setEstimationDone(true);
13630
13682
  }
13631
13683
  } catch (err) {
13632
13684
  console.error("[PS-SDK] face-recommend network error:", err);
13685
+ await minVisible;
13633
13686
  setErrorMessage(t("Unable to connect to sizing service. Please try again."));
13634
13687
  setView("error");
13635
13688
  setEstimationDone(true);
@@ -13824,6 +13877,7 @@ function PrimeStyleTryonInner({
13824
13877
  const measurementType = detectMeasurementType(productTitle);
13825
13878
  if (measurementType === "face" || measurementType === "head") {
13826
13879
  setFaceLandmarks(null);
13880
+ const minVisible = new Promise((r) => setTimeout(r, 3200));
13827
13881
  try {
13828
13882
  const faceResult = await detectFaceMeasurements(objUrl);
13829
13883
  if (faceResult) setFaceLandmarks(faceResult.landmarks);
@@ -13846,6 +13900,7 @@ function PrimeStyleTryonInner({
13846
13900
  });
13847
13901
  if (recRes.ok) {
13848
13902
  const recData = await recRes.json();
13903
+ await minVisible;
13849
13904
  setSizingResult(recData);
13850
13905
  onComplete?.(recData);
13851
13906
  persistResultToProfile(
@@ -13861,10 +13916,12 @@ function PrimeStyleTryonInner({
13861
13916
  recData
13862
13917
  );
13863
13918
  } else {
13919
+ await minVisible;
13864
13920
  setEstimationDone(true);
13865
13921
  }
13866
13922
  } catch (err) {
13867
13923
  console.error("[ps-sdk] face-recommend failed:", err);
13924
+ await minVisible;
13868
13925
  setEstimationDone(true);
13869
13926
  }
13870
13927
  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" } }) }),
@@ -22295,6 +22329,21 @@ function AccessorySizeView({
22295
22329
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#3267c3", fontSize: "0.65vw", fontWeight: 600 }, children: t2("Quick Tip") })
22296
22330
  ] }),
22297
22331
  /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-primary)", lineHeight: 1.6 }, children: isCloseUp ? t2("A clear, well-lit face photo gives the most accurate fit for eyewear and headwear.") : t2("The simpler your photo is, the more accurate your virtual try-on results will be.") })
22332
+ ] }),
22333
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: {
22334
+ background: "rgba(33, 84, 239, 0.04)",
22335
+ border: "1px solid rgba(33, 84, 239, 0.16)",
22336
+ borderRadius: "0.5vw",
22337
+ padding: "0.6vw 0.8vw",
22338
+ display: "flex",
22339
+ flexDirection: "column",
22340
+ gap: "0.3vw"
22341
+ }, children: [
22342
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.35vw" }, children: [
22343
+ /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { viewBox: "0 0 24 24", width: "0.75vw", height: "0.75vw", fill: "none", stroke: "#2154ef", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }) }),
22344
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#2154ef", fontSize: "0.62vw", fontWeight: 700, letterSpacing: "0.12em", textTransform: "uppercase" }, children: t2("Legal Notice") })
22345
+ ] }),
22346
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { style: { fontSize: "0.58vw", color: "var(--ps-text-secondary)", lineHeight: 1.55 }, children: t2("Your image will be used to generate a virtual try-on preview showing how selected items may look and fit. Images are processed securely and are not stored after generation.") })
22298
22347
  ] })
22299
22348
  ] })
22300
22349
  ] }),
@@ -23035,6 +23084,7 @@ function PrimeStyleTryonInner({
23035
23084
  category: measurementType,
23036
23085
  ...Object.keys(faceMm).length > 0 && { faceMeasurementsMm: faceMm, irisConfidence: 1 }
23037
23086
  };
23087
+ const minVisible = new Promise((r2) => setTimeout(r2, 3200));
23038
23088
  try {
23039
23089
  const resp = await fetch(`${baseUrl}/api/v1/sizing/face-recommend`, {
23040
23090
  method: "POST",
@@ -23043,17 +23093,20 @@ function PrimeStyleTryonInner({
23043
23093
  });
23044
23094
  if (resp.ok) {
23045
23095
  const data = await resp.json();
23096
+ await minVisible;
23046
23097
  setSizingResult(data);
23047
23098
  onComplete?.(data);
23048
23099
  } else {
23049
23100
  const body = await resp.text().catch(() => "");
23050
23101
  console.error("[PS-SDK] face-recommend failed:", resp.status, body);
23102
+ await minVisible;
23051
23103
  setErrorMessage(t2("Unable to get size recommendation. Please try again."));
23052
23104
  setView("error");
23053
23105
  setEstimationDone(true);
23054
23106
  }
23055
23107
  } catch (err) {
23056
23108
  console.error("[PS-SDK] face-recommend network error:", err);
23109
+ await minVisible;
23057
23110
  setErrorMessage(t2("Unable to connect to sizing service. Please try again."));
23058
23111
  setView("error");
23059
23112
  setEstimationDone(true);
@@ -23248,6 +23301,7 @@ function PrimeStyleTryonInner({
23248
23301
  const measurementType = detectMeasurementType(productTitle);
23249
23302
  if (measurementType === "face" || measurementType === "head") {
23250
23303
  setFaceLandmarks(null);
23304
+ const minVisible = new Promise((r2) => setTimeout(r2, 3200));
23251
23305
  try {
23252
23306
  const faceResult = await detectFaceMeasurements(objUrl);
23253
23307
  if (faceResult) setFaceLandmarks(faceResult.landmarks);
@@ -23270,6 +23324,7 @@ function PrimeStyleTryonInner({
23270
23324
  });
23271
23325
  if (recRes.ok) {
23272
23326
  const recData = await recRes.json();
23327
+ await minVisible;
23273
23328
  setSizingResult(recData);
23274
23329
  onComplete?.(recData);
23275
23330
  persistResultToProfile(
@@ -23285,10 +23340,12 @@ function PrimeStyleTryonInner({
23285
23340
  recData
23286
23341
  );
23287
23342
  } else {
23343
+ await minVisible;
23288
23344
  setEstimationDone(true);
23289
23345
  }
23290
23346
  } catch (err) {
23291
23347
  console.error("[ps-sdk] face-recommend failed:", err);
23348
+ await minVisible;
23292
23349
  setEstimationDone(true);
23293
23350
  }
23294
23351
  setSizingLoading(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "5.8.47",
3
+ "version": "5.8.49",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",