@primestyleai/tryon 3.1.1 → 3.2.1

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 +111 -87
  2. package/package.json +1 -1
@@ -434,8 +434,6 @@ function PrimeStyleTryonInner({
434
434
  if (sizeGuide?.found) payload.sizeGuide = sizeGuide;
435
435
  if (sizingMethod === "exact") {
436
436
  const m = { gender: formRef.current.gender || "male" };
437
- if (formRef.current.height) m.heightCm = heightUnit === "ft" ? ftInToCm(parseFloat(formRef.current.heightFeet || "0"), parseFloat(formRef.current.heightInches || "0")) : parseFloat(formRef.current.height);
438
- if (formRef.current.weight) m.weightKg = weightUnit === "lbs" ? lbsToKg(parseFloat(formRef.current.weight)) : parseFloat(formRef.current.weight);
439
437
  const keys = ["chest", "bust", "waist", "hips", "shoulderWidth", "sleeveLength", "inseam", "neckCircumference", "footLengthCm"];
440
438
  for (const k of keys) {
441
439
  if (formRef.current[k]) m[k] = sizingUnit === "in" ? inToCm(parseFloat(formRef.current[k])) : parseFloat(formRef.current[k]);
@@ -446,9 +444,16 @@ function PrimeStyleTryonInner({
446
444
  if (formRef.current.fitPreference) m.fitPreference = formRef.current.fitPreference;
447
445
  payload.measurements = m;
448
446
  } else {
447
+ const qHeight = heightUnit === "ft" ? ftInToCm(parseFloat(formRef.current.heightFeet || "0"), parseFloat(formRef.current.heightInches || "0")) : parseFloat(formRef.current.height || "0");
448
+ const qWeight = weightUnit === "lbs" ? lbsToKg(parseFloat(formRef.current.weight || "0")) : parseFloat(formRef.current.weight || "0");
449
+ if (!qHeight || qHeight < 100 || !qWeight || qWeight < 30) {
450
+ console.warn("[PrimeStyle] Skipping sizing — invalid height/weight:", { qHeight, qWeight });
451
+ setSizingLoading(false);
452
+ return;
453
+ }
449
454
  payload.quickEstimate = {
450
- heightCm: heightUnit === "ft" ? ftInToCm(parseFloat(formRef.current.heightFeet || "0"), parseFloat(formRef.current.heightInches || "0")) : parseFloat(formRef.current.height || "0"),
451
- weightKg: weightUnit === "lbs" ? lbsToKg(parseFloat(formRef.current.weight || "0")) : parseFloat(formRef.current.weight || "0"),
455
+ heightCm: qHeight,
456
+ weightKg: qWeight,
452
457
  gender: formRef.current.gender || "male"
453
458
  };
454
459
  }
@@ -744,75 +749,71 @@ function PrimeStyleTryonInner({
744
749
  ] });
745
750
  }
746
751
  function UploadView() {
747
- return /* @__PURE__ */ jsxs(Fragment, { children: [
748
- profiles.length > 0 && /* @__PURE__ */ jsx("div", { className: "ps-tryon-profile-bar", children: /* @__PURE__ */ jsxs("select", { className: "ps-tryon-profile-select", value: activeProfileId || "", onChange: (e) => {
749
- if (e.target.value) applyProfile(e.target.value);
750
- }, children: [
751
- /* @__PURE__ */ jsx("option", { value: "", children: "Auto-fill from saved profile..." }),
752
- profiles.map((p) => /* @__PURE__ */ jsxs("option", { value: p.id, children: [
753
- p.name,
754
- " (",
755
- p.gender === "female" ? "Women's" : "Men's",
756
- ")"
757
- ] }, p.id))
758
- ] }) }),
759
- selectedFile && previewUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
760
- /* @__PURE__ */ jsxs("div", { className: cx("ps-tryon-preview", cn.preview), children: [
761
- /* @__PURE__ */ jsx("img", { src: previewUrl, alt: "Your photo", className: cn.previewImage }),
762
- /* @__PURE__ */ jsx("button", { onClick: handleRemovePreview, className: cx("ps-tryon-preview-remove", cn.removeButton), children: "×" })
763
- ] }),
764
- /* @__PURE__ */ jsxs("button", { onClick: () => setView("sizing-choice"), className: cx("ps-tryon-submit", cn.submitButton), children: [
765
- "Continue to Sizing ",
766
- /* @__PURE__ */ jsx(ArrowRightIcon, {})
767
- ] })
768
- ] }) : /* @__PURE__ */ jsxs(
769
- "div",
770
- {
771
- className: cx(`ps-tryon-upload${dragOver ? " ps-tryon-drag-over" : ""}`, cn.uploadZone),
772
- onClick: () => fileInputRef.current?.click(),
773
- onDragOver: (e) => {
774
- e.preventDefault();
775
- setDragOver(true);
776
- },
777
- onDragLeave: () => setDragOver(false),
778
- onDrop: (e) => {
779
- e.preventDefault();
780
- setDragOver(false);
781
- const f = e.dataTransfer?.files?.[0];
782
- if (f) handleFileSelect(f);
783
- },
784
- children: [
785
- /* @__PURE__ */ jsx(
786
- "input",
787
- {
788
- ref: fileInputRef,
789
- type: "file",
790
- accept: "image/jpeg,image/png,image/webp",
791
- style: { display: "none" },
792
- onChange: (e) => {
793
- const f = e.target.files?.[0];
794
- if (f) handleFileSelect(f);
795
- }
752
+ return /* @__PURE__ */ jsx(Fragment, { children: selectedFile && previewUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
753
+ /* @__PURE__ */ jsxs("div", { className: cx("ps-tryon-preview", cn.preview), children: [
754
+ /* @__PURE__ */ jsx("img", { src: previewUrl, alt: "Your photo", className: cn.previewImage }),
755
+ /* @__PURE__ */ jsx("button", { onClick: handleRemovePreview, className: cx("ps-tryon-preview-remove", cn.removeButton), children: "×" })
756
+ ] }),
757
+ /* @__PURE__ */ jsxs("button", { onClick: () => setView("sizing-choice"), className: cx("ps-tryon-submit", cn.submitButton), children: [
758
+ "Continue to Sizing ",
759
+ /* @__PURE__ */ jsx(ArrowRightIcon, {})
760
+ ] })
761
+ ] }) : /* @__PURE__ */ jsxs(
762
+ "div",
763
+ {
764
+ className: cx(`ps-tryon-upload${dragOver ? " ps-tryon-drag-over" : ""}`, cn.uploadZone),
765
+ onClick: () => fileInputRef.current?.click(),
766
+ onDragOver: (e) => {
767
+ e.preventDefault();
768
+ setDragOver(true);
769
+ },
770
+ onDragLeave: () => setDragOver(false),
771
+ onDrop: (e) => {
772
+ e.preventDefault();
773
+ setDragOver(false);
774
+ const f = e.dataTransfer?.files?.[0];
775
+ if (f) handleFileSelect(f);
776
+ },
777
+ children: [
778
+ /* @__PURE__ */ jsx(
779
+ "input",
780
+ {
781
+ ref: fileInputRef,
782
+ type: "file",
783
+ accept: "image/jpeg,image/png,image/webp",
784
+ style: { display: "none" },
785
+ onChange: (e) => {
786
+ const f = e.target.files?.[0];
787
+ if (f) handleFileSelect(f);
796
788
  }
797
- ),
798
- /* @__PURE__ */ jsx(UploadIcon, {}),
799
- /* @__PURE__ */ jsx("p", { className: cx("ps-tryon-upload-text", cn.uploadText), children: "Drop or upload your full body photo!" }),
800
- /* @__PURE__ */ jsx("p", { className: cx("ps-tryon-upload-hint", cn.uploadHint), children: "JPEG, PNG or WebP (max 10MB)" })
801
- ]
802
- }
803
- )
804
- ] });
789
+ }
790
+ ),
791
+ /* @__PURE__ */ jsx(UploadIcon, {}),
792
+ /* @__PURE__ */ jsx("p", { className: cx("ps-tryon-upload-text", cn.uploadText), children: "Drop or upload your full body photo!" }),
793
+ /* @__PURE__ */ jsx("p", { className: cx("ps-tryon-upload-hint", cn.uploadHint), children: "JPEG, PNG or WebP (max 10MB)" })
794
+ ]
795
+ }
796
+ ) });
805
797
  }
