@primestyleai/tryon 5.10.0 → 5.10.2

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.
@@ -840,12 +840,15 @@ const STYLES = `
840
840
  .ps-tryon-logo-img { height: var(--ps-logo-height); width: auto; }
841
841
  .ps-tryon-header-actions { display: flex; align-items: center; gap: 0.42vw; }
842
842
  .ps-tryon-header-icon {
843
- width: 2.2vw; height: 2.2vw; display: flex; align-items: center; justify-content: center;
844
- border: 1.5px solid var(--ps-border-color); border-radius: 0.52vw; background: transparent;
843
+ /* Pure vw sizing collapsed to ~8 px on 375 px mobile. Clamp so the icon
844
+ stays finger-tappable (min 30 px) while scaling up on large screens. */
845
+ width: clamp(30px, 2.2vw, 34px); height: clamp(30px, 2.2vw, 34px);
846
+ display: flex; align-items: center; justify-content: center;
847
+ border: 1.5px solid var(--ps-border-color); border-radius: clamp(6px, 0.52vw, 10px); background: transparent;
845
848
  cursor: pointer; color: var(--ps-text-secondary); transition: all 0.2s;
846
849
  }
847
850
  .ps-tryon-header-icon:hover { border-color: var(--ps-accent); color: var(--ps-accent); }
848
- .ps-tryon-header-icon svg { stroke: currentColor; fill: none; width: 0.9vw; height: 0.9vw; }
851
+ .ps-tryon-header-icon svg { stroke: currentColor; fill: none; width: clamp(14px, 0.9vw, 16px); height: clamp(14px, 0.9vw, 16px); }
849
852
  .ps-tryon-close {
850
853
  width: 2.2vw; height: 2.2vw; display: flex; align-items: center; justify-content: center;
851
854
  background: none; border: none; color: var(--ps-modal-close-color, #999);
@@ -893,7 +896,10 @@ const STYLES = `
893
896
  }
894
897
 
895
898
  .ps-tryon-lang-list {
896
- max-height: min(18vw, 280px); overflow-y: auto; padding: clamp(3px, 0.31vw, 5px);
899
+ /* max(...) picks the larger of the two values so the dropdown is tall
900
+ enough to scroll through a handful of languages on any viewport.
901
+ Pure min(18vw, 280px) collapsed to ~67 px on 375 px mobile. */
902
+ max-height: max(260px, min(18vw, 280px)); overflow-y: auto; padding: clamp(3px, 0.31vw, 5px);
897
903
  scrollbar-width: thin; scrollbar-color: rgba(0,0,0,0.15) transparent;
898
904
  }
