@primestyleai/tryon 2.3.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.
- package/dist/react/index.d.ts +3 -23
- package/dist/react/index.js +103 -60
- package/package.json +1 -1
package/dist/react/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type CSSProperties } from "react";
|
|
2
|
-
import type { ButtonStyles, ModalStyles, PrimeStyleClassNames
|
|
2
|
+
import type { ButtonStyles, ModalStyles, PrimeStyleClassNames } from "../types";
|
|
3
3
|
export interface PrimeStyleTryonProps {
|
|
4
4
|
productImage: string;
|
|
5
5
|
productTitle?: string;
|
|
@@ -23,27 +23,7 @@ export interface PrimeStyleTryonProps {
|
|
|
23
23
|
message: string;
|
|
24
24
|
code?: string;
|
|
25
25
|
}) => void;
|
|
26
|
-
/**
|
|
27
|
-
|
|
28
|
-
/** Product description HTML for AI size guide extraction */
|
|
29
|
-
productDescription?: string;
|
|
30
|
-
/** Product vendor/brand for AI extraction */
|
|
31
|
-
productVendor?: string;
|
|
32
|
-
/** Product type (e.g. "T-Shirt", "Jacket") */
|
|
33
|
-
productType?: string;
|
|
34
|
-
/** Product tags for AI extraction */
|
|
35
|
-
productTags?: string[];
|
|
36
|
-
/** Product variants with size options */
|
|
37
|
-
productVariants?: Array<{
|
|
38
|
-
title: string;
|
|
39
|
-
option1?: string | null;
|
|
40
|
-
option2?: string | null;
|
|
41
|
-
option3?: string | null;
|
|
42
|
-
}>;
|
|
43
|
-
/** Product options (e.g. [{name: "Size", values: ["S","M","L"]}]) */
|
|
44
|
-
productOptions?: Array<{
|
|
45
|
-
name: string;
|
|
46
|
-
values?: string[];
|
|
47
|
-
}>;
|
|
26
|
+
/** Size guide data from your backend — pass as-is, any format (HTML, JSON, object, string). Our AI will parse it. */
|
|
27
|
+
sizeGuideData?: unknown;
|
|
48
28
|
}
|
|
49
29
|
export declare function PrimeStyleTryon(props: PrimeStyleTryonProps): import("react/jsx-runtime").JSX.Element | null;
|
package/dist/react/index.js
CHANGED
|
@@ -179,13 +179,7 @@ function PrimeStyleTryonInner({
|
|
|
179
179
|
onProcessing,
|
|
180
180
|
onComplete,
|
|
181
181
|
onError,
|
|
182
|
-
|
|
183
|
-
productDescription,
|
|
184
|
-
productVendor,
|
|
185
|
-
productType,
|
|
186
|
-
productTags,
|
|
187
|
-
productVariants,
|
|
188
|
-
productOptions
|
|
182
|
+
sizeGuideData
|
|
189
183
|
}) {
|
|
190
184
|
const [view, setView] = useState("idle");
|
|
191
185
|
const [selectedFile, setSelectedFile] = useState(null);
|
|
@@ -193,9 +187,6 @@ function PrimeStyleTryonInner({
|
|
|
193
187
|
const [resultImageUrl, setResultImageUrl] = useState(null);
|
|
194
188
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
195
189
|
const [dragOver, setDragOver] = useState(false);
|
|
196
|
-
const countdownRef = useRef(25);
|
|
197
|
-
const countdownElRef = useRef(null);
|
|
198
|
-
const countdownCircleRef = useRef(null);
|
|
199
190
|
const [sizingMethod, setSizingMethod] = useState(null);
|
|
200
191
|
const [sizingResult, setSizingResult] = useState(null);
|
|
201
192
|
const [sizeGuide, setSizeGuide] = useState(null);
|
|
@@ -208,6 +199,7 @@ function PrimeStyleTryonInner({
|
|
|
208
199
|
const [weightUnit, setWeightUnit] = useState(imperial ? "lbs" : "kg");
|
|
209
200
|
const formRef = useRef({});
|
|
210
201
|
const [formGender, setFormGender] = useState("male");
|
|
202
|
+
const [formKey, setFormKey] = useState(0);
|
|
211
203
|
const [profiles, setProfiles] = useState(() => lsGet("profiles", []));
|
|
212
204
|
const [history, setHistory] = useState(() => lsGet("history", []));
|
|
213
205
|
const [activeProfileId, setActiveProfileId] = useState(null);
|
|
@@ -246,15 +238,33 @@ function PrimeStyleTryonInner({
|
|
|
246
238
|
if (pollingRef.current) clearInterval(pollingRef.current);
|
|
247
239
|
};
|
|
248
240
|
}, [apiUrl]);
|
|
241
|
+
const progressRef = useRef(0);
|
|
242
|
+
const progressBarRef = useRef(null);
|
|
243
|
+
const progressTextRef = useRef(null);
|
|
244
|
+
const progressStatusRef = useRef(null);
|
|
249
245
|
useEffect(() => {
|
|
250
246
|
if (view === "processing") {
|
|
251
|
-
|
|
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
|
+
];
|
|
252
256
|
const interval = setInterval(() => {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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);
|
|
258
268
|
return () => clearInterval(interval);
|
|
259
269
|
}
|
|
260
270
|
}, [view]);
|
|
@@ -290,31 +300,22 @@ function PrimeStyleTryonInner({
|
|
|
290
300
|
useEffect(() => {
|
|
291
301
|
if (view !== "sizing-choice" || sizeGuideFetchedRef.current || !apiRef.current) return;
|
|
292
302
|
sizeGuideFetchedRef.current = true;
|
|
293
|
-
if (
|
|
294
|
-
setSizeGuide(
|
|
303
|
+
if (!sizeGuideData) {
|
|
304
|
+
setSizeGuide({ found: false });
|
|
295
305
|
return;
|
|
296
306
|
}
|
|
297
307
|
setSizeGuideFetching(true);
|
|
298
308
|
const baseUrl = getApiUrl(apiUrl);
|
|
299
309
|
const key = getApiKey();
|
|
300
|
-
const productPayload = {
|
|
301
|
-
title: productTitle,
|
|
302
|
-
variants: productVariants || []
|
|
303
|
-
};
|
|
304
|
-
if (productDescription) productPayload.description = productDescription;
|
|
305
|
-
if (productVendor) productPayload.vendor = productVendor;
|
|
306
|
-
if (productType) productPayload.productType = productType;
|
|
307
|
-
if (productTags?.length) productPayload.tags = productTags;
|
|
308
|
-
if (productOptions?.length) productPayload.options = productOptions;
|
|
309
310
|
fetch(`${baseUrl}/api/v1/sizing/sizeguide`, {
|
|
310
311
|
method: "POST",
|
|
311
312
|
headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
|
|
312
|
-
body: JSON.stringify({ product:
|
|
313
|
+
body: JSON.stringify({ product: { title: productTitle }, sizeGuideRaw: sizeGuideData })
|
|
313
314
|
}).then((r) => r.ok ? r.json() : null).then((data) => {
|
|
314
315
|
if (data) setSizeGuide(data);
|
|
315
316
|
else setSizeGuide({ found: false });
|
|
316
317
|
}).catch(() => setSizeGuide({ found: false })).finally(() => setSizeGuideFetching(false));
|
|
317
|
-
}, [view, apiUrl, productTitle,
|
|
318
|
+
}, [view, apiUrl, productTitle, sizeGuideData]);
|
|
318
319
|
const stepIndex = useMemo(() => {
|
|
319
320
|
switch (view) {
|
|
320
321
|
case "welcome":
|
|
@@ -400,8 +401,12 @@ function PrimeStyleTryonInner({
|
|
|
400
401
|
});
|
|
401
402
|
if (!completedRef.current) {
|
|
402
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!";
|
|
403
408
|
cleanupJob();
|
|
404
|
-
setView("result");
|
|
409
|
+
setTimeout(() => setView("result"), 400);
|
|
405
410
|
onComplete?.({ jobId: update.galleryId, imageUrl: update.imageUrl });
|
|
406
411
|
}
|
|
407
412
|
} else if (update.status === "failed") {
|
|
@@ -422,14 +427,7 @@ function PrimeStyleTryonInner({
|
|
|
422
427
|
const payload = {
|
|
423
428
|
method: sizingMethod,
|
|
424
429
|
locale: sizingCountry,
|
|
425
|
-
product: {
|
|
426
|
-
title: productTitle,
|
|
427
|
-
description: productDescription || "",
|
|
428
|
-
variants: productVariants || [],
|
|
429
|
-
...productVendor && { vendor: productVendor },
|
|
430
|
-
...productType && { productType },
|
|
431
|
-
...productTags?.length && { tags: productTags }
|
|
432
|
-
}
|
|
430
|
+
product: { title: productTitle, description: "", variants: [] }
|
|
433
431
|
};
|
|
434
432
|
if (sizeGuide?.found) payload.sizeGuide = sizeGuide;
|
|
435
433
|
if (sizingMethod === "exact") {
|
|
@@ -563,6 +561,11 @@ function PrimeStyleTryonInner({
|
|
|
563
561
|
if (p.fitPreference) fd.fitPreference = p.fitPreference;
|
|
564
562
|
formRef.current = fd;
|
|
565
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);
|
|
566
569
|
}, [profiles]);
|
|
567
570
|
const saveProfile = useCallback((name) => {
|
|
568
571
|
const id = activeProfileId || `p_${Date.now()}`;
|
|
@@ -585,6 +588,10 @@ function PrimeStyleTryonInner({
|
|
|
585
588
|
shoeUS: formRef.current.shoeUS,
|
|
586
589
|
shoeUK: formRef.current.shoeUK,
|
|
587
590
|
fitPreference: formRef.current.fitPreference,
|
|
591
|
+
country: sizingCountry,
|
|
592
|
+
sizingUnit,
|
|
593
|
+
heightUnit,
|
|
594
|
+
weightUnit,
|
|
588
595
|
createdAt: Date.now()
|
|
589
596
|
};
|
|
590
597
|
setProfiles((prev) => {
|
|
@@ -598,7 +605,7 @@ function PrimeStyleTryonInner({
|
|
|
598
605
|
});
|
|
599
606
|
setActiveProfileId(id);
|
|
600
607
|
setProfileSaved(true);
|
|
601
|
-
}, [activeProfileId]);
|
|
608
|
+
}, [activeProfileId, sizingCountry, sizingUnit, heightUnit, weightUnit]);
|
|
602
609
|
const saveHistoryEntry = useCallback(() => {
|
|
603
610
|
const entry = {
|
|
604
611
|
id: `h_${Date.now()}`,
|
|
@@ -849,7 +856,11 @@ function PrimeStyleTryonInner({
|
|
|
849
856
|
/* @__PURE__ */ jsx("label", { children: "Sizing region" }),
|
|
850
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)) })
|
|
851
858
|
] }),
|
|
852
|
-
sizingMethod === "exact" && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(UnitToggle, { options: [{ label: "cm", value: "cm" }, { label: "in", value: "in" }], value: sizingUnit, onChange:
|
|
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
|
+
} }) }),
|
|
853
864
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
|
|
854
865
|
/* @__PURE__ */ jsx("label", { children: "Height" }),
|
|
855
866
|
heightUnit === "ft" ? /* @__PURE__ */ jsxs("div", { className: "ps-tryon-height-ft", children: [
|
|
@@ -905,18 +916,20 @@ function PrimeStyleTryonInner({
|
|
|
905
916
|
"Get My Size & Try On ",
|
|
906
917
|
/* @__PURE__ */ jsx(ArrowRightIcon, {})
|
|
907
918
|
] })
|
|
908
|
-
] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}`);
|
|
919
|
+
] }, `form-${formGender}-${sizingUnit}-${heightUnit}-${sizingCountry}-${formKey}`);
|
|
909
920
|
}
|
|
910
921
|
function ProcessingView() {
|
|
911
922
|
return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-processing", children: [
|
|
912
|
-
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-
|
|
913
|
-
/* @__PURE__ */
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
] }),
|
|
917
|
-
/* @__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" })
|
|
918
927
|
] }),
|
|
919
|
-
/* @__PURE__ */
|
|
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..." }),
|
|
920
933
|
/* @__PURE__ */ jsx("p", { className: cx("ps-tryon-processing-sub", cn.processingSubText), children: "This usually takes 15-25 seconds" })
|
|
921
934
|
] });
|
|
922
935
|
}
|
|
@@ -1346,19 +1359,49 @@ const STYLES = `
|
|
|
1346
1359
|
.ps-tryon-disclaimer { font-size: 11px; color: #666; margin: 4px 0 0; }
|
|
1347
1360
|
|
|
1348
1361
|
/* Processing */
|
|
1349
|
-
.ps-tryon-processing { text-align: center; padding:
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
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;
|
|
1354
1375
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
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;
|
|
1382
|
+
}
|
|
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
|
+
|
|
1362
1405
|
@keyframes ps-scale-in { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } }
|
|
1363
1406
|
.ps-tryon-processing-text { font-size: 14px; color: #fff; margin: 0 0 4px; }
|
|
1364
1407
|
.ps-tryon-processing-sub { font-size: 12px; color: #999; margin: 0; }
|