@primestyleai/tryon 3.1.0 → 3.2.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.js +120 -73
- package/package.json +1 -1
package/dist/react/index.js
CHANGED
|
@@ -189,6 +189,7 @@ function PrimeStyleTryonInner({
|
|
|
189
189
|
const [dragOver, setDragOver] = useState(false);
|
|
190
190
|
const [sizingMethod, setSizingMethod] = useState(null);
|
|
191
191
|
const [sizingResult, setSizingResult] = useState(null);
|
|
192
|
+
const [sizingLoading, setSizingLoading] = useState(false);
|
|
192
193
|
const [sizeGuide, setSizeGuide] = useState(null);
|
|
193
194
|
const [sizeGuideFetching, setSizeGuideFetching] = useState(false);
|
|
194
195
|
const sizeGuideFetchedRef = useRef(false);
|
|
@@ -348,6 +349,7 @@ function PrimeStyleTryonInner({
|
|
|
348
349
|
setErrorMessage(null);
|
|
349
350
|
setSizingMethod(null);
|
|
350
351
|
setSizingResult(null);
|
|
352
|
+
setSizingLoading(false);
|
|
351
353
|
setSizeGuide(null);
|
|
352
354
|
setProfileSaved(false);
|
|
353
355
|
formRef.current = {};
|
|
@@ -444,9 +446,16 @@ function PrimeStyleTryonInner({
|
|
|
444
446
|
if (formRef.current.fitPreference) m.fitPreference = formRef.current.fitPreference;
|
|
445
447
|
payload.measurements = m;
|
|
446
448
|
} else {
|
|
449
|
+
const qHeight = heightUnit === "ft" ? ftInToCm(parseFloat(formRef.current.heightFeet || "0"), parseFloat(formRef.current.heightInches || "0")) : parseFloat(formRef.current.height || "0");
|
|
450
|
+
const qWeight = weightUnit === "lbs" ? lbsToKg(parseFloat(formRef.current.weight || "0")) : parseFloat(formRef.current.weight || "0");
|
|
451
|
+
if (!qHeight || qHeight < 100 || !qWeight || qWeight < 30) {
|
|
452
|
+
console.warn("[PrimeStyle] Skipping sizing — invalid height/weight:", { qHeight, qWeight });
|
|
453
|
+
setSizingLoading(false);
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
447
456
|
payload.quickEstimate = {
|
|
448
|
-
heightCm:
|
|
449
|
-
weightKg:
|
|
457
|
+
heightCm: qHeight,
|
|
458
|
+
weightKg: qWeight,
|
|
450
459
|
gender: formRef.current.gender || "male"
|
|
451
460
|
};
|
|
452
461
|
}
|
|
@@ -459,8 +468,13 @@ function PrimeStyleTryonInner({
|
|
|
459
468
|
if (res.ok) {
|
|
460
469
|
const data = await res.json();
|
|
461
470
|
setSizingResult(data);
|
|
471
|
+
} else {
|
|
472
|
+
console.warn("[PrimeStyle] Sizing API error:", res.status, await res.text().catch(() => ""));
|
|
462
473
|
}
|
|
463
|
-
} catch {
|
|
474
|
+
} catch (err) {
|
|
475
|
+
console.warn("[PrimeStyle] Sizing request failed:", err);
|
|
476
|
+
} finally {
|
|
477
|
+
setSizingLoading(false);
|
|
464
478
|
}
|
|
465
479
|
}, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle]);
|
|
466
480
|
const handleSubmit = useCallback(async () => {
|
|
@@ -473,7 +487,10 @@ function PrimeStyleTryonInner({
|
|
|
473
487
|
}
|
|
474
488
|
completedRef.current = false;
|
|
475
489
|
setView("processing");
|
|
476
|
-
if (sizingMethod)
|
|
490
|
+
if (sizingMethod) {
|
|
491
|
+
setSizingLoading(true);
|
|
492
|
+
submitSizing();
|
|
493
|
+
}
|
|
477
494
|
try {
|
|
478
495
|
const modelImage = await compressImage(selectedFile);
|
|
479
496
|
const response = await apiRef.current.submitTryOn(modelImage, productImage);
|
|
@@ -536,6 +553,7 @@ function PrimeStyleTryonInner({
|
|
|
536
553
|
setErrorMessage(null);
|
|
537
554
|
setSizingMethod(null);
|
|
538
555
|
setSizingResult(null);
|
|
556
|
+
setSizingLoading(false);
|
|
539
557
|
setProfileSaved(false);
|
|
540
558
|
setView("upload");
|
|
541
559
|
}, [previewUrl, cleanupJob]);
|
|
@@ -733,75 +751,71 @@ function PrimeStyleTryonInner({
|
|
|
733
751
|
] });
|
|
734
752
|
}
|
|
735
753
|
function UploadView() {
|
|
736
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
},
|
|
773
|
-
children: [
|
|
774
|
-
/* @__PURE__ */ jsx(
|
|
775
|
-
"input",
|
|
776
|
-
{
|
|
777
|
-
ref: fileInputRef,
|
|
778
|
-
type: "file",
|
|
779
|
-
accept: "image/jpeg,image/png,image/webp",
|
|
780
|
-
style: { display: "none" },
|
|
781
|
-
onChange: (e) => {
|
|
782
|
-
const f = e.target.files?.[0];
|
|
783
|
-
if (f) handleFileSelect(f);
|
|
784
|
-
}
|
|
754
|
+
return /* @__PURE__ */ jsx(Fragment, { children: selectedFile && previewUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
755
|
+
/* @__PURE__ */ jsxs("div", { className: cx("ps-tryon-preview", cn.preview), children: [
|
|
756
|
+
/* @__PURE__ */ jsx("img", { src: previewUrl, alt: "Your photo", className: cn.previewImage }),
|
|
757
|
+
/* @__PURE__ */ jsx("button", { onClick: handleRemovePreview, className: cx("ps-tryon-preview-remove", cn.removeButton), children: "×" })
|
|
758
|
+
] }),
|
|
759
|
+
/* @__PURE__ */ jsxs("button", { onClick: () => setView("sizing-choice"), className: cx("ps-tryon-submit", cn.submitButton), children: [
|
|
760
|
+
"Continue to Sizing ",
|
|
761
|
+
/* @__PURE__ */ jsx(ArrowRightIcon, {})
|
|
762
|
+
] })
|
|
763
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
764
|
+
"div",
|
|
765
|
+
{
|
|
766
|
+
className: cx(`ps-tryon-upload${dragOver ? " ps-tryon-drag-over" : ""}`, cn.uploadZone),
|
|
767
|
+
onClick: () => fileInputRef.current?.click(),
|
|
768
|
+
onDragOver: (e) => {
|
|
769
|
+
e.preventDefault();
|
|
770
|
+
setDragOver(true);
|
|
771
|
+
},
|
|
772
|
+
onDragLeave: () => setDragOver(false),
|
|
773
|
+
onDrop: (e) => {
|
|
774
|
+
e.preventDefault();
|
|
775
|
+
setDragOver(false);
|
|
776
|
+
const f = e.dataTransfer?.files?.[0];
|
|
777
|
+
if (f) handleFileSelect(f);
|
|
778
|
+
},
|
|
779
|
+
children: [
|
|
780
|
+
/* @__PURE__ */ jsx(
|
|
781
|
+
"input",
|
|
782
|
+
{
|
|
783
|
+
ref: fileInputRef,
|
|
784
|
+
type: "file",
|
|
785
|
+
accept: "image/jpeg,image/png,image/webp",
|
|
786
|
+
style: { display: "none" },
|
|
787
|
+
onChange: (e) => {
|
|
788
|
+
const f = e.target.files?.[0];
|
|
789
|
+
if (f) handleFileSelect(f);
|
|
785
790
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
791
|
+
}
|
|
792
|
+
),
|
|
793
|
+
/* @__PURE__ */ jsx(UploadIcon, {}),
|
|
794
|
+
/* @__PURE__ */ jsx("p", { className: cx("ps-tryon-upload-text", cn.uploadText), children: "Drop or upload your full body photo!" }),
|
|
795
|
+
/* @__PURE__ */ jsx("p", { className: cx("ps-tryon-upload-hint", cn.uploadHint), children: "JPEG, PNG or WebP (max 10MB)" })
|
|
796
|
+
]
|
|
797
|
+
}
|
|
798
|
+
) });
|
|
794
799
|
}
|
|
795
800
|
function SizingChoiceView() {
|
|
796
801
|
const sgAvailable = sizeGuide?.found === true;
|
|
797
|
-
const
|
|
798
|
-
|
|
802
|
+
const sgChecked = !sizeGuideFetching && sizeGuide !== null;
|
|
803
|
+
if (sizeGuideFetching) {
|
|
804
|
+
return /* @__PURE__ */ jsx("div", { className: "ps-tryon-sizing-choice", children: /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sg-checking", children: [
|
|
805
|
+
/* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-checking-spinner" }),
|
|
806
|
+
/* @__PURE__ */ jsx("h3", { className: "ps-tryon-section-title", children: "Checking size guide..." }),
|
|
807
|
+
/* @__PURE__ */ jsx("p", { className: "ps-tryon-sg-checking-sub", children: "Looking for size chart data for this product" })
|
|
808
|
+
] }) });
|
|
809
|
+
}
|
|
799
810
|
return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sizing-choice", children: [
|
|
800
811
|
/* @__PURE__ */ jsx("h3", { className: "ps-tryon-section-title", children: "How would you like to find your size?" }),
|
|
801
|
-
|
|
802
|
-
|
|
812
|
+
sgChecked && !sgAvailable && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-notice", children: "Size guide is not available for this product — sizing will use standard measurements" }),
|
|
813
|
+
sgChecked && sgAvailable && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sg-notice ps-tryon-sg-found", children: [
|
|
814
|
+
/* @__PURE__ */ jsx(CheckIcon, { size: 14 }),
|
|
815
|
+
" Size guide found for this product"
|
|
816
|
+
] }),
|
|
803
817
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-choice-cards", children: [
|
|
804
|
-
/* @__PURE__ */ jsxs("button", { className:
|
|
818
|
+
/* @__PURE__ */ jsxs("button", { className: "ps-tryon-choice-card", onClick: () => {
|
|
805
819
|
setSizingMethod("exact");
|
|
806
820
|
setView("sizing-form");
|
|
807
821
|
}, children: [
|
|
@@ -812,7 +826,7 @@ function PrimeStyleTryonInner({
|
|
|
812
826
|
] }),
|
|
813
827
|
/* @__PURE__ */ jsx("span", { className: "ps-tryon-choice-badge", children: "Best accuracy" })
|
|
814
828
|
] }),
|
|
815
|
-
/* @__PURE__ */ jsxs("button", { className:
|
|
829
|
+
/* @__PURE__ */ jsxs("button", { className: "ps-tryon-choice-card", onClick: () => {
|
|
816
830
|
setSizingMethod("quick");
|
|
817
831
|
setView("sizing-form");
|
|
818
832
|
}, children: [
|
|
@@ -839,6 +853,17 @@ function PrimeStyleTryonInner({
|
|
|
839
853
|
const isFemale = formGender === "female";
|
|
840
854
|
const isCm = sizingUnit === "cm";
|
|
841
855
|
return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sizing-form", children: [
|
|
856
|
+
profiles.length > 0 && /* @__PURE__ */ jsx("div", { className: "ps-tryon-profile-bar", children: /* @__PURE__ */ jsxs("select", { className: "ps-tryon-profile-select", value: activeProfileId || "", onChange: (e) => {
|
|
857
|
+
if (e.target.value) applyProfile(e.target.value);
|
|
858
|
+
}, children: [
|
|
859
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Auto-fill from saved profile..." }),
|
|
860
|
+
profiles.map((p) => /* @__PURE__ */ jsxs("option", { value: p.id, children: [
|
|
861
|
+
p.name,
|
|
862
|
+
" (",
|
|
863
|
+
p.gender === "female" ? "Women's" : "Men's",
|
|
864
|
+
")"
|
|
865
|
+
] }, p.id))
|
|
866
|
+
] }) }),
|
|
842
867
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
|
|
843
868
|
/* @__PURE__ */ jsx("label", { children: "I'm shopping for" }),
|
|
844
869
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-unit-toggle", children: [
|
|
@@ -934,11 +959,16 @@ function PrimeStyleTryonInner({
|
|
|
934
959
|
] });
|
|
935
960
|
}
|
|
936
961
|
function ResultView() {
|
|
937
|
-
const
|
|
962
|
+
const hasSizing = !!sizingResult || sizingLoading;
|
|
963
|
+
const hasBoth = !!resultImageUrl && hasSizing;
|
|
938
964
|
const [profileName, setProfileName] = useState("");
|
|
939
965
|
return /* @__PURE__ */ jsxs("div", { className: `ps-tryon-result-layout${hasBoth ? " ps-tryon-result-split" : ""}`, children: [
|
|
940
966
|
resultImageUrl && /* @__PURE__ */ jsx("div", { className: "ps-tryon-result-image-col", children: /* @__PURE__ */ jsx("img", { src: resultImageUrl, alt: "Try-on result", className: cn.resultImage }) }),
|
|
941
967
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-result-info-col", children: [
|
|
968
|
+
sizingLoading && !sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-size-recommend ps-tryon-sizing-loading", children: [
|
|
969
|
+
/* @__PURE__ */ jsx("div", { className: "ps-tryon-size-loading-spinner" }),
|
|
970
|
+
/* @__PURE__ */ jsx("p", { className: "ps-tryon-size-reasoning", children: "Analyzing your size..." })
|
|
971
|
+
] }),
|
|
942
972
|
sizingResult && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-size-recommend", children: [
|
|
943
973
|
/* @__PURE__ */ jsx("div", { className: "ps-tryon-size-badge", children: sizingResult.recommendedSize }),
|
|
944
974
|
/* @__PURE__ */ jsxs("div", { className: "ps-tryon-size-confidence", children: [
|
|
@@ -1312,15 +1342,25 @@ const STYLES = `
|
|
|
1312
1342
|
padding: 3px 10px; border-radius: 20px; flex-shrink: 0;
|
|
1313
1343
|
background: rgba(187,148,92,0.12); color: #bb945c; font-size: 10px; font-weight: 600;
|
|
1314
1344
|
}
|
|
1315
|
-
.ps-tryon-choice-disabled { opacity: 0.4; cursor: not-allowed !important; pointer-events: none; }
|
|
1316
|
-
.ps-tryon-choice-disabled:hover { border-color: #333; transform: none; box-shadow: none; }
|
|
1317
|
-
.ps-tryon-choice-disabled::before { display: none; }
|
|
1318
|
-
.ps-tryon-choice-loading { opacity: 0.5; cursor: wait !important; pointer-events: none; }
|
|
1319
1345
|
.ps-tryon-sg-notice {
|
|
1320
1346
|
font-size: 12px; color: #999; text-align: center; padding: 10px 14px;
|
|
1321
1347
|
margin-bottom: 12px; border: 1px solid #333; border-radius: 10px; background: #1a1b1a;
|
|
1322
1348
|
}
|
|
1323
|
-
.ps-tryon-sg-
|
|
1349
|
+
.ps-tryon-sg-found {
|
|
1350
|
+
color: #4ade80; border-color: rgba(74,222,128,0.2); background: rgba(74,222,128,0.05);
|
|
1351
|
+
display: flex; align-items: center; justify-content: center; gap: 6px;
|
|
1352
|
+
}
|
|
1353
|
+
.ps-tryon-sg-found svg { stroke: #4ade80; }
|
|
1354
|
+
.ps-tryon-sg-checking {
|
|
1355
|
+
display: flex; flex-direction: column; align-items: center; padding: 40px 20px; text-align: center;
|
|
1356
|
+
}
|
|
1357
|
+
.ps-tryon-sg-checking-spinner {
|
|
1358
|
+
width: 40px; height: 40px; border: 3px solid #333;
|
|
1359
|
+
border-top-color: #bb945c; border-radius: 50%;
|
|
1360
|
+
animation: ps-spin 0.8s linear infinite; margin-bottom: 20px;
|
|
1361
|
+
}
|
|
1362
|
+
.ps-tryon-sg-checking .ps-tryon-section-title { margin-bottom: 6px; }
|
|
1363
|
+
.ps-tryon-sg-checking-sub { font-size: 13px; color: #999; margin: 0; }
|
|
1324
1364
|
|
|
1325
1365
|
/* Sizing form */
|
|
1326
1366
|
.ps-tryon-sizing-form { display: flex; flex-direction: column; gap: 12px; }
|
|
@@ -1431,6 +1471,13 @@ const STYLES = `
|
|
|
1431
1471
|
background: linear-gradient(135deg, #bb945c, #d6ba7d);
|
|
1432
1472
|
color: #111; font-size: 20px; font-weight: 700; margin-bottom: 10px;
|
|
1433
1473
|
}
|
|
1474
|
+
.ps-tryon-sizing-loading { text-align: center; padding: 20px 0; }
|
|
1475
|
+
.ps-tryon-size-loading-spinner {
|
|
1476
|
+
width: 36px; height: 36px; border: 3px solid #333;
|
|
1477
|
+
border-top-color: #bb945c; border-radius: 50%;
|
|
1478
|
+
animation: ps-spin 0.8s linear infinite; margin: 0 auto 12px;
|
|
1479
|
+
}
|
|
1480
|
+
@keyframes ps-spin { to { transform: rotate(360deg); } }
|
|
1434
1481
|
.ps-tryon-size-confidence { font-size: 12px; color: #999; margin-bottom: 8px; }
|
|
1435
1482
|
.ps-conf-high { color: #4ade80; } .ps-conf-medium { color: #bb945c; } .ps-conf-low { color: #ef4444; }
|
|
1436
1483
|
.ps-tryon-size-reasoning { font-size: 13px; color: #ccc; line-height: 1.5; margin-bottom: 12px; }
|