@primestyleai/tryon 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/react/index.js +97 -32
  2. package/package.json +1 -1
@@ -187,9 +187,6 @@ function PrimeStyleTryonInner({
187
187
  const [resultImageUrl, setResultImageUrl] = useState(null);
188
188
  const [errorMessage, setErrorMessage] = useState(null);
189
189
  const [dragOver, setDragOver] = useState(false);
190
- const countdownRef = useRef(25);
191
- const countdownElRef = useRef(null);
192
- const countdownCircleRef = useRef(null);
193
190
  const [sizingMethod, setSizingMethod] = useState(null);
194
191
  const [sizingResult, setSizingResult] = useState(null);
195
192
  const [sizeGuide, setSizeGuide] = useState(null);
@@ -202,6 +199,7 @@ function PrimeStyleTryonInner({
202
199
  const [weightUnit, setWeightUnit] = useState(imperial ? "lbs" : "kg");
203
200
  const formRef = useRef({});
204
201
  const [formGender, setFormGender] = useState("male");
202
+ const [formKey, setFormKey] = useState(0);
205
203
  const [profiles, setProfiles] = useState(() => lsGet("profiles", []));
206
204
  const [history, setHistory] = useState(() => lsGet("history", []));
207
205
  const [activeProfileId, setActiveProfileId] = useState(null);
@@ -240,15 +238,33 @@ function PrimeStyleTryonInner({
240
238
  if (pollingRef.current) clearInterval(pollingRef.current);
241
239
  };
242
240
  }, [apiUrl]);
241
+ const progressRef = useRef(0);
242
+ const progressBarRef = useRef(null);
243
+ const progressTextRef = useRef(null);
244
+ const progressStatusRef = useRef(null);
243
245
  useEffect(() => {
244
246
  if (view === "processing") {
245
- countdownRef.current = 25;
247
+ progressRef.current = 0;
248
+ const statuses = [
249
+ { at: 0, text: "Preparing your image..." },
250
+ { at: 15, text: "Analyzing body proportions..." },
251
+ { at: 30, text: "Matching garment to your photo..." },
252
+ { at: 50, text: "Generating virtual try-on..." },
253
+ { at: 75, text: "Refining details..." },
254
+ { at: 90, text: "Almost there..." }
255
+ ];
246
256
  const interval = setInterval(() => {
247
- countdownRef.current -= 1;
248
- if (countdownElRef.current) countdownElRef.current.textContent = String(Math.max(countdownRef.current, 0));
249
- if (countdownCircleRef.current) countdownCircleRef.current.style.strokeDashoffset = `${Math.max(countdownRef.current, 0) / 25 * 326.73}`;
250
- if (countdownRef.current <= 0) clearInterval(interval);
251
- }, 1e3);
257
+ const p = progressRef.current;
258
+ const increment = p < 30 ? 1.2 : p < 60 ? 0.8 : p < 80 ? 0.4 : p < 95 ? 0.15 : 0;
259
+ progressRef.current = Math.min(p + increment, 95);
260
+ const val = Math.round(progressRef.current);
261
+ if (progressBarRef.current) progressBarRef.current.style.width = `${val}%`;
262
+ if (progressTextRef.current) progressTextRef.current.textContent = `${val}%`;
263
+ if (progressStatusRef.current) {
264
+ const status = [...statuses].reverse().find((s) => val >= s.at);
265
+ if (status) progressStatusRef.current.textContent = status.text;
266
+ }
267
+ }, 200);
252
268
  return () => clearInterval(interval);
253
269
  }
254
270
  }, [view]);
@@ -385,8 +401,12 @@ function PrimeStyleTryonInner({
385
401
  });
386
402
  if (!completedRef.current) {
387
403
  completedRef.current = true;
404
+ progressRef.current = 100;
405
+ if (progressBarRef.current) progressBarRef.current.style.width = "100%";
406
+ if (progressTextRef.current) progressTextRef.current.textContent = "100%";
407
+ if (progressStatusRef.current) progressStatusRef.current.textContent = "Complete!";
388
408
  cleanupJob();
389
- setView("result");
409
+ setTimeout(() => setView("result"), 400);
390
410
  onComplete?.({ jobId: update.galleryId, imageUrl: update.imageUrl });
391
411
  }
392
412
  } else if (update.status === "failed") {
@@ -541,6 +561,11 @@ function PrimeStyleTryonInner({
541
561
  if (p.fitPreference) fd.fitPreference = p.fitPreference;
542
562
  formRef.current = fd;
543
563
  setFormGender(fd.gender || "male");
564
+ if (p.country) setSizingCountry(p.country);
565
+ if (p.sizingUnit) setSizingUnit(p.sizingUnit);
566
+ if (p.heightUnit) setHeightUnit(p.heightUnit);
567
+ if (p.weightUnit) setWeightUnit(p.weightUnit);
568
+ setFormKey((k) => k + 1);
544
569
  }, [profiles]);
545
570
  const saveProfile = useCallback((name) => {
546
571
  const id = activeProfileId || `p_${Date.now()}`;
@@ -563,6 +588,10 @@ function PrimeStyleTryonInner({
563
588
  shoeUS: formRef.current.shoeUS,
564
589
  shoeUK: formRef.current.shoeUK,
565
590
  fitPreference: formRef.current.fitPreference,
591
+ country: sizingCountry,
592
+ sizingUnit,
593
+ heightUnit,
594
+ weightUnit,
566
595
  createdAt: Date.now()
567
596
  };
568
597
  setProfiles((prev) => {
@@ -576,7 +605,7 @@ function PrimeStyleTryonInner({
576
605
  });
577
606
  setActiveProfileId(id);
578
607
  setProfileSaved(true);
579
- }, [activeProfileId]);
608
+ }, [activeProfileId, sizingCountry, sizingUnit, heightUnit, weightUnit]);
580
609
  const saveHistoryEntry = useCallback(() => {
581
610
  const entry = {
582
611
  id: `h_${Date.now()}`,
@@ -827,7 +856,11 @@ function PrimeStyleTryonInner({
827
856
  /* @__PURE__ */ jsx("label", { children: "Sizing region" }),
828
857
  /* @__PURE__ */ jsx("select", { className: "ps-tryon-country-select", value: sizingCountry, onChange: (e) => setSizingCountry(e.target.value), children: SIZING_COUNTRIES.map((c) => /* @__PURE__ */ jsx("option", { value: c.code, children: c.label }, c.code)) })
829
858
  ] }),
830
- sizingMethod === "exact" && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(UnitToggle, { options: [{ label: "cm", value: "cm" }, { label: "in", value: "in" }], value: sizingUnit, onChange: setSizingUnit }) }),
859
+ sizingMethod === "exact" && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(UnitToggle, { options: [{ label: "cm", value: "cm" }, { label: "in", value: "in" }], value: sizingUnit, onChange: (v) => {
860
+ setSizingUnit(v);
861
+ setHeightUnit(v === "cm" ? "cm" : "ft");
862
+ setWeightUnit(v === "cm" ? "kg" : "lbs");
863
+ } }) }),
831
864
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
832
865
  /* @__PURE__ */ jsx("label", { children: "Height" }),