806
798
  function SizingChoiceView() {
807
799
  const sgAvailable = sizeGuide?.found === true;
808
- const sgDisabled = !sizeGuideFetching && !sgAvailable;
809
- const disabledClass = sgDisabled ? " ps-tryon-choice-disabled" : sizeGuideFetching ? " ps-tryon-choice-loading" : "";
800
+ const sgChecked = !sizeGuideFetching && sizeGuide !== null;
801
+ if (sizeGuideFetching) {
802
+ return /* @__PURE__ */ jsx("div", { className: "ps-tryon-sizing-choice", children: /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sg-checking", children: [
803
+ /* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-checking-spinner" }),
804
+ /* @__PURE__ */ jsx("h3", { className: "ps-tryon-section-title", children: "Checking size guide..." }),
805
+ /* @__PURE__ */ jsx("p", { className: "ps-tryon-sg-checking-sub", children: "Looking for size chart data for this product" })
806
+ ] }) });
807
+ }
810
808
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sizing-choice", children: [
811
809
  /* @__PURE__ */ jsx("h3", { className: "ps-tryon-section-title", children: "How would you like to find your size?" }),
812
- sgDisabled && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-notice", children: "Size guide is not available for this product" }),
813
- sizeGuideFetching && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-notice ps-tryon-sg-loading", children: "Checking size guide availability..." }),
810
+ sgChecked && !sgAvailable && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-notice", children: "Size guide is not available for this product — sizing will use standard measurements" }),
811
+ sgChecked && sgAvailable && /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sg-notice ps-tryon-sg-found", children: [
812
+ /* @__PURE__ */ jsx(CheckIcon, { size: 14 }),
813
+ " Size guide found for this product"
814
+ ] }),
814
815
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-choice-cards", children: [
815
- /* @__PURE__ */ jsxs("button", { className: `ps-tryon-choice-card${disabledClass}`, disabled: sgDisabled || sizeGuideFetching, onClick: () => {
816
+ /* @__PURE__ */ jsxs("button", { className: "ps-tryon-choice-card", onClick: () => {
816
817
  setSizingMethod("exact");
817
818
  setView("sizing-form");
818
819
  }, children: [
@@ -823,7 +824,7 @@ function PrimeStyleTryonInner({
823
824
  ] }),
824
825
  /* @__PURE__ */ jsx("span", { className: "ps-tryon-choice-badge", children: "Best accuracy" })
825
826
  ] }),
826
- /* @__PURE__ */ jsxs("button", { className: `ps-tryon-choice-card${disabledClass}`, disabled: sgDisabled || sizeGuideFetching, onClick: () => {
827
+ /* @__PURE__ */ jsxs("button", { className: "ps-tryon-choice-card", onClick: () => {
827
828
  setSizingMethod("quick");
828
829
  setView("sizing-form");
829
830
  }, children: [
@@ -850,6 +851,17 @@ function PrimeStyleTryonInner({
850
851
  const isFemale = formGender === "female";
851
852
  const isCm = sizingUnit === "cm";
852
853
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sizing-form", children: [
854
+ profiles.length > 0 && /* @__PURE__ */ jsx("div", { className: "ps-tryon-profile-bar", children: /* @__PURE__ */ jsxs("select", { className: "ps-tryon-profile-select", value: activeProfileId || "", onChange: (e) => {
855
+ if (e.target.value) applyProfile(e.target.value);
856
+ }, children: [
857
+ /* @__PURE__ */ jsx("option", { value: "", children: "Auto-fill from saved profile..." }),
858
+ profiles.map((p) => /* @__PURE__ */ jsxs("option", { value: p.id, children: [
859
+ p.name,
860
+ " (",
861
+ p.gender === "female" ? "Women's" : "Men's",
862
+ ")"
863
+ ] }, p.id))
864
+ ] }) }),
853
865
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
854
866
  /* @__PURE__ */ jsx("label", { children: "I'm shopping for" }),
855
867
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-unit-toggle", children: [
@@ -872,16 +884,6 @@ function PrimeStyleTryonInner({
872
884
  setHeightUnit(v === "cm" ? "cm" : "ft");
873
885
  setWeightUnit(v === "cm" ? "kg" : "lbs");
874
886
  } }) }),
875
- /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
876
- /* @__PURE__ */ jsx("label", { children: "Height" }),
877
- heightUnit === "ft" ? /* @__PURE__ */ jsxs("div", { className: "ps-tryon-height-ft", children: [
878
- /* @__PURE__ */ jsx("input", { type: "number", placeholder: "5", defaultValue: formRef.current.heightFeet || "", onInput: (e) => updateField("heightFeet", e.target.value) }),
879
- /* @__PURE__ */ jsx("span", { children: "ft" }),
880
- /* @__PURE__ */ jsx("input", { type: "number", placeholder: "4", defaultValue: formRef.current.heightInches || "", onInput: (e) => updateField("heightInches", e.target.value) }),
881
- /* @__PURE__ */ jsx("span", { children: "in" })
882
- ] }) : /* @__PURE__ */ jsx("input", { type: "number", placeholder: "e.g. 175", defaultValue: formRef.current.height || "", onInput: (e) => updateField("height", e.target.value) }),
883
- /* @__PURE__ */ jsx(UnitToggle, { options: [{ label: "cm", value: "cm" }, { label: "ft", value: "ft" }], value: heightUnit, onChange: setHeightUnit })
884
- ] }),
885
887
  sizingMethod === "exact" ? /* @__PURE__ */ jsxs(Fragment, { children: [
886
888
  isFemale ? /* @__PURE__ */ jsxs(Fragment, { children: [
887
889
  /* @__PURE__ */ jsx(InputRow, { label: "Bust *", fieldKey: "bust", placeholder: isCm ? "e.g. 88" : "e.g. 35", type: "number", unit: sizingUnit }),
@@ -917,11 +919,23 @@ function PrimeStyleTryonInner({
917
919
  /* @__PURE__ */ jsx(InputRow, { label: "Foot length", fieldKey: "footLengthCm", placeholder: isCm ? "e.g. 27" : "e.g. 10.5", type: "number", unit: sizingUnit }),
918
920
  /* @__PURE__ */ jsx(InputRow, { label: shoeField.label, fieldKey: shoeField.key, placeholder: shoeField.ph })
919
921
  ] })
920
- ] }) : /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
921
- /* @__PURE__ */ jsx("label", { children: "Weight" }),
922
- /* @__PURE__ */ jsx("input", { type: "number", placeholder: weightUnit === "lbs" ? "e.g. 170" : "e.g. 75", defaultValue: formRef.current.weight || "", onInput: (e) => updateField("weight", e.target.value) }),
923
- /* @__PURE__ */ jsx(UnitToggle, { options: [{ label: "kg", value: "kg" }, { label: "lbs", value: "lbs" }], value: weightUnit, onChange: setWeightUnit })
924
- ] }) }),
922
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
923
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
924
+ /* @__PURE__ */ jsx("label", { children: "Height *" }),
925
+ heightUnit === "ft" ? /* @__PURE__ */ jsxs("div", { className: "ps-tryon-height-ft", children: [
926
+ /* @__PURE__ */ jsx("input", { type: "number", placeholder: "5", defaultValue: formRef.current.heightFeet || "", onInput: (e) => updateField("heightFeet", e.target.value) }),
927
+ /* @__PURE__ */ jsx("span", { children: "ft" }),
928
+ /* @__PURE__ */ jsx("input", { type: "number", placeholder: "4", defaultValue: formRef.current.heightInches || "", onInput: (e) => updateField("heightInches", e.target.value) }),
929
+ /* @__PURE__ */ jsx("span", { children: "in" })
930
+ ] }) : /* @__PURE__ */ jsx("input", { type: "number", placeholder: "e.g. 175", defaultValue: formRef.current.height || "", onInput: (e) => updateField("height", e.target.value) }),
931
+ /* @__PURE__ */ jsx(UnitToggle, { options: [{ label: "cm", value: "cm" }, { label: "ft", value: "ft" }], value: heightUnit, onChange: setHeightUnit })
932
+ ] }),
933
+ /* @__PURE__ */ jsxs("div", { className: "ps-tryon-input-row", children: [
934
+ /* @__PURE__ */ jsx("label", { children: "Weight *" }),
935
+ /* @__PURE__ */ jsx("input", { type: "number", placeholder: weightUnit === "lbs" ? "e.g. 170" : "e.g. 75", defaultValue: formRef.current.weight || "", onInput: (e) => updateField("weight", e.target.value) }),
936
+ /* @__PURE__ */ jsx(UnitToggle, { options: [{ label: "kg", value: "kg" }, { label: "lbs", value: "lbs" }], value: weightUnit, onChange: setWeightUnit })
937
+ ] })
938
+ ] }),
925
939
  /* @__PURE__ */ jsx("p", { className: "ps-tryon-disclaimer", children: "Fill in what you know — more measurements = better accuracy." }),