899
905
  .ps-tryon-lang-item {
@@ -3286,6 +3292,33 @@ const STYLES = `
3286
3292
 
3287
3293
  /* ── Preserve existing previews and modal-wide overrides ── */
3288
3294
  .ps-tryon-preview { height: 320px; }
3295
+
3296
+ /* ── Drawer list (history / profiles / settings) ── */
3297
+ /* Pure vw gap (0.52vw = ~2 px on mobile) crushed list rows together. */
3298
+ .ps-tryon-drawer-list { gap: 12px !important; padding: 0 !important; }
3299
+ .ps-tryon-drawer { padding: 16px !important; }
3300
+
3301
+ /* ── Profile cards inside the sizing-profiles drawer ── */
3302
+ .ps-msp-card {
3303
+ padding: 16px !important;
3304
+ border-radius: 14px !important;
3305
+ }
3306
+ .ps-msp-card-tag { font-size: 11px !important; padding: 4px 8px !important; border-radius: 999px !important; }
3307
+ .ps-msp-card-circle { width: 72px !important; height: 72px !important; margin: 8px auto 12px !important; }
3308
+ .ps-msp-card-name { font-size: 16px !important; margin-bottom: 6px !important; }
3309
+ .ps-msp-meta-row { padding: 6px 0 !important; gap: 8px !important; }
3310
+ .ps-msp-card-meta { font-size: 12px !important; }
3311
+ .ps-msp-card-actions { gap: 8px !important; margin-top: 10px !important; }
3312
+ .ps-msp-card-select { font-size: 13px !important; padding: 10px 12px !important; border-radius: 8px !important; }
3313
+ .ps-msp-card-edit, .ps-msp-card-delete { width: 36px !important; height: 36px !important; border-radius: 8px !important; }
3314
+ .ps-msp-card-create { min-height: 120px !important; font-size: 14px !important; }
3315
+
3316
+ /* ── Language switcher dropdown ── */
3317
+ /* Default min(18vw, 280px) collapsed to ~67 px on mobile — unusable. */
3318
+ .ps-tryon-lang-list { max-height: 320px !important; }
3319
+ .ps-tryon-lang-item { padding: 10px 14px !important; gap: 10px !important; }
3320
+ .ps-tryon-lang-name { font-size: 14px !important; }
3321
+ .ps-tryon-lang-code { font-size: 11px !important; }
3289
3322
  }
3290
3323
 
3291
3324
  @keyframes ps-mobile-slide-up {
@@ -7854,30 +7887,22 @@ function MultiSectionMobile({
7854
7887
  children: t("Continue Shopping")
7855
7888
  }
7856
7889
  )
7857
- ] }) : sizingResult?.found === false ? (
7858
- // Backend couldn't find a size that fits Try-On is meaningless
7859
- // without a recommendation, so surface a clear terminal action.
7860
- /* @__PURE__ */ jsx(
7890
+ ] }) : (
7891
+ // Try-On stays enabled even when the backend couldn't find a size —
7892
+ // it's a visual preview, independent of the recommendation.
7893
+ /* @__PURE__ */ jsxs(
7861
7894
  "button",
7862
7895
  {
7863
7896
  type: "button",
7864
7897
  className: "ps-msr-tryon-cta",
7865
- onClick: onClose,
7866
- children: t("Continue Shopping")
7898
+ onClick: onTryOn,
7899
+ disabled: tryOnProcessing,
7900
+ children: [
7901
+ /* @__PURE__ */ jsx(CameraIcon, {}),
7902
+ tryOnProcessing ? t("Processing...") : t("See how it looks on yourself")
7903
+ ]
7867
7904
  }
7868
7905
  )
7869
- ) : /* @__PURE__ */ jsxs(
7870
- "button",
7871
- {
7872
- type: "button",
7873
- className: "ps-msr-tryon-cta",
7874
- onClick: onTryOn,
7875
- disabled: tryOnProcessing,
7876
- children: [
7877
- /* @__PURE__ */ jsx(CameraIcon, {}),
7878
- tryOnProcessing ? t("Processing...") : t("See how it looks on yourself")
7879
- ]
7880
- }
7881
7906
  ) }),
7882
7907
  sizeGuide ? null : null
7883
7908
  ] });
@@ -8389,8 +8414,7 @@ function SectionDetailView({
8389
8414
  const hasBadFit = details.some((d) => BAD_FIT.test(d.fit || ""));
8390
8415
  return hasBadFit ? t("Not Recommended") : t("Your Selection");
8391
8416
  }, [isRecommended, sectionResult, t]);
8392
- const noFitMessage = t("We couldn't find a size that fits for this product");
8393
- const displaySizeLabel = sectionFound === false ? noFitMessage : selectedCountry && isRecommended && internationalSizes && internationalSizes[selectedCountry] ? internationalSizes[selectedCountry] : displaySize;
8417
+ const displaySizeLabel = selectedCountry && isRecommended && internationalSizes && internationalSizes[selectedCountry] ? internationalSizes[selectedCountry] : displaySize;
8394
8418
  const columnUnits = useMemo(() => {
8395
8419
  const units = [];
8396
8420
  for (let i = 0; i < section.headers.length; i++) {
@@ -8559,7 +8583,11 @@ function SectionDetailView({
8559
8583
  const showMatchPercent = !renderRaw;
8560
8584
  const secAny = sectionResult;
8561
8585
  const backendSize = secAny?.size || recSize;
8562
- const backendLength = secAny?.length || recLength;
8586
+ const lengthFromDetails = (sectionResult?.matchDetails || []).find(
8587
+ (m) => /inseam|length/i.test(m.measurement) && !/neck|arm|sleeve|back|shoulder/i.test(m.measurement)
8588
+ );
8589
+ const inseamFallback = lengthFromDetails ? (lengthFromDetails.chartRange || "").replace(/\s*(cm|in|inches)\s*/i, "").trim() : "";
8590
+ const backendLength = secAny?.length || recLength || inseamFallback;
8563
8591
  const backendAvailableSizes = secAny?.availableSizes || [];
8564
8592
  const backendAvailableLengths = secAny?.availableLengths || [];
8565
8593
  const finalDisplayLength = selectedLength || backendLength;
@@ -8646,7 +8674,12 @@ function SectionDetailView({
8646
8674
  }
8647
8675
  )
8648
8676
  ] }),
8649
- /* @__PURE__ */ jsxs("div", { className: "ps-msd-card", children: [
8677
+ sectionFound === false && /* @__PURE__ */ jsxs("div", { className: "ps-msd-card", style: { textAlign: "center" }, children: [
8678
+ /* @__PURE__ */ jsx("span", { className: "ps-msd-card-eyebrow", children: t("NO MATCHING SIZE") }),
8679
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "15px", fontWeight: 600, color: "var(--ps-text-primary)", margin: "10px 0 4px" }, children: t("We couldn't find a size that fits for this product.") }),
8680
+ /* @__PURE__ */ jsx("p", { className: "ps-msd-card-note", children: t("Your measurements are outside this product's size range — the chart doesn't include a close match. You can still try it on visually below.") })
8681
+ ] }),
8682
+ sectionFound !== false && /* @__PURE__ */ jsxs("div", { className: "ps-msd-card", children: [
8650
8683
  /* @__PURE__ */ jsx("span", { className: "ps-msd-card-eyebrow", children: displaySize === backendSize ? t("RECOMMENDED SIZE") : t("TRYING SIZE") }),
8651
8684
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "10px", marginTop: "6px", marginBottom: "4px" }, children: [
8652
8685
  countryOptions.length > 1 && /* @__PURE__ */ jsxs(
@@ -8706,7 +8739,7 @@ function SectionDetailView({
8706
8739
  ] }),
8707
8740
  /* @__PURE__ */ jsx("p", { className: "ps-msd-card-note", children: t("Based on your measurements and the garment's tailoring chart.") })
8708
8741
  ] }),
8709
- fitRows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-msd-blueprint", children: [
8742
+ sectionFound !== false && fitRows.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-msd-blueprint", children: [
8710
8743
  /* @__PURE__ */ jsx("span", { className: "ps-msd-blueprint-title", children: t("MEASUREMENT BLUEPRINT") }),
8711
8744
  /* @__PURE__ */ jsx("div", { className: "ps-msd-blueprint-underline" }),
8712
8745
  /* @__PURE__ */ jsx("div", { className: "ps-msd-rows", children: fitRows.map((row, i) => {
@@ -8741,7 +8774,7 @@ function SectionDetailView({
8741
8774
  ] }, i);
8742
8775
  }) })
8743
8776
  ] }),
8744
- visibleSizes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "ps-msd-sizes", children: [
8777
+ sectionFound !== false && visibleSizes.length > 1 && /* @__PURE__ */ jsxs("div", { className: "ps-msd-sizes", children: [
8745
8778
  /* @__PURE__ */ jsx("span", { className: "ps-msd-sizes-label", children: t("TRY ANOTHER SIZE") }),
8746
8779
  /* @__PURE__ */ jsx("div", { className: "ps-msd-sizes-pills", children: visibleSizes.map((s) => {
8747
8780
  const isActive = s === displaySize;
@@ -8757,7 +8790,7 @@ function SectionDetailView({
8757
8790
  );
8758
8791
  }) })
8759
8792
  ] }),
8760
- visibleLengths.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-msd-sizes", children: [
8793
+ sectionFound !== false && visibleLengths.length > 0 && /* @__PURE__ */ jsxs("div", { className: "ps-msd-sizes", children: [
8761
8794
  /* @__PURE__ */ jsx("span", { className: "ps-msd-sizes-label", children: t("LENGTH ADJUSTMENT") }),
8762
8795
  /* @__PURE__ */ jsx("div", { className: "ps-msd-sizes-pills", children: visibleLengths.map((s) => {
8763
8796
  const isActive = finalDisplayLength === s;
@@ -8773,7 +8806,7 @@ function SectionDetailView({
8773
8806
  );
8774
8807
  }) })
8775
8808
  ] }),
8776
- /* @__PURE__ */ jsxs("p", { className: "ps-msd-footer-note", children: [
8809
+ sectionFound !== false && /* @__PURE__ */ jsxs("p", { className: "ps-msd-footer-note", children: [
8777
8810
  t("*Our sizing engine recommends"),
8778
8811
  " ",
8779
8812
  backendSize,
@@ -8784,7 +8817,19 @@ function SectionDetailView({
8784
8817
  ] }) });
8785
8818
  }
8786
8819
  return /* @__PURE__ */ jsxs("div", { className: "ps-tryon-sec-detail", style: { padding: "1.5vw", display: "flex", flexDirection: "column", height: "100%", background: "#F8F9FA" }, children: [
8787
- /* @__PURE__ */ jsxs("div", { style: { flex: 1, minHeight: 0, overflowY: "auto", scrollbarWidth: "thin", scrollbarColor: "rgba(0,0,0,0.04) transparent" }, children: [
8820
+ /* @__PURE__ */ jsx("div", { style: { flex: 1, minHeight: 0, overflowY: "auto", scrollbarWidth: "thin", scrollbarColor: "rgba(0,0,0,0.04) transparent" }, children: sectionFound === false ? /* @__PURE__ */ jsxs("div", { style: {
8821
+ background: "white",
8822
+ borderRadius: "0.7vw",
8823
+ border: "1px solid rgba(0,0,0,0.06)",
8824
+ padding: "1.4vw 1.6vw",
8825
+ textAlign: "center",
8826
+ marginTop: "0.4vw",
8827
+ marginBottom: "1vw"
8828
+ }, children: [
8829
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "0.7vw", fontWeight: 700, color: "var(--ps-accent)", textTransform: "uppercase", letterSpacing: "0.12em" }, children: t("No matching size") }),
8830
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "1vw", fontWeight: 600, color: "var(--ps-text-primary)", margin: "0.6vw 0 0.4vw" }, children: t("We couldn't find a size that fits for this product.") }),
8831
+ /* @__PURE__ */ jsx("p", { style: { fontSize: "0.7vw", color: "var(--ps-text-secondary)", lineHeight: 1.55, margin: 0, maxWidth: "26vw", marginLeft: "auto", marginRight: "auto" }, children: t("Your measurements are outside this product's size range. You can still try it on visually below.") })
8832
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
8788
8833
  /* @__PURE__ */ jsx("span", { style: { fontSize: "0.75vw", fontWeight: 700, color: "var(--ps-accent)", textTransform: "uppercase", letterSpacing: "0.12em" }, children: isRecommended ? t("Recommended Size") : altSizeLabel ?? t("Your Selection") }),
8789
8834
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "0.5vw", marginTop: "0.35vw", marginBottom: "0.4vw" }, children: [
8790
8835
  countryOptions.length > 1 && /* @__PURE__ */ jsxs(
@@ -8919,7 +8964,7 @@ function SectionDetailView({
8919
8964
  );
8920
8965
  }) })
8921
8966
  ] })
8922
- ] }),
8967
+ ] }) }),
8923
8968
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", paddingTop: "0.6vw", borderTop: "1px solid rgba(0,0,0,0.06)", flexShrink: 0 }, children: [
8924
8969
  /* @__PURE__ */ jsxs("button", { className: "ps-bp-back-btn", onClick: onBack, type: "button", style: { fontSize: "0.7vw" }, children: [
8925
8970
  /* @__PURE__ */ jsx("span", { className: "ps-bp-back-arrow", children: "←" }),
@@ -9244,7 +9289,7 @@ function SizeResultView({
9244
9289
  const allDone = hasPhoto ? sizingDone && tryOnDone : sizingDone;
9245
9290
  const isMobile = useIsMobile();
9246
9291
  const isAccessory = measurementType === "face" || measurementType === "head";
9247
- const noFit = sizingResult?.found === false;
9292
+ sizingResult?.found === false;
9248
9293
  const vtoExcluded = measurementType === "foot";
9249
9294
  console.log("[PS-SDK] SizeResultView render:", {
9250
9295
  hasPhoto,
@@ -9525,7 +9570,7 @@ function SizeResultView({
9525
9570
  " →"
9526
9571
  ]
9527
9572
  }
9528
- ) : vtoExcluded || noFit ? /* @__PURE__ */ jsxs(
9573
+ ) : vtoExcluded ? /* @__PURE__ */ jsxs(
9529
9574
  "button",
9530
9575
  {
9531
9576
  className: "ps-tryon-v2-cta",
@@ -9586,7 +9631,7 @@ function SizeResultView({
9586
9631
  onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
9587
9632
  backLabel: t("Back"),
9588
9633
  internationalSizes: sizingResult?.internationalSizes,
9589
- onTryOn: resultImageUrl || vtoExcluded || noFit ? void 0 : handleSingleTryOn,
9634
+ onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
9590
9635
  continueLabel: resultImageUrl ? t("Continue Shopping") : void 0,
9591
9636
  tryOnProcessing,
9592
9637
  productImage: resultImageUrl || productImage,
@@ -9634,7 +9679,7 @@ function SizeResultView({
9634
9679
  onBack: resultImageUrl ? onClose || (() => setView("body-profile")) : () => setView("body-profile"),
9635
9680
  backLabel: t("Back"),
9636
9681
  internationalSizes: sizingResult?.internationalSizes,
9637
- onTryOn: resultImageUrl || vtoExcluded || noFit ? void 0 : handleSingleTryOn,
9682
+ onTryOn: resultImageUrl || vtoExcluded ? void 0 : handleSingleTryOn,
9638
9683
  continueLabel: resultImageUrl ? t("Continue Shopping") : void 0,
9639
9684
  tryOnProcessing,
9640
9685
  t,
@@ -14330,7 +14375,14 @@ function PrimeStyleTryonInner({
14330
14375
  }
14331
14376
  }, [apiUrl, sizingMethod, sizingCountry, heightUnit, weightUnit, sizingUnit, sizeGuide, productTitle, dynamicFields, persistResultToProfile]);
14332
14377
  const handleQuickEstimate = useCallback(async (height, weight, heightUnit2, weightUnit2, gender, age, bodyType, chestProfile, midsectionProfile, hipProfile, bodyImage) => {
14333
- if (!apiRef.current) return;
14378
+ if (!apiRef.current) {
14379
+ const msg = t("SDK not configured. Please refresh and try again.");
14380
+ console.warn("[ps-sdk] handleQuickEstimate BAILED — apiRef is null. API key not loaded.");
14381
+ setErrorMessage(msg);
14382
+ setView("error");
14383
+ onError?.({ message: msg, code: "SDK_NOT_CONFIGURED" });
14384
+ return;
14385
+ }
14334
14386
  getApiUrl(apiUrl);
14335
14387
  getApiKey();
14336
14388
  const SKIP_ESTIMATE_KEYS = /* @__PURE__ */ new Set(["weight", "weightKg", "height", "heightCm"]);
@@ -14387,7 +14439,11 @@ function PrimeStyleTryonInner({
14387
14439
  apiUrl
14388
14440
  });
14389
14441
  if (!apiRef.current || !sseRef.current) {
14442
+ const msg = t("SDK not configured. Please refresh and try again.");
14390
14443
  console.warn("[ps-sdk] handleSnapSubmit BAILED — apiRef or sseRef is null. Check api init.");
14444
+ setErrorMessage(msg);
14445
+ setView("error");
14446
+ onError?.({ message: msg, code: "SDK_NOT_CONFIGURED" });
14391
14447
  return;
14392
14448
  }
14393
14449
  const baseUrl = getApiUrl(apiUrl);