833
866
  heightUnit === "ft" ? /* @__PURE__ */ jsxs("div", { className: "ps-tryon-height-ft", children: [
@@ -883,18 +916,20 @@ function PrimeStyleTryonInner({
883
916
  "Get My Size & Try On ",
884
917
  /* @__PURE__ */ jsx(ArrowRightIcon, {})
885
918
  ] })
886
- ] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}`);
919
+ ] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}-${formKey}`);
887
920
  }
888
921
  function ProcessingView() {
889
922
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-processing", children: [
890
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-countdown-ring", children: [
891
- /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 120 120", children: [
892
- /* @__PURE__ */ jsx("circle", { className: "ps-tryon-countdown-track", cx: "60", cy: "60", r: "52" }),
893
- /* @__PURE__ */ jsx("circle", { ref: countdownCircleRef, className: "ps-tryon-countdown-progress", cx: "60", cy: "60", r: "52", style: { strokeDashoffset: `${25 / 25 * 326.73}` } })
894
- ] }),
895
- /* @__PURE__ */ jsx("span", { ref: countdownElRef, className: "ps-tryon-countdown-number", children: "25" })
923
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-processing-image-wrap", children: [
924
+ previewUrl && /* @__PURE__ */ jsx("img", { src: previewUrl, alt: "Your photo", className: "ps-tryon-processing-model" }),
925
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-scan-line" }),
926
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-scan-overlay" })
896
927
  ] }),
897
- /* @__PURE__ */ jsx("p", { className: cx("ps-tryon-processing-text", cn.processingText), children: "Generating your try-on..." }),
928
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-progress-section", children: [
929
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-progress-bar-wrap", children: /* @__PURE__ */ jsx("div", { ref: progressBarRef, className: "ps-tryon-progress-bar-fill", style: { width: "0%" } }) }),
930
+ /* @__PURE__ */ jsx("span", { ref: progressTextRef, className: "ps-tryon-progress-pct", children: "0%" })
931
+ ] }),
932
+ /* @__PURE__ */ jsx("div", { ref: progressStatusRef, className: cx("ps-tryon-processing-text", cn.processingText), children: "Preparing your image..." }),
898
933
  /* @__PURE__ */ jsx("p", { className: cx("ps-tryon-processing-sub", cn.processingSubText), children: "This usually takes 15-25 seconds" })
