@primestyleai/tryon 2.0.3 → 2.0.5

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.
@@ -7,6 +7,7 @@ function cx(base, override) {
7
7
  }
8
8
  const LS_PREFIX = "primestyle_";
9
9
  function lsGet(key, fallback) {
10
+ if (typeof window === "undefined") return fallback;
10
11
  try {
11
12
  const raw = localStorage.getItem(LS_PREFIX + key);
12
13
  return raw ? JSON.parse(raw) : fallback;
@@ -37,6 +38,7 @@ const SIZING_COUNTRIES = [
37
38
  const STEP_LABELS = ["", "Welcome", "Photo", "Size", "Generate", "Results"];
38
39
  const TOTAL_STEPS = 5;
39
40
  function detectLocale() {
41
+ if (typeof window === "undefined") return "US";
40
42
  const l = (navigator.language || "en-US").toLowerCase();
41
43
  if (l.includes("en-gb") || l.includes("en-au")) return "UK";
42
44
  if (l.includes("en")) return "US";
@@ -182,6 +184,8 @@ function PrimeStyleTryon({
182
184
  const [sizingMethod, setSizingMethod] = useState(null);
183
185
  const [sizingResult, setSizingResult] = useState(null);
184
186
  const [sizeGuide, setSizeGuide] = useState(null);
187
+ const [sizeGuideFetching, setSizeGuideFetching] = useState(false);
188
+ const sizeGuideFetchedRef = useRef(false);
185
189
  const [sizingCountry, setSizingCountry] = useState(detectLocale);
186
190
  const imperial = isImperial(sizingCountry);
187
191
  const [sizingUnit, setSizingUnit] = useState(imperial ? "in" : "cm");
@@ -202,6 +206,17 @@ function PrimeStyleTryon({
202
206
  const pollingRef = useRef(null);
203
207
  const completedRef = useRef(false);
204
208
  const bodyRef = useRef(null);
209
+ useEffect(() => {
210
+ const id = "ps-tryon-styles";
211
+ if (document.getElementById(id)) return;
212
+ const el = document.createElement("style");
213
+ el.id = id;
214
+ el.textContent = STYLES;
215
+ document.head.appendChild(el);
216
+ return () => {
217
+ el.remove();
218
+ };
219
+ }, []);
205
220
  useEffect(() => {
206
221
  try {
207
222
  const key = getApiKey();
@@ -257,6 +272,21 @@ function PrimeStyleTryon({
257
272
  useEffect(() => {
258
273
  lsSet("history", history);
259
274
  }, [history]);
275
+ useEffect(() => {
276
+ if (view !== "sizing-choice" || sizeGuideFetchedRef.current || !apiRef.current) return;
277
+ sizeGuideFetchedRef.current = true;
278
+ setSizeGuideFetching(true);
279
+ const baseUrl = getApiUrl(apiUrl);
280
+ const key = getApiKey();
281
+ fetch(`${baseUrl}/api/v1/sizing/sizeguide`, {
282
+ method: "POST",
283
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${key}` },
284
+ body: JSON.stringify({ product: { title: productTitle, variants: [] } })
285
+ }).then((r) => r.ok ? r.json() : null).then((data) => {
286
+ if (data) setSizeGuide(data);
287
+ else setSizeGuide({ found: false });
288
+ }).catch(() => setSizeGuide({ found: false })).finally(() => setSizeGuideFetching(false));
289
+ }, [view, apiUrl, productTitle]);
260
290
  const stepIndex = useMemo(() => {
261
291
  switch (view) {
262
292
  case "welcome":
@@ -293,6 +323,8 @@ function PrimeStyleTryon({
293
323
  setProfileSaved(false);
294
324
  formRef.current = {};
295
325
  setFormGender("male");
326
+ sizeGuideFetchedRef.current = false;
327
+ setSizeGuideFetching(false);
296
328
  unsubRef.current?.();
297
329
  unsubRef.current = null;
298
330
  if (pollingRef.current) {
@@ -719,10 +751,15 @@ function PrimeStyleTryon({
719
751
  ] });
720
752
  }
721
753
  function SizingChoiceView() {
754
+ const sgAvailable = sizeGuide?.found === true;
755
+ const sgDisabled = !sizeGuideFetching && !sgAvailable;
756
+ const disabledClass = sgDisabled ? " ps-tryon-choice-disabled" : sizeGuideFetching ? " ps-tryon-choice-loading" : "";
722
757
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sizing-choice", children: [
723
758
  /* @__PURE__ */ jsx("h3", { className: "ps-tryon-section-title", children: "How would you like to find your size?" }),
759
+ sgDisabled && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-notice", children: "Size guide is not available for this product" }),
760
+ sizeGuideFetching && /* @__PURE__ */ jsx("div", { className: "ps-tryon-sg-notice ps-tryon-sg-loading", children: "Checking size guide availability..." }),
724
761
  /* @__PURE__ */ jsxs("div", { className: "ps-tryon-choice-cards", children: [
725
- /* @__PURE__ */ jsxs("button", { className: "ps-tryon-choice-card", onClick: () => {
762
+ /* @__PURE__ */ jsxs("button", { className: `ps-tryon-choice-card${disabledClass}`, disabled: sgDisabled || sizeGuideFetching, onClick: () => {
726
763
  setSizingMethod("exact");
727
764
  setView("sizing-form");
728
765
  }, children: [
@@ -733,7 +770,7 @@ function PrimeStyleTryon({
733
770
  ] }),
734
771
  /* @__PURE__ */ jsx("span", { className: "ps-tryon-choice-badge", children: "Best accuracy" })
735
772
  ] }),
736
- /* @__PURE__ */ jsxs("button", { className: "ps-tryon-choice-card", onClick: () => {
773
+ /* @__PURE__ */ jsxs("button", { className: `ps-tryon-choice-card${disabledClass}`, disabled: sgDisabled || sizeGuideFetching, onClick: () => {
737
774
  setSizingMethod("quick");
738
775
  setView("sizing-form");
739
776
  }, children: [
@@ -1051,8 +1088,7 @@ function PrimeStyleTryon({
1051
1088
  /* @__PURE__ */ jsx("a", { href: "https://myaifitting.com", target: "_blank", rel: "noopener noreferrer", children: "PrimeStyle AI" })
1052
1089
  ] })
1053
1090
  ] }) }),
1054
- /* @__PURE__ */ jsx(ProfileDetailModal, {}),
1055
- /* @__PURE__ */ jsx("style", { children: STYLES })
1091
+ /* @__PURE__ */ jsx(ProfileDetailModal, {})
1056
1092
  ] });
1057
1093
  }
1058
1094
  const STYLES = `
@@ -1228,6 +1264,15 @@ const STYLES = `
1228
1264
  padding: 3px 10px; border-radius: 20px; flex-shrink: 0;
1229
1265
  background: rgba(187,148,92,0.12); color: #bb945c; font-size: 10px; font-weight: 600;
1230
1266
  }
1267
+ .ps-tryon-choice-disabled { opacity: 0.4; cursor: not-allowed !important; pointer-events: none; }
1268
+ .ps-tryon-choice-disabled:hover { border-color: #333; transform: none; box-shadow: none; }
1269
+ .ps-tryon-choice-disabled::before { display: none; }
1270
+ .ps-tryon-choice-loading { opacity: 0.5; cursor: wait !important; pointer-events: none; }
1271
+ .ps-tryon-sg-notice {
1272
+ font-size: 12px; color: #999; text-align: center; padding: 10px 14px;
1273
+ margin-bottom: 12px; border: 1px solid #333; border-radius: 10px; background: #1a1b1a;
1274
+ }
1275
+ .ps-tryon-sg-loading { border-style: dashed; }
1231
1276
 
1232
1277
  /* Sizing form */
1233
1278
  .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": "2.0.3",
3
+ "version": "2.0.5",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",