926
940
  /* @__PURE__ */ jsxs("button", { className: "ps-tryon-submit", onClick: handleSubmit, children: [
927
941
  "Get My Size & Try On ",
@@ -1328,15 +1342,25 @@ const STYLES = `
1328
1342
  padding: 3px 10px; border-radius: 20px; flex-shrink: 0;
1329
1343
  background: rgba(187,148,92,0.12); color: #bb945c; font-size: 10px; font-weight: 600;
1330
1344
  }
1331
- .ps-tryon-choice-disabled { opacity: 0.4; cursor: not-allowed !important; pointer-events: none; }
1332
- .ps-tryon-choice-disabled:hover { border-color: #333; transform: none; box-shadow: none; }
1333
- .ps-tryon-choice-disabled::before { display: none; }
1334
- .ps-tryon-choice-loading { opacity: 0.5; cursor: wait !important; pointer-events: none; }
1335
1345
  .ps-tryon-sg-notice {
1336
1346
  font-size: 12px; color: #999; text-align: center; padding: 10px 14px;
1337
1347
  margin-bottom: 12px; border: 1px solid #333; border-radius: 10px; background: #1a1b1a;
1338
1348
  }
1339
- .ps-tryon-sg-loading { border-style: dashed; }
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; }
1340
1364
 
1341
1365
  /* Sizing form */
1342
1366
  .ps-tryon-sizing-form { display: flex; flex-direction: column; gap: 12px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "3.1.1",
3
+ "version": "3.2.1",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",