899
934
  ] });
900
935
  }
@@ -1324,19 +1359,49 @@ const STYLES = `
1324
1359
  .ps-tryon-disclaimer { font-size: 11px; color: #666; margin: 4px 0 0; }
1325
1360
 
1326
1361
  /* Processing */
1327
- .ps-tryon-processing { text-align: center; padding: 40px 24px; }
1328
- .ps-tryon-spinner {
1329
- width: 48px; height: 48px; border: 3px solid #333;
1330
- border-top-color: var(--ps-loader, #bb945c); border-radius: 50%;
1331
- animation: ps-spin 0.8s linear infinite; margin: 0 auto 16px;
1362
+ .ps-tryon-processing { text-align: center; padding: 24px; display: flex; flex-direction: column; align-items: center; }
1363
+
1364
+ .ps-tryon-processing-image-wrap {
1365
+ position: relative; width: 200px; height: 260px; margin: 0 auto 24px;
1366
+ border-radius: 16px; overflow: hidden; border: 2px solid #333;
1367
+ }
1368
+ .ps-tryon-processing-model {
1369
+ width: 100%; height: 100%; object-fit: cover; display: block;
1370
+ }
1371
+ .ps-tryon-scan-overlay {
1372
+ position: absolute; inset: 0;
1373
+ background: linear-gradient(180deg, rgba(187,148,92,0.05) 0%, transparent 40%, transparent 60%, rgba(187,148,92,0.05) 100%);
1374
+ pointer-events: none;
1375
+ }
1376
+ .ps-tryon-scan-line {
1377
+ position: absolute; left: 0; right: 0; height: 3px;
1378
+ background: linear-gradient(90deg, transparent, #bb945c 20%, #d6ba7d 50%, #bb945c 80%, transparent);
1379
+ box-shadow: 0 0 15px rgba(187,148,92,0.6), 0 0 40px rgba(187,148,92,0.2);
1380
+ animation: ps-scan 2.5s ease-in-out infinite;
1381
+ pointer-events: none; z-index: 2;
1332
1382
  }
1333
- @keyframes ps-spin { to { transform: rotate(360deg); } }
1334
- .ps-tryon-countdown-ring { position: relative; width: 100px; height: 100px; margin: 0 auto 20px; }
1335
- .ps-tryon-countdown-ring svg { width: 100%; height: 100%; transform: rotate(-90deg); }
1336
- .ps-tryon-countdown-track { fill: none; stroke: #333; stroke-width: 4; }
1337
- .ps-tryon-countdown-progress { fill: none; stroke: var(--ps-loader, #bb945c); stroke-width: 4; stroke-linecap: round; stroke-dasharray: 326.73; transition: stroke-dashoffset 1s linear; }
1338
- .ps-tryon-countdown-number { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 28px; font-weight: 700; color: #fff; font-variant-numeric: tabular-nums; }
1339
- .ps-tryon-done-pulse { animation: ps-scale-in 0.4s ease both; }
1383
+ @keyframes ps-scan {
1384
+ 0% { top: 0; opacity: 0; }
1385
+ 5% { opacity: 1; }
1386
+ 95% { opacity: 1; }
1387
+ 100% { top: calc(100% - 3px); opacity: 0; }
1388
+ }
1389
+
1390
+ .ps-tryon-progress-section {
1391
+ display: flex; align-items: center; gap: 12px; width: 100%; max-width: 300px; margin-bottom: 16px;
1392
+ }
1393
+ .ps-tryon-progress-bar-wrap {
1394
+ flex: 1; height: 6px; background: #333; border-radius: 3px; overflow: hidden;
1395
+ }
1396
+ .ps-tryon-progress-bar-fill {
1397
+ height: 100%; background: linear-gradient(90deg, #bb945c, #d6ba7d);
1398
+ border-radius: 3px; transition: width 0.3s ease;
1399
+ }
1400
+ .ps-tryon-progress-pct {
1401
+ font-size: 13px; font-weight: 700; color: #bb945c; min-width: 36px; text-align: right;
1402
+ font-variant-numeric: tabular-nums;
1403
+ }
1404
+
1340
1405
  @keyframes ps-scale-in { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
1341
1406
  .ps-tryon-processing-text { font-size: 14px; color: #fff; margin: 0 0 4px; }
1342
1407
  .ps-tryon-processing-sub { font-size: 12px; color: #999; margin: 0